mirror of
https://github.com/bancika/diy-layout-creator
synced 2026-11-08 12:54:55 +10:00
Compare commits
77 Commits
v5.5.0
...
explorer-tree
| Author | SHA1 | Date | |
|---|---|---|---|
| 525024ac56 | |||
| 2383f9ae77 | |||
| 14ec18dfef | |||
| 2f492c8fa8 | |||
| 9db43cd2f4 | |||
| 344c238f73 | |||
| 3376aa995a | |||
| ec75f1b6eb | |||
| 5f2b20ea41 | |||
| 9d130c0ff3 | |||
| 119c41dc86 | |||
| c5613a6fea | |||
| 479bd50540 | |||
| 502a4b8d39 | |||
| 1f85c07453 | |||
| 53ec1e780e | |||
| 2de3def32e | |||
| 61b3c63bbf | |||
| 348eb3ac00 | |||
| 9972e0ee46 | |||
| de971b707b | |||
| 0dcc06f712 | |||
| 109b0788a8 | |||
| d190ebe208 | |||
| fa93af2707 | |||
| 6b59966816 | |||
| b065c9d6fb | |||
| 49d805a60c | |||
| a9a98bf3a4 | |||
| 840d416282 | |||
| dc2cc4c7c8 | |||
| 3d51d2a38d | |||
| 1a31819431 | |||
| 4dfb1ca630 | |||
| e9c8686960 | |||
| 9be97f5213 | |||
| 721d582dcd | |||
| 443f76f3f9 | |||
| 3ad9c49f53 | |||
| 61077ed5a4 | |||
| 1fe3706d0f | |||
| 16df8fd173 | |||
| f7806d4d3c | |||
| 04e44e174e | |||
| f74fe19b77 | |||
| e7487069a5 | |||
| 9c48b80515 | |||
| 814f4296a9 | |||
| 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 |
@@ -147,3 +147,10 @@ buildNumber.properties
|
||||
|
||||
# Avoid ignoring Maven wrapper jar file (.jar files are usually ignored)
|
||||
!/.mvn/wrapper/maven-wrapper.jar
|
||||
|
||||
######################
|
||||
###### Custom ######
|
||||
######################
|
||||
|
||||
# Deployment folder
|
||||
deploy/
|
||||
|
||||
Executable
+123
@@ -0,0 +1,123 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Script to build all Maven profiles and copy zip files to deployment folder
|
||||
# Usage: ./build-all-profiles.sh [to-deploy-folder]
|
||||
|
||||
set -e # Exit on error
|
||||
|
||||
# Parse arguments
|
||||
DEPLOY_DIR="${1:-}"
|
||||
|
||||
# Configuration
|
||||
PROJECT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
SWING_MODULE_DIR="${PROJECT_DIR}/diylc-swing"
|
||||
DEPLOY_DIR="${DEPLOY_DIR:-${PROJECT_DIR}/deploy}"
|
||||
|
||||
# List of profiles to build
|
||||
PROFILES=(
|
||||
"universal-zip"
|
||||
"linux-appimage"
|
||||
"win64-zip"
|
||||
"win64-installer"
|
||||
"mac-x86"
|
||||
"mac-arm"
|
||||
)
|
||||
|
||||
echo "=========================================="
|
||||
echo "Building all Maven profiles"
|
||||
echo "=========================================="
|
||||
echo "Project directory: ${PROJECT_DIR}"
|
||||
echo "Swing module directory: ${SWING_MODULE_DIR}"
|
||||
echo "Deployment directory: ${DEPLOY_DIR}"
|
||||
echo "Tests: Skipped"
|
||||
echo ""
|
||||
|
||||
# Create deployment directory if it doesn't exist
|
||||
mkdir -p "${DEPLOY_DIR}"
|
||||
|
||||
# Change to project root directory
|
||||
cd "${PROJECT_DIR}"
|
||||
|
||||
# Build each profile (clean before each one)
|
||||
TARGET_DIR="${SWING_MODULE_DIR}/target"
|
||||
TOTAL_ZIP_COUNT=0
|
||||
|
||||
for profile in "${PROFILES[@]}"; do
|
||||
echo "----------------------------------------"
|
||||
echo "Building profile: ${profile}"
|
||||
echo "----------------------------------------"
|
||||
|
||||
# Clean and build from root, specifying the swing module and profile
|
||||
# This ensures dependencies (diylc-core, diylc-library) are built first
|
||||
mvn clean package -pl diylc-swing -am -P"${profile}" -DskipTests || {
|
||||
echo "ERROR: Failed to build profile ${profile}"
|
||||
echo "Continuing with next profile..."
|
||||
continue
|
||||
}
|
||||
|
||||
echo "✓ Profile ${profile} built successfully"
|
||||
|
||||
# Copy zip and exe files immediately after each build (before next clean)
|
||||
if [ -d "${TARGET_DIR}" ]; then
|
||||
echo "Looking for artifacts in ${TARGET_DIR}..."
|
||||
PROFILE_FILE_COUNT=0
|
||||
|
||||
# Find all zip files in target directory (excluding DIYLC-notarize.zip)
|
||||
while IFS= read -r -d '' zipfile; do
|
||||
filename=$(basename "${zipfile}")
|
||||
# Skip DIYLC-notarize.zip files
|
||||
if [[ "${filename}" != "DIYLC-notarize.zip" ]] && [ -f "${zipfile}" ]; then
|
||||
dest="${DEPLOY_DIR}/${filename}"
|
||||
|
||||
echo " → Copying: ${filename}"
|
||||
cp "${zipfile}" "${dest}"
|
||||
TOTAL_ZIP_COUNT=$((TOTAL_ZIP_COUNT + 1))
|
||||
PROFILE_FILE_COUNT=$((PROFILE_FILE_COUNT + 1))
|
||||
fi
|
||||
done < <(find "${TARGET_DIR}" -maxdepth 1 -type f -name "*.zip" -print0 2>/dev/null)
|
||||
|
||||
# Find all exe files in target directory
|
||||
while IFS= read -r -d '' exefile; do
|
||||
if [ -f "${exefile}" ]; then
|
||||
filename=$(basename "${exefile}")
|
||||
dest="${DEPLOY_DIR}/${filename}"
|
||||
|
||||
echo " → Copying: ${filename}"
|
||||
cp "${exefile}" "${dest}"
|
||||
TOTAL_ZIP_COUNT=$((TOTAL_ZIP_COUNT + 1))
|
||||
PROFILE_FILE_COUNT=$((PROFILE_FILE_COUNT + 1))
|
||||
fi
|
||||
done < <(find "${TARGET_DIR}" -maxdepth 1 -type f -name "*.exe" -print0 2>/dev/null)
|
||||
|
||||
if [ ${PROFILE_FILE_COUNT} -eq 0 ]; then
|
||||
echo " ⚠ Warning: No artifacts found for profile ${profile}"
|
||||
# List what's actually in the target directory for debugging
|
||||
echo " Debug: Files in target directory:"
|
||||
ls -la "${TARGET_DIR}"/*.{zip,exe} 2>/dev/null | head -5 || echo " (no zip/exe files found)"
|
||||
else
|
||||
echo " ✓ Copied ${PROFILE_FILE_COUNT} file(s) from ${profile}"
|
||||
fi
|
||||
else
|
||||
echo " ⚠ Warning: Target directory ${TARGET_DIR} does not exist"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
done
|
||||
|
||||
echo "=========================================="
|
||||
echo "Copying summary"
|
||||
echo "=========================================="
|
||||
if [ ${TOTAL_ZIP_COUNT} -eq 0 ]; then
|
||||
echo "No artifacts were copied"
|
||||
else
|
||||
echo "✓ Copied ${TOTAL_ZIP_COUNT} file(s) total to ${DEPLOY_DIR}"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "=========================================="
|
||||
echo "Build and deployment complete!"
|
||||
echo "=========================================="
|
||||
echo "Deployment folder: ${DEPLOY_DIR}"
|
||||
echo "Files:"
|
||||
ls -lh "${DEPLOY_DIR}"/*.{zip,exe} 2>/dev/null || echo " (no zip/exe files found)"
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
<parent>
|
||||
<groupId>org.diylc</groupId>
|
||||
<artifactId>diylc-parent</artifactId>
|
||||
<version>5.5.0</version>
|
||||
<version>5.8.0</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>diylc-core</artifactId>
|
||||
|
||||
@@ -68,5 +68,9 @@ public enum DrawOption {
|
||||
/**
|
||||
* Whether locked components should be rendered as transparent or not.
|
||||
*/
|
||||
LOCKED_ALPHA;
|
||||
LOCKED_ALPHA,
|
||||
/**
|
||||
* When applied, all colors will be converted to blackest black or whitest white
|
||||
*/
|
||||
MONOCHROMATIC;
|
||||
}
|
||||
|
||||
@@ -21,7 +21,6 @@
|
||||
*/
|
||||
package org.diylc.common;
|
||||
|
||||
import java.awt.Rectangle;
|
||||
import java.awt.geom.Point2D;
|
||||
|
||||
import org.diylc.core.Project;
|
||||
@@ -55,12 +54,7 @@ public enum EventType {
|
||||
*/
|
||||
FILE_STATUS_CHANGED,
|
||||
/**
|
||||
* Called when selection rectangle is changed. Object of type {@link Rectangle} is the only
|
||||
* parameter and it contains the new selection rectangle.
|
||||
*/
|
||||
SELECTION_RECT_CHANGED,
|
||||
/**
|
||||
* Called when component selection is changed. New {@link ComponentSelection} is attached as a
|
||||
* Called when component selection is changed. New list of components and control point map is attached as a
|
||||
* parameter.
|
||||
*/
|
||||
SELECTION_CHANGED,
|
||||
|
||||
@@ -41,16 +41,18 @@ import org.diylc.core.IDIYComponent;
|
||||
import org.diylc.core.Project;
|
||||
import org.diylc.core.Template;
|
||||
import org.diylc.core.Theme;
|
||||
import org.diylc.core.measures.ResizeDimensions;
|
||||
import org.diylc.core.measures.Size;
|
||||
import org.diylc.filter.IComponentFilter;
|
||||
import org.diylc.test.ITestProcessor;
|
||||
|
||||
/**
|
||||
* Interface for communication between plug-ins and the application. Plug-ins may acquire
|
||||
* information or make changes through this interface. Application events are dispatched to plug-ins
|
||||
* through {@link MessageDispatcher}
|
||||
*
|
||||
*
|
||||
* @author Branislav Stojkovic
|
||||
*
|
||||
*
|
||||
* @see IPlugIn
|
||||
* @see MessageDispatcher
|
||||
* @see IMessageListener
|
||||
@@ -84,18 +86,20 @@ public interface IPlugInPort extends ISelectionProcessor, IMouseProcessor, IKeyP
|
||||
public static final String RENUMBER_ON_PASTE_KEY = "renumberOnPaste";
|
||||
public static final String RULER_IN_SUBDIVISION_KEY = "rulerInSubdivision";
|
||||
public static final String CACHING_ENABLED_KEY = "cachingEnabled";
|
||||
|
||||
public static final String FONT_SCALE_FACTOR_KEY = "fontScaleFactor";
|
||||
public static final String SHOW_RESIZE_DIMENSIONS_TOOLTIP_KEY = "showResizeDimensionsTooltip";
|
||||
|
||||
public static final String RULER_IN_SUBDIVISION_2 = "base of 2";
|
||||
public static final String RULER_IN_SUBDIVISION_10 = "base of 10";
|
||||
public static final String RULER_IN_SUBDIVISION_DEFAULT = RULER_IN_SUBDIVISION_10;
|
||||
|
||||
|
||||
public static final String SNAP_TO_NONE = "None";
|
||||
public static final String SNAP_TO_GRID = "Grid";
|
||||
public static final String SNAP_TO_COMPONENTS = "Components";
|
||||
public static final String SNAP_TO_DEFAULT = SNAP_TO_GRID;
|
||||
|
||||
|
||||
public static final String LOCKED_ALPHA = "lockedAlpha";
|
||||
|
||||
|
||||
public static final String LANGUAGE = "language";
|
||||
public static final String LANGUAGE_DEFAULT = "English";
|
||||
|
||||
@@ -108,25 +112,25 @@ public interface IPlugInPort extends ISelectionProcessor, IMouseProcessor, IKeyP
|
||||
public static final int NOBUTTON = 0;
|
||||
public static final int BUTTON1 = 1;
|
||||
public static final int BUTTON2 = 2;
|
||||
public static final int BUTTON3 = 3;
|
||||
public static final int BUTTON3 = 3;
|
||||
|
||||
/**
|
||||
* Returns size of the canvas that takes project dimensions into account as well as zoom level.
|
||||
* Each dimension is calculated as the product of the actual size and the number of pixels per
|
||||
* unit. If <code>useZoom</code> is set to true, the result is scaled by zoom factor.
|
||||
*
|
||||
*
|
||||
* @param useZoom
|
||||
* @param includeExtraSpace
|
||||
*
|
||||
*
|
||||
* @return canvas dimensions
|
||||
*/
|
||||
Dimension getCanvasDimensions(boolean useZoom, boolean includeExtraSpace);
|
||||
|
||||
/**
|
||||
* Returns an instance of {@link Cursor} that should be used at the specified location.
|
||||
*
|
||||
*
|
||||
* Note: point coordinates are scaled for zoom factor.
|
||||
*
|
||||
*
|
||||
* @param point
|
||||
* @param ctrlDown
|
||||
* @param shiftDown
|
||||
@@ -137,14 +141,14 @@ public interface IPlugInPort extends ISelectionProcessor, IMouseProcessor, IKeyP
|
||||
|
||||
/**
|
||||
* Returns an instance of currently loaded project.
|
||||
*
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
Project getCurrentProject();
|
||||
|
||||
/**
|
||||
* Loads specified {@link Project}.
|
||||
*
|
||||
*
|
||||
* @param project
|
||||
* @param freshStart
|
||||
* @param filename file name when loading from a file
|
||||
@@ -158,22 +162,22 @@ public interface IPlugInPort extends ISelectionProcessor, IMouseProcessor, IKeyP
|
||||
|
||||
/**
|
||||
* Loads a project from the specified file.
|
||||
*
|
||||
*
|
||||
* @param fileName
|
||||
*/
|
||||
void loadProjectFromFile(String fileName);
|
||||
|
||||
/**
|
||||
* Saves the current project into the specified file.
|
||||
*
|
||||
*
|
||||
* @param fileName
|
||||
* @param isBackup
|
||||
*/
|
||||
void saveProjectToFile(String fileName, boolean isBackup);
|
||||
|
||||
|
||||
/**
|
||||
* Exports the current project into one or more files - one per layer and potentially drill layer.
|
||||
*
|
||||
*
|
||||
* @param fileNameBase
|
||||
* @param g2d
|
||||
*/
|
||||
@@ -191,7 +195,7 @@ public interface IPlugInPort extends ISelectionProcessor, IMouseProcessor, IKeyP
|
||||
|
||||
/**
|
||||
* Shows a user dialog if there are changes to confirm that it's safe to proceed.
|
||||
*
|
||||
*
|
||||
* @return true, if file actions (new, open, close) can be taken
|
||||
*/
|
||||
boolean allowFileAction();
|
||||
@@ -200,7 +204,7 @@ public interface IPlugInPort extends ISelectionProcessor, IMouseProcessor, IKeyP
|
||||
* Returns all available {@link ComponentType}s classified by category. Result is a {@link Map}
|
||||
* between category name to a {@link List} of all {@link ComponentType}s that share that category
|
||||
* name.
|
||||
*
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
Map<String, List<ComponentType>> getComponentTypes();
|
||||
@@ -208,7 +212,7 @@ public interface IPlugInPort extends ISelectionProcessor, IMouseProcessor, IKeyP
|
||||
/**
|
||||
* Draws project on the provided {@link Graphics2D}. If the provided filter is not null, it will
|
||||
* be used to filter the components that are shown.
|
||||
*
|
||||
*
|
||||
* @param g2d
|
||||
* @param drawOptions specific drawing options
|
||||
* @param filter
|
||||
@@ -222,35 +226,35 @@ public interface IPlugInPort extends ISelectionProcessor, IMouseProcessor, IKeyP
|
||||
|
||||
/**
|
||||
* Returns current zoom level where <code>zoomLevel = 1.0d</code> means 100%.
|
||||
*
|
||||
*
|
||||
* @return current zoom level
|
||||
*/
|
||||
double getZoomLevel();
|
||||
|
||||
/**
|
||||
* Changes current zoom level where <code>zoomLevel = 1.0d</code> means 100%
|
||||
*
|
||||
*
|
||||
* @param zoomLevel new zoom leve
|
||||
*/
|
||||
void setZoomLevel(double zoomLevel);
|
||||
|
||||
/**
|
||||
* Returns current version number.
|
||||
*
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
VersionNumber getCurrentVersionNumber();
|
||||
|
||||
|
||||
/**
|
||||
* Returns few most recent versions.
|
||||
*
|
||||
* Returns few most recent versions.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
List<Version> getRecentUpdates();
|
||||
|
||||
/**
|
||||
* Adds a list of components to the project.
|
||||
*
|
||||
*
|
||||
* @param componentTransferable
|
||||
* @param autoGroup
|
||||
* @param assignNewNames
|
||||
@@ -269,7 +273,7 @@ public interface IPlugInPort extends ISelectionProcessor, IMouseProcessor, IKeyP
|
||||
|
||||
/**
|
||||
* Sets default value for the specified property name for currently selected component types.
|
||||
*
|
||||
*
|
||||
* @param propertyName display name for property
|
||||
* @param value new default value, must not be null
|
||||
*/
|
||||
@@ -277,7 +281,7 @@ public interface IPlugInPort extends ISelectionProcessor, IMouseProcessor, IKeyP
|
||||
|
||||
/**
|
||||
* Sets default value for the specified property name for projects.
|
||||
*
|
||||
*
|
||||
* @param clazz class to set defaults to
|
||||
* @param propertyName display name for property
|
||||
* @param value new default value, must not be null
|
||||
@@ -291,6 +295,15 @@ public interface IPlugInPort extends ISelectionProcessor, IMouseProcessor, IKeyP
|
||||
*/
|
||||
List<PropertyWrapper> getMutualSelectionProperties();
|
||||
|
||||
/**
|
||||
* Available only during resizing, returns the dimensions of the component being resized.
|
||||
* Otherwise returns null.
|
||||
*
|
||||
* @return ResizeDimensions with width, height (in mm or in depending on configuration),
|
||||
* and length (if component implements IHaveLength), or null if not resizing
|
||||
*/
|
||||
ResizeDimensions getResizeDimensions();
|
||||
|
||||
void editSelection();
|
||||
|
||||
/**
|
||||
|
||||
@@ -37,16 +37,14 @@ import com.thoughtworks.xstream.annotations.XStreamOmitField;
|
||||
import org.diylc.common.IPlugInPort;
|
||||
import org.diylc.common.LineStyle;
|
||||
import org.diylc.common.ObjectCache;
|
||||
import org.diylc.core.ComponentState;
|
||||
import org.diylc.core.IDrawingObserver;
|
||||
import org.diylc.core.Project;
|
||||
import org.diylc.core.VisibilityPolicy;
|
||||
import org.diylc.core.*;
|
||||
import org.diylc.core.annotations.EditableProperty;
|
||||
import org.diylc.core.measures.Size;
|
||||
import org.diylc.core.measures.SizeUnit;
|
||||
import org.diylc.utils.Constants;
|
||||
|
||||
public abstract class AbstractCurvedComponent<T> extends AbstractTransparentComponent<T> {
|
||||
public abstract class AbstractCurvedComponent<T> extends AbstractTransparentComponent<T> implements
|
||||
IHaveLength {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@@ -337,8 +335,8 @@ public abstract class AbstractCurvedComponent<T> extends AbstractTransparentComp
|
||||
// }
|
||||
}
|
||||
|
||||
@EditableProperty
|
||||
public Size getLength() {
|
||||
@EditableProperty(name = "Length")
|
||||
public Size calculateLength() {
|
||||
double d = 0;
|
||||
try {
|
||||
Point2D[] p = getControlPoints();
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
|
||||
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.core;
|
||||
|
||||
import org.diylc.core.measures.Size;
|
||||
|
||||
public interface IHaveLength {
|
||||
|
||||
Size calculateLength();
|
||||
}
|
||||
@@ -300,7 +300,7 @@ public class GerberG2DWrapper extends Graphics2D
|
||||
|
||||
@Override
|
||||
public AffineTransform getTransform() {
|
||||
return tx;
|
||||
return new AffineTransform(tx);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
*
|
||||
* 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.core.measures;
|
||||
|
||||
/**
|
||||
* Record containing resize dimensions for a component being resized.
|
||||
*
|
||||
* @param width the width of the component (in mm or in, depending on configuration)
|
||||
* @param height the height of the component (in mm or in, depending on configuration)
|
||||
* @param length the length of the component (if it implements IHaveLength), null otherwise
|
||||
*/
|
||||
public record ResizeDimensions(Size width, Size height, Size length) {
|
||||
}
|
||||
|
||||
+1
-1
@@ -19,7 +19,7 @@
|
||||
along with DIYLC. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
*/
|
||||
package org.diylc.common;
|
||||
package org.diylc.filter;
|
||||
|
||||
import org.diylc.core.IDIYComponent;
|
||||
|
||||
+1
-2
@@ -19,11 +19,10 @@
|
||||
along with DIYLC. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
*/
|
||||
package org.diylc.presenter;
|
||||
package org.diylc.filter;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import org.diylc.common.IComponentFilter;
|
||||
import org.diylc.common.PCBLayer;
|
||||
import org.diylc.core.IDIYComponent;
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
package org.diylc.filter;
|
||||
|
||||
import org.diylc.core.IBoard;
|
||||
import org.diylc.core.IDIYComponent;
|
||||
import org.diylc.core.gerber.GerberRenderMode;
|
||||
import org.diylc.core.gerber.IGerberComponent;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
public class SilkScreenFilter implements IComponentFilter {
|
||||
|
||||
@Override
|
||||
public boolean testComponent(IDIYComponent<?> component) {
|
||||
if (component instanceof IBoard board) {
|
||||
return board.shouldExportToGerber();
|
||||
}
|
||||
if (!(component instanceof IGerberComponent gerberComponent)) {
|
||||
return false;
|
||||
}
|
||||
Set<GerberRenderMode> gerberRenderModes = gerberComponent.getGerberRenderModes();
|
||||
return gerberRenderModes.contains(GerberRenderMode.Outline);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
*/
|
||||
package org.diylc.presenter;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.geom.Point2D;
|
||||
import java.util.List;
|
||||
|
||||
@@ -71,4 +72,54 @@ public class CalcUtils {
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the relative luminance (perceived brightness) of a color.
|
||||
* Uses the standard formula: L = 0.299*R + 0.587*G + 0.114*B
|
||||
*
|
||||
* @param color the color to calculate luminance for
|
||||
* @return the luminance value between 0 and 255
|
||||
*/
|
||||
public static double calculateLuminance(Color color) {
|
||||
if (color == null) {
|
||||
return 0.0;
|
||||
}
|
||||
return 0.299 * color.getRed() + 0.587 * color.getGreen() + 0.114 * color.getBlue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a color to monochrome (black or white) based on its luminance.
|
||||
* Colors with luminance below the threshold (128) are converted to black,
|
||||
* otherwise to white. The alpha channel is preserved if present.
|
||||
*
|
||||
* @param color the color to convert
|
||||
* @param threshold the luminance threshold (default 128 for middle gray)
|
||||
* @return black or white color, preserving alpha channel
|
||||
*/
|
||||
public static Color convertToMonochrome(Color color, double threshold) {
|
||||
if (color == null) {
|
||||
return null;
|
||||
}
|
||||
double luminance = calculateLuminance(color);
|
||||
// Use <= to handle edge case where luminance equals threshold (should be white)
|
||||
// Add small epsilon to account for floating point precision issues
|
||||
Color monochromeColor = (luminance < threshold - 1e-10) ? Color.BLACK : Color.WHITE;
|
||||
// Preserve alpha channel if present
|
||||
if (color.getAlpha() < 255) {
|
||||
monochromeColor = new Color(monochromeColor.getRed(), monochromeColor.getGreen(),
|
||||
monochromeColor.getBlue(), color.getAlpha());
|
||||
}
|
||||
return monochromeColor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a color to monochrome (black or white) based on its luminance.
|
||||
* Uses the default threshold of 128 (middle gray).
|
||||
*
|
||||
* @param color the color to convert
|
||||
* @return black or white color, preserving alpha channel
|
||||
*/
|
||||
public static Color convertToMonochrome(Color color) {
|
||||
return convertToMonochrome(color, 128.0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,10 +26,12 @@ import java.lang.ref.SoftReference;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
import org.apache.log4j.Logger;
|
||||
|
||||
import org.diylc.common.ComponentType;
|
||||
import org.diylc.common.DrawOption;
|
||||
import org.diylc.core.ComponentForRender;
|
||||
import org.diylc.core.ComponentState;
|
||||
import org.diylc.core.IDIYComponent;
|
||||
@@ -67,14 +69,14 @@ public class DrawingCache {
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public void bulkPrepare(Collection<ComponentForRender> components, G2DWrapper g2d, boolean outlineMode, Project project,
|
||||
double zoom, double scaleFactor) {
|
||||
double zoom, double scaleFactor, Set<DrawOption> drawOptions) {
|
||||
// run all renders in parallel to utilize all CPU cores
|
||||
components.stream().parallel().forEach(x -> {
|
||||
ComponentType type = ComponentProcessor.getInstance()
|
||||
.extractComponentTypeFrom((Class<? extends IDIYComponent<?>>) x.getComponent().getClass());
|
||||
|
||||
if (type != null && type.getEnableCache()) {
|
||||
renderAndCache(x.getComponent(), g2d, x.getState(), outlineMode, project, zoom, scaleFactor, x.getZOrder());
|
||||
renderAndCache(x.getComponent(), g2d, x.getState(), outlineMode, project, zoom, scaleFactor, x.getZOrder(), drawOptions);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -92,7 +94,7 @@ public class DrawingCache {
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public void draw(IDIYComponent<?> component, G2DWrapper g2d, ComponentState componentState,
|
||||
boolean outlineMode, Project project, double zoom, double scaleFactor, int zOrder, Rectangle2D visibleRect) {
|
||||
boolean outlineMode, Project project, double zoom, double scaleFactor, int zOrder, Rectangle2D visibleRect, Set<DrawOption> drawOptions) {
|
||||
ComponentType type = ComponentProcessor.getInstance()
|
||||
.extractComponentTypeFrom((Class<? extends IDIYComponent<?>>) component.getClass());
|
||||
|
||||
@@ -100,7 +102,7 @@ public class DrawingCache {
|
||||
// if we need to apply caching
|
||||
Point2D firstPoint = component.getControlPoint(0);
|
||||
|
||||
CacheValue value = renderAndCache(component, g2d, componentState, outlineMode, project, zoom, scaleFactor, zOrder);
|
||||
CacheValue value = renderAndCache(component, g2d, componentState, outlineMode, project, zoom, scaleFactor, zOrder, drawOptions);
|
||||
|
||||
if (value == null) {
|
||||
return;
|
||||
@@ -180,7 +182,7 @@ public class DrawingCache {
|
||||
}
|
||||
|
||||
private CacheValue renderAndCache(IDIYComponent<?> component, G2DWrapper g2d, ComponentState componentState,
|
||||
boolean outlineMode, Project project, double zoom, double scaleFactor, int zOrder) {
|
||||
boolean outlineMode, Project project, double zoom, double scaleFactor, int zOrder, Set<DrawOption> drawOptions) {
|
||||
|
||||
if (!g2d.getClipBounds().intersects(component.getCachingBounds())) {
|
||||
return null;
|
||||
@@ -199,9 +201,11 @@ public class DrawingCache {
|
||||
}
|
||||
|
||||
// only honor the cache if the component hasn't changed in the meantime
|
||||
// Also check if drawOptions changed (e.g., MONOCHROMATIC affects rendering)
|
||||
boolean drawOptionsChanged = value == null || !value.getDrawOptions().equals(drawOptions);
|
||||
if (value == null || !value.getComponent().equalsTo(component)
|
||||
|| value.getState() != componentState || value.getZoom() != zoom || value.getOutlineMode() != outlineMode
|
||||
|| value.getScaleFactor() != scaleFactor) {
|
||||
|| value.getScaleFactor() != scaleFactor || drawOptionsChanged) {
|
||||
// figure out size and placement of buffer image
|
||||
Rectangle2D rect = component.getCachingBounds();
|
||||
int width = (int)Math.round(rect.getWidth() * zoom);
|
||||
@@ -233,7 +237,7 @@ public class DrawingCache {
|
||||
cg2d.translate(-firstPoint.getX() + dx, -firstPoint.getY() + dy);
|
||||
|
||||
// wrap the graphics so we can track
|
||||
G2DWrapper wrapper = new G2DWrapper(cg2d, zoom);
|
||||
G2DWrapper wrapper = new G2DWrapper(cg2d, zoom, drawOptions);
|
||||
|
||||
// initialize wrapper
|
||||
wrapper.startedDrawingComponent(zOrder);
|
||||
@@ -275,7 +279,7 @@ public class DrawingCache {
|
||||
|
||||
// add to the cache
|
||||
value = new CacheValue(component, image, wrapper.getCurrentArea(), wrapper.getContinuityPositiveAreas(),
|
||||
wrapper.getContinuityNegativeAreas(), componentState, outlineMode, zoom, scaleFactor, dx, dy, rect);
|
||||
wrapper.getContinuityNegativeAreas(), componentState, outlineMode, zoom, scaleFactor, dx, dy, rect, drawOptions);
|
||||
imageCache.put(component, new SoftReference<DrawingCache.CacheValue>(value));
|
||||
}
|
||||
|
||||
@@ -295,11 +299,12 @@ public class DrawingCache {
|
||||
int dx;
|
||||
int dy;
|
||||
Rectangle2D cacheBounds;
|
||||
Set<DrawOption> drawOptions;
|
||||
|
||||
public CacheValue(IDIYComponent<?> component, BufferedImage image, Area currentArea,
|
||||
Map<String, Area> continuityPositiveAreas, Map<String, Area> continuityNegativeAreas,
|
||||
ComponentState state, boolean outlineMode, double zoom, double scaleFactor, int dx, int dy,
|
||||
Rectangle2D cacheBounds) {
|
||||
Rectangle2D cacheBounds, Set<DrawOption> drawOptions) {
|
||||
super();
|
||||
this.cacheBounds = cacheBounds;
|
||||
// take a copy of the component, so we can check if it changed in the meantime
|
||||
@@ -317,6 +322,7 @@ public class DrawingCache {
|
||||
this.dx = dx;
|
||||
this.dy = dy;
|
||||
this.scaleFactor = scaleFactor;
|
||||
this.drawOptions = drawOptions;
|
||||
}
|
||||
|
||||
public IDIYComponent<?> getComponent() {
|
||||
@@ -366,6 +372,10 @@ public class DrawingCache {
|
||||
public Rectangle2D getCacheBounds() {
|
||||
return cacheBounds;
|
||||
}
|
||||
|
||||
public Set<DrawOption> getDrawOptions() {
|
||||
return drawOptions;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
|
||||
@@ -54,6 +54,7 @@ import org.diylc.components.AbstractTransparentComponent;
|
||||
import org.diylc.core.*;
|
||||
import org.diylc.core.measures.Size;
|
||||
import org.diylc.core.measures.SizeUnit;
|
||||
import org.diylc.filter.IComponentFilter;
|
||||
import org.diylc.netlist.ContinuityGraph;
|
||||
import org.diylc.netlist.NetlistBuilder;
|
||||
import org.diylc.utils.Constants;
|
||||
@@ -183,7 +184,7 @@ public class DrawingManager {
|
||||
if (externalZoom != null)
|
||||
zoom *= externalZoom;
|
||||
|
||||
G2DWrapper g2dWrapper = new G2DWrapper(g2d, zoom);
|
||||
G2DWrapper g2dWrapper = new G2DWrapper(g2d, zoom, drawOptions);
|
||||
|
||||
configureRenderingHints(g2d, drawOptions);
|
||||
|
||||
@@ -267,7 +268,7 @@ public class DrawingManager {
|
||||
|
||||
// give the cache a chance to prepare all the components in multiple threads
|
||||
if (drawOptions.contains(DrawOption.ENABLE_CACHING)) {
|
||||
DrawingCache.Instance.bulkPrepare(componentForRenderList, g2dWrapper, outlineMode, project, zoom, scaleFactor);
|
||||
DrawingCache.Instance.bulkPrepare(componentForRenderList, g2dWrapper, outlineMode, project, zoom, scaleFactor, drawOptions);
|
||||
}
|
||||
|
||||
// componentAreaMap.clear();
|
||||
@@ -561,10 +562,12 @@ public class DrawingManager {
|
||||
}
|
||||
}
|
||||
|
||||
private static void drawComponent(Project project, Set<DrawOption> drawOptions, Double scaleFactor, Rectangle2D visibleRect, IDIYComponent<?> component, G2DWrapper g2dWrapper, ComponentState state, double zoom, int i, boolean outlineMode) {
|
||||
private static void drawComponent(Project project, Set<DrawOption> drawOptions, Double scaleFactor,
|
||||
Rectangle2D visibleRect, IDIYComponent<?> component, G2DWrapper g2dWrapper, ComponentState state,
|
||||
double zoom, int i, boolean outlineMode) {
|
||||
if (drawOptions.contains(DrawOption.ENABLE_CACHING)) // go through the DrawingCache
|
||||
DrawingCache.Instance.draw(component, g2dWrapper, state,
|
||||
drawOptions.contains(DrawOption.OUTLINE_MODE), project, zoom, scaleFactor, i, visibleRect);
|
||||
drawOptions.contains(DrawOption.OUTLINE_MODE), project, zoom, scaleFactor, i, visibleRect, drawOptions);
|
||||
else // go straight to the wrapper
|
||||
component.draw(g2dWrapper, state, outlineMode, project, g2dWrapper);
|
||||
}
|
||||
|
||||
@@ -53,17 +53,11 @@ import java.awt.image.renderable.RenderableImage;
|
||||
import java.io.PrintWriter;
|
||||
import java.io.StringWriter;
|
||||
import java.text.AttributedCharacterIterator;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.*;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import org.apache.log4j.Logger;
|
||||
import org.diylc.common.DrawOption;
|
||||
import org.diylc.common.ObjectCache;
|
||||
import org.diylc.common.ZoomableStroke;
|
||||
import org.diylc.core.IDrawingObserver;
|
||||
@@ -71,7 +65,7 @@ import org.diylc.core.IDrawingObserver;
|
||||
/**
|
||||
* {@link Graphics2D} wrapper that keeps track of all drawing actions and creates an {@link Area}
|
||||
* that corresponds to drawn objects. Before each component is drawn,
|
||||
* {@link #startedDrawingComponent()} should be called. After the component is drawn, area may be
|
||||
* {@link #startedDrawingComponent(int)} should be called. After the component is drawn, area may be
|
||||
* retrieved using {@link #finishedDrawingComponent()}. Graphics configuration (color, font, etc) is
|
||||
* reset between each two components.
|
||||
*
|
||||
@@ -96,7 +90,9 @@ class G2DWrapper extends Graphics2D implements IDrawingObserver {
|
||||
|
||||
private int uniqueCounter = 0;
|
||||
|
||||
private Graphics2D canvasGraphics;
|
||||
private final Graphics2D canvasGraphics;
|
||||
private final Set<DrawOption> drawOptions;
|
||||
|
||||
private Stroke originalStroke;
|
||||
private Color originalColor;
|
||||
private Composite originalComposite;
|
||||
@@ -115,12 +111,14 @@ class G2DWrapper extends Graphics2D implements IDrawingObserver {
|
||||
|
||||
/**
|
||||
* Creates a wrapper around specified {@link Graphics2D} object.
|
||||
*
|
||||
*
|
||||
* @param canvasGraphics
|
||||
* @param drawOptions
|
||||
*/
|
||||
public G2DWrapper(Graphics2D canvasGraphics, double zoom) {
|
||||
public G2DWrapper(Graphics2D canvasGraphics, double zoom, Set<DrawOption> drawOptions) {
|
||||
super();
|
||||
this.canvasGraphics = canvasGraphics;
|
||||
this.drawOptions = drawOptions;
|
||||
String debugAreaPerformanceStr = System.getProperty(DEBUG_AREA_PERFORMANCE);
|
||||
debugAreaPerformance = "true".equalsIgnoreCase(debugAreaPerformanceStr);
|
||||
this.zoom = zoom;
|
||||
@@ -482,7 +480,9 @@ class G2DWrapper extends Graphics2D implements IDrawingObserver {
|
||||
|
||||
@Override
|
||||
public void setComposite(Composite comp) {
|
||||
canvasGraphics.setComposite(comp);
|
||||
if (!drawOptions.contains(DrawOption.OUTLINE_MODE)) {
|
||||
canvasGraphics.setComposite(comp);
|
||||
}
|
||||
// FIXME: check this.
|
||||
}
|
||||
|
||||
@@ -758,7 +758,11 @@ class G2DWrapper extends Graphics2D implements IDrawingObserver {
|
||||
|
||||
@Override
|
||||
public void setColor(Color c) {
|
||||
canvasGraphics.setColor(c);
|
||||
if (drawOptions.contains(DrawOption.MONOCHROMATIC) && c != null) {
|
||||
canvasGraphics.setColor(CalcUtils.convertToMonochrome(c));
|
||||
} else {
|
||||
canvasGraphics.setColor(c);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -59,18 +59,13 @@ import com.thoughtworks.xstream.security.AnyTypePermission;
|
||||
|
||||
import org.diylc.clipboard.ComponentTransferable;
|
||||
import org.diylc.common.*;
|
||||
import org.diylc.core.ExpansionMode;
|
||||
import org.diylc.core.IDIYComponent;
|
||||
import org.diylc.core.IDatasheetSupport;
|
||||
import org.diylc.core.IView;
|
||||
import org.diylc.core.Project;
|
||||
import org.diylc.core.Template;
|
||||
import org.diylc.core.Theme;
|
||||
import org.diylc.core.VisibilityPolicy;
|
||||
import org.diylc.core.*;
|
||||
import org.diylc.core.annotations.IAutoCreator;
|
||||
import org.diylc.core.gerber.GerberExporter;
|
||||
import org.diylc.core.measures.ResizeDimensions;
|
||||
import org.diylc.core.measures.Size;
|
||||
import org.diylc.core.measures.SizeUnit;
|
||||
import org.diylc.filter.IComponentFilter;
|
||||
import org.diylc.lang.LangUtil;
|
||||
import org.diylc.netlist.INetlistParser;
|
||||
import org.diylc.netlist.Netlist;
|
||||
@@ -250,8 +245,10 @@ public class Presenter implements IPlugInPort {
|
||||
@Override
|
||||
public Cursor getCursorAt(Point point, boolean ctrlDown, boolean shiftDown, boolean altDown) {
|
||||
// Only change the cursor if we're not making a new component.
|
||||
if (operationMode == OperationMode.HIGHLIGHT_CONNECTED_AREAS || altDown)
|
||||
if (operationMode == OperationMode.HIGHLIGHT_CONNECTED_AREAS || altDown) {
|
||||
return Cursor.getPredefinedCursor(Cursor.CROSSHAIR_CURSOR);
|
||||
}
|
||||
|
||||
if (instantiationManager.getComponentTypeSlot() == null) {
|
||||
// Scale point to remove zoom factor.
|
||||
Point2D scaledPoint = scalePoint(point);
|
||||
@@ -268,6 +265,7 @@ public class Presenter implements IPlugInPort {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Cursor.getDefaultCursor();
|
||||
}
|
||||
|
||||
@@ -427,7 +425,7 @@ public class Presenter implements IPlugInPort {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(Graphics2D g2d, Set<DrawOption> drawOptions, final IComponentFilter filter, Double externalZoom,
|
||||
public void draw(Graphics2D g2d, Set<DrawOption> drawOptions, final IComponentFilter filter, Double externalZoom,
|
||||
Double scaleFactor, Rectangle2D visibleRect) {
|
||||
if (currentProject == null) {
|
||||
return;
|
||||
@@ -924,29 +922,36 @@ public class Presenter implements IPlugInPort {
|
||||
messageDispatcher.dispatchMessage(EventType.REPAINT);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Rectangle2D getSelectionBounds(boolean applyZoom) {
|
||||
if (selectedComponents == null || selectedComponents.isEmpty())
|
||||
return getComponentBounds(selectedComponents, applyZoom);
|
||||
}
|
||||
|
||||
private Rectangle2D getComponentBounds(Collection<IDIYComponent<?>> components, boolean applyZoom) {
|
||||
if (components == null || components.isEmpty())
|
||||
return null;
|
||||
|
||||
int minX = Integer.MAX_VALUE;
|
||||
int maxX = Integer.MIN_VALUE;
|
||||
int minY = Integer.MAX_VALUE;
|
||||
int maxY = Integer.MIN_VALUE;
|
||||
for (IDIYComponent<?> c : selectedComponents) {
|
||||
|
||||
for (IDIYComponent<?> c : components) {
|
||||
|
||||
ComponentArea compArea = drawingManager.getComponentArea(c);
|
||||
if (compArea != null && compArea.getOutlineArea() != null) {
|
||||
Rectangle rect = compArea.getOutlineArea().getBounds();
|
||||
if (rect.x < minX)
|
||||
minX = rect.x;
|
||||
if (rect.x + rect.width > maxX)
|
||||
maxX = rect.x + rect.width;
|
||||
if (rect.y < minY)
|
||||
minY = rect.y;
|
||||
if (rect.y + rect.height > maxY)
|
||||
maxY = rect.y + rect.height;
|
||||
} else if (currentProject.getComponents().contains(c))
|
||||
if (compArea == null || compArea.getOutlineArea() == null) {
|
||||
LOG.debug("Area is null for " + c.getName() + " of type " + c.getClass().getName());
|
||||
return null;
|
||||
}
|
||||
|
||||
Rectangle rect = compArea.getOutlineArea().getBounds();
|
||||
if (rect.x < minX)
|
||||
minX = rect.x;
|
||||
if (rect.x + rect.width > maxX)
|
||||
maxX = rect.x + rect.width;
|
||||
if (rect.y < minY)
|
||||
minY = rect.y;
|
||||
if (rect.y + rect.height > maxY)
|
||||
maxY = rect.y + rect.height;
|
||||
}
|
||||
|
||||
if (configManager.readBoolean(EXTRA_SPACE_KEY, true)) {
|
||||
@@ -1015,8 +1020,9 @@ public class Presenter implements IPlugInPort {
|
||||
}
|
||||
updateSelection(matching);
|
||||
messageDispatcher.dispatchMessage(EventType.REPAINT, true);
|
||||
if (matching.size() > 0)
|
||||
if (!matching.isEmpty()) {
|
||||
messageDispatcher.dispatchMessage(EventType.SCROLL_TO, getSelectionBounds(true));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -1233,11 +1239,14 @@ public class Presenter implements IPlugInPort {
|
||||
Map<String, Object> params = new HashMap<String, Object>();
|
||||
params.put("point", point);
|
||||
test.addStep(DIYTest.DRAG_OVER, params);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (point == null || operationMode == OperationMode.HIGHLIGHT_CONNECTED_AREAS) {
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean resizeInProgress = false;
|
||||
|
||||
Point2D scaledPoint = scalePoint(point);
|
||||
if (controlPointMap != null && !controlPointMap.isEmpty()) {
|
||||
// We're dragging control point(s).
|
||||
@@ -1248,6 +1257,13 @@ public class Presenter implements IPlugInPort {
|
||||
if (actualD == null)
|
||||
return true;
|
||||
|
||||
// if there's at least one component with only one control point being dragged it means that we are resizing
|
||||
if (controlPointMap.size() == 1 && controlPointMap.entrySet().stream()
|
||||
.anyMatch(entry -> entry.getValue().size() == 1
|
||||
&& entry.getKey().getControlPointCount() > 1)) {
|
||||
resizeInProgress = true;
|
||||
}
|
||||
|
||||
previousDragPoint.setLocation(previousDragPoint.getX() + actualD.getX(), previousDragPoint.getY() + actualD.getY());
|
||||
} else if (selectedComponents.isEmpty() && instantiationManager.getComponentTypeSlot() == null
|
||||
&& previousDragPoint != null) {
|
||||
@@ -1265,10 +1281,50 @@ public class Presenter implements IPlugInPort {
|
||||
this.previousScaledPoint = scalePoint(point);
|
||||
instantiationManager.updateSingleClick(previousScaledPoint, isSnapToGrid(), currentProject.getGridSpacing());
|
||||
}
|
||||
messageDispatcher.dispatchMessage(EventType.REPAINT);
|
||||
|
||||
messageDispatcher.dispatchMessage(EventType.REPAINT, false, resizeInProgress);
|
||||
return true;
|
||||
}
|
||||
|
||||
public ResizeDimensions getResizeDimensions() {
|
||||
|
||||
if (controlPointMap == null || controlPointMap.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Find the component being resized (one with a single control point being dragged)
|
||||
IDIYComponent<?> resizingComponent = null;
|
||||
for (Map.Entry<IDIYComponent<?>, Set<Integer>> entry : controlPointMap.entrySet()) {
|
||||
if (entry.getValue().size() == 1 && entry.getKey().getControlPointCount() > 1) {
|
||||
resizingComponent = entry.getKey();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (resizingComponent == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Rectangle2D componentBounds = getComponentBounds(controlPointMap.keySet(), false);
|
||||
if (componentBounds == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Convert pixel dimensions to Size (mm or in based on configuration)
|
||||
boolean metric = configManager.readBoolean(METRIC_KEY, true);
|
||||
SizeUnit unit = metric ? SizeUnit.mm : SizeUnit.in;
|
||||
Size width = Size.fromPixels(componentBounds.getWidth(), unit);
|
||||
Size height = Size.fromPixels(componentBounds.getHeight(), unit);
|
||||
|
||||
// Get length if component implements IHaveLength
|
||||
Size length = null;
|
||||
if (resizingComponent instanceof IHaveLength iHaveLength) {
|
||||
length = iHaveLength.calculateLength();
|
||||
}
|
||||
|
||||
return new ResizeDimensions(width, height, length);
|
||||
}
|
||||
|
||||
private Point2D moveComponents(Map<IDIYComponent<?>, Set<Integer>> controlPointMap, int dx, int dy, boolean snapToGrid, boolean snapToObjects) {
|
||||
// After we make the transfer and snap to grid, calculate actual dx
|
||||
// and dy. We'll use them to translate the previous drag point.
|
||||
@@ -1580,6 +1636,7 @@ public class Presenter implements IPlugInPort {
|
||||
drawingManager.clearContinuityArea();
|
||||
projectFileManager.notifyFileChange();
|
||||
}
|
||||
|
||||
messageDispatcher.dispatchMessage(EventType.REPAINT);
|
||||
dragInProgress = false;
|
||||
}
|
||||
@@ -2166,6 +2223,10 @@ public class Presenter implements IPlugInPort {
|
||||
}
|
||||
Rectangle2D rect = getSelectionBounds(false);
|
||||
|
||||
if (rect == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
double width = rect.getWidth();
|
||||
double height = rect.getHeight();
|
||||
|
||||
@@ -2508,7 +2569,7 @@ public class Presenter implements IPlugInPort {
|
||||
}
|
||||
try {
|
||||
BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(
|
||||
"import-defaults/variants.xml"));
|
||||
"pending-variants.xml"));
|
||||
XStream xStream = new XStream(new DomDriver());
|
||||
xStream.addPermission(AnyTypePermission.ANY);
|
||||
ProjectFileManager.xStreamSerializer.toXML(defaultVariantMap, out);
|
||||
|
||||
@@ -4128,4 +4128,204 @@
|
||||
</org.diylc.core.Template>
|
||||
</list>
|
||||
</entry>
|
||||
</map>
|
||||
<entry>
|
||||
<string>org.diylc.components.electromechanical.TactileMicroSwitch</string>
|
||||
<list>
|
||||
<org.diylc.core.Template>
|
||||
<name>6x6 (e.g. Omron B3F-1000)</name>
|
||||
<values>
|
||||
<entry>
|
||||
<string>Spacing X</string>
|
||||
<org.diylc.core.measures.Size value="6.5" unit="mm"/>
|
||||
</entry>
|
||||
<entry>
|
||||
<string>Spacing Y</string>
|
||||
<org.diylc.core.measures.Size value="4.5" unit="mm"/>
|
||||
</entry>
|
||||
<entry>
|
||||
<string>Label</string>
|
||||
<awt-color hex="ffffff"/>
|
||||
</entry>
|
||||
<entry>
|
||||
<string>Orientation</string>
|
||||
<org.diylc.common.Orientation>DEFAULT</org.diylc.common.Orientation>
|
||||
</entry>
|
||||
<entry>
|
||||
<string>Case</string>
|
||||
<org.diylc.core.measures.Size value="6.0" unit="mm"/>
|
||||
</entry>
|
||||
<entry>
|
||||
<string>Name</string>
|
||||
<string>SW2</string>
|
||||
</entry>
|
||||
<entry>
|
||||
<string>Font Size Override</string>
|
||||
<null/>
|
||||
</entry>
|
||||
<entry>
|
||||
<string>Value</string>
|
||||
<string></string>
|
||||
</entry>
|
||||
<entry>
|
||||
<string>Alpha</string>
|
||||
<byte>127</byte>
|
||||
</entry>
|
||||
<entry>
|
||||
<string>Body</string>
|
||||
<awt-color hex="c0c0c0"/>
|
||||
</entry>
|
||||
<entry>
|
||||
<string>Border</string>
|
||||
<awt-color hex="595959"/>
|
||||
</entry>
|
||||
<entry>
|
||||
<string>Display</string>
|
||||
<org.diylc.common.Display>BOTH</org.diylc.common.Display>
|
||||
</entry>
|
||||
<entry>
|
||||
<string>Pin Pairing Mode</string>
|
||||
<diylc.electromechanical.TactileMicroSwitch_-PinPairingMode>Opposite</diylc.electromechanical.TactileMicroSwitch_-PinPairingMode>
|
||||
</entry>
|
||||
</values>
|
||||
<points>
|
||||
<point x="0.0" y="0.0"/>
|
||||
<point x="0.0" y="0.175"/>
|
||||
<point x="0.255" y="0.0"/>
|
||||
<point x="0.255" y="0.175"/>
|
||||
</points>
|
||||
<defaultFlag>false</defaultFlag>
|
||||
<createdOn>2026-01-02T10:01:17.805688</createdOn>
|
||||
</org.diylc.core.Template>
|
||||
<org.diylc.core.Template>
|
||||
<name>12x12 (e.g. Omron B3F-4000)</name>
|
||||
<values>
|
||||
<entry>
|
||||
<string>Spacing X</string>
|
||||
<org.diylc.core.measures.Size value="12.6" unit="mm"/>
|
||||
</entry>
|
||||
<entry>
|
||||
<string>Spacing Y</string>
|
||||
<org.diylc.core.measures.Size value="5.0" unit="mm"/>
|
||||
</entry>
|
||||
<entry>
|
||||
<string>Label</string>
|
||||
<awt-color hex="ffffff"/>
|
||||
</entry>
|
||||
<entry>
|
||||
<string>Orientation</string>
|
||||
<org.diylc.common.Orientation>DEFAULT</org.diylc.common.Orientation>
|
||||
</entry>
|
||||
<entry>
|
||||
<string>Case</string>
|
||||
<org.diylc.core.measures.Size value="12.0" unit="mm"/>
|
||||
</entry>
|
||||
<entry>
|
||||
<string>Name</string>
|
||||
<string>SW1</string>
|
||||
</entry>
|
||||
<entry>
|
||||
<string>Font Size Override</string>
|
||||
<null/>
|
||||
</entry>
|
||||
<entry>
|
||||
<string>Value</string>
|
||||
<string></string>
|
||||
</entry>
|
||||
<entry>
|
||||
<string>Alpha</string>
|
||||
<byte>127</byte>
|
||||
</entry>
|
||||
<entry>
|
||||
<string>Body</string>
|
||||
<awt-color hex="c0c0c0"/>
|
||||
</entry>
|
||||
<entry>
|
||||
<string>Border</string>
|
||||
<awt-color hex="595959"/>
|
||||
</entry>
|
||||
<entry>
|
||||
<string>Display</string>
|
||||
<org.diylc.common.Display>BOTH</org.diylc.common.Display>
|
||||
</entry>
|
||||
<entry>
|
||||
<string>Pin Pairing Mode</string>
|
||||
<diylc.electromechanical.TactileMicroSwitch_-PinPairingMode>Opposite</diylc.electromechanical.TactileMicroSwitch_-PinPairingMode>
|
||||
</entry>
|
||||
</values>
|
||||
<points>
|
||||
<point x="0.0" y="0.0"/>
|
||||
<point x="0.0" y="0.195"/>
|
||||
<point x="0.495" y="0.0"/>
|
||||
<point x="0.495" y="0.195"/>
|
||||
</points>
|
||||
<defaultFlag>false</defaultFlag>
|
||||
<createdOn>2026-01-02T10:00:31.950676</createdOn>
|
||||
</org.diylc.core.Template>
|
||||
<org.diylc.core.Template>
|
||||
<name>5x5 Low Profile SMD (e.g. Alps SKQG)</name>
|
||||
<values>
|
||||
<entry>
|
||||
<string>Spacing X</string>
|
||||
<org.diylc.core.measures.Size value="5.2" unit="mm"/>
|
||||
</entry>
|
||||
<entry>
|
||||
<string>Spacing Y</string>
|
||||
<org.diylc.core.measures.Size value="3.7" unit="mm"/>
|
||||
</entry>
|
||||
<entry>
|
||||
<string>Label</string>
|
||||
<awt-color hex="ffffff"/>
|
||||
</entry>
|
||||
<entry>
|
||||
<string>Orientation</string>
|
||||
<org.diylc.common.Orientation>DEFAULT</org.diylc.common.Orientation>
|
||||
</entry>
|
||||
<entry>
|
||||
<string>Case</string>
|
||||
<org.diylc.core.measures.Size value="5.0" unit="mm"/>
|
||||
</entry>
|
||||
<entry>
|
||||
<string>Name</string>
|
||||
<string>SW2</string>
|
||||
</entry>
|
||||
<entry>
|
||||
<string>Font Size Override</string>
|
||||
<null/>
|
||||
</entry>
|
||||
<entry>
|
||||
<string>Value</string>
|
||||
<string></string>
|
||||
</entry>
|
||||
<entry>
|
||||
<string>Alpha</string>
|
||||
<byte>127</byte>
|
||||
</entry>
|
||||
<entry>
|
||||
<string>Border</string>
|
||||
<awt-color hex="595959"/>
|
||||
</entry>
|
||||
<entry>
|
||||
<string>Body</string>
|
||||
<awt-color hex="c0c0c0"/>
|
||||
</entry>
|
||||
<entry>
|
||||
<string>Display</string>
|
||||
<org.diylc.common.Display>BOTH</org.diylc.common.Display>
|
||||
</entry>
|
||||
<entry>
|
||||
<string>Pin Pairing Mode</string>
|
||||
<diylc.electromechanical.TactileMicroSwitch_-PinPairingMode>Diagonal</diylc.electromechanical.TactileMicroSwitch_-PinPairingMode>
|
||||
</entry>
|
||||
</values>
|
||||
<points>
|
||||
<point x="0.0" y="0.0"/>
|
||||
<point x="0.0" y="0.145"/>
|
||||
<point x="0.2" y="0.0"/>
|
||||
<point x="0.2" y="0.145"/>
|
||||
</points>
|
||||
<defaultFlag>false</defaultFlag>
|
||||
<createdOn>2026-01-02T10:04:33.795741</createdOn>
|
||||
</org.diylc.core.Template>
|
||||
</list>
|
||||
</entry>
|
||||
</map>
|
||||
|
||||
@@ -324,5 +324,225 @@
|
||||
</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-30 00:00:00.000 CET</releaseDate>
|
||||
<name></name>
|
||||
<changes>
|
||||
<org.diylc.appframework.update.Change>
|
||||
<changeType>NEW_FEATURE</changeType>
|
||||
<description>Added Silkscreen export to PDF/PNG and print capabilities</description>
|
||||
</org.diylc.appframework.update.Change>
|
||||
<org.diylc.appframework.update.Change>
|
||||
<changeType>NEW_FEATURE</changeType>
|
||||
<description>Added 'Multiwatt IC' package with 11 and 15 pin varieties</description>
|
||||
</org.diylc.appframework.update.Change>
|
||||
<org.diylc.appframework.update.Change>
|
||||
<changeType>NEW_FEATURE</changeType>
|
||||
<description>Added new property in 'LP Switch' that allows having the middle terminals connected (as they were) or disconnected to allow more flexibility</description>
|
||||
</org.diylc.appframework.update.Change>
|
||||
<org.diylc.appframework.update.Change>
|
||||
<changeType>NEW_FEATURE</changeType>
|
||||
<description>Added 'Ending Style' property to 'Copper Trace' and 'Curved Trace' to allow choosing round or flat style</description>
|
||||
</org.diylc.appframework.update.Change>
|
||||
<org.diylc.appframework.update.Change>
|
||||
<changeType>NEW_FEATURE</changeType>
|
||||
<description>Added Chinese simplified (thank you chenjingyuanku) and Hindi UI translation and updated all other languages with new translations</description>
|
||||
</org.diylc.appframework.update.Change>
|
||||
<org.diylc.appframework.update.Change>
|
||||
<changeType>NEW_FEATURE</changeType>
|
||||
<description>Added configuration for text scale factor in rare cases when system scale factor is not applied correctly; requires adding a configuration property to config.xml with key 'fontScaleFactor' and a desired float value</description>
|
||||
</org.diylc.appframework.update.Change>
|
||||
<org.diylc.appframework.update.Change>
|
||||
<changeType>IMPROVEMENT</changeType>
|
||||
<description>Improved behavior of mouse cursor during drag operations; this should also fix behavior on some linux machines that show a "forbidden" cursor while dragging</description>
|
||||
</org.diylc.appframework.update.Change>
|
||||
<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>IMPROVEMENT</changeType>
|
||||
<description>Improve contrast of tooltips on some Linux machines that show white text against yellow balloon</description>
|
||||
</org.diylc.appframework.update.Change>
|
||||
<org.diylc.appframework.update.Change>
|
||||
<changeType>IMPROVEMENT</changeType>
|
||||
<description>Honor label color settings for 'Tape Measure' component</description>
|
||||
</org.diylc.appframework.update.Change>
|
||||
<org.diylc.appframework.update.Change>
|
||||
<changeType>IMPROVEMENT</changeType>
|
||||
<description>Reorganized export and print related items in the File menu to reduce clutter</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>
|
||||
<org.diylc.appframework.update.Change>
|
||||
<changeType>BUG_FIX</changeType>
|
||||
<description>Application freezes when using underside rendering or mirroring on 'Pin Header' when sized at 1x1</description>
|
||||
</org.diylc.appframework.update.Change>
|
||||
<org.diylc.appframework.update.Change>
|
||||
<changeType>BUG_FIX</changeType>
|
||||
<description>'Rotary Switch (Open)' cannot be selected by clicking on the wafer for 4-pole variations</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>9</minor>
|
||||
<build>0</build>
|
||||
</versionNumber>
|
||||
<releaseDate>2025-12-30 00:00:00.000 CET</releaseDate>
|
||||
<name></name>
|
||||
<changes>
|
||||
<org.diylc.appframework.update.Change>
|
||||
<changeType>NEW_FEATURE</changeType>
|
||||
<description>Added balloon tooltip next to cursor showing component width, height and length (where available) while resizing; can be disabled from Config menu</description>
|
||||
</org.diylc.appframework.update.Change>
|
||||
<org.diylc.appframework.update.Change>
|
||||
<changeType>NEW_FEATURE</changeType>
|
||||
<description>Added 'Freeway 3x3-05 (Ultra) Switch' (thank you mm-rnd)</description>
|
||||
</org.diylc.appframework.update.Change>
|
||||
<org.diylc.appframework.update.Change>
|
||||
<changeType>NEW_FEATURE</changeType>
|
||||
<description>Configurable 'Tactile Micro Switch' dimensions and added a few most commonly used variants</description>
|
||||
</org.diylc.appframework.update.Change>
|
||||
<org.diylc.appframework.update.Change>
|
||||
<changeType>NEW_FEATURE</changeType>
|
||||
<description>Exposed the most common pin pairing modes and added switching logic for 'Tactile Micro Switch' to allow using it for analysis</description>
|
||||
</org.diylc.appframework.update.Change>
|
||||
<org.diylc.appframework.update.Change>
|
||||
<changeType>NEW_FEATURE</changeType>
|
||||
<description>'Potentiometer Panel' can now render numerical lug markers for easier identification</description>
|
||||
</org.diylc.appframework.update.Change>
|
||||
<org.diylc.appframework.update.Change>
|
||||
<changeType>IMPROVEMENT</changeType>
|
||||
<description>'Auto-Wrap Label' retains line breaks from the input text</description>
|
||||
</org.diylc.appframework.update.Change>
|
||||
<org.diylc.appframework.update.Change>
|
||||
<changeType>IMPROVEMENT</changeType>
|
||||
<description>'Cliff 1/8" Jack', 'Open 1/8" Jack' and 'Open 1/4" Jack' can be rotated</description>
|
||||
</org.diylc.appframework.update.Change>
|
||||
<org.diylc.appframework.update.Change>
|
||||
<changeType>IMPROVEMENT</changeType>
|
||||
<description>Fixed the offset of terminals on a DP4T Tele type of 'Lever Switch' to be aligned with the physical switch (attention - this will break existing diagrams and will require adjusting leads to match the new terminal positions)</description>
|
||||
</org.diylc.appframework.update.Change>
|
||||
<org.diylc.appframework.update.Change>
|
||||
<changeType>IMPROVEMENT</changeType>
|
||||
<description>Make the label of schematic symbols (e.g. 'Resistor Symbol') clickable as well</description>
|
||||
</org.diylc.appframework.update.Change>
|
||||
<org.diylc.appframework.update.Change>
|
||||
<changeType>IMPROVEMENT</changeType>
|
||||
<description>Ability to resize 'Transformer Core' schematic symbol after it's already placed</description>
|
||||
</org.diylc.appframework.update.Change>
|
||||
<org.diylc.appframework.update.Change>
|
||||
<changeType>BUG_FIX</changeType>
|
||||
<description>Clicking on 'Tactile Micro Switch' body doesn't register</description>
|
||||
</org.diylc.appframework.update.Change>
|
||||
<org.diylc.appframework.update.Change>
|
||||
<changeType>BUG_FIX</changeType>
|
||||
<description>New lines at the end of the 'Label' text get erroneously rendered as br HTML tags</description>
|
||||
</org.diylc.appframework.update.Change>
|
||||
</changes>
|
||||
<url>https://github.com/bancika/diy-layout-creator/releases</url>
|
||||
</org.diylc.appframework.update.Version>
|
||||
</a>
|
||||
</java.util.Arrays_-ArrayList>
|
||||
|
||||
@@ -3,6 +3,7 @@ package org.diylc.presenter;
|
||||
import static org.junit.Assert.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.geom.Point2D;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
@@ -142,4 +143,116 @@ public class CalcUtilsTest {
|
||||
point2.setLocation(10.0, 21.0);
|
||||
assertFalse(CalcUtils.pointsMatch(point1, point2, 0.5));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCalculateLuminance() {
|
||||
// Test pure black
|
||||
assertEquals(0.0, CalcUtils.calculateLuminance(Color.BLACK), 0.001);
|
||||
|
||||
// Test pure white
|
||||
assertEquals(255.0, CalcUtils.calculateLuminance(Color.WHITE), 0.001);
|
||||
|
||||
// Test pure red
|
||||
double redLuminance = CalcUtils.calculateLuminance(Color.RED);
|
||||
assertEquals(76.245, redLuminance, 0.001); // 0.299 * 255
|
||||
|
||||
// Test pure green
|
||||
double greenLuminance = CalcUtils.calculateLuminance(Color.GREEN);
|
||||
assertEquals(149.685, greenLuminance, 0.001); // 0.587 * 255
|
||||
|
||||
// Test pure blue
|
||||
double blueLuminance = CalcUtils.calculateLuminance(Color.BLUE);
|
||||
assertEquals(29.07, blueLuminance, 0.001); // 0.114 * 255
|
||||
|
||||
// Test middle gray (128, 128, 128)
|
||||
Color gray = new Color(128, 128, 128);
|
||||
double grayLuminance = CalcUtils.calculateLuminance(gray);
|
||||
assertEquals(128.0, grayLuminance, 0.001);
|
||||
|
||||
// Test null color
|
||||
assertEquals(0.0, CalcUtils.calculateLuminance(null), 0.001);
|
||||
|
||||
// Test custom color
|
||||
Color custom = new Color(100, 150, 200);
|
||||
double customLuminance = CalcUtils.calculateLuminance(custom);
|
||||
double expected = 0.299 * 100 + 0.587 * 150 + 0.114 * 200;
|
||||
assertEquals(expected, customLuminance, 0.001);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConvertToMonochrome() {
|
||||
// Test pure black -> should stay black
|
||||
Color result = CalcUtils.convertToMonochrome(Color.BLACK);
|
||||
assertEquals(Color.BLACK, result);
|
||||
|
||||
// Test pure white -> should stay white
|
||||
result = CalcUtils.convertToMonochrome(Color.WHITE);
|
||||
assertEquals(Color.WHITE, result);
|
||||
|
||||
// Test dark color -> should become black
|
||||
Color darkBlue = new Color(0, 0, 50);
|
||||
result = CalcUtils.convertToMonochrome(darkBlue);
|
||||
assertEquals(Color.BLACK, result);
|
||||
|
||||
// Test light color -> should become white
|
||||
Color lightYellow = new Color(255, 255, 200);
|
||||
result = CalcUtils.convertToMonochrome(lightYellow);
|
||||
assertEquals(Color.WHITE, result);
|
||||
|
||||
// Test middle gray -> should become white (threshold is 128, gray is exactly 128)
|
||||
Color gray = new Color(128, 128, 128);
|
||||
result = CalcUtils.convertToMonochrome(gray);
|
||||
assertEquals(Color.WHITE, result);
|
||||
|
||||
// Test color just below threshold -> should become black
|
||||
Color darkGray = new Color(127, 127, 127);
|
||||
result = CalcUtils.convertToMonochrome(darkGray);
|
||||
assertEquals(Color.BLACK, result);
|
||||
|
||||
// Test color just above threshold -> should become white
|
||||
Color lightGray = new Color(129, 129, 129);
|
||||
result = CalcUtils.convertToMonochrome(lightGray);
|
||||
assertEquals(Color.WHITE, result);
|
||||
|
||||
// Test with custom threshold
|
||||
Color mediumColor = new Color(100, 100, 100);
|
||||
result = CalcUtils.convertToMonochrome(mediumColor, 50.0);
|
||||
assertEquals(Color.WHITE, result); // Luminance ~100, threshold 50, so white
|
||||
|
||||
result = CalcUtils.convertToMonochrome(mediumColor, 150.0);
|
||||
assertEquals(Color.BLACK, result); // Luminance ~100, threshold 150, so black
|
||||
|
||||
// Test null color
|
||||
assertNull(CalcUtils.convertToMonochrome(null));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConvertToMonochromeWithAlpha() {
|
||||
// Test color with alpha channel - alpha should be preserved
|
||||
// Red (255, 0, 0) has luminance ~76, which is < 128, so it becomes BLACK
|
||||
Color transparentRed = new Color(255, 0, 0, 128);
|
||||
Color result = CalcUtils.convertToMonochrome(transparentRed);
|
||||
assertEquals(Color.BLACK.getRed(), result.getRed());
|
||||
assertEquals(Color.BLACK.getGreen(), result.getGreen());
|
||||
assertEquals(Color.BLACK.getBlue(), result.getBlue());
|
||||
assertEquals(128, result.getAlpha());
|
||||
|
||||
// Test light color with alpha - should become WHITE
|
||||
Color transparentLight = new Color(255, 255, 200, 128);
|
||||
result = CalcUtils.convertToMonochrome(transparentLight);
|
||||
assertEquals(Color.WHITE.getRed(), result.getRed());
|
||||
assertEquals(Color.WHITE.getGreen(), result.getGreen());
|
||||
assertEquals(Color.WHITE.getBlue(), result.getBlue());
|
||||
assertEquals(128, result.getAlpha());
|
||||
|
||||
// Test fully transparent color
|
||||
Color transparent = new Color(100, 150, 200, 0);
|
||||
result = CalcUtils.convertToMonochrome(transparent);
|
||||
assertEquals(0, result.getAlpha());
|
||||
|
||||
// Test opaque color - alpha should remain 255
|
||||
Color opaque = new Color(50, 50, 50);
|
||||
result = CalcUtils.convertToMonochrome(opaque);
|
||||
assertEquals(255, result.getAlpha());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
<parent>
|
||||
<groupId>org.diylc</groupId>
|
||||
<artifactId>diylc-parent</artifactId>
|
||||
<version>5.5.0</version>
|
||||
<version>5.8.0</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>diylc-library</artifactId>
|
||||
|
||||
@@ -31,7 +31,6 @@ import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.diylc.common.HorizontalAlignment;
|
||||
import org.diylc.common.VerticalAlignment;
|
||||
@@ -72,13 +71,18 @@ public class StringUtils {
|
||||
* @return a non-empty list of strings
|
||||
*/
|
||||
public static List<String> wrap(String str, FontMetrics fm, int maxWidth) {
|
||||
String[] split = str.split("\\<br\\>");
|
||||
List<String> lines = Arrays.stream(split).flatMap(s -> splitIntoLines(s).stream()).collect(Collectors.toList());
|
||||
if (lines.size() == 0)
|
||||
return lines;
|
||||
// First split by actual newlines to honor them, then split by <br> tags
|
||||
List<String> lines = splitIntoLines(str);
|
||||
List<String> allLines = new ArrayList<String>();
|
||||
for (String line : lines) {
|
||||
String[] split = line.split("\\<br\\>");
|
||||
allLines.addAll(Arrays.asList(split));
|
||||
}
|
||||
if (allLines.size() == 0)
|
||||
return allLines;
|
||||
|
||||
ArrayList<String> strings = new ArrayList<String>();
|
||||
for (Iterator<String> iter = lines.iterator(); iter.hasNext();)
|
||||
for (Iterator<String> iter = allLines.iterator(); iter.hasNext();)
|
||||
wrapLineInto((String) iter.next(), strings, fm, maxWidth);
|
||||
return strings;
|
||||
}
|
||||
@@ -126,8 +130,8 @@ public class StringUtils {
|
||||
line = line.substring(pos).trim();
|
||||
len = line.length();
|
||||
}
|
||||
if (len > 0)
|
||||
list.add(line);
|
||||
// Always add the line, even if empty, to preserve empty lines from newlines
|
||||
list.add(line);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -209,12 +213,24 @@ public class StringUtils {
|
||||
if (text == null)
|
||||
return;
|
||||
|
||||
String[] parts = text.split("\\<br\\>");
|
||||
// Use -1 limit to preserve trailing empty strings so we can filter them out
|
||||
String[] parts = text.split("\\<br\\>", -1);
|
||||
if (parts.length > 1) {
|
||||
// Remove trailing empty strings to avoid rendering extra blank lines
|
||||
int lastNonEmpty = parts.length - 1;
|
||||
while (lastNonEmpty >= 0 && parts[lastNonEmpty].trim().isEmpty()) {
|
||||
lastNonEmpty--;
|
||||
}
|
||||
// If all parts are empty, don't render anything
|
||||
if (lastNonEmpty < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
FontMetrics fontMetrics = g2d.getFontMetrics();
|
||||
Rectangle stringBounds = fontMetrics.getStringBounds(parts[0], g2d).getBounds();
|
||||
for (int i = 0; i < parts.length; i++)
|
||||
drawCenteredText(g2d, parts[i], x, (int)(y - stringBounds.height * (parts.length - 1) / 2d + i * stringBounds.height), horizontalAlignment, verticalAlignment);
|
||||
int actualLength = lastNonEmpty + 1;
|
||||
for (int i = 0; i < actualLength; i++)
|
||||
drawCenteredText(g2d, parts[i], x, (int)(y - stringBounds.height * (actualLength - 1) / 2d + i * stringBounds.height), horizontalAlignment, verticalAlignment);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
+36
-11
@@ -43,12 +43,7 @@ import org.diylc.common.IPlugInPort;
|
||||
import org.diylc.common.LineStyle;
|
||||
import org.diylc.common.ObjectCache;
|
||||
import org.diylc.common.VerticalAlignment;
|
||||
import org.diylc.components.AbstractLabeledComponent;
|
||||
import org.diylc.core.ComponentState;
|
||||
import org.diylc.core.IDrawingObserver;
|
||||
import org.diylc.core.Project;
|
||||
import org.diylc.core.Theme;
|
||||
import org.diylc.core.VisibilityPolicy;
|
||||
import org.diylc.core.*;
|
||||
import org.diylc.core.annotations.EditableProperty;
|
||||
import org.diylc.core.annotations.PositiveNonZeroMeasureValidator;
|
||||
import org.diylc.core.measures.Size;
|
||||
@@ -63,7 +58,8 @@ import org.diylc.utils.Constants;
|
||||
*
|
||||
* @author Branislav Stojkovic
|
||||
*/
|
||||
public abstract class AbstractLeadedComponent<T> extends AbstractLabeledComponent<T> {
|
||||
public abstract class AbstractLeadedComponent<T> extends AbstractLabeledComponent<T> implements
|
||||
IHaveLength {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@@ -367,8 +363,8 @@ public abstract class AbstractLeadedComponent<T> extends AbstractLabeledComponen
|
||||
offset = -offset;
|
||||
}
|
||||
|
||||
drawingObserver.startTracking();
|
||||
if (getMoveLabel()) {
|
||||
drawingObserver.startTracking();
|
||||
g2d.setTransform(oldTransform);
|
||||
g2d.translate(getPoints()[2].getX(), getPoints()[2].getY());
|
||||
if (getLabelOriantation() != LabelOriantation.Horizontal) {
|
||||
@@ -378,19 +374,18 @@ public abstract class AbstractLeadedComponent<T> extends AbstractLabeledComponen
|
||||
g2d.setTransform(oldTransform);
|
||||
} else {
|
||||
if (isStanding() || getLabelOriantation() == LabelOriantation.Horizontal) {
|
||||
drawingObserver.startTracking();
|
||||
g2d.setTransform(oldTransform);
|
||||
double x = (getPoints()[0].getX() + getPoints()[1].getX() - length) / 2.0;
|
||||
double y = (getPoints()[0].getY() + getPoints()[1].getY() - width) / 2.0;
|
||||
g2d.drawString(label, (int) (x + (length - textRect.getWidth()) / 2 + offset),
|
||||
(int) (y + calculateLabelYOffset(shapeRect, textRect, fontMetrics)));
|
||||
} else {
|
||||
drawingObserver.stopTracking();
|
||||
g2d.drawString(label, (int) (length - textRect.getWidth()) / 2 + offset,
|
||||
calculateLabelYOffset(shapeRect, textRect, fontMetrics));
|
||||
g2d.setTransform(oldTransform);
|
||||
}
|
||||
}
|
||||
drawingObserver.stopTracking();
|
||||
}
|
||||
// if (getLabelOriantation() == LabelOriantation.Horizontal) {
|
||||
//
|
||||
@@ -434,7 +429,7 @@ public abstract class AbstractLeadedComponent<T> extends AbstractLabeledComponen
|
||||
Stroke stroke = null;
|
||||
switch (getStyle()) {
|
||||
case SOLID:
|
||||
stroke = ObjectCache.getInstance().fetchBasicStroke(thickness);
|
||||
stroke = ObjectCache.getInstance().fetchStroke(thickness, getEndingStyle() == LineEndingStyle.Round ? BasicStroke.CAP_ROUND : BasicStroke.CAP_BUTT);
|
||||
break;
|
||||
case DASHED:
|
||||
stroke =
|
||||
@@ -641,6 +636,32 @@ public abstract class AbstractLeadedComponent<T> extends AbstractLabeledComponen
|
||||
this.length = length;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Size calculateLength() {
|
||||
|
||||
try {
|
||||
Point2D[] points = getPoints();
|
||||
if (points.length < 2) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Calculate distance between ending points
|
||||
double distanceInPixels = points[0].distance(points[1]);
|
||||
|
||||
// Convert to Size with appropriate unit (mm or in based on configuration)
|
||||
SizeUnit unit;
|
||||
if (ConfigurationManager.getInstance().readBoolean(IPlugInPort.METRIC_KEY, true)) {
|
||||
unit = SizeUnit.mm;
|
||||
} else {
|
||||
unit = SizeUnit.in;
|
||||
}
|
||||
|
||||
return Size.fromPixels(distanceInPixels, unit);
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@EditableProperty(name = "Width", defaultable = true, validatorClass = PositiveNonZeroMeasureValidator.class, sortOrder = 102)
|
||||
public Size getWidth() {
|
||||
return width;
|
||||
@@ -747,6 +768,10 @@ public abstract class AbstractLeadedComponent<T> extends AbstractLabeledComponen
|
||||
hideShortLeads = false;
|
||||
return hideShortLeads;
|
||||
}
|
||||
|
||||
protected LineEndingStyle getEndingStyle() {
|
||||
return LineEndingStyle.Round;
|
||||
}
|
||||
|
||||
public void setHideShortLeads(Boolean hideShortLeads) {
|
||||
this.hideShortLeads = hideShortLeads;
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
package org.diylc.components;
|
||||
|
||||
public enum LineEndingStyle {
|
||||
Round, Flat
|
||||
}
|
||||
@@ -69,18 +69,18 @@ public class BlankBoard extends AbstractBoard {
|
||||
}
|
||||
|
||||
g2d.setStroke(ObjectCache.getInstance().fetchBasicStroke(1));
|
||||
if (componentState != ComponentState.DRAGGING) {
|
||||
Composite oldComposite = g2d.getComposite();
|
||||
if (alpha < MAX_ALPHA) {
|
||||
g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 1f * alpha / MAX_ALPHA));
|
||||
}
|
||||
g2d.setColor(boardColor);
|
||||
if (getType() == Type.SQUARE)
|
||||
g2d.fillRect((int)firstPoint.getX(), (int)firstPoint.getY(), (int)(finalSecondPoint.getX() - firstPoint.getX()), (int)(finalSecondPoint.getY() - firstPoint.getY()));
|
||||
else
|
||||
g2d.fillOval((int)firstPoint.getX(), (int)firstPoint.getY(), (int)(finalSecondPoint.getX() - firstPoint.getX()), (int)(finalSecondPoint.getY() - firstPoint.getY()));
|
||||
g2d.setComposite(oldComposite);
|
||||
// if (componentState != ComponentState.DRAGGING) {
|
||||
Composite oldComposite = g2d.getComposite();
|
||||
if (alpha < MAX_ALPHA) {
|
||||
g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 1f * alpha / MAX_ALPHA));
|
||||
}
|
||||
g2d.setColor(boardColor);
|
||||
if (getType() == Type.SQUARE)
|
||||
g2d.fillRect((int)firstPoint.getX(), (int)firstPoint.getY(), (int)(finalSecondPoint.getX() - firstPoint.getX()), (int)(finalSecondPoint.getY() - firstPoint.getY()));
|
||||
else
|
||||
g2d.fillOval((int)firstPoint.getX(), (int)firstPoint.getY(), (int)(finalSecondPoint.getX() - firstPoint.getX()), (int)(finalSecondPoint.getY() - firstPoint.getY()));
|
||||
g2d.setComposite(oldComposite);
|
||||
// }
|
||||
// Do not track any changes that follow because the whole board has been
|
||||
// tracked so far.
|
||||
drawingObserver.stopTracking();
|
||||
|
||||
@@ -64,8 +64,15 @@ public class ChassisPanel extends AbstractShape {
|
||||
g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 1f * alpha / MAX_ALPHA));
|
||||
}
|
||||
g2d.setColor(color);
|
||||
g2d.fillRoundRect((int)firstPoint.getX(), (int)firstPoint.getY(), (int)(secondPoint.getX() - firstPoint.getX()), (int)(secondPoint.getY() - firstPoint.getY()), radius,
|
||||
radius);
|
||||
if (radius == 0) {
|
||||
g2d.fillRect((int) firstPoint.getX(), (int) firstPoint.getY(),
|
||||
(int) (secondPoint.getX() - firstPoint.getX()),
|
||||
(int) (secondPoint.getY() - firstPoint.getY()));
|
||||
} else {
|
||||
g2d.fillRoundRect((int) firstPoint.getX(), (int) firstPoint.getY(),
|
||||
(int) (secondPoint.getX() - firstPoint.getX()),
|
||||
(int) (secondPoint.getY() - firstPoint.getY()), radius, radius);
|
||||
}
|
||||
g2d.setComposite(oldComposite);
|
||||
}
|
||||
// Do not track any changes that follow because the whole rect has been
|
||||
@@ -73,8 +80,15 @@ public class ChassisPanel extends AbstractShape {
|
||||
drawingObserver.stopTracking();
|
||||
g2d.setColor(componentState == ComponentState.SELECTED || componentState == ComponentState.DRAGGING ? SELECTION_COLOR
|
||||
: borderColor);
|
||||
g2d.drawRoundRect((int)(firstPoint.getX()), (int)(firstPoint.getY()), (int)(secondPoint.getX() - firstPoint.getX()), (int)(secondPoint.getY() - firstPoint.getY()), radius,
|
||||
radius);
|
||||
if (radius == 0) {
|
||||
g2d.drawRect((int) (firstPoint.getX()), (int) (firstPoint.getY()),
|
||||
(int) (secondPoint.getX() - firstPoint.getX()),
|
||||
(int) (secondPoint.getY() - firstPoint.getY()));
|
||||
} else {
|
||||
g2d.drawRoundRect((int) (firstPoint.getX()), (int) (firstPoint.getY()),
|
||||
(int) (secondPoint.getX() - firstPoint.getX()),
|
||||
(int) (secondPoint.getY() - firstPoint.getY()), radius, radius);
|
||||
}
|
||||
}
|
||||
|
||||
@EditableProperty(name = "Radius")
|
||||
|
||||
@@ -28,6 +28,7 @@ import com.bancika.gerberwriter.GerberFunctions;
|
||||
import org.diylc.common.ObjectCache;
|
||||
import org.diylc.common.PCBLayer;
|
||||
import org.diylc.components.AbstractLeadedComponent;
|
||||
import org.diylc.components.LineEndingStyle;
|
||||
import org.diylc.components.transform.SimpleComponentTransformer;
|
||||
import org.diylc.core.ComponentState;
|
||||
import org.diylc.core.CreationMethod;
|
||||
@@ -58,6 +59,7 @@ public class CopperTrace extends AbstractLeadedComponent<Void> implements ILayer
|
||||
|
||||
private Size thickness = THICKNESS;
|
||||
private PCBLayer layer = PCBLayer._1;
|
||||
private LineEndingStyle endingStyle = LineEndingStyle.Round;
|
||||
|
||||
public CopperTrace() {
|
||||
super();
|
||||
@@ -202,6 +204,19 @@ public class CopperTrace extends AbstractLeadedComponent<Void> implements ILayer
|
||||
return false;
|
||||
}
|
||||
|
||||
@EditableProperty(name = "Ending Style")
|
||||
@Override
|
||||
public LineEndingStyle getEndingStyle() {
|
||||
if (endingStyle == null) {
|
||||
endingStyle = LineEndingStyle.Round;
|
||||
}
|
||||
return endingStyle;
|
||||
}
|
||||
|
||||
public void setEndingStyle(LineEndingStyle endingStyle) {
|
||||
this.endingStyle = endingStyle;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<GerberRenderMode> getGerberRenderModes() {
|
||||
return EnumSet.of(GerberRenderMode.Normal);
|
||||
|
||||
+17
-3
@@ -17,8 +17,7 @@
|
||||
*/
|
||||
package org.diylc.components.connectivity;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.*;
|
||||
import java.awt.geom.Path2D;
|
||||
import java.util.EnumSet;
|
||||
import java.util.Set;
|
||||
@@ -29,6 +28,8 @@ import org.diylc.common.LineStyle;
|
||||
import org.diylc.common.ObjectCache;
|
||||
import org.diylc.common.PCBLayer;
|
||||
import org.diylc.components.AbstractCurvedComponent;
|
||||
import org.diylc.components.AbstractLeadedComponent;
|
||||
import org.diylc.components.LineEndingStyle;
|
||||
import org.diylc.components.transform.SimpleComponentTransformer;
|
||||
import org.diylc.core.ComponentState;
|
||||
import org.diylc.core.IDIYComponent;
|
||||
@@ -58,6 +59,7 @@ public class CurvedTrace extends AbstractCurvedComponent<Void> implements ILayer
|
||||
|
||||
protected Size size = SIZE;
|
||||
private PCBLayer layer = PCBLayer._1;
|
||||
private LineEndingStyle endingStyle = LineEndingStyle.Round;
|
||||
|
||||
@Override
|
||||
protected Color getDefaultColor() {
|
||||
@@ -67,7 +69,7 @@ public class CurvedTrace extends AbstractCurvedComponent<Void> implements ILayer
|
||||
@Override
|
||||
protected void drawCurve(Path2D curve, Graphics2D g2d, ComponentState componentState, IDrawingObserver drawingObserver) {
|
||||
float thickness = (float) size.convertToPixels();
|
||||
g2d.setStroke(ObjectCache.getInstance().fetchBasicStroke(thickness));
|
||||
g2d.setStroke(ObjectCache.getInstance().fetchStroke(thickness, getEndingStyle() == LineEndingStyle.Round ? BasicStroke.CAP_ROUND : BasicStroke.CAP_BUTT));
|
||||
Color curveColor =
|
||||
componentState == ComponentState.SELECTED || componentState == ComponentState.DRAGGING ? SELECTION_COLOR
|
||||
: color;
|
||||
@@ -97,6 +99,18 @@ public class CurvedTrace extends AbstractCurvedComponent<Void> implements ILayer
|
||||
public void setLayer(PCBLayer layer) {
|
||||
this.layer = layer;
|
||||
}
|
||||
|
||||
@EditableProperty(name = "Ending Style")
|
||||
public LineEndingStyle getEndingStyle() {
|
||||
if (endingStyle == null) {
|
||||
endingStyle = LineEndingStyle.Round;
|
||||
}
|
||||
return endingStyle;
|
||||
}
|
||||
|
||||
public void setEndingStyle(LineEndingStyle endingStyle) {
|
||||
this.endingStyle = endingStyle;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getLayerId() {
|
||||
|
||||
+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]);
|
||||
}
|
||||
|
||||
|
||||
+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);
|
||||
}
|
||||
}
|
||||
+11
-4
@@ -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)
|
||||
|
||||
+2
-1
@@ -42,6 +42,7 @@ import org.diylc.common.Orientation;
|
||||
import org.diylc.common.VerticalAlignment;
|
||||
import org.diylc.components.AbstractMultiPartComponent;
|
||||
import org.diylc.components.electromechanical.OpenJack1_4.OpenJackType;
|
||||
import org.diylc.components.transform.OpenJackTransformer;
|
||||
import org.diylc.core.ComponentState;
|
||||
import org.diylc.core.IDIYComponent;
|
||||
import org.diylc.core.IDrawingObserver;
|
||||
@@ -56,7 +57,7 @@ import org.diylc.utils.Constants;
|
||||
|
||||
@ComponentDescriptor(name = "Open 1/4\" Jack", category = "Electro-Mechanical", author = "Branislav Stojkovic",
|
||||
description = "Switchcraft-style open panel mount 1/4\" phono jack, stereo and mono",
|
||||
zOrder = IDIYComponent.COMPONENT, instanceNamePrefix = "J", enableCache = true)
|
||||
zOrder = IDIYComponent.COMPONENT, instanceNamePrefix = "J", transformer = OpenJackTransformer.class, enableCache = true)
|
||||
public class OpenJack1_4 extends AbstractMultiPartComponent<OpenJackType> {
|
||||
|
||||
private static final float FONT_SCALE = 0.8f;
|
||||
|
||||
+2
-1
@@ -42,6 +42,7 @@ import org.diylc.common.Orientation;
|
||||
import org.diylc.common.VerticalAlignment;
|
||||
import org.diylc.components.AbstractMultiPartComponent;
|
||||
import org.diylc.components.electromechanical.OpenJack1_8.OpenJackType;
|
||||
import org.diylc.components.transform.OpenJackTransformer;
|
||||
import org.diylc.core.ComponentState;
|
||||
import org.diylc.core.IDIYComponent;
|
||||
import org.diylc.core.IDrawingObserver;
|
||||
@@ -56,7 +57,7 @@ import org.diylc.utils.Constants;
|
||||
|
||||
@ComponentDescriptor(name = "Open 1/8\" Jack", category = "Electro-Mechanical", author = "Branislav Stojkovic",
|
||||
description = "Switchcraft-style open panel mount 1/8\" phono jack, stereo and mono",
|
||||
zOrder = IDIYComponent.COMPONENT, instanceNamePrefix = "J", enableCache = true)
|
||||
zOrder = IDIYComponent.COMPONENT, instanceNamePrefix = "J", transformer = OpenJackTransformer.class, enableCache = true)
|
||||
public class OpenJack1_8 extends AbstractMultiPartComponent<OpenJackType> {
|
||||
|
||||
private static final float FONT_SCALE = 0.6f;
|
||||
|
||||
+2
-2
@@ -374,13 +374,13 @@ public class RotarySwitchOpen extends AbstractAngledComponent<RotarySwitchOpenTy
|
||||
// Draw body
|
||||
if (componentState != ComponentState.DRAGGING) {
|
||||
g2d.setColor(outlineMode ? Constants.TRANSPARENT_COLOR : getColor());
|
||||
drawingObserver.startTracking();
|
||||
g2d.fill(body[0]);
|
||||
drawingObserver.stopTracking();
|
||||
}
|
||||
|
||||
g2d.setColor(finalBorderColor);
|
||||
drawingObserver.startTracking();
|
||||
g2d.draw(body[0]);
|
||||
drawingObserver.stopTracking();
|
||||
|
||||
// Draw pins
|
||||
for (int i = 1; i <= secondLevelStart; i++) {
|
||||
|
||||
+133
-25
@@ -35,13 +35,10 @@ import org.diylc.common.IPlugInPort;
|
||||
import org.diylc.common.ObjectCache;
|
||||
import org.diylc.common.Orientation;
|
||||
import org.diylc.components.AbstractLabeledComponent;
|
||||
import org.diylc.core.ComponentState;
|
||||
import org.diylc.core.IDIYComponent;
|
||||
import org.diylc.core.IDrawingObserver;
|
||||
import org.diylc.core.Project;
|
||||
import org.diylc.core.Theme;
|
||||
import org.diylc.core.VisibilityPolicy;
|
||||
import org.diylc.components.guitar.LeverSwitchPositionPropertyValueSource;
|
||||
import org.diylc.core.*;
|
||||
import org.diylc.core.annotations.ComponentDescriptor;
|
||||
import org.diylc.core.annotations.DynamicEditableProperty;
|
||||
import org.diylc.core.annotations.EditableProperty;
|
||||
import org.diylc.core.annotations.KeywordPolicy;
|
||||
import org.diylc.core.gerber.IGerberComponentSimple;
|
||||
@@ -53,22 +50,21 @@ import org.diylc.utils.Constants;
|
||||
category = "Electro-Mechanical", instanceNamePrefix = "SW",
|
||||
description = "4-pin tactile momentary switch", zOrder = IDIYComponent.COMPONENT,
|
||||
keywordPolicy = KeywordPolicy.SHOW_VALUE, enableCache = true)
|
||||
public class TactileMicroSwitch extends AbstractLabeledComponent<String> implements IGerberComponentSimple {
|
||||
public class TactileMicroSwitch extends AbstractLabeledComponent<String> implements IGerberComponentSimple,
|
||||
ISwitch {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public static Color BODY_COLOR = Color.lightGray;
|
||||
public static Color BORDER_COLOR = Color.gray.darker();
|
||||
public static Color PIN_COLOR = METAL_COLOR;
|
||||
public static Color PIN_BORDER_COLOR = PIN_COLOR.darker();
|
||||
public static Color INDENT_COLOR = Color.gray.darker();
|
||||
public static Color LABEL_COLOR = Color.white;
|
||||
public static Size PIN_SIZE = new Size(0.04d, SizeUnit.in);
|
||||
public static int PIN_COUNT = 4;
|
||||
public static Size CASE_SIZE = new Size(6d, SizeUnit.mm);
|
||||
public static Size INDENT_SIZE = new Size(3d, SizeUnit.mm);
|
||||
private static Size PIN_SPACING = new Size(4.5d, SizeUnit.mm);
|
||||
private static Size ROW_SPACING = new Size(6.5d, SizeUnit.mm);
|
||||
private static final Color BODY_COLOR = Color.lightGray;
|
||||
private static final Color BORDER_COLOR = Color.gray.darker();
|
||||
private static final Color PIN_COLOR = METAL_COLOR;
|
||||
private static final Color PIN_BORDER_COLOR = PIN_COLOR.darker();
|
||||
private static final Color INDENT_COLOR = Color.gray.darker();
|
||||
private static final Color LABEL_COLOR = Color.white;
|
||||
private static final Size PIN_SIZE = new Size(0.04d, SizeUnit.in);
|
||||
private static final Size CASE_SIZE = new Size(6d, SizeUnit.mm);
|
||||
private static final Size PIN_SPACING = new Size(4.5d, SizeUnit.mm);
|
||||
private static final Size ROW_SPACING = new Size(6.5d, SizeUnit.mm);
|
||||
|
||||
private String value = "";
|
||||
private Orientation orientation = Orientation.DEFAULT;
|
||||
@@ -80,6 +76,12 @@ public class TactileMicroSwitch extends AbstractLabeledComponent<String> impleme
|
||||
private Color labelColor = LABEL_COLOR;
|
||||
transient private Area[] body;
|
||||
|
||||
private Size caseSize;
|
||||
private Size pinSpacing;
|
||||
private Size rowSpacing;
|
||||
private PinPairingMode pinPairingMode;
|
||||
private int selectedPosition;
|
||||
|
||||
public TactileMicroSwitch() {
|
||||
super();
|
||||
updateControlPoints();
|
||||
@@ -146,10 +148,11 @@ public class TactileMicroSwitch extends AbstractLabeledComponent<String> impleme
|
||||
|
||||
private void updateControlPoints() {
|
||||
Point2D firstPoint = controlPoints[0];
|
||||
int PIN_COUNT = 4;
|
||||
controlPoints = new Point2D[PIN_COUNT];
|
||||
controlPoints[0] = firstPoint;
|
||||
double pinSpacing = PIN_SPACING.convertToPixels();
|
||||
double rowSpacing = ROW_SPACING.convertToPixels();
|
||||
double pinSpacing = getPinSpacing().convertToPixels();
|
||||
double rowSpacing = getRowSpacing().convertToPixels();
|
||||
// Update control points.
|
||||
double dx1;
|
||||
double dy1;
|
||||
@@ -198,8 +201,8 @@ public class TactileMicroSwitch extends AbstractLabeledComponent<String> impleme
|
||||
double y = controlPoints[0].getY();
|
||||
double width;
|
||||
double height;
|
||||
double caseSize = CASE_SIZE.convertToPixels();
|
||||
double indentSize = INDENT_SIZE.convertToPixels();
|
||||
double caseSize = getCaseSize().convertToPixels();
|
||||
double actuatorSize = caseSize / 2;
|
||||
|
||||
width = height = caseSize;
|
||||
x = (controlPoints[0].getX() + controlPoints[3].getX() - width) / 2;
|
||||
@@ -207,7 +210,7 @@ public class TactileMicroSwitch extends AbstractLabeledComponent<String> impleme
|
||||
|
||||
body[0] = new Area(new Rectangle2D.Double(x, y, width, height));
|
||||
|
||||
width = height = indentSize;
|
||||
width = height = actuatorSize;
|
||||
x = (controlPoints[0].getX() + controlPoints[3].getX() - width) / 2;
|
||||
y = (controlPoints[0].getY() + controlPoints[3].getY() - height) / 2;
|
||||
|
||||
@@ -244,7 +247,7 @@ public class TactileMicroSwitch extends AbstractLabeledComponent<String> impleme
|
||||
g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 1f * alpha / MAX_ALPHA));
|
||||
}
|
||||
g2d.setColor(outlineMode ? Constants.TRANSPARENT_COLOR : getBodyColor());
|
||||
drawingObserver.stopTracking();
|
||||
drawingObserver.startTracking();
|
||||
g2d.fill(mainArea);
|
||||
drawingObserver.stopTracking();
|
||||
|
||||
@@ -391,6 +394,59 @@ public class TactileMicroSwitch extends AbstractLabeledComponent<String> impleme
|
||||
this.labelColor = labelColor;
|
||||
}
|
||||
|
||||
@EditableProperty(name = "Case Size")
|
||||
public Size getCaseSize() {
|
||||
if (caseSize == null) {
|
||||
caseSize = CASE_SIZE;
|
||||
}
|
||||
return caseSize;
|
||||
}
|
||||
|
||||
public void setCaseSize(Size caseSize) {
|
||||
this.caseSize = caseSize;
|
||||
this.body = null;
|
||||
}
|
||||
|
||||
@EditableProperty(name = "Pin Spacing")
|
||||
public Size getPinSpacing() {
|
||||
if (pinSpacing == null) {
|
||||
pinSpacing = PIN_SPACING;
|
||||
}
|
||||
return pinSpacing;
|
||||
}
|
||||
|
||||
public void setPinSpacing(Size pinSpacing) {
|
||||
this.pinSpacing = pinSpacing;
|
||||
this.body = null;
|
||||
updateControlPoints();
|
||||
}
|
||||
|
||||
@EditableProperty(name = "Row Spacing")
|
||||
public Size getRowSpacing() {
|
||||
if (rowSpacing == null) {
|
||||
rowSpacing = ROW_SPACING;
|
||||
}
|
||||
return rowSpacing;
|
||||
}
|
||||
|
||||
public void setRowSpacing(Size rowSpacing) {
|
||||
this.rowSpacing = rowSpacing;
|
||||
this.body = null;
|
||||
updateControlPoints();
|
||||
}
|
||||
|
||||
@EditableProperty(name = "Pin Pairing Mode")
|
||||
public PinPairingMode getPinPairingMode() {
|
||||
if (pinPairingMode == null) {
|
||||
pinPairingMode = PinPairingMode.Opposite;
|
||||
}
|
||||
return pinPairingMode;
|
||||
}
|
||||
|
||||
public void setPinPairingMode(PinPairingMode pinPairingMode) {
|
||||
this.pinPairingMode = pinPairingMode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canPointMoveFreely(int pointIndex) {
|
||||
return false;
|
||||
@@ -418,4 +474,56 @@ public class TactileMicroSwitch extends AbstractLabeledComponent<String> impleme
|
||||
return new Rectangle2D.Double(minX - margin, minY - margin, maxX - minX + 2 * margin,
|
||||
maxY - minY + 2 * margin);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPositionCount() {
|
||||
return 2;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPositionName(int position) {
|
||||
return position == 0 ? "Released" : "Pressed";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean arePointsConnected(int index1, int index2, int position) {
|
||||
// Only connect pins when switch is pressed (position = 1)
|
||||
if (position != 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
PinPairingMode mode = getPinPairingMode();
|
||||
|
||||
switch (mode) {
|
||||
case Opposite:
|
||||
// Pins on opposite sides (same row): 0-2 and 1-3
|
||||
return (index1 == 0 && index2 == 2) || (index1 == 2 && index2 == 0) ||
|
||||
(index1 == 1 && index2 == 3) || (index1 == 3 && index2 == 1);
|
||||
case Diagonal:
|
||||
// Diagonally opposite pins: 0-3 and 1-2
|
||||
return (index1 == 0 && index2 == 3) || (index1 == 3 && index2 == 0) ||
|
||||
(index1 == 1 && index2 == 2) || (index1 == 2 && index2 == 1);
|
||||
case Adjacent:
|
||||
// Pins next to each other (same column): 0-1 and 2-3
|
||||
return (index1 == 0 && index2 == 1) || (index1 == 1 && index2 == 0) ||
|
||||
(index1 == 2 && index2 == 3) || (index1 == 3 && index2 == 2);
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@DynamicEditableProperty(source = TactileMicroSwitchPositionPropertyValueSource.class)
|
||||
@EditableProperty(name = "Selected Position")
|
||||
@Override
|
||||
public Integer getSelectedPosition() {
|
||||
return selectedPosition;
|
||||
}
|
||||
|
||||
public void setSelectedPosition(Integer selectedPosition) {
|
||||
this.selectedPosition = selectedPosition;
|
||||
}
|
||||
|
||||
public enum PinPairingMode {
|
||||
Opposite, Diagonal, Adjacent;
|
||||
}
|
||||
}
|
||||
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
package org.diylc.components.electromechanical;
|
||||
|
||||
import org.diylc.common.PropertyWrapper;
|
||||
import org.diylc.components.guitar.LeverSwitch;
|
||||
import org.diylc.core.AbstractDynamicPropertyValueSource;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
public class TactileMicroSwitchPositionPropertyValueSource extends AbstractDynamicPropertyValueSource {
|
||||
|
||||
@Override
|
||||
public List<Object> getAvailableValues() {
|
||||
return List.of("Released", "Pressed");
|
||||
}
|
||||
}
|
||||
@@ -59,8 +59,8 @@ import org.diylc.core.measures.SizeUnit;
|
||||
import org.diylc.utils.Constants;
|
||||
import org.diylc.utils.SwitchUtils;
|
||||
|
||||
@ComponentDescriptor(name = "Freeway 3X3-03 Toggle", category = "Guitar",
|
||||
author = "Branislav Stojkovic", description = "Freeway 3X3-03 Toggle Switch",
|
||||
@ComponentDescriptor(name = "Freeway 3X3-03 Switch", category = "Guitar",
|
||||
author = "Branislav Stojkovic", description = "Freeway 3X3-03 (Standard) Toggle Switch",
|
||||
zOrder = IDIYComponent.COMPONENT, instanceNamePrefix = "SW",
|
||||
keywordPolicy = KeywordPolicy.SHOW_TAG, keywordTag = "Guitar Wiring Diagram",
|
||||
transformer = Freeway3x3_03Transformer.class, enableCache = true)
|
||||
|
||||
@@ -0,0 +1,493 @@
|
||||
/*
|
||||
*
|
||||
* DIY Layout Creator (DIYLC). Copyright (c) 2009-2025 held jointly by the individual authors.
|
||||
*
|
||||
* This file is part of DIYLC.
|
||||
*
|
||||
* DIYLC is free software: you can redistribute it and/or modify it under the terms of the GNU
|
||||
* General Public License as published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* DIYLC is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
|
||||
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
|
||||
* Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with DIYLC. If not, see
|
||||
* <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.diylc.components.guitar;
|
||||
|
||||
import com.kitfox.svg.SVGDiagram;
|
||||
import com.kitfox.svg.SVGElement;
|
||||
import com.kitfox.svg.SVGUniverse;
|
||||
import com.kitfox.svg.ShapeElement;
|
||||
import org.apache.log4j.Logger;
|
||||
import org.apache.poi.util.IOUtils;
|
||||
import org.diylc.appframework.miscutils.ConfigurationManager;
|
||||
import org.diylc.awt.StringUtils;
|
||||
import org.diylc.common.*;
|
||||
import org.diylc.components.AbstractTransparentComponent;
|
||||
import org.diylc.components.transform.Freeway3x3_03Transformer;
|
||||
import org.diylc.core.*;
|
||||
import org.diylc.core.annotations.ComponentDescriptor;
|
||||
import org.diylc.core.annotations.DynamicEditableProperty;
|
||||
import org.diylc.core.annotations.EditableProperty;
|
||||
import org.diylc.core.annotations.KeywordPolicy;
|
||||
import org.diylc.core.measures.Size;
|
||||
import org.diylc.core.measures.SizeUnit;
|
||||
import org.diylc.utils.Constants;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.geom.*;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
|
||||
import static org.diylc.core.measures.SizeUnit.px;
|
||||
|
||||
@ComponentDescriptor(name = "Freeway 3X3-05 (Ultra) Switch", category = "Guitar",
|
||||
author = "Martin Morrell", description = "Freeway 3X3-05 (Ultra) Toggle Switch",
|
||||
zOrder = IDIYComponent.COMPONENT, instanceNamePrefix = "SW",
|
||||
keywordPolicy = KeywordPolicy.SHOW_TAG, keywordTag = "Guitar Wiring Diagram",
|
||||
transformer = Freeway3x3_03Transformer.class, enableCache = false)
|
||||
public class Freeway3x3_05 extends AbstractTransparentComponent<Void> implements ISwitch {
|
||||
|
||||
private static final Logger LOG = Logger.getLogger(Freeway3x3_05.class);
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private static Color BASE_COLOR = FR4_COLOR;// Color.DARK_GRAY;
|
||||
private static Color PAD_COLOR = COPPER_COLOR;
|
||||
private static Color LABEL_COLOR = Color.WHITE;
|
||||
private static Color CASE_COLOR = METAL_COLOR;
|
||||
private static Color MARKER_COLOR = Color.lightGray;
|
||||
|
||||
private static final double DIAMETER_SWITCH = 34.0;
|
||||
private static final double PAD_RADIUS = 15.3;
|
||||
private static final double PAD_SIZE = 1.5;
|
||||
private static final int NUM_CONTACTS = 28;
|
||||
private static final double CONTACT_SPACING_RAD = Math.PI * (360.0 / NUM_CONTACTS) / 180.0;
|
||||
private static final double CONTACT_SPACING_INITIAL = (CONTACT_SPACING_RAD * 14) + CONTACT_SPACING_RAD / 2;
|
||||
private static final double GROUND_DIAMETER = 20;
|
||||
private static final double CENTRE_LEN = 17;
|
||||
private static final double CENTRE_WID = 10;
|
||||
private static final double MARKER_OFFSET = 3.1;
|
||||
|
||||
private static final String[] PAD_NAMES = new String[] {
|
||||
"1B", "2B", "3B", "6B", "5B", "4B", "CB", "TB", "6B", "5B", "4B", "1B", "2B", "3B",
|
||||
"6A", "5A", "4A", "1A", "2A", "3A", "TA", "CA", "1A", "2A", "3A", "6A", "5A", "4A"
|
||||
};
|
||||
|
||||
private static final AffineTransform SCALE_TX = AffineTransform.getScaleInstance(0.615d, 0.615d);
|
||||
|
||||
private Point2D[] controlPoints = new Point2D[] {new Point2D.Double(0, 0)};
|
||||
|
||||
private Orientation orientation = Orientation.DEFAULT;
|
||||
|
||||
private Integer selectedPosition;
|
||||
private Boolean showMarkers;
|
||||
|
||||
private transient SVGDiagram svgDiagram;
|
||||
private transient List<Shape> pads;
|
||||
private transient List<Shape> labels;
|
||||
private transient Shape base;
|
||||
private transient Shape caseShape;
|
||||
private transient double[] xOffsetsPx;
|
||||
private transient double[] yOffsetsPx;
|
||||
|
||||
public Freeway3x3_05() {
|
||||
super();
|
||||
updateControlPoints();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getControlPointNodeName(int index) {
|
||||
return PAD_NAMES[index];
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(Graphics2D g2d, ComponentState componentState, boolean outlineMode,
|
||||
Project project, IDrawingObserver drawingObserver) {
|
||||
|
||||
double x = controlPoints[0].getX();
|
||||
double y = controlPoints[0].getY();
|
||||
|
||||
if (getOrientation() != Orientation.DEFAULT) {
|
||||
double theta = orientation.toRadians();
|
||||
g2d.rotate(theta, x, y);
|
||||
}
|
||||
|
||||
Composite oldComposite = g2d.getComposite();
|
||||
if (alpha < MAX_ALPHA) {
|
||||
g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 1f * alpha / MAX_ALPHA));
|
||||
}
|
||||
|
||||
double offsetX = getXOffsetsPx()[0];
|
||||
double offsetY = getYOffsetsPx()[0];
|
||||
//double origin = DIAMETER_SWITCH / 2;
|
||||
|
||||
int diameter = (int)(new Size(DIAMETER_SWITCH, SizeUnit.mm)).convertToPixels();
|
||||
|
||||
double originX = x - offsetX + diameter / 2.0;
|
||||
double originY = y - offsetY + diameter / 2.0;
|
||||
|
||||
//Main circular shape
|
||||
Color finalBorderColor;
|
||||
if (outlineMode) {
|
||||
Theme theme = (Theme) ConfigurationManager.getInstance().readObject(IPlugInPort.THEME_KEY,
|
||||
Constants.DEFAULT_THEME);
|
||||
finalBorderColor =
|
||||
componentState == ComponentState.SELECTED || componentState == ComponentState.DRAGGING
|
||||
? SELECTION_COLOR
|
||||
: theme.getOutlineColor();
|
||||
} else {
|
||||
finalBorderColor =
|
||||
componentState == ComponentState.SELECTED || componentState == ComponentState.DRAGGING
|
||||
? SELECTION_COLOR
|
||||
: BASE_COLOR.darker();
|
||||
}
|
||||
Shape base = new Ellipse2D.Double( originX, originY, diameter, diameter);
|
||||
drawingObserver.startTracking();
|
||||
g2d.setColor(BASE_COLOR);
|
||||
g2d.fill(base);
|
||||
drawingObserver.stopTracking();
|
||||
g2d.setStroke(new BasicStroke(1f));
|
||||
g2d.setColor(finalBorderColor);
|
||||
g2d.draw(base);
|
||||
|
||||
//Ground circular shape
|
||||
int gndDiameter = (int)(new Size(GROUND_DIAMETER, SizeUnit.mm)).convertToPixels();
|
||||
int gndShift = (int)(new Size( (DIAMETER_SWITCH - GROUND_DIAMETER) / 2, SizeUnit.mm).convertToPixels());
|
||||
Shape gnd = new Ellipse2D.Double( originX + gndShift, originY + gndShift, gndDiameter, gndDiameter);
|
||||
g2d.setPaint(PAD_COLOR);
|
||||
g2d.fill(gnd);
|
||||
g2d.setColor(PAD_COLOR.darker());
|
||||
g2d.draw(gnd);
|
||||
|
||||
//Centre rectangle casing block
|
||||
int rectLen = (int)(new Size(CENTRE_LEN, SizeUnit.mm)).convertToPixels();
|
||||
int rectWid = (int)(new Size(CENTRE_WID, SizeUnit.mm)).convertToPixels();
|
||||
|
||||
int rectShiftX = (int)(new Size( (DIAMETER_SWITCH - CENTRE_WID) / 2, SizeUnit.mm).convertToPixels());
|
||||
int rectShiftY = (int)(new Size( (DIAMETER_SWITCH - CENTRE_LEN) / 2, SizeUnit.mm).convertToPixels());
|
||||
|
||||
int rectangleArc = (int)(new Size(1.0, SizeUnit.mm)).convertToPixels();
|
||||
|
||||
Shape casing = new RoundRectangle2D.Double(originX + rectShiftX, originY + rectShiftY, rectWid, rectLen, rectangleArc, rectangleArc);
|
||||
|
||||
g2d.setPaint(CASE_COLOR);
|
||||
g2d.fill(casing);
|
||||
g2d.setColor(CASE_COLOR.darker());
|
||||
g2d.draw(casing);
|
||||
|
||||
//Pads
|
||||
double circleShift = (int)(new Size( DIAMETER_SWITCH / 2, SizeUnit.mm).convertToPixels());
|
||||
double circleCentreX = originX + circleShift;
|
||||
double circleCentreY = originY + circleShift;
|
||||
double radChange = CONTACT_SPACING_RAD * 0.45;
|
||||
double extendedRadius = (int)new Size(PAD_RADIUS + PAD_SIZE, SizeUnit.mm).convertToPixels();
|
||||
double reducedRadius = (int)new Size(PAD_RADIUS - PAD_SIZE, SizeUnit.mm).convertToPixels();
|
||||
for (int i = 0; i < NUM_CONTACTS; i++) {
|
||||
double rad = ((i + 0.5) * CONTACT_SPACING_RAD);
|
||||
|
||||
double rotExX = Math.cos(rad) * (extendedRadius * 1.01) + circleCentreX;
|
||||
double rotExY = Math.sin(rad) * (extendedRadius * 1.01) + circleCentreY;
|
||||
double rotExPosX = Math.cos(rad + radChange) * extendedRadius + circleCentreX;
|
||||
double rotExPosY = Math.sin(rad + radChange) * extendedRadius + circleCentreY;
|
||||
double rotExNegX = Math.cos(rad - radChange) * extendedRadius + circleCentreX;
|
||||
double rotExNegY = Math.sin(rad - radChange) * extendedRadius + circleCentreY;
|
||||
double rotReX = Math.cos(rad) * (reducedRadius * 1.01) + circleCentreX;
|
||||
double rotReY = Math.sin(rad) * (reducedRadius * 1.01) + circleCentreY;
|
||||
double rotRePosX = Math.cos(rad + radChange) * reducedRadius + circleCentreX;
|
||||
double rotRePosY = Math.sin(rad + radChange) * reducedRadius + circleCentreY;
|
||||
double rotReNegX = Math.cos(rad - radChange) * reducedRadius + circleCentreX;
|
||||
double rotReNegY = Math.sin(rad - radChange) * reducedRadius + circleCentreY;
|
||||
|
||||
Path2D pad = new Path2D.Double();
|
||||
pad.moveTo(rotExNegX, rotExNegY);
|
||||
pad.quadTo(rotExX, rotExY, rotExPosX, rotExPosY);
|
||||
pad.lineTo(rotRePosX, rotRePosY);
|
||||
pad.quadTo(rotReX, rotReY,rotReNegX, rotReNegY);
|
||||
pad.lineTo(rotExNegX, rotExNegY);
|
||||
pad.closePath();
|
||||
|
||||
g2d.setPaint(PAD_COLOR);
|
||||
g2d.fill(pad);
|
||||
g2d.setColor(PAD_COLOR.darker());
|
||||
g2d.draw(pad);
|
||||
}
|
||||
|
||||
//Labels
|
||||
double offset = (int) new Size(MARKER_OFFSET, SizeUnit.mm).convertToPixels();
|
||||
for (int i = 0; i < NUM_CONTACTS; i++) {
|
||||
Point2D p = getControlPoint(i);
|
||||
|
||||
double px = Math.sin(CONTACT_SPACING_INITIAL + i * CONTACT_SPACING_RAD) * (PAD_RADIUS - MARKER_OFFSET);
|
||||
double py = Math.cos(CONTACT_SPACING_INITIAL + i * CONTACT_SPACING_RAD) * (PAD_RADIUS - MARKER_OFFSET);
|
||||
|
||||
g2d.setPaint(LABEL_COLOR);
|
||||
StringUtils.drawCenteredText(g2d, PAD_NAMES[i],
|
||||
(new Size(px + DIAMETER_SWITCH / 2.0, SizeUnit.mm).convertToPixels()) + originX,
|
||||
(new Size(py + DIAMETER_SWITCH / 2.0, SizeUnit.mm).convertToPixels()) + originY,
|
||||
HorizontalAlignment.CENTER, VerticalAlignment.CENTER);
|
||||
}
|
||||
|
||||
//Add other text
|
||||
int makeLen = (int)(new Size(12.0, SizeUnit.mm)).convertToPixels();
|
||||
int modelLen = (int)(new Size(8.0, SizeUnit.mm)).convertToPixels();
|
||||
double mWidth = 1.8;
|
||||
int modelWid = (int)(new Size(mWidth, SizeUnit.mm)).convertToPixels();
|
||||
|
||||
int makeShiftX = (int)(new Size( (DIAMETER_SWITCH / 2) - 7.25, SizeUnit.mm).convertToPixels());
|
||||
int makeShiftY = (int)(new Size( (DIAMETER_SWITCH / 2) - 6.0, SizeUnit.mm).convertToPixels());
|
||||
int modelShiftX = (int)(new Size( (DIAMETER_SWITCH / 2) + 5.25, SizeUnit.mm).convertToPixels());
|
||||
int modelShiftY = (int)(new Size( (DIAMETER_SWITCH / 2) - 4.0, SizeUnit.mm).convertToPixels());
|
||||
|
||||
int modelArc = (int)(new Size(mWidth, SizeUnit.mm)).convertToPixels();
|
||||
|
||||
Shape make = new RoundRectangle2D.Double(originX + makeShiftX, originY + makeShiftY, modelWid, makeLen, modelArc, modelArc);
|
||||
Shape model = new RoundRectangle2D.Double(originX + modelShiftX, originY + modelShiftY, modelWid, modelLen, modelArc, modelArc);
|
||||
|
||||
g2d.setPaint(BASE_COLOR);
|
||||
g2d.fill(make);
|
||||
g2d.fill(model);
|
||||
g2d.setColor(PAD_COLOR.darker());
|
||||
g2d.draw(make);
|
||||
g2d.draw(model);
|
||||
|
||||
/*String makeTxt = "FREE-WAY·ULTRA";
|
||||
String modelTxt = "3X3-05";
|
||||
g2d.setPaint(LABEL_COLOR);
|
||||
StringUtils.drawCenteredText(g2d, makeTxt,
|
||||
(new Size(0.0, SizeUnit.mm).convertToPixels()) + originX + makeShiftX,
|
||||
(new Size(0.0, SizeUnit.mm).convertToPixels()) + originY + makeShiftY,
|
||||
HorizontalAlignment.CENTER, VerticalAlignment.CENTER);
|
||||
StringUtils.drawCenteredText(g2d, modelTxt,
|
||||
(new Size(0.0, SizeUnit.mm).convertToPixels()) + originX + modelShiftX,
|
||||
(new Size(0.0, SizeUnit.mm).convertToPixels()) + originY + modelShiftY,
|
||||
HorizontalAlignment.CENTER, VerticalAlignment.CENTER);*/
|
||||
|
||||
if (getShowMarkers()) {
|
||||
g2d.setColor(MARKER_COLOR);
|
||||
|
||||
for (int i = 0; i < getControlPointCount(); i++) {
|
||||
Point2D p = getControlPoint(i);
|
||||
Point2D labelPoint = new Point2D.Double(offset, 0);
|
||||
|
||||
double px = Math.sin(CONTACT_SPACING_INITIAL + i * CONTACT_SPACING_RAD) * (PAD_RADIUS + MARKER_OFFSET);
|
||||
double py = Math.cos(CONTACT_SPACING_INITIAL + i * CONTACT_SPACING_RAD) * (PAD_RADIUS + MARKER_OFFSET);
|
||||
|
||||
String marker = PAD_NAMES[i];
|
||||
if(marker.equals("CA")) {
|
||||
marker = "A";
|
||||
} else if (marker.equals("CB")) {
|
||||
marker = "B";
|
||||
} else if (marker.startsWith("T")) {
|
||||
continue;
|
||||
} else {
|
||||
marker = marker.substring(0, 1);
|
||||
}
|
||||
|
||||
StringUtils.drawCenteredText(g2d, marker,
|
||||
(new Size(px + DIAMETER_SWITCH / 2.0, SizeUnit.mm).convertToPixels()) + originX,
|
||||
(new Size(py + DIAMETER_SWITCH / 2.0, SizeUnit.mm).convertToPixels()) + originY,
|
||||
HorizontalAlignment.CENTER, VerticalAlignment.CENTER);
|
||||
}
|
||||
}
|
||||
|
||||
g2d.setComposite(oldComposite);
|
||||
}
|
||||
|
||||
public double[] getXOffsetsPx() {
|
||||
if (xOffsetsPx == null) {
|
||||
xOffsetsPx = new double[NUM_CONTACTS];
|
||||
for (int i = 0; i < NUM_CONTACTS; i++) {
|
||||
double x = Math.sin(CONTACT_SPACING_INITIAL + i * CONTACT_SPACING_RAD) * PAD_RADIUS;
|
||||
xOffsetsPx[i] = new Size(x + DIAMETER_SWITCH, SizeUnit.mm).convertToPixels();
|
||||
}
|
||||
}
|
||||
return xOffsetsPx;
|
||||
}
|
||||
|
||||
public double[] getYOffsetsPx() {
|
||||
if (yOffsetsPx == null) {
|
||||
yOffsetsPx = new double[NUM_CONTACTS];
|
||||
for (int i = 0; i < NUM_CONTACTS; i++) {
|
||||
double y = Math.cos(CONTACT_SPACING_INITIAL + i * CONTACT_SPACING_RAD) * PAD_RADIUS;
|
||||
yOffsetsPx[i] = new Size(y + DIAMETER_SWITCH , SizeUnit.mm).convertToPixels();
|
||||
}
|
||||
}
|
||||
return yOffsetsPx;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawIcon(Graphics2D g2d, int width, int height) {
|
||||
Shape base = new Ellipse2D.Double( 0, 0, width, height);
|
||||
g2d.setColor(BASE_COLOR);
|
||||
g2d.fill(base);
|
||||
|
||||
double contactOffset = 1;
|
||||
Shape contacts = new Ellipse2D.Double( contactOffset, contactOffset, width - contactOffset * 2, height - contactOffset * 2);
|
||||
g2d.setColor(PAD_COLOR);
|
||||
g2d.fill(contacts);
|
||||
|
||||
double centreOffset = 4;
|
||||
Shape centre = new Ellipse2D.Double( centreOffset, centreOffset, width - centreOffset * 2, height - centreOffset * 2);
|
||||
g2d.setColor(BASE_COLOR);
|
||||
g2d.fill(centre);
|
||||
|
||||
double gndOffset = 9;
|
||||
Shape gnd = new Ellipse2D.Double( gndOffset, gndOffset, width - gndOffset * 2, height - gndOffset * 2);
|
||||
g2d.setColor(PAD_COLOR);
|
||||
g2d.fill(gnd);
|
||||
|
||||
double arc = 2.0;
|
||||
Shape casing = new RoundRectangle2D.Double(12, 10, 8, 12, arc, arc);
|
||||
g2d.setColor(CASE_COLOR);
|
||||
g2d.fill(casing);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getControlPointCount() {
|
||||
return NUM_CONTACTS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Point2D getControlPoint(int index) {
|
||||
return controlPoints[index];
|
||||
}
|
||||
|
||||
private void updateControlPoints() {
|
||||
Point2D firstPoint = controlPoints[0];
|
||||
controlPoints = new Point2D[NUM_CONTACTS];
|
||||
controlPoints[0] = firstPoint;
|
||||
double[] xOffsetsPx = getXOffsetsPx();
|
||||
double[] yOffsetsPx = getYOffsetsPx();
|
||||
double theta = orientation.toRadians();
|
||||
AffineTransform tx =
|
||||
AffineTransform.getRotateInstance(theta, firstPoint.getX(), firstPoint.getY());
|
||||
for (int i = 1; i < controlPoints.length; i++) {
|
||||
controlPoints[i] = new Point2D.Double(firstPoint.getX() + (xOffsetsPx[i] - xOffsetsPx[0]),
|
||||
firstPoint.getY() + (yOffsetsPx[i] - yOffsetsPx[0]));
|
||||
tx.transform(controlPoints[i], controlPoints[i]);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isControlPointSticky(int index) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public VisibilityPolicy getControlPointVisibilityPolicy(int index) {
|
||||
return VisibilityPolicy.ALWAYS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setControlPoint(Point2D point, int index) {
|
||||
controlPoints[index].setLocation(point);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void getValue() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setValue(Void value) {}
|
||||
|
||||
@Override
|
||||
public boolean canPointMoveFreely(int pointIndex) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@EditableProperty
|
||||
public Orientation getOrientation() {
|
||||
if (orientation == null)
|
||||
orientation = Orientation.DEFAULT;
|
||||
return orientation;
|
||||
}
|
||||
|
||||
public void setOrientation(Orientation orientation) {
|
||||
this.orientation = orientation;
|
||||
updateControlPoints();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPositionCount() {
|
||||
return 6;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPositionName(int position) {
|
||||
return Integer.toString(position + 1);
|
||||
}
|
||||
|
||||
@DynamicEditableProperty(source = Freeway3x4_03SwitchPositionPropertyValueSource.class)
|
||||
@EditableProperty(name = "Selected Position")
|
||||
@Override
|
||||
public Integer getSelectedPosition() {
|
||||
return selectedPosition;
|
||||
}
|
||||
|
||||
public void setSelectedPosition(Integer selectedPosition) {
|
||||
this.selectedPosition = selectedPosition;
|
||||
}
|
||||
|
||||
@EditableProperty(name = "Markers")
|
||||
public Boolean getShowMarkers() {
|
||||
if (showMarkers == null) {
|
||||
showMarkers = false;
|
||||
}
|
||||
return showMarkers;
|
||||
}
|
||||
|
||||
public void setShowMarkers(Boolean showMarkers) {
|
||||
this.showMarkers = showMarkers;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean arePointsConnected(int index1, int index2, int position) {
|
||||
if (position == 0 &&
|
||||
((index1 == 0 && index2 == 6) || (index1 == 6 && index2 == 11) || (index1 == 0 && index2 == 11) ||
|
||||
(index1 == 17 && index2 == 21) || (index1 == 21 && index2 == 22) || (index1 == 17 && index2 == 22))
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
if (position == 1 &&
|
||||
((index1 == 1 && index2 == 6) || (index1 == 6 && index2 == 12) || (index1 == 1 && index2 == 12) ||
|
||||
(index1 == 18 && index2 == 21) || (index1 == 21 && index2 == 23) || (index1 == 18 && index2 == 23))
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
if (position == 2 &&
|
||||
((index1 == 2 && index2 == 6) || (index1 == 6 && index2 == 13) || (index1 == 2 && index2 == 13) ||
|
||||
(index1 == 19 && index2 == 21) || (index1 == 21 && index2 == 24) || (index1 == 19 && index2 == 24))
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
if (position == 3 &&
|
||||
((index1 == 5 && index2 == 6) || (index1 == 6 && index2 == 10) || (index1 == 5 && index2 == 10) ||
|
||||
(index1 == 16 && index2 == 21) || (index1 == 21 && index2 == 27) || (index1 == 16 && index2 == 27))
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
if (position == 4 &&
|
||||
((index1 == 4 && index2 == 6) || (index1 == 6 && index2 == 9) || (index1 == 4 && index2 == 9) ||
|
||||
(index1 == 15 && index2 == 21) || (index1 == 21 && index2 == 26) || (index1 == 15 && index2 == 26))
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
if (position == 5 &&
|
||||
((index1 == 3 && index2 == 6) || (index1 == 6 && index2 == 8) || (index1 == 3 && index2 == 8) ||
|
||||
(index1 == 14 && index2 == 21) || (index1 == 21 && index2 == 25) || (index1 == 14 && index2 == 25))
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -63,15 +63,18 @@ public class LPSwitch extends AbstractTransparentComponent<String> implements IS
|
||||
private static Size LENGTH = new Size(1.3d, SizeUnit.in);
|
||||
private static Size BASE_LENGTH = new Size(18d, SizeUnit.mm);
|
||||
private static Size WAFER_THICKNESS = new Size(0.05d, SizeUnit.in);
|
||||
private static Size TERMINAL_SPACING = new Size(0.2d, SizeUnit.in);
|
||||
private static Size TERMINAL_SPACING_CONNECTED = new Size(0.2d, SizeUnit.in);
|
||||
private static Size TERMINAL_SPACING_DISCONNECTED = new Size(0.1d, SizeUnit.in);
|
||||
|
||||
private String value = "";
|
||||
private Point2D[] controlPoints = new Point2D[] {new Point2D.Double(0, 0), new Point2D.Double(0, 0), new Point2D.Double(0, 0), new Point2D.Double(0, 0)};
|
||||
transient Shape[] body;
|
||||
private Orientation orientation = Orientation.DEFAULT;
|
||||
private MiddleTerminalConfiguration middleTerminalConfiguration;
|
||||
|
||||
public LPSwitch() {
|
||||
super();
|
||||
middleTerminalConfiguration = MiddleTerminalConfiguration.Connected;
|
||||
updateControlPoints();
|
||||
}
|
||||
|
||||
@@ -127,7 +130,7 @@ public class LPSwitch extends AbstractTransparentComponent<String> implements IS
|
||||
g2d.setColor(finalBorderColor);
|
||||
g2d.draw(body[1]);
|
||||
|
||||
g2d.setStroke(ObjectCache.getInstance().fetchBasicStroke(2));
|
||||
g2d.setStroke(ObjectCache.getInstance().fetchBasicStroke(3));
|
||||
g2d.setColor(METAL_COLOR);
|
||||
drawingObserver.startTracking();
|
||||
g2d.draw(body[2]);
|
||||
@@ -178,24 +181,44 @@ public class LPSwitch extends AbstractTransparentComponent<String> implements IS
|
||||
}
|
||||
}
|
||||
|
||||
int terminalSpacing = (int) TERMINAL_SPACING.convertToPixels();
|
||||
|
||||
GeneralPath terminalPath = new GeneralPath(GeneralPath.WIND_EVEN_ODD);
|
||||
terminalPath.moveTo(x - waferThickness * 3 / 2, bodyY + 1);
|
||||
terminalPath.lineTo(x - waferThickness * 3 / 2, bodyY + baseLength);
|
||||
terminalPath.lineTo(x, y + length);
|
||||
|
||||
terminalPath.moveTo(x + waferThickness * 3 / 2, bodyY + 1);
|
||||
terminalPath.lineTo(x + waferThickness * 3 / 2, bodyY + baseLength);
|
||||
terminalPath.lineTo(x, y + length);
|
||||
if (getMiddleTerminalConfiguration() == MiddleTerminalConfiguration.Connected) {
|
||||
int terminalSpacing = (int) TERMINAL_SPACING_CONNECTED.convertToPixels();
|
||||
terminalPath.moveTo(x - waferThickness * 3 / 2, bodyY + 1);
|
||||
terminalPath.lineTo(x - waferThickness * 3 / 2, bodyY + baseLength);
|
||||
terminalPath.lineTo(x, y + length);
|
||||
|
||||
terminalPath.moveTo(x - waferThickness * 5 / 2, bodyY + 1);
|
||||
terminalPath.lineTo(x - waferThickness * 5 / 2, bodyY + baseLength);
|
||||
terminalPath.lineTo(x - terminalSpacing, y + length);
|
||||
terminalPath.moveTo(x + waferThickness * 3 / 2, bodyY + 1);
|
||||
terminalPath.lineTo(x + waferThickness * 3 / 2, bodyY + baseLength);
|
||||
terminalPath.lineTo(x, y + length);
|
||||
|
||||
terminalPath.moveTo(x + waferThickness * 5 / 2, bodyY + 1);
|
||||
terminalPath.lineTo(x + waferThickness * 5 / 2, bodyY + baseLength);
|
||||
terminalPath.lineTo(x + terminalSpacing, y + length);
|
||||
terminalPath.moveTo(x - waferThickness * 5 / 2, bodyY + 1);
|
||||
terminalPath.lineTo(x - waferThickness * 5 / 2, bodyY + baseLength);
|
||||
terminalPath.lineTo(x - terminalSpacing, y + length);
|
||||
|
||||
terminalPath.moveTo(x + waferThickness * 5 / 2, bodyY + 1);
|
||||
terminalPath.lineTo(x + waferThickness * 5 / 2, bodyY + baseLength);
|
||||
terminalPath.lineTo(x + terminalSpacing, y + length);
|
||||
} else {
|
||||
int terminalSpacing = (int) TERMINAL_SPACING_DISCONNECTED.convertToPixels();
|
||||
terminalPath.moveTo(x - waferThickness * 3 / 2, bodyY + 1);
|
||||
terminalPath.lineTo(x - waferThickness * 3 / 2, bodyY + baseLength);
|
||||
terminalPath.lineTo(x- terminalSpacing, y + length);
|
||||
|
||||
terminalPath.moveTo(x + waferThickness * 3 / 2, bodyY + 1);
|
||||
terminalPath.lineTo(x + waferThickness * 3 / 2, bodyY + baseLength);
|
||||
terminalPath.lineTo(x + terminalSpacing, y + length);
|
||||
|
||||
terminalPath.moveTo(x - waferThickness * 5 / 2, bodyY + 1);
|
||||
terminalPath.lineTo(x - waferThickness * 5 / 2, bodyY + baseLength);
|
||||
terminalPath.lineTo(x - terminalSpacing * 2, y + length);
|
||||
|
||||
terminalPath.moveTo(x + waferThickness * 5 / 2, bodyY + 1);
|
||||
terminalPath.lineTo(x + waferThickness * 5 / 2, bodyY + baseLength);
|
||||
terminalPath.lineTo(x + terminalSpacing * 2, y + length);
|
||||
}
|
||||
body[2] = terminalPath;
|
||||
|
||||
// Rotate if needed
|
||||
@@ -219,14 +242,37 @@ public class LPSwitch extends AbstractTransparentComponent<String> implements IS
|
||||
|
||||
@SuppressWarnings("incomplete-switch")
|
||||
private void updateControlPoints() {
|
||||
|
||||
Point2D[] newControlPoints = null;
|
||||
if (getMiddleTerminalConfiguration() == MiddleTerminalConfiguration.Connected && controlPoints.length == 5) {
|
||||
newControlPoints = new Point2D[4];
|
||||
} else if (getMiddleTerminalConfiguration() == MiddleTerminalConfiguration.Disconnected && controlPoints.length == 4) {
|
||||
newControlPoints = new Point2D[5];
|
||||
newControlPoints[4] = new Point2D.Double();
|
||||
}
|
||||
if (newControlPoints != null) {
|
||||
for (int i = 0; i < newControlPoints.length && i < controlPoints.length; i++) {
|
||||
newControlPoints[i] = controlPoints[i];
|
||||
}
|
||||
controlPoints = newControlPoints;
|
||||
}
|
||||
|
||||
double x = controlPoints[0].getX();
|
||||
double y = controlPoints[0].getY();
|
||||
int terminalSpacing = (int) TERMINAL_SPACING.convertToPixels();
|
||||
int length = (int) LENGTH.convertToPixels();
|
||||
|
||||
controlPoints[1].setLocation(x - terminalSpacing, y + length);
|
||||
controlPoints[2].setLocation(x, y + length);
|
||||
controlPoints[3].setLocation(x + terminalSpacing, y + length);
|
||||
if (getMiddleTerminalConfiguration() == MiddleTerminalConfiguration.Connected) {
|
||||
int terminalSpacing = (int) TERMINAL_SPACING_CONNECTED.convertToPixels();
|
||||
controlPoints[1].setLocation(x - terminalSpacing, y + length);
|
||||
controlPoints[2].setLocation(x, y + length);
|
||||
controlPoints[3].setLocation(x + terminalSpacing, y + length);
|
||||
} else {
|
||||
int terminalSpacing = (int) TERMINAL_SPACING_DISCONNECTED.convertToPixels();
|
||||
controlPoints[1].setLocation(x - terminalSpacing * 2, y + length);
|
||||
controlPoints[2].setLocation(x - terminalSpacing, y + length);
|
||||
controlPoints[3].setLocation(x + terminalSpacing, y + length);
|
||||
controlPoints[4].setLocation(x + terminalSpacing * 2, y + length);
|
||||
}
|
||||
|
||||
// Rotate if needed
|
||||
if (orientation != Orientation.DEFAULT) {
|
||||
@@ -331,8 +377,24 @@ public class LPSwitch extends AbstractTransparentComponent<String> implements IS
|
||||
// Invalidate the body
|
||||
body = null;
|
||||
}
|
||||
|
||||
// @Override
|
||||
|
||||
@EditableProperty(name = "Middle Terminals")
|
||||
public MiddleTerminalConfiguration getMiddleTerminalConfiguration() {
|
||||
if (middleTerminalConfiguration == null) {
|
||||
middleTerminalConfiguration = MiddleTerminalConfiguration.Connected;
|
||||
}
|
||||
return middleTerminalConfiguration;
|
||||
}
|
||||
|
||||
public void setMiddleTerminalConfiguration(
|
||||
MiddleTerminalConfiguration middleTerminalConfiguration) {
|
||||
this.middleTerminalConfiguration = middleTerminalConfiguration;
|
||||
updateControlPoints();
|
||||
// Invalidate the body
|
||||
body = null;
|
||||
}
|
||||
|
||||
// @Override
|
||||
// public String getControlPointNodeName(int index) {
|
||||
// // we don't want the switch to produce any nodes, it just makes connections
|
||||
// return null;
|
||||
@@ -347,27 +409,34 @@ public class LPSwitch extends AbstractTransparentComponent<String> implements IS
|
||||
|
||||
@Override
|
||||
public String getPositionName(int position) {
|
||||
switch (position) {
|
||||
case 0:
|
||||
return "Treble";
|
||||
case 1:
|
||||
return "Middle";
|
||||
case 2:
|
||||
return "Rhythm";
|
||||
}
|
||||
return null;
|
||||
return switch (position) {
|
||||
case 0 -> "Treble";
|
||||
case 1 -> "Middle";
|
||||
case 2 -> "Rhythm";
|
||||
default -> null;
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean arePointsConnected(int index1, int index2, int position) {
|
||||
switch (position) {
|
||||
case 0:
|
||||
return index1 == 1 && index2 == 2;
|
||||
case 1:
|
||||
return index1 > 0 && index2 > 0 && index1 < index2;
|
||||
case 2:
|
||||
return index1 == 2 && index2 == 3;
|
||||
if (getMiddleTerminalConfiguration() == MiddleTerminalConfiguration.Connected) {
|
||||
return switch (position) {
|
||||
case 0 -> index1 == 1 && index2 == 2;
|
||||
case 1 -> index1 > 0 && index2 > 0 && index1 < index2;
|
||||
case 2 -> index1 == 2 && index2 == 3;
|
||||
default -> false;
|
||||
};
|
||||
} else {
|
||||
return switch (position) {
|
||||
case 0 -> index1 == 1 && index2 == 2;
|
||||
case 1 -> (index1 == 1 && index2 == 2) || (index1 == 3 && index2 == 4);
|
||||
case 2 -> index1 == 3 && index2 == 4;
|
||||
default -> false;
|
||||
};
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public enum MiddleTerminalConfiguration {
|
||||
Connected, Disconnected
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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++) {
|
||||
@@ -303,7 +321,8 @@ public class LeverSwitch extends AbstractAngledComponent<LeverSwitch.LeverSwitch
|
||||
case DP4T:
|
||||
controlPoints = new Point2D[10];
|
||||
for (int i = 0; i < controlPoints.length; i++) {
|
||||
controlPoints[i] = new Point2D.Double(x + (i % 2 == 1 ? terminalLength : 0), y + i * terminalSpacing);
|
||||
double yPos = i % 2 == 1 ? y + (i - 2) * terminalSpacing : y + i * terminalSpacing;
|
||||
controlPoints[i] = new Point2D.Double(x + (i % 2 == 1 ? terminalLength : 0), yPos);
|
||||
}
|
||||
break;
|
||||
case DP5T:
|
||||
@@ -319,6 +338,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 +481,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 +569,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 +628,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;
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -45,7 +45,7 @@ import org.diylc.core.measures.Size;
|
||||
import org.diylc.core.measures.SizeUnit;
|
||||
|
||||
@ComponentDescriptor(name = "Auto-Wrap Label", author = "Branislav Stojkovic", category = "Misc",
|
||||
description = "User defined label with auto-wrapped text", instanceNamePrefix = "L", zOrder = IDIYComponent.TEXT, flexibleZOrder = true,
|
||||
description = "User defined label with auto-wrapped text", instanceNamePrefix = "LAB", zOrder = IDIYComponent.TEXT, flexibleZOrder = true,
|
||||
bomPolicy = BomPolicy.NEVER_SHOW, transformer = TextTransformer.class)
|
||||
public class WrapLabel extends AbstractComponent<String> {
|
||||
|
||||
|
||||
+75
@@ -35,9 +35,12 @@ import java.awt.geom.Rectangle2D;
|
||||
import java.awt.geom.RoundRectangle2D;
|
||||
import org.diylc.appframework.miscutils.ConfigurationManager;
|
||||
|
||||
import org.diylc.awt.StringUtils;
|
||||
import org.diylc.common.HorizontalAlignment;
|
||||
import org.diylc.common.IPlugInPort;
|
||||
import org.diylc.common.ObjectCache;
|
||||
import org.diylc.common.Orientation;
|
||||
import org.diylc.common.VerticalAlignment;
|
||||
import org.diylc.components.transform.PotentiometerTransformer;
|
||||
import org.diylc.core.ComponentState;
|
||||
import org.diylc.core.CreationMethod;
|
||||
@@ -66,10 +69,12 @@ public class PotentiometerPanel extends AbstractPotentiometer implements ILayere
|
||||
protected static Size PIN_SIZE = new Size(0.05d, SizeUnit.in);
|
||||
protected static Size NUT_SIZE = new Size(0.4d, SizeUnit.in);
|
||||
protected static Size SHAFT_SIZE = new Size(1 / 4d, SizeUnit.in);
|
||||
protected static Size MARKER_OFFSET = new Size(0.05d, SizeUnit.in);
|
||||
protected static Color BODY_COLOR = Color.lightGray;
|
||||
protected static Color WAFER_COLOR = Color.decode("#CD8500");
|
||||
protected static Color BORDER_COLOR = Color.gray;
|
||||
protected static Color NUT_COLOR = Color.decode("#CBD5DB");
|
||||
protected static Color MARKER_COLOR_DEFAULT = Color.gray;
|
||||
|
||||
|
||||
protected Size bodyDiameter = BODY_DIAMETER;
|
||||
@@ -83,6 +88,8 @@ public class PotentiometerPanel extends AbstractPotentiometer implements ILayere
|
||||
@Deprecated
|
||||
protected boolean showShaft = false;
|
||||
protected View view;
|
||||
private Boolean showMarkers;
|
||||
private Color markerColor;
|
||||
// Array of 7 elements: 3 lug connectors, 1 pot body and 3 lugs
|
||||
transient protected Area[] body = null;
|
||||
|
||||
@@ -353,6 +360,50 @@ public class PotentiometerPanel extends AbstractPotentiometer implements ILayere
|
||||
y = panelHeight / 2 + fontMetrics.getAscent();
|
||||
|
||||
g2d.drawString(getValueForDisplay(), (int) (bodyRect.getX() + x), (int) (bodyRect.getY() + y));
|
||||
|
||||
// Draw markers if enabled.
|
||||
if (getShowMarkers()) {
|
||||
g2d.setColor(outlineMode ? theme.getOutlineColor() : getMarkerColor());
|
||||
|
||||
// Get circle center and radius
|
||||
Rectangle2D circleBounds = body[3].getBounds2D();
|
||||
double centerX = circleBounds.getCenterX();
|
||||
double centerY = circleBounds.getCenterY();
|
||||
double radius = getBodyDiameter().convertToPixels() / 2;
|
||||
|
||||
// Fixed distance from circle edge (inside the circle)
|
||||
double markerOffset = MARKER_OFFSET.convertToPixels() + 2;
|
||||
double markerDistance = radius - markerOffset;
|
||||
|
||||
// Draw markers "1", "2", "3" along radial line from center to each lug, close to circle edge
|
||||
// Reverse numbering when view is ShaftDown
|
||||
boolean reverseOrder = getView() == View.ShaftDown;
|
||||
|
||||
for (int i = 0; i < 3; i++) {
|
||||
Point2D lugPoint = controlPoints[i];
|
||||
|
||||
// Calculate direction from center to lug
|
||||
double dx = lugPoint.getX() - centerX;
|
||||
double dy = lugPoint.getY() - centerY;
|
||||
double distance = Math.sqrt(dx * dx + dy * dy);
|
||||
|
||||
if (distance > 0) {
|
||||
// Normalize direction vector
|
||||
double unitX = dx / distance;
|
||||
double unitY = dy / distance;
|
||||
|
||||
// Position marker at fixed distance from circle edge along radial line
|
||||
double markerX = centerX + unitX * markerDistance;
|
||||
double markerY = centerY + unitY * markerDistance;
|
||||
|
||||
// Determine marker number based on view
|
||||
int markerNumber = reverseOrder ? (3 - i) : (i + 1);
|
||||
|
||||
StringUtils.drawCenteredText(g2d, Integer.toString(markerNumber), markerX, markerY,
|
||||
HorizontalAlignment.CENTER, VerticalAlignment.CENTER);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -474,6 +525,30 @@ public class PotentiometerPanel extends AbstractPotentiometer implements ILayere
|
||||
public void setWaferColor(Color waferColor) {
|
||||
this.waferColor = waferColor;
|
||||
}
|
||||
|
||||
@EditableProperty(name = "Markers")
|
||||
public Boolean getShowMarkers() {
|
||||
if (showMarkers == null) {
|
||||
showMarkers = false;
|
||||
}
|
||||
return showMarkers;
|
||||
}
|
||||
|
||||
public void setShowMarkers(Boolean showMarkers) {
|
||||
this.showMarkers = showMarkers;
|
||||
}
|
||||
|
||||
@EditableProperty(name = "Marker Color")
|
||||
public Color getMarkerColor() {
|
||||
if (markerColor == null) {
|
||||
markerColor = MARKER_COLOR_DEFAULT;
|
||||
}
|
||||
return markerColor;
|
||||
}
|
||||
|
||||
public void setMarkerColor(Color markerColor) {
|
||||
this.markerColor = markerColor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getInternalLinkName(int index1, int index2) {
|
||||
|
||||
@@ -157,6 +157,6 @@ public class TransformerCore extends AbstractComponent<Void> {
|
||||
|
||||
@Override
|
||||
public boolean canPointMoveFreely(int pointIndex) {
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
+693
@@ -0,0 +1,693 @@
|
||||
/*
|
||||
|
||||
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.semiconductors;
|
||||
|
||||
import java.awt.AlphaComposite;
|
||||
import java.awt.Color;
|
||||
import java.awt.Composite;
|
||||
import java.awt.FontMetrics;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.Rectangle;
|
||||
import java.awt.geom.Area;
|
||||
import java.awt.geom.Point2D;
|
||||
import java.awt.geom.Rectangle2D;
|
||||
import org.diylc.appframework.miscutils.ConfigurationManager;
|
||||
|
||||
import org.diylc.common.Display;
|
||||
import org.diylc.common.IPlugInPort;
|
||||
import org.diylc.common.ObjectCache;
|
||||
import org.diylc.common.Orientation;
|
||||
import org.diylc.components.AbstractTransparentComponent;
|
||||
import org.diylc.core.ComponentState;
|
||||
import org.diylc.core.IDIYComponent;
|
||||
import org.diylc.core.IDrawingObserver;
|
||||
import org.diylc.core.Project;
|
||||
import org.diylc.core.Theme;
|
||||
import org.diylc.core.VisibilityPolicy;
|
||||
import org.diylc.core.annotations.ComponentDescriptor;
|
||||
import org.diylc.core.annotations.EditableProperty;
|
||||
import org.diylc.core.annotations.KeywordPolicy;
|
||||
import org.diylc.core.gerber.IGerberComponentSimple;
|
||||
import org.diylc.core.measures.Size;
|
||||
import org.diylc.core.measures.SizeUnit;
|
||||
import org.diylc.utils.Constants;
|
||||
|
||||
@ComponentDescriptor(name = "Multiwatt IC", author = "Branislav Stojkovic", category = "Semiconductors",
|
||||
instanceNamePrefix = "IC", description = "Multiwatt package IC typically used for audio amplifiers of high power applications",
|
||||
zOrder = IDIYComponent.COMPONENT, keywordPolicy = KeywordPolicy.SHOW_VALUE,
|
||||
enableCache = true)
|
||||
public class MultiwattIC extends AbstractTransparentComponent<String> implements IGerberComponentSimple {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public static Color BODY_COLOR = Color.gray;
|
||||
public static Color BORDER_COLOR = Color.gray.darker();
|
||||
public static Color PIN_COLOR = Color.decode("#00B2EE");
|
||||
public static Color PIN_BORDER_COLOR = PIN_COLOR.darker();
|
||||
public static Color LABEL_COLOR = Color.white;
|
||||
public static Color TAB_COLOR = Color.lightGray;
|
||||
public static Color TAB_BORDER_COLOR = TAB_COLOR.darker();
|
||||
public static int EDGE_RADIUS = 6;
|
||||
public static Size PIN_SIZE = new Size(0.7d, SizeUnit.mm);
|
||||
public static Size THICKNESS = new Size(5d, SizeUnit.mm);
|
||||
public static Size TAB_THICKNESS = new Size(1.6d, SizeUnit.mm);
|
||||
// Multiwatt packages use 0.1 inch (2.54mm) spacing between pins in the same row
|
||||
public static Size PIN_SPACING = new Size(0.1d, SizeUnit.in);
|
||||
// Multiwatt packages have staggered pins with 0.2 inch (5.08mm) row spacing
|
||||
public static Size ROW_SPACING = new Size(0.2d, SizeUnit.in);
|
||||
// Lead offset due to bent leads
|
||||
public static Size LEAD_OFFSET = new Size(4d, SizeUnit.mm);
|
||||
|
||||
private String value = "";
|
||||
private Orientation orientation = Orientation.DEFAULT;
|
||||
private MultiwattType type = MultiwattType.MULTIWATT_11;
|
||||
private Point2D[] controlPoints = new Point2D[] {new Point2D.Double(0, 0)};
|
||||
protected Display display = Display.NAME;
|
||||
private Color bodyColor = BODY_COLOR;
|
||||
private Color borderColor = BORDER_COLOR;
|
||||
private Color labelColor = LABEL_COLOR;
|
||||
private Color tabColor = TAB_COLOR;
|
||||
private Color tabBorderColor = TAB_BORDER_COLOR;
|
||||
transient private Area[] body;
|
||||
|
||||
public MultiwattIC() {
|
||||
super();
|
||||
updateControlPoints();
|
||||
alpha = 100;
|
||||
}
|
||||
|
||||
@EditableProperty
|
||||
public String getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public void setValue(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
@EditableProperty
|
||||
public Orientation getOrientation() {
|
||||
return orientation;
|
||||
}
|
||||
|
||||
public void setOrientation(Orientation orientation) {
|
||||
this.orientation = orientation;
|
||||
updateControlPoints();
|
||||
// Reset body shape.
|
||||
body = null;
|
||||
}
|
||||
|
||||
@EditableProperty(name = "Type")
|
||||
public MultiwattType getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public void setType(MultiwattType type) {
|
||||
this.type = type;
|
||||
updateControlPoints();
|
||||
// Reset body shape;
|
||||
body = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canPointMoveFreely(int pointIndex) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@EditableProperty
|
||||
public Display getDisplay() {
|
||||
if (display == null) {
|
||||
display = Display.VALUE;
|
||||
}
|
||||
return display;
|
||||
}
|
||||
|
||||
public void setDisplay(Display display) {
|
||||
this.display = display;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getControlPointCount() {
|
||||
return controlPoints.length;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Point2D getControlPoint(int index) {
|
||||
return controlPoints[index];
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isControlPointSticky(int index) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public VisibilityPolicy getControlPointVisibilityPolicy(int index) {
|
||||
return VisibilityPolicy.NEVER;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setControlPoint(Point2D point, int index) {
|
||||
controlPoints[index].setLocation(point);
|
||||
body = null;
|
||||
}
|
||||
|
||||
private void updateControlPoints() {
|
||||
Point2D firstPoint = controlPoints[0];
|
||||
int pinCount = type.getPinCount();
|
||||
int pinsLeft = type.getPinsLeft();
|
||||
int pinsRight = type.getPinsRight();
|
||||
controlPoints = new Point2D.Double[pinCount];
|
||||
controlPoints[0] = firstPoint;
|
||||
double pinSpacing = PIN_SPACING.convertToPixels();
|
||||
double rowSpacing = ROW_SPACING.convertToPixels();
|
||||
// Update control points for staggered layout
|
||||
double dx1, dy1, dx2, dy2;
|
||||
|
||||
// Left side pins (first row)
|
||||
for (int i = 0; i < pinsLeft; i++) {
|
||||
switch (orientation) {
|
||||
case DEFAULT:
|
||||
dx1 = 0;
|
||||
dy1 = i * pinSpacing;
|
||||
break;
|
||||
case _90:
|
||||
dx1 = -i * pinSpacing;
|
||||
dy1 = 0;
|
||||
break;
|
||||
case _180:
|
||||
dx1 = 0;
|
||||
dy1 = -i * pinSpacing;
|
||||
break;
|
||||
case _270:
|
||||
dx1 = i * pinSpacing;
|
||||
dy1 = 0;
|
||||
break;
|
||||
default:
|
||||
throw new RuntimeException("Unexpected orientation: " + orientation);
|
||||
}
|
||||
controlPoints[i] = new Point2D.Double((int) (firstPoint.getX() + dx1), (int) (firstPoint.getY() + dy1));
|
||||
}
|
||||
|
||||
// Right side pins (second row, staggered - offset by half pin spacing)
|
||||
for (int i = 0; i < pinsRight; i++) {
|
||||
switch (orientation) {
|
||||
case DEFAULT:
|
||||
dx2 = rowSpacing;
|
||||
dy2 = (i + 0.5) * pinSpacing; // Offset by half spacing for staggered effect
|
||||
break;
|
||||
case _90:
|
||||
dx2 = -(i + 0.5) * pinSpacing; // Offset by half spacing for staggered effect
|
||||
dy2 = rowSpacing;
|
||||
break;
|
||||
case _180:
|
||||
dx2 = -rowSpacing;
|
||||
dy2 = -(i + 0.5) * pinSpacing; // Offset by half spacing for staggered effect
|
||||
break;
|
||||
case _270:
|
||||
dx2 = (i + 0.5) * pinSpacing; // Offset by half spacing for staggered effect
|
||||
dy2 = -rowSpacing;
|
||||
break;
|
||||
default:
|
||||
throw new RuntimeException("Unexpected orientation: " + orientation);
|
||||
}
|
||||
controlPoints[i + pinsLeft] = new Point2D.Double((int) (firstPoint.getX() + dx2), (int) (firstPoint.getY() + dy2));
|
||||
}
|
||||
}
|
||||
|
||||
public Area[] getBody() {
|
||||
if (body == null) {
|
||||
body = new Area[2];
|
||||
double x = controlPoints[0].getX();
|
||||
double y = controlPoints[0].getY();
|
||||
double pinSize = PIN_SIZE.convertToPixels();
|
||||
double pinSpacing = PIN_SPACING.convertToPixels();
|
||||
double rowSpacing = ROW_SPACING.convertToPixels();
|
||||
double tabThickness = TAB_THICKNESS.convertToPixels();
|
||||
double leadOffset = LEAD_OFFSET.convertToPixels();
|
||||
int pinsLeft = type.getPinsLeft();
|
||||
int pinsRight = type.getPinsRight();
|
||||
// Calculate height accounting for staggered pins (right row offset by half spacing)
|
||||
double leftRowHeight = (pinsLeft - 1) * pinSpacing;
|
||||
double rightRowHeight = (pinsRight - 1 + 0.5) * pinSpacing; // Account for half-spacing offset
|
||||
double maxHeight = Math.max(leftRowHeight, rightRowHeight) + pinSpacing; // Add spacing for last pin
|
||||
double bodyX, bodyY, bodyWidth, bodyHeight;
|
||||
double tabX, tabY, tabWidth, tabHeight;
|
||||
|
||||
switch (orientation) {
|
||||
case DEFAULT:
|
||||
bodyWidth = rowSpacing - pinSize;
|
||||
bodyHeight = maxHeight;
|
||||
// Offset body to the left by lead offset (bent leads)
|
||||
bodyX = x + pinSize / 2 - leadOffset;
|
||||
bodyY = y - pinSpacing / 2;
|
||||
// Tab on the left side (where there are more pins) inside the body bounds
|
||||
tabX = bodyX;
|
||||
tabY = bodyY;
|
||||
tabWidth = tabThickness;
|
||||
tabHeight = bodyHeight;
|
||||
break;
|
||||
case _90:
|
||||
bodyWidth = maxHeight;
|
||||
bodyHeight = rowSpacing - pinSize;
|
||||
// Body positioned between the two pin rows
|
||||
// Left pins extend to negative X, body starts from leftmost pin
|
||||
// For _90: left pins go left (negative X), so leftmost is at x - (pinsLeft-1)*pinSpacing
|
||||
// Offset body upward by lead offset (bent leads)
|
||||
bodyX = x - (pinsLeft - 1) * pinSpacing - pinSpacing / 2;
|
||||
bodyY = y + pinSize / 2 - leadOffset;
|
||||
// Tab on the top side (where there are more pins - left row at y) inside the body bounds
|
||||
tabX = bodyX;
|
||||
tabY = bodyY;
|
||||
tabWidth = bodyWidth;
|
||||
tabHeight = tabThickness;
|
||||
break;
|
||||
case _180:
|
||||
bodyWidth = rowSpacing - pinSize;
|
||||
bodyHeight = maxHeight;
|
||||
// Body positioned between the two pin rows
|
||||
// For _180: left pins go up (negative Y), right pins at x - rowSpacing
|
||||
// Left row is at x, right row is at x - rowSpacing
|
||||
// Pins extend upward from y, so body should start from top of pin array
|
||||
// Offset body to the right by lead offset (bent leads)
|
||||
bodyX = x - rowSpacing + pinSize / 2 + leadOffset;
|
||||
bodyY = y - (pinsLeft - 1) * pinSpacing - pinSpacing / 2;
|
||||
// Tab on the right side (where there are more pins - left row at x) inside the body bounds
|
||||
tabX = bodyX + bodyWidth - tabThickness;
|
||||
tabY = bodyY;
|
||||
tabWidth = tabThickness;
|
||||
tabHeight = bodyHeight;
|
||||
break;
|
||||
case _270:
|
||||
bodyWidth = maxHeight;
|
||||
bodyHeight = rowSpacing - pinSize;
|
||||
// Body positioned between the two pin rows
|
||||
// For _270: left pins go right (positive X), so leftmost is at x
|
||||
// Left row is at y, right row is at y - rowSpacing
|
||||
// Similar to _90 but mirrored
|
||||
// Offset body downward by lead offset (bent leads)
|
||||
bodyX = x - pinSpacing / 2;
|
||||
bodyY = y - rowSpacing + pinSize / 2 + leadOffset;
|
||||
// Tab on the bottom side (where there are more pins - left row at y) inside the body bounds
|
||||
tabX = bodyX;
|
||||
tabY = bodyY + bodyHeight - tabThickness;
|
||||
tabWidth = bodyWidth;
|
||||
tabHeight = tabThickness;
|
||||
break;
|
||||
default:
|
||||
throw new RuntimeException("Unexpected orientation: " + orientation);
|
||||
}
|
||||
body[0] = new Area(new Rectangle2D.Double(bodyX, bodyY, bodyWidth, bodyHeight));
|
||||
body[1] = new Area(new Rectangle2D.Double(tabX, tabY, tabWidth, tabHeight));
|
||||
}
|
||||
return body;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(Graphics2D g2d, ComponentState componentState, boolean outlineMode, Project project,
|
||||
IDrawingObserver drawingObserver) {
|
||||
if (checkPointsClipped(g2d.getClip())) {
|
||||
return;
|
||||
}
|
||||
Area mainArea = getBody()[0];
|
||||
|
||||
g2d.setStroke(ObjectCache.getInstance().fetchBasicStroke(1f));
|
||||
if (!outlineMode) {
|
||||
int pinSize = (int) PIN_SIZE.convertToPixels() / 2 * 2;
|
||||
Rectangle2D bodyBounds = mainArea.getBounds2D();
|
||||
double leadThickness = pinSize; // Lead width matches pin size
|
||||
|
||||
for (Point2D point : controlPoints) {
|
||||
double pinX = point.getX();
|
||||
double pinY = point.getY();
|
||||
|
||||
// Check if pin is inside the body
|
||||
boolean insideBody = mainArea.contains(pinX, pinY);
|
||||
|
||||
if (insideBody) {
|
||||
// Draw pin as square inside body
|
||||
Rectangle2D rect = new Rectangle2D.Double(pinX - pinSize / 2,
|
||||
pinY - pinSize / 2, pinSize, pinSize);
|
||||
g2d.setColor(PIN_COLOR);
|
||||
drawingObserver.startTracking();
|
||||
g2d.fill(rect);
|
||||
drawingObserver.stopTracking();
|
||||
g2d.setColor(PIN_BORDER_COLOR);
|
||||
g2d.draw(rect);
|
||||
} else {
|
||||
// Draw rectangular lead from body edge to pin position
|
||||
double bodyX = bodyBounds.getX();
|
||||
double bodyY = bodyBounds.getY();
|
||||
double bodyWidth = bodyBounds.getWidth();
|
||||
double bodyHeight = bodyBounds.getHeight();
|
||||
|
||||
// Find the closest point on body edge to the pin
|
||||
double edgeX, edgeY;
|
||||
|
||||
// Determine which side of the body the pin is on
|
||||
if (pinX < bodyX) {
|
||||
// Pin is to the left of body
|
||||
edgeX = bodyX;
|
||||
edgeY = Math.max(bodyY, Math.min(bodyY + bodyHeight, pinY));
|
||||
} else if (pinX > bodyX + bodyWidth) {
|
||||
// Pin is to the right of body
|
||||
edgeX = bodyX + bodyWidth;
|
||||
edgeY = Math.max(bodyY, Math.min(bodyY + bodyHeight, pinY));
|
||||
} else if (pinY < bodyY) {
|
||||
// Pin is above body
|
||||
edgeX = Math.max(bodyX, Math.min(bodyX + bodyWidth, pinX));
|
||||
edgeY = bodyY;
|
||||
} else {
|
||||
// Pin is below body
|
||||
edgeX = Math.max(bodyX, Math.min(bodyX + bodyWidth, pinX));
|
||||
edgeY = bodyY + bodyHeight;
|
||||
}
|
||||
|
||||
// Draw rectangular lead
|
||||
double leadX = Math.min(edgeX, pinX) - leadThickness / 2;
|
||||
double leadY = Math.min(edgeY, pinY) - leadThickness / 2;
|
||||
double leadWidth = Math.abs(pinX - edgeX) + leadThickness;
|
||||
double leadHeight = Math.abs(pinY - edgeY) + leadThickness;
|
||||
|
||||
// If lead is horizontal or vertical, draw as rectangle
|
||||
if (Math.abs(pinX - edgeX) < 0.1) {
|
||||
// Vertical lead
|
||||
leadWidth = leadThickness;
|
||||
leadX = edgeX - leadThickness / 2;
|
||||
} else if (Math.abs(pinY - edgeY) < 0.1) {
|
||||
// Horizontal lead
|
||||
leadHeight = leadThickness;
|
||||
leadY = edgeY - leadThickness / 2;
|
||||
}
|
||||
|
||||
Rectangle2D leadRect = new Rectangle2D.Double(leadX, leadY, leadWidth, leadHeight);
|
||||
g2d.setColor(PIN_COLOR);
|
||||
drawingObserver.startTracking();
|
||||
g2d.fill(leadRect);
|
||||
drawingObserver.stopTracking();
|
||||
g2d.setColor(PIN_BORDER_COLOR);
|
||||
g2d.draw(leadRect);
|
||||
|
||||
// Draw pin at the end of the lead
|
||||
Rectangle2D pinRect = new Rectangle2D.Double(pinX - pinSize / 2,
|
||||
pinY - pinSize / 2, pinSize, pinSize);
|
||||
g2d.setColor(PIN_COLOR);
|
||||
drawingObserver.startTracking();
|
||||
g2d.fill(pinRect);
|
||||
drawingObserver.stopTracking();
|
||||
g2d.setColor(PIN_BORDER_COLOR);
|
||||
g2d.draw(pinRect);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
drawingObserver.startTracking();
|
||||
Composite oldComposite = g2d.getComposite();
|
||||
if (alpha < MAX_ALPHA) {
|
||||
g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 1f * alpha / MAX_ALPHA));
|
||||
}
|
||||
g2d.setColor(outlineMode ? Constants.TRANSPARENT_COLOR : getBodyColor());
|
||||
g2d.fill(mainArea);
|
||||
drawingObserver.stopTracking();
|
||||
|
||||
// Draw heat sink tab
|
||||
Area tabArea = getBody()[1];
|
||||
Color finalTabColor;
|
||||
if (outlineMode) {
|
||||
Theme theme =
|
||||
(Theme) ConfigurationManager.getInstance().readObject(IPlugInPort.THEME_KEY, Constants.DEFAULT_THEME);
|
||||
finalTabColor = theme.getOutlineColor();
|
||||
} else {
|
||||
finalTabColor = tabColor;
|
||||
}
|
||||
g2d.setColor(finalTabColor);
|
||||
drawingObserver.startTracking();
|
||||
g2d.fill(tabArea);
|
||||
drawingObserver.stopTracking();
|
||||
g2d.setComposite(oldComposite);
|
||||
|
||||
if (!outlineMode) {
|
||||
g2d.setStroke(ObjectCache.getInstance().fetchBasicStroke(1));
|
||||
g2d.setColor(tabBorderColor);
|
||||
g2d.draw(tabArea);
|
||||
}
|
||||
|
||||
Color finalBorderColor;
|
||||
if (outlineMode) {
|
||||
Theme theme =
|
||||
(Theme) ConfigurationManager.getInstance().readObject(IPlugInPort.THEME_KEY, Constants.DEFAULT_THEME);
|
||||
finalBorderColor =
|
||||
componentState == ComponentState.SELECTED || componentState == ComponentState.DRAGGING ? SELECTION_COLOR
|
||||
: theme.getOutlineColor();
|
||||
} else {
|
||||
finalBorderColor =
|
||||
componentState == ComponentState.SELECTED || componentState == ComponentState.DRAGGING ? SELECTION_COLOR
|
||||
: getBorderColor();
|
||||
}
|
||||
g2d.setColor(finalBorderColor);
|
||||
g2d.setStroke(ObjectCache.getInstance().fetchBasicStroke(1));
|
||||
g2d.draw(mainArea);
|
||||
// Draw label.
|
||||
g2d.setFont(project.getFont());
|
||||
Color finalLabelColor;
|
||||
if (outlineMode) {
|
||||
Theme theme =
|
||||
(Theme) ConfigurationManager.getInstance().readObject(IPlugInPort.THEME_KEY, Constants.DEFAULT_THEME);
|
||||
finalLabelColor =
|
||||
componentState == ComponentState.SELECTED || componentState == ComponentState.DRAGGING ? LABEL_COLOR_SELECTED
|
||||
: theme.getOutlineColor();
|
||||
} else {
|
||||
finalLabelColor =
|
||||
componentState == ComponentState.SELECTED || componentState == ComponentState.DRAGGING ? LABEL_COLOR_SELECTED
|
||||
: getLabelColor();
|
||||
}
|
||||
g2d.setColor(finalLabelColor);
|
||||
FontMetrics fontMetrics = g2d.getFontMetrics(g2d.getFont());
|
||||
String label = "";
|
||||
label = (getDisplay() == Display.NAME) ? getName() : getValue();
|
||||
if (getDisplay() == Display.NONE) {
|
||||
label = "";
|
||||
}
|
||||
if (getDisplay() == Display.BOTH) {
|
||||
label = getName() + " " + (getValue() == null ? "" : getValue().toString());
|
||||
}
|
||||
Rectangle2D rect = fontMetrics.getStringBounds(label, g2d);
|
||||
int textHeight = (int) (rect.getHeight());
|
||||
int textWidth = (int) (rect.getWidth());
|
||||
// Center text horizontally and vertically
|
||||
Rectangle bounds = mainArea.getBounds();
|
||||
int x = (int) (bounds.getX() + (bounds.width - textWidth) / 2);
|
||||
int y = (int) (bounds.getY() + (bounds.height - textHeight) / 2 + fontMetrics.getAscent());
|
||||
g2d.drawString(label, x, y);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawIcon(Graphics2D g2d, int width, int height) {
|
||||
int margin = 2;
|
||||
int pinSize = 2 * width / 32;
|
||||
int leftPinX = width / 6 + 9; // Move left pins 9px to the right (4px + 3px + 2px)
|
||||
int rightPinX = 5 * width / 6;
|
||||
int leadOffset = (int) (LEAD_OFFSET.convertToPixels() * width / 100); // Scale lead offset for icon
|
||||
int bodyX = leftPinX + pinSize - leadOffset + 2; // Offset body to the left, then move 2px to the right
|
||||
int bodyWidth = (rightPinX - bodyX - pinSize) / 2 + 2; // Thinner body, make 2px thicker
|
||||
int bodyHeight = height - 2 * margin;
|
||||
int bodyY = margin;
|
||||
|
||||
// Calculate pin spacing to use all vertical space
|
||||
// Use same spacing for both rows, with front row offset by half spacing
|
||||
int backPins = 4;
|
||||
int frontPins = 3;
|
||||
// Use the spacing that fits the back row (more pins)
|
||||
int pinSpacing = (bodyHeight - pinSize) / (backPins - 1);
|
||||
int backStartY = margin + pinSize / 2 + 1; // Move pins 1px down
|
||||
// Front pins start with half spacing offset for staggered effect
|
||||
int frontStartY = margin + pinSize / 2 + pinSpacing / 2 + 1; // Move pins 1px down
|
||||
|
||||
// Draw thin body between pin rows
|
||||
g2d.setColor(BODY_COLOR);
|
||||
g2d.fillRect(bodyX, bodyY, bodyWidth, bodyHeight);
|
||||
g2d.setColor(BORDER_COLOR);
|
||||
g2d.drawRect(bodyX, bodyY, bodyWidth, bodyHeight);
|
||||
|
||||
// Draw heat sink tab on left side (where there are more pins)
|
||||
int tabWidth = pinSize * 2;
|
||||
g2d.setColor(TAB_COLOR);
|
||||
g2d.fillRect(bodyX, bodyY, tabWidth, bodyHeight);
|
||||
g2d.setColor(TAB_BORDER_COLOR);
|
||||
g2d.drawRect(bodyX, bodyY, tabWidth, bodyHeight);
|
||||
|
||||
// Draw 4 pins in the back (left row, more pins) as squares - inside body
|
||||
g2d.setColor(PIN_COLOR);
|
||||
for (int i = 0; i < backPins; i++) {
|
||||
int pinY = backStartY + i * pinSpacing - pinSize / 2;
|
||||
g2d.fillRect(leftPinX - pinSize, pinY, pinSize, pinSize);
|
||||
g2d.setColor(PIN_BORDER_COLOR);
|
||||
g2d.drawRect(leftPinX - pinSize, pinY, pinSize, pinSize);
|
||||
g2d.setColor(PIN_COLOR);
|
||||
}
|
||||
|
||||
// Draw 3 pins in the front (right row, fewer pins) with leads going left to body
|
||||
// Use same spacing as back row, just offset by half spacing
|
||||
g2d.setColor(PIN_COLOR);
|
||||
for (int i = 0; i < frontPins; i++) {
|
||||
int pinY = frontStartY + i * pinSpacing;
|
||||
int pinX = rightPinX;
|
||||
int bodyRightEdge = bodyX + bodyWidth;
|
||||
|
||||
// Draw lead from body edge to pin (horizontal lead going left)
|
||||
if (pinX > bodyRightEdge) {
|
||||
int leadX = bodyRightEdge;
|
||||
int leadY = pinY - pinSize / 2;
|
||||
int leadWidth = pinX - bodyRightEdge;
|
||||
int leadHeight = pinSize;
|
||||
|
||||
g2d.fillRect(leadX, leadY, leadWidth, leadHeight);
|
||||
g2d.setColor(PIN_BORDER_COLOR);
|
||||
g2d.drawRect(leadX, leadY, leadWidth, leadHeight);
|
||||
g2d.setColor(PIN_COLOR);
|
||||
}
|
||||
|
||||
// Draw pin at the end of the lead
|
||||
g2d.fillRect(pinX - pinSize / 2, pinY - pinSize / 2, pinSize, pinSize);
|
||||
g2d.setColor(PIN_BORDER_COLOR);
|
||||
g2d.drawRect(pinX - pinSize / 2, pinY - pinSize / 2, pinSize, pinSize);
|
||||
g2d.setColor(PIN_COLOR);
|
||||
}
|
||||
}
|
||||
|
||||
@EditableProperty(name = "Body")
|
||||
public Color getBodyColor() {
|
||||
if (bodyColor == null) {
|
||||
bodyColor = BODY_COLOR;
|
||||
}
|
||||
return bodyColor;
|
||||
}
|
||||
|
||||
public void setBodyColor(Color bodyColor) {
|
||||
this.bodyColor = bodyColor;
|
||||
}
|
||||
|
||||
@EditableProperty(name = "Border")
|
||||
public Color getBorderColor() {
|
||||
if (borderColor == null) {
|
||||
borderColor = BORDER_COLOR;
|
||||
}
|
||||
return borderColor;
|
||||
}
|
||||
|
||||
public void setBorderColor(Color borderColor) {
|
||||
this.borderColor = borderColor;
|
||||
}
|
||||
|
||||
@EditableProperty(name = "Label")
|
||||
public Color getLabelColor() {
|
||||
if (labelColor == null) {
|
||||
labelColor = LABEL_COLOR;
|
||||
}
|
||||
return labelColor;
|
||||
}
|
||||
|
||||
public void setLabelColor(Color labelColor) {
|
||||
this.labelColor = labelColor;
|
||||
}
|
||||
|
||||
@EditableProperty(name = "Tab")
|
||||
public Color getTabColor() {
|
||||
if (tabColor == null) {
|
||||
tabColor = TAB_COLOR;
|
||||
}
|
||||
return tabColor;
|
||||
}
|
||||
|
||||
public void setTabColor(Color tabColor) {
|
||||
this.tabColor = tabColor;
|
||||
}
|
||||
|
||||
@EditableProperty(name = "Tab Border")
|
||||
public Color getTabBorderColor() {
|
||||
if (tabBorderColor == null) {
|
||||
tabBorderColor = TAB_BORDER_COLOR;
|
||||
}
|
||||
return tabBorderColor;
|
||||
}
|
||||
|
||||
public void setTabBorderColor(Color tabBorderColor) {
|
||||
this.tabBorderColor = tabBorderColor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Rectangle2D getCachingBounds() {
|
||||
double minX = Integer.MAX_VALUE;
|
||||
double maxX = Integer.MIN_VALUE;
|
||||
double minY = Integer.MAX_VALUE;
|
||||
double maxY = Integer.MIN_VALUE;
|
||||
int margin = 50;
|
||||
for (int i = 0; i < getControlPointCount(); i++) {
|
||||
Point2D p = getControlPoint(i);
|
||||
if (p.getX() < minX)
|
||||
minX = p.getX();
|
||||
if (p.getX() > maxX)
|
||||
maxX = p.getX();
|
||||
if (p.getY() < minY)
|
||||
minY = p.getY();
|
||||
if (p.getY() > maxY)
|
||||
maxY = p.getY();
|
||||
}
|
||||
|
||||
return new Rectangle2D.Double(minX - margin, minY - margin, maxX - minX + 2 * margin, maxY - minY + 2 * margin);
|
||||
}
|
||||
|
||||
public static enum MultiwattType {
|
||||
// Multiwatt-11: 6 pins on left, 5 pins on right (staggered)
|
||||
MULTIWATT_11(11, 6, 5),
|
||||
// Multiwatt-15: 8 pins on left, 7 pins on right (staggered)
|
||||
MULTIWATT_15(15, 8, 7);
|
||||
|
||||
private final int pinCount;
|
||||
private final int pinsLeft;
|
||||
private final int pinsRight;
|
||||
|
||||
MultiwattType(int pinCount, int pinsLeft, int pinsRight) {
|
||||
this.pinCount = pinCount;
|
||||
this.pinsLeft = pinsLeft;
|
||||
this.pinsRight = pinsRight;
|
||||
}
|
||||
|
||||
public int getPinCount() {
|
||||
return pinCount;
|
||||
}
|
||||
|
||||
public int getPinsLeft() {
|
||||
return pinsLeft;
|
||||
}
|
||||
|
||||
public int getPinsRight() {
|
||||
return pinsRight;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Multiwatt " + pinCount;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,7 +77,7 @@ public class TapeMeasure extends AbstractLeadedComponent<String> {
|
||||
g2d.setColor(COLOR);
|
||||
g2d.drawLine(1, height - 2, width / 4, height * 3 / 4 - 1);
|
||||
g2d.drawLine(width * 3 / 4, height / 4 - 1, width - 2, 1);
|
||||
g2d.setBackground(Color.black);
|
||||
g2d.setColor(Color.black);
|
||||
g2d.fillPolygon(new int[] {1, 1, 4}, new int[] {height - 4, height - 1, height - 1}, 3);
|
||||
g2d.fillPolygon(new int[] {width - 4, width - 1, width - 1}, new int[] {1, 1, 4}, 3);
|
||||
g2d.rotate(-Math.PI / 4, width / 2, height / 2);
|
||||
@@ -174,6 +174,7 @@ public class TapeMeasure extends AbstractLeadedComponent<String> {
|
||||
g2d.rotate(theta);
|
||||
|
||||
g2d.setFont(font);
|
||||
g2d.setColor(componentState == ComponentState.SELECTED ? SELECTION_COLOR : labelColor);
|
||||
g2d.drawString(text, x, y);
|
||||
}
|
||||
|
||||
|
||||
+47
-10
@@ -27,18 +27,19 @@ import java.awt.geom.Point2D;
|
||||
import org.diylc.common.IComponentTransformer;
|
||||
import org.diylc.common.Orientation;
|
||||
import org.diylc.components.electromechanical.CliffJack1_4;
|
||||
import org.diylc.components.electromechanical.CliffJack1_8;
|
||||
import org.diylc.core.IDIYComponent;
|
||||
|
||||
public class CliffJackTransformer implements IComponentTransformer {
|
||||
|
||||
@Override
|
||||
public boolean canRotate(IDIYComponent<?> component) {
|
||||
return component.getClass().equals(CliffJack1_4.class);
|
||||
return component.getClass().equals(CliffJack1_4.class) || component.getClass().equals(CliffJack1_8.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canMirror(IDIYComponent<?> component) {
|
||||
return component.getClass().equals(CliffJack1_4.class);
|
||||
return component.getClass().equals(CliffJack1_4.class) || component.getClass().equals(CliffJack1_8.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -55,8 +56,15 @@ public class CliffJackTransformer implements IComponentTransformer {
|
||||
component.setControlPoint(p, index);
|
||||
}
|
||||
|
||||
CliffJack1_4 jack = (CliffJack1_4) component;
|
||||
Orientation o = jack.getOrientation();
|
||||
Orientation o;
|
||||
if (component instanceof CliffJack1_4) {
|
||||
o = ((CliffJack1_4) component).getOrientation();
|
||||
} else if (component instanceof CliffJack1_8) {
|
||||
o = ((CliffJack1_8) component).getOrientation();
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
int oValue = o.ordinal();
|
||||
oValue += direction;
|
||||
if (oValue < 0)
|
||||
@@ -64,18 +72,33 @@ public class CliffJackTransformer implements IComponentTransformer {
|
||||
if (oValue >= Orientation.values().length)
|
||||
oValue = 0;
|
||||
o = Orientation.values()[oValue];
|
||||
jack.setOrientation(o);
|
||||
|
||||
if (component instanceof CliffJack1_4) {
|
||||
((CliffJack1_4) component).setOrientation(o);
|
||||
} else if (component instanceof CliffJack1_8) {
|
||||
((CliffJack1_8) component).setOrientation(o);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mirror(IDIYComponent<?> component, Point2D center, int direction) {
|
||||
CliffJack1_4 ic = (CliffJack1_4) component;
|
||||
if (!(component instanceof CliffJack1_4) && !(component instanceof CliffJack1_8)) {
|
||||
return;
|
||||
}
|
||||
|
||||
IDIYComponent<?> ic = component;
|
||||
|
||||
if (direction == IComponentTransformer.HORIZONTAL) {
|
||||
double dx = 2 * (center.getX() - ic.getControlPoint(1).getX());
|
||||
double dy = 0;
|
||||
|
||||
Orientation o = ic.getOrientation();
|
||||
Orientation o;
|
||||
if (component instanceof CliffJack1_4) {
|
||||
o = ((CliffJack1_4) component).getOrientation();
|
||||
} else {
|
||||
o = ((CliffJack1_8) component).getOrientation();
|
||||
}
|
||||
|
||||
switch (o) {
|
||||
case DEFAULT:
|
||||
dy += (ic.getControlPoint(0).getY() - ic.getControlPoint(ic.getControlPointCount() - 1).getY());
|
||||
@@ -98,12 +121,22 @@ public class CliffJackTransformer implements IComponentTransformer {
|
||||
ic.setControlPoint(new Point2D.Double(p.getX() + dx, p.getY() + dy), i);
|
||||
}
|
||||
|
||||
ic.setOrientation(o);
|
||||
if (component instanceof CliffJack1_4) {
|
||||
((CliffJack1_4) component).setOrientation(o);
|
||||
} else {
|
||||
((CliffJack1_8) component).setOrientation(o);
|
||||
}
|
||||
} else {
|
||||
double dx = 0;
|
||||
double dy = 2 * (center.getY() - ic.getControlPoint(1).getY());
|
||||
|
||||
Orientation o = ic.getOrientation();
|
||||
Orientation o;
|
||||
if (component instanceof CliffJack1_4) {
|
||||
o = ((CliffJack1_4) component).getOrientation();
|
||||
} else {
|
||||
o = ((CliffJack1_8) component).getOrientation();
|
||||
}
|
||||
|
||||
switch (o) {
|
||||
case DEFAULT:
|
||||
// dx += ic.getControlPoint(0).getX() - ic.getControlPoint(ic.getControlPointCount() - 1).getX();
|
||||
@@ -128,7 +161,11 @@ public class CliffJackTransformer implements IComponentTransformer {
|
||||
ic.setControlPoint(new Point2D.Double(p.getX() + dx, p.getY() + dy), i);
|
||||
}
|
||||
|
||||
ic.setOrientation(o);
|
||||
if (component instanceof CliffJack1_4) {
|
||||
((CliffJack1_4) component).setOrientation(o);
|
||||
} else {
|
||||
((CliffJack1_8) component).setOrientation(o);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+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);
|
||||
|
||||
+161
@@ -0,0 +1,161 @@
|
||||
/*
|
||||
|
||||
DIY Layout Creator (DIYLC).
|
||||
Copyright (c) 2009-2025 held jointly by the individual authors.
|
||||
|
||||
This file is part of DIYLC.
|
||||
|
||||
DIYLC is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
DIYLC is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with DIYLC. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
*/
|
||||
package org.diylc.components.transform;
|
||||
|
||||
import java.awt.geom.AffineTransform;
|
||||
import java.awt.geom.Point2D;
|
||||
|
||||
import org.diylc.common.IComponentTransformer;
|
||||
import org.diylc.components.electromechanical.OpenJack1_4;
|
||||
import org.diylc.components.electromechanical.OpenJack1_8;
|
||||
import org.diylc.core.IDIYComponent;
|
||||
|
||||
public class OpenJackTransformer implements IComponentTransformer {
|
||||
|
||||
@Override
|
||||
public boolean canRotate(IDIYComponent<?> component) {
|
||||
return component.getClass().equals(OpenJack1_4.class) || component.getClass().equals(OpenJack1_8.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canMirror(IDIYComponent<?> component) {
|
||||
return component.getClass().equals(OpenJack1_4.class) || component.getClass().equals(OpenJack1_8.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean mirroringChangesCircuit() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void rotate(IDIYComponent<?> component, Point2D center, int direction) {
|
||||
AffineTransform rotate = AffineTransform.getRotateInstance(Math.PI / 2 * direction, center.getX(), center.getY());
|
||||
for (int index = 0; index < component.getControlPointCount(); index++) {
|
||||
Point2D p = new Point2D.Double();
|
||||
rotate.transform(component.getControlPoint(index), p);
|
||||
component.setControlPoint(p, index);
|
||||
}
|
||||
|
||||
Integer angle;
|
||||
if (component instanceof OpenJack1_4) {
|
||||
angle = ((OpenJack1_4) component).getAngle();
|
||||
} else if (component instanceof OpenJack1_8) {
|
||||
angle = ((OpenJack1_8) component).getAngle();
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
if (angle == null) {
|
||||
angle = 0;
|
||||
}
|
||||
|
||||
int newAngle = angle + direction * 90;
|
||||
// Normalize to 0-360 range
|
||||
while (newAngle < 0) {
|
||||
newAngle += 360;
|
||||
}
|
||||
while (newAngle >= 360) {
|
||||
newAngle -= 360;
|
||||
}
|
||||
|
||||
if (component instanceof OpenJack1_4) {
|
||||
((OpenJack1_4) component).setAngle(newAngle);
|
||||
} else if (component instanceof OpenJack1_8) {
|
||||
((OpenJack1_8) component).setAngle(newAngle);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mirror(IDIYComponent<?> component, Point2D center, int direction) {
|
||||
if (!(component instanceof OpenJack1_4) && !(component instanceof OpenJack1_8)) {
|
||||
return;
|
||||
}
|
||||
|
||||
IDIYComponent<?> ic = component;
|
||||
|
||||
if (direction == IComponentTransformer.HORIZONTAL) {
|
||||
// Mirror control points horizontally
|
||||
for (int i = 0; i < ic.getControlPointCount(); i++) {
|
||||
Point2D p = ic.getControlPoint(i);
|
||||
double dx = 2 * (center.getX() - p.getX());
|
||||
ic.setControlPoint(new Point2D.Double(p.getX() + dx, p.getY()), i);
|
||||
}
|
||||
|
||||
Integer angle;
|
||||
if (component instanceof OpenJack1_4) {
|
||||
angle = ((OpenJack1_4) component).getAngle();
|
||||
} else {
|
||||
angle = ((OpenJack1_8) component).getAngle();
|
||||
}
|
||||
|
||||
if (angle == null) {
|
||||
angle = 0;
|
||||
}
|
||||
|
||||
// Mirror horizontally: reflect angle across vertical axis
|
||||
int newAngle = 360 - angle;
|
||||
while (newAngle >= 360) {
|
||||
newAngle -= 360;
|
||||
}
|
||||
|
||||
if (component instanceof OpenJack1_4) {
|
||||
((OpenJack1_4) component).setAngle(newAngle);
|
||||
} else {
|
||||
((OpenJack1_8) component).setAngle(newAngle);
|
||||
}
|
||||
} else {
|
||||
// Mirror control points vertically
|
||||
for (int i = 0; i < ic.getControlPointCount(); i++) {
|
||||
Point2D p = ic.getControlPoint(i);
|
||||
double dy = 2 * (center.getY() - p.getY());
|
||||
ic.setControlPoint(new Point2D.Double(p.getX(), p.getY() + dy), i);
|
||||
}
|
||||
|
||||
Integer angle;
|
||||
if (component instanceof OpenJack1_4) {
|
||||
angle = ((OpenJack1_4) component).getAngle();
|
||||
} else {
|
||||
angle = ((OpenJack1_8) component).getAngle();
|
||||
}
|
||||
|
||||
if (angle == null) {
|
||||
angle = 0;
|
||||
}
|
||||
|
||||
// Mirror vertically: reflect angle across horizontal axis
|
||||
int newAngle = 180 - angle;
|
||||
while (newAngle < 0) {
|
||||
newAngle += 360;
|
||||
}
|
||||
while (newAngle >= 360) {
|
||||
newAngle -= 360;
|
||||
}
|
||||
|
||||
if (component instanceof OpenJack1_4) {
|
||||
((OpenJack1_4) component).setAngle(newAngle);
|
||||
} else {
|
||||
((OpenJack1_8) component).setAngle(newAngle);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+14
@@ -70,6 +70,20 @@ public class PinHeaderTransformer implements IComponentTransformer {
|
||||
@Override
|
||||
public void mirror(IDIYComponent<?> component, Point2D center, int direction) {
|
||||
PinHeader header = (PinHeader) component;
|
||||
int controlPointCount = header.getControlPointCount();
|
||||
|
||||
// Handle single-pin case
|
||||
if (controlPointCount == 1) {
|
||||
Point2D p = header.getControlPoint(0);
|
||||
if (direction == IComponentTransformer.HORIZONTAL) {
|
||||
double dx = 2 * (center.getX() - p.getX());
|
||||
header.setControlPoint(new Point2D.Double(p.getX() + dx, p.getY()), 0);
|
||||
} else {
|
||||
double dy = 2 * (center.getY() - p.getY());
|
||||
header.setControlPoint(new Point2D.Double(p.getX(), p.getY() + dy), 0);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (direction == IComponentTransformer.HORIZONTAL) {
|
||||
double dx = 2 * (center.getX() - header.getControlPoint(1).getX());
|
||||
|
||||
@@ -0,0 +1,247 @@
|
||||
/*
|
||||
DIY Layout Creator (DIYLC).
|
||||
Copyright (c) 2009-2025 held jointly by the individual authors.
|
||||
|
||||
This file is part of DIYLC.
|
||||
|
||||
DIYLC is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
DIYLC is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with DIYLC. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.diylc.awt;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.awt.Font;
|
||||
import java.awt.FontMetrics;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
public class StringUtilsTest {
|
||||
|
||||
private FontMetrics fontMetrics;
|
||||
private Graphics2D g2d;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
BufferedImage image = new BufferedImage(100, 100, BufferedImage.TYPE_INT_RGB);
|
||||
g2d = image.createGraphics();
|
||||
g2d.setFont(new Font("Arial", Font.PLAIN, 12));
|
||||
fontMetrics = g2d.getFontMetrics();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWrapWithNewline() {
|
||||
String text = "Line 1\nLine 2\nLine 3";
|
||||
int maxWidth = 1000; // Large enough to not wrap
|
||||
|
||||
List<String> lines = StringUtils.wrap(text, fontMetrics, maxWidth);
|
||||
|
||||
assertEquals("Should split into 3 lines", 3, lines.size());
|
||||
assertEquals("Line 1", lines.get(0));
|
||||
assertEquals("Line 2", lines.get(1));
|
||||
assertEquals("Line 3", lines.get(2));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWrapWithCarriageReturn() {
|
||||
String text = "Line 1\rLine 2\rLine 3";
|
||||
int maxWidth = 1000;
|
||||
|
||||
List<String> lines = StringUtils.wrap(text, fontMetrics, maxWidth);
|
||||
|
||||
assertEquals("Should split into 3 lines", 3, lines.size());
|
||||
assertEquals("Line 1", lines.get(0));
|
||||
assertEquals("Line 2", lines.get(1));
|
||||
assertEquals("Line 3", lines.get(2));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWrapWithCarriageReturnLineFeed() {
|
||||
String text = "Line 1\r\nLine 2\r\nLine 3";
|
||||
int maxWidth = 1000;
|
||||
|
||||
List<String> lines = StringUtils.wrap(text, fontMetrics, maxWidth);
|
||||
|
||||
assertEquals("Should split into 3 lines", 3, lines.size());
|
||||
assertEquals("Line 1", lines.get(0));
|
||||
assertEquals("Line 2", lines.get(1));
|
||||
assertEquals("Line 3", lines.get(2));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWrapWithMixedNewlines() {
|
||||
String text = "Line 1\nLine 2\rLine 3\r\nLine 4";
|
||||
int maxWidth = 1000;
|
||||
|
||||
List<String> lines = StringUtils.wrap(text, fontMetrics, maxWidth);
|
||||
|
||||
assertEquals("Should split into 4 lines", 4, lines.size());
|
||||
assertEquals("Line 1", lines.get(0));
|
||||
assertEquals("Line 2", lines.get(1));
|
||||
assertEquals("Line 3", lines.get(2));
|
||||
assertEquals("Line 4", lines.get(3));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWrapWithNewlineAndBrTag() {
|
||||
String text = "Line 1\nLine 2<br>Line 3";
|
||||
int maxWidth = 1000;
|
||||
|
||||
List<String> lines = StringUtils.wrap(text, fontMetrics, maxWidth);
|
||||
|
||||
assertEquals("Should split into 3 lines", 3, lines.size());
|
||||
assertEquals("Line 1", lines.get(0));
|
||||
assertEquals("Line 2", lines.get(1));
|
||||
assertEquals("Line 3", lines.get(2));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWrapWithNewlineAndWrapping() {
|
||||
String text = "First line\nSecond line with very long text that should wrap";
|
||||
int maxWidth = fontMetrics.stringWidth("Second line with very long text that should"); // Force wrapping
|
||||
|
||||
List<String> lines = StringUtils.wrap(text, fontMetrics, maxWidth);
|
||||
|
||||
assertTrue("Should have at least 2 lines (newline + wrapped)", lines.size() >= 2);
|
||||
assertEquals("First line", lines.get(0));
|
||||
// The second line should be wrapped, so we should have more lines
|
||||
assertTrue("Second line should be wrapped", lines.size() >= 3);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWrapWithEmptyLines() {
|
||||
String text = "Line 1\n\nLine 3";
|
||||
int maxWidth = 1000;
|
||||
|
||||
List<String> lines = StringUtils.wrap(text, fontMetrics, maxWidth);
|
||||
|
||||
assertEquals("Should split into 3 lines including empty line", 3, lines.size());
|
||||
assertEquals("Line 1", lines.get(0));
|
||||
assertEquals("", lines.get(1));
|
||||
assertEquals("Line 3", lines.get(2));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWrapWithNewlineAtEnd() {
|
||||
String text = "Line 1\nLine 2\n";
|
||||
int maxWidth = 1000;
|
||||
|
||||
List<String> lines = StringUtils.wrap(text, fontMetrics, maxWidth);
|
||||
|
||||
assertEquals("Should split into 2 lines (no trailing empty line)", 2, lines.size());
|
||||
assertEquals("Line 1", lines.get(0));
|
||||
assertEquals("Line 2", lines.get(1));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWrapWithNoNewlines() {
|
||||
String text = "This is a single line of text";
|
||||
int maxWidth = 1000;
|
||||
|
||||
List<String> lines = StringUtils.wrap(text, fontMetrics, maxWidth);
|
||||
|
||||
assertEquals("Should have 1 line", 1, lines.size());
|
||||
assertEquals("This is a single line of text", lines.get(0));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWrapWithBrTagOnly() {
|
||||
String text = "Line 1<br>Line 2<br>Line 3";
|
||||
int maxWidth = 1000;
|
||||
|
||||
List<String> lines = StringUtils.wrap(text, fontMetrics, maxWidth);
|
||||
|
||||
assertEquals("Should split into 3 lines", 3, lines.size());
|
||||
assertEquals("Line 1", lines.get(0));
|
||||
assertEquals("Line 2", lines.get(1));
|
||||
assertEquals("Line 3", lines.get(2));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWrapWithNewlineAndBrTagMixed() {
|
||||
String text = "Line 1\nLine 2<br>Line 3\nLine 4<br>Line 5";
|
||||
int maxWidth = 1000;
|
||||
|
||||
List<String> lines = StringUtils.wrap(text, fontMetrics, maxWidth);
|
||||
|
||||
assertEquals("Should split into 5 lines", 5, lines.size());
|
||||
assertEquals("Line 1", lines.get(0));
|
||||
assertEquals("Line 2", lines.get(1));
|
||||
assertEquals("Line 3", lines.get(2));
|
||||
assertEquals("Line 4", lines.get(3));
|
||||
assertEquals("Line 5", lines.get(4));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWrapWithMultipleConsecutiveNewlines() {
|
||||
String text = "Line 1\n\n\nLine 4";
|
||||
int maxWidth = 1000;
|
||||
|
||||
List<String> lines = StringUtils.wrap(text, fontMetrics, maxWidth);
|
||||
|
||||
assertEquals("Should split into 4 lines with 2 empty lines", 4, lines.size());
|
||||
assertEquals("Line 1", lines.get(0));
|
||||
assertEquals("", lines.get(1));
|
||||
assertEquals("", lines.get(2));
|
||||
assertEquals("Line 4", lines.get(3));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWrapWithMultipleConsecutiveNewlinesAtStart() {
|
||||
String text = "\n\nLine 3";
|
||||
int maxWidth = 1000;
|
||||
|
||||
List<String> lines = StringUtils.wrap(text, fontMetrics, maxWidth);
|
||||
|
||||
assertEquals("Should split into 3 lines with 2 empty lines at start", 3, lines.size());
|
||||
assertEquals("", lines.get(0));
|
||||
assertEquals("", lines.get(1));
|
||||
assertEquals("Line 3", lines.get(2));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWrapWithMultipleConsecutiveNewlinesAtEnd() {
|
||||
String text = "Line 1\n\n\n";
|
||||
int maxWidth = 1000;
|
||||
|
||||
List<String> lines = StringUtils.wrap(text, fontMetrics, maxWidth);
|
||||
|
||||
// Trailing newlines: the last newline at end doesn't create an empty line
|
||||
// So \n\n\n at end creates 2 empty lines (from first 2 newlines)
|
||||
assertEquals("Should split into 3 lines (2 empty from trailing newlines)", 3, lines.size());
|
||||
assertEquals("Line 1", lines.get(0));
|
||||
assertEquals("", lines.get(1));
|
||||
assertEquals("", lines.get(2));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWrapWithMultipleConsecutiveNewlinesMixed() {
|
||||
String text = "Line 1\n\nLine 3\n\n\nLine 6";
|
||||
int maxWidth = 1000;
|
||||
|
||||
List<String> lines = StringUtils.wrap(text, fontMetrics, maxWidth);
|
||||
|
||||
assertEquals("Should split into 6 lines", 6, lines.size());
|
||||
assertEquals("Line 1", lines.get(0));
|
||||
assertEquals("", lines.get(1));
|
||||
assertEquals("Line 3", lines.get(2));
|
||||
assertEquals("", lines.get(3));
|
||||
assertEquals("", lines.get(4));
|
||||
assertEquals("Line 6", lines.get(5));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
package org.diylc.netlist;
|
||||
|
||||
import org.diylc.components.guitar.Freeway3x3_05;
|
||||
import org.junit.Test;
|
||||
|
||||
public class Freeway3x3_05Tests extends AbstractSwitchTests {
|
||||
|
||||
@Test
|
||||
public void testFreeway3x3_05() {
|
||||
Freeway3x3_05 sw = new Freeway3x3_05();
|
||||
|
||||
String[] validCombinations = new String[]{
|
||||
"0,0,6", "0,6,11", "0,0,11", "0,17,21", "0,21,22", "0,17,22",
|
||||
"1,1,6", "1,6,12", "1,1,12", "1,18,21", "1,21,23", "1,18,23",
|
||||
"2,2,6", "2,6,13", "2,2,13", "2,19,21", "2,21,24", "2,19,24",
|
||||
"3,5,6", "3,6,10", "3,5,10", "3,16,21", "3,21,27", "3,16,27",
|
||||
"4,4,6", "4,6,9", "4,4,9", "4,15,21", "4,21,26", "4,15,26",
|
||||
"5,3,6", "5,6,8", "5,3,8", "5,14,21", "5,21,25", "5,14,25",
|
||||
};
|
||||
|
||||
testSwitch(sw, validCombinations);
|
||||
}
|
||||
}
|
||||
@@ -1,15 +1,117 @@
|
||||
package org.diylc.netlist;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNull;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import org.diylc.components.guitar.LPSwitch;
|
||||
import org.diylc.components.guitar.LPSwitch.MiddleTerminalConfiguration;
|
||||
import org.diylc.common.Orientation;
|
||||
import org.diylc.core.IDIYComponent;
|
||||
|
||||
public class LPSwitchTests extends AbstractSwitchTests {
|
||||
|
||||
@Test
|
||||
public void testLPSwitch() {
|
||||
public void testLPSwitchConnected() {
|
||||
LPSwitch lpSwitch = new LPSwitch();
|
||||
// Default configuration is Connected
|
||||
assertEquals(MiddleTerminalConfiguration.Connected, lpSwitch.getMiddleTerminalConfiguration());
|
||||
// Connected has 4 control points (0, 1, 2, 3)
|
||||
assertEquals(4, ((IDIYComponent<?>) lpSwitch).getControlPointCount());
|
||||
// Position 0 (Treble): 1-2 connected
|
||||
// Position 1 (Middle): 1-2, 1-3, 2-3 connected (all terminals > 0 connected)
|
||||
// Position 2 (Rhythm): 2-3 connected
|
||||
String[] validCombinations = new String[] {"0,1,2", "1,1,2", "1,2,3", "1,1,3", "2,2,3"};
|
||||
testSwitch(lpSwitch, validCombinations);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLPSwitchDisconnected() {
|
||||
LPSwitch lpSwitch = new LPSwitch();
|
||||
lpSwitch.setMiddleTerminalConfiguration(MiddleTerminalConfiguration.Disconnected);
|
||||
assertEquals(MiddleTerminalConfiguration.Disconnected, lpSwitch.getMiddleTerminalConfiguration());
|
||||
// Disconnected has 5 control points (0, 1, 2, 3, 4)
|
||||
assertEquals(5, ((IDIYComponent<?>) lpSwitch).getControlPointCount());
|
||||
// Position 0 (Treble): 1-2 connected
|
||||
// Position 1 (Middle): 1-2 and 3-4 connected (both pairs)
|
||||
// Position 2 (Rhythm): 3-4 connected
|
||||
String[] validCombinations = new String[] {"0,1,2", "1,1,2", "1,3,4", "2,3,4"};
|
||||
testSwitch(lpSwitch, validCombinations);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testControlPointCountChange() {
|
||||
LPSwitch lpSwitch = new LPSwitch();
|
||||
// Start with Connected (4 points)
|
||||
assertEquals(4, ((IDIYComponent<?>) lpSwitch).getControlPointCount());
|
||||
|
||||
// Switch to Disconnected (5 points)
|
||||
lpSwitch.setMiddleTerminalConfiguration(MiddleTerminalConfiguration.Disconnected);
|
||||
assertEquals(5, ((IDIYComponent<?>) lpSwitch).getControlPointCount());
|
||||
|
||||
// Switch back to Connected (4 points)
|
||||
lpSwitch.setMiddleTerminalConfiguration(MiddleTerminalConfiguration.Connected);
|
||||
assertEquals(4, ((IDIYComponent<?>) lpSwitch).getControlPointCount());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPositionNames() {
|
||||
LPSwitch lpSwitch = new LPSwitch();
|
||||
assertEquals(3, lpSwitch.getPositionCount());
|
||||
assertEquals("Treble", lpSwitch.getPositionName(0));
|
||||
assertEquals("Middle", lpSwitch.getPositionName(1));
|
||||
assertEquals("Rhythm", lpSwitch.getPositionName(2));
|
||||
assertNull(lpSwitch.getPositionName(3)); // Invalid position
|
||||
assertNull(lpSwitch.getPositionName(-1)); // Invalid position
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOrientationDoesNotAffectConnectivity() {
|
||||
LPSwitch lpSwitch = new LPSwitch();
|
||||
// Test with default Connected configuration
|
||||
String[] validCombinations = new String[] {"0,1,2", "1,1,2", "1,2,3", "1,1,3", "2,2,3"};
|
||||
|
||||
// Test all orientations - connectivity should remain the same
|
||||
for (Orientation orientation : Orientation.values()) {
|
||||
lpSwitch.setOrientation(orientation);
|
||||
assertEquals(orientation, lpSwitch.getOrientation());
|
||||
// Connectivity should be unchanged regardless of orientation
|
||||
testSwitch(lpSwitch, validCombinations);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOrientationWithDisconnectedConfiguration() {
|
||||
LPSwitch lpSwitch = new LPSwitch();
|
||||
lpSwitch.setMiddleTerminalConfiguration(MiddleTerminalConfiguration.Disconnected);
|
||||
String[] validCombinations = new String[] {"0,1,2", "1,1,2", "1,3,4", "2,3,4"};
|
||||
|
||||
// Test all orientations with Disconnected configuration
|
||||
for (Orientation orientation : Orientation.values()) {
|
||||
lpSwitch.setOrientation(orientation);
|
||||
assertEquals(orientation, lpSwitch.getOrientation());
|
||||
// Connectivity should be unchanged regardless of orientation
|
||||
testSwitch(lpSwitch, validCombinations);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConfigurationSwitchPreservesConnectivity() {
|
||||
LPSwitch lpSwitch = new LPSwitch();
|
||||
|
||||
// Test Connected configuration
|
||||
lpSwitch.setMiddleTerminalConfiguration(MiddleTerminalConfiguration.Connected);
|
||||
String[] connectedCombinations = new String[] {"0,1,2", "1,1,2", "1,2,3", "1,1,3", "2,2,3"};
|
||||
testSwitch(lpSwitch, connectedCombinations);
|
||||
|
||||
// Switch to Disconnected
|
||||
lpSwitch.setMiddleTerminalConfiguration(MiddleTerminalConfiguration.Disconnected);
|
||||
String[] disconnectedCombinations = new String[] {"0,1,2", "1,1,2", "1,3,4", "2,3,4"};
|
||||
testSwitch(lpSwitch, disconnectedCombinations);
|
||||
|
||||
// Switch back to Connected
|
||||
lpSwitch.setMiddleTerminalConfiguration(MiddleTerminalConfiguration.Connected);
|
||||
testSwitch(lpSwitch, connectedCombinations);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
package org.diylc.netlist;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import org.diylc.components.electromechanical.TactileMicroSwitch;
|
||||
import org.diylc.components.electromechanical.TactileMicroSwitch.PinPairingMode;
|
||||
|
||||
public class TactileMicroSwitchTests extends AbstractSwitchTests {
|
||||
|
||||
@Test
|
||||
public void testTactileMicroSwitchOpposite() {
|
||||
TactileMicroSwitch tactileSwitch = new TactileMicroSwitch();
|
||||
tactileSwitch.setPinPairingMode(PinPairingMode.Opposite);
|
||||
// Position 0 (Released): no connections
|
||||
// Position 1 (Pressed): pins on opposite sides (same row): 0-2 and 1-3
|
||||
String[] validCombinations = new String[] {
|
||||
"1,0,2", "1,2,0", // Position 1: 0-2 (both directions)
|
||||
"1,1,3", "1,3,1" // Position 1: 1-3 (both directions)
|
||||
};
|
||||
testSwitch(tactileSwitch, validCombinations);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTactileMicroSwitchDiagonal() {
|
||||
TactileMicroSwitch tactileSwitch = new TactileMicroSwitch();
|
||||
tactileSwitch.setPinPairingMode(PinPairingMode.Diagonal);
|
||||
// Position 0 (Released): no connections
|
||||
// Position 1 (Pressed): diagonally opposite pins: 0-3 and 1-2
|
||||
String[] validCombinations = new String[] {
|
||||
"1,0,3", "1,3,0", // Position 1: 0-3 (both directions)
|
||||
"1,1,2", "1,2,1" // Position 1: 1-2 (both directions)
|
||||
};
|
||||
testSwitch(tactileSwitch, validCombinations);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTactileMicroSwitchAdjacent() {
|
||||
TactileMicroSwitch tactileSwitch = new TactileMicroSwitch();
|
||||
tactileSwitch.setPinPairingMode(PinPairingMode.Adjacent);
|
||||
// Position 0 (Released): no connections
|
||||
// Position 1 (Pressed): pins next to each other (same column): 0-1 and 2-3
|
||||
String[] validCombinations = new String[] {
|
||||
"1,0,1", "1,1,0", // Position 1: 0-1 (both directions)
|
||||
"1,2,3", "1,3,2" // Position 1: 2-3 (both directions)
|
||||
};
|
||||
testSwitch(tactileSwitch, validCombinations);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.5.0</version>
|
||||
<version>5.8.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>
|
||||
|
||||
@@ -42,6 +42,7 @@ import org.diylc.serialization.ProjectFileManager;
|
||||
import org.diylc.swing.gui.MainFrame;
|
||||
import org.diylc.swing.gui.TemplateDialog;
|
||||
import org.diylc.swing.plugins.file.ProjectDrawingProvider;
|
||||
import org.diylc.utils.SwingUtils;
|
||||
|
||||
/**
|
||||
* Main class that runs DIYLC.
|
||||
@@ -137,6 +138,12 @@ public class DIYLCStarter {
|
||||
|
||||
try {
|
||||
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
|
||||
|
||||
// Fix tooltip colors for Linux systems where they may be unreadable
|
||||
SwingUtils.fixTooltipColors();
|
||||
|
||||
// Apply configurable font scaling from VM argument: -Dorg.diylc.fontScaleFactor=X
|
||||
org.diylc.utils.FontScalingUtils.applyFontScaling();
|
||||
} catch (Exception e) {
|
||||
LOG.error("Could not set Look&Feel", e);
|
||||
}
|
||||
|
||||
+3
-7
@@ -5,11 +5,7 @@ import java.io.BufferedOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.*;
|
||||
import javax.swing.AbstractAction;
|
||||
import org.diylc.appframework.miscutils.ConfigurationManager;
|
||||
import org.diylc.swingframework.CheckBoxListDialog;
|
||||
@@ -69,7 +65,7 @@ public class ExportBlocksAction extends AbstractAction {
|
||||
|
||||
dialog.setVisible(true);
|
||||
|
||||
if (dialog.getSelectedButtonCaption() != "OK")
|
||||
if (!Objects.equals(dialog.getSelectedButtonCaption(), "OK"))
|
||||
return;
|
||||
|
||||
Object[] selected = dialog.getSelectedOptions();
|
||||
@@ -133,4 +129,4 @@ public class ExportBlocksAction extends AbstractAction {
|
||||
}, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+5
-11
@@ -5,13 +5,7 @@ import java.io.BufferedOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
import java.util.*;
|
||||
import javax.swing.AbstractAction;
|
||||
import org.diylc.appframework.miscutils.ConfigurationManager;
|
||||
import org.diylc.swingframework.CheckBoxListDialog;
|
||||
@@ -73,7 +67,7 @@ public class ExportVariantsAction extends AbstractAction {
|
||||
ActionFactory.LOG.warn("Could not find type for: " + className);
|
||||
}
|
||||
|
||||
Collections.sort(types, new Comparator<ComponentType>() {
|
||||
types.sort(new Comparator<ComponentType>() {
|
||||
|
||||
@Override
|
||||
public int compare(ComponentType o1, ComponentType o2) {
|
||||
@@ -86,7 +80,7 @@ public class ExportVariantsAction extends AbstractAction {
|
||||
|
||||
dialog.setVisible(true);
|
||||
|
||||
if (dialog.getSelectedButtonCaption() != "OK")
|
||||
if (!Objects.equals(dialog.getSelectedButtonCaption(), "OK"))
|
||||
return;
|
||||
|
||||
Object[] selected = dialog.getSelectedOptions();
|
||||
@@ -131,7 +125,7 @@ public class ExportVariantsAction extends AbstractAction {
|
||||
BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(file));
|
||||
ProjectFileManager.xStreamSerializer.toXML(variantPkg, out);
|
||||
out.close();
|
||||
ActionFactory.LOG.info("Exported variants succesfully");
|
||||
ActionFactory.LOG.info("Exported variants successfully");
|
||||
} catch (IOException e) {
|
||||
ActionFactory.LOG.error("Could not export variants", e);
|
||||
}
|
||||
@@ -153,4 +147,4 @@ public class ExportVariantsAction extends AbstractAction {
|
||||
}, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
+12
-1
@@ -21,6 +21,7 @@
|
||||
*/
|
||||
package org.diylc.swing.plugins.canvas;
|
||||
|
||||
import java.awt.Cursor;
|
||||
import java.awt.dnd.DragGestureEvent;
|
||||
import java.awt.dnd.DragGestureListener;
|
||||
import java.awt.event.InputEvent;
|
||||
@@ -54,7 +55,17 @@ class CanvasDragGestureListener implements DragGestureListener {
|
||||
}
|
||||
canvasPanel.setClickInProgress(false);
|
||||
presenter.dragStarted(dge.getDragOrigin(), dge.getDragAction(), forceReSelection);
|
||||
dge.startDrag(presenter.getCursorAt(dge.getDragOrigin(), false, false, false), new EmptyTransferable(), new CanvasSourceListener(
|
||||
// Get the appropriate cursor from the presenter (e.g., HAND_CURSOR over components)
|
||||
// Preserve the cursor context (HAND_CURSOR, CROSSHAIR_CURSOR, etc.) for drag operations
|
||||
// On Linux, DEFAULT_CURSOR during drag can show "No" cursor, so use MOVE_CURSOR as fallback
|
||||
Cursor requestedCursor = presenter.getCursorAt(dge.getDragOrigin(), false, false, false);
|
||||
Cursor dragCursor = requestedCursor;
|
||||
// Only replace DEFAULT_CURSOR with MOVE_CURSOR on Linux to prevent "No" cursor issue
|
||||
// Preserve HAND_CURSOR, CROSSHAIR_CURSOR, etc. as they are appropriate for drag operations
|
||||
if (requestedCursor.getType() == Cursor.DEFAULT_CURSOR) {
|
||||
dragCursor = Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR);
|
||||
}
|
||||
dge.startDrag(dragCursor, new EmptyTransferable(), new CanvasSourceListener(
|
||||
presenter));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -41,6 +41,7 @@ import org.apache.log4j.Logger;
|
||||
import org.diylc.appframework.miscutils.IConfigListener;
|
||||
import org.diylc.appframework.miscutils.IConfigurationManager;
|
||||
import org.diylc.appframework.miscutils.Utils;
|
||||
import org.diylc.core.measures.ResizeDimensions;
|
||||
import org.diylc.swingframework.ruler.IRulerListener;
|
||||
import org.diylc.swingframework.ruler.Ruler.InchSubdivision;
|
||||
import org.diylc.swingframework.ruler.RulerScrollPane;
|
||||
@@ -53,7 +54,6 @@ import org.diylc.common.BadPositionException;
|
||||
import org.diylc.common.EventType;
|
||||
import org.diylc.common.IPlugIn;
|
||||
import org.diylc.common.IPlugInPort;
|
||||
import org.diylc.core.IDIYComponent;
|
||||
import org.diylc.core.measures.Size;
|
||||
import org.diylc.core.measures.SizeUnit;
|
||||
import org.diylc.presenter.Presenter;
|
||||
@@ -68,6 +68,8 @@ public class CanvasPlugin implements IPlugIn{
|
||||
private RulerScrollPane scrollPane;
|
||||
private CanvasPanel canvasPanel;
|
||||
private ComponentPopupMenu popupMenu;
|
||||
private ResizeInfoTooltip resizeInfoTooltip;
|
||||
private boolean resizingInProgress = false;
|
||||
|
||||
private IPlugInPort plugInPort;
|
||||
private ISwingUI swingUI;
|
||||
@@ -193,6 +195,7 @@ public class CanvasPlugin implements IPlugIn{
|
||||
public CanvasPanel getCanvasPanel() {
|
||||
if (canvasPanel == null) {
|
||||
canvasPanel = new CanvasPanel(plugInPort, configManager);
|
||||
resizeInfoTooltip = new ResizeInfoTooltip(canvasPanel, configManager);
|
||||
canvasPanel.addMouseListener(new MouseAdapter() {
|
||||
|
||||
private MouseEvent pressedEvent;
|
||||
@@ -271,10 +274,21 @@ public class CanvasPlugin implements IPlugIn{
|
||||
public void mouseMoved(MouseEvent e) {
|
||||
if (scrollPane.isMouseScrollMode())
|
||||
return;
|
||||
// if needed uncomment this code, it forces the cursor to refresh when it's stuck
|
||||
// canvasPanel.setCursor(null);
|
||||
canvasPanel.setCursor(plugInPort.getCursorAt(e.getPoint(), Utils.isMac() ? e.isMetaDown() : e.isControlDown(),
|
||||
e.isShiftDown(), e.isAltDown()));
|
||||
plugInPort.mouseMoved(e.getPoint(), Utils.isMac() ? e.isMetaDown() : e.isControlDown(),
|
||||
e.isShiftDown(), e.isAltDown());
|
||||
|
||||
// Update tooltip position if resizing is in progress and tooltip is enabled, otherwise hide it
|
||||
if (resizeInfoTooltip != null) {
|
||||
if (resizingInProgress && configManager.readBoolean(IPlugInPort.SHOW_RESIZE_DIMENSIONS_TOOLTIP_KEY, true)) {
|
||||
resizeInfoTooltip.updatePosition(e.getPoint());
|
||||
} else {
|
||||
resizeInfoTooltip.hideTooltip();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -503,11 +517,13 @@ public class CanvasPlugin implements IPlugIn{
|
||||
canvasPanel.repaint();
|
||||
// Refresh selection bounds after we're done with painting to ensure we have traced the
|
||||
// component areas
|
||||
boolean panToSelection = params.length > 0 && Boolean.TRUE.equals(params[0]);
|
||||
this.resizingInProgress = params.length > 1 && Boolean.TRUE.equals(params[1]);
|
||||
SwingUtilities.invokeLater(new Runnable() {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
if (params.length > 0 && Boolean.TRUE.equals(params[0])) {
|
||||
if (panToSelection) {
|
||||
Rectangle2D selectionBounds = plugInPort.getSelectionBounds(true);
|
||||
if (selectionBounds != null) {
|
||||
Rectangle visibleRect = scrollPane.getVisibleRect();
|
||||
@@ -517,8 +533,28 @@ public class CanvasPlugin implements IPlugIn{
|
||||
);
|
||||
}
|
||||
}
|
||||
if (configManager.readBoolean(IPlugInPort.SHOW_RULERS_KEY, true))
|
||||
if (configManager.readBoolean(IPlugInPort.SHOW_RULERS_KEY, true)) {
|
||||
scrollPane.setSelectionRectangle(plugInPort.getSelectionBounds(true));
|
||||
}
|
||||
if (CanvasPlugin.this.resizingInProgress && getCanvasPanel().getMousePosition() != null) {
|
||||
// Only show tooltip if enabled in configuration
|
||||
if (configManager.readBoolean(IPlugInPort.SHOW_RESIZE_DIMENSIONS_TOOLTIP_KEY, true)) {
|
||||
ResizeDimensions resizeDimensions = plugInPort.getResizeDimensions();
|
||||
if (resizeInfoTooltip != null) {
|
||||
resizeInfoTooltip.update(resizeDimensions, getCanvasPanel().getMousePosition());
|
||||
}
|
||||
} else {
|
||||
// Hide tooltip if disabled
|
||||
if (resizeInfoTooltip != null) {
|
||||
resizeInfoTooltip.hideTooltip();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Hide tooltip when not resizing
|
||||
if (resizeInfoTooltip != null) {
|
||||
resizeInfoTooltip.hideTooltip();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
break;
|
||||
@@ -561,25 +597,4 @@ public class CanvasPlugin implements IPlugIn{
|
||||
double extraSpace = CanvasPlugin.this.plugInPort.getExtraSpace();
|
||||
getScrollPane().setZeroLocation(new Point2D.Double(extraSpace, extraSpace));
|
||||
}
|
||||
|
||||
class LockAction extends AbstractAction {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private IDIYComponent<?> component;
|
||||
private boolean locked;
|
||||
|
||||
public LockAction(IDIYComponent<?> component, boolean locked) {
|
||||
super();
|
||||
this.locked = locked;
|
||||
this.component = component;
|
||||
putValue(AbstractAction.NAME, component.getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
plugInPort.lockComponent(component, locked);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
+31
-2
@@ -21,11 +21,16 @@
|
||||
*/
|
||||
package org.diylc.swing.plugins.canvas;
|
||||
|
||||
import java.awt.Component;
|
||||
import java.awt.Cursor;
|
||||
import java.awt.Point;
|
||||
import java.awt.dnd.DragSourceDragEvent;
|
||||
import java.awt.dnd.DragSourceDropEvent;
|
||||
import java.awt.dnd.DragSourceEvent;
|
||||
import java.awt.dnd.DragSourceListener;
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
import javax.swing.SwingUtilities;
|
||||
|
||||
import org.diylc.common.IPlugInPort;
|
||||
|
||||
@@ -50,13 +55,16 @@ class CanvasSourceListener implements DragSourceListener {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dragEnter(DragSourceDragEvent dsde) {}
|
||||
public void dragEnter(DragSourceDragEvent dsde) {
|
||||
updateCursor(dsde);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dragExit(DragSourceEvent dse) {}
|
||||
|
||||
@Override
|
||||
public void dragOver(DragSourceDragEvent dsde) {
|
||||
updateCursor(dsde);
|
||||
Point p = dsde.getDragSourceContext().getComponent().getMousePosition();
|
||||
if (p != null) {
|
||||
dsde.getDragSourceContext().getComponent().firePropertyChange("dragPoint", p.x, p.y);
|
||||
@@ -64,5 +72,26 @@ class CanvasSourceListener implements DragSourceListener {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dropActionChanged(DragSourceDragEvent dsde) {}
|
||||
public void dropActionChanged(DragSourceDragEvent dsde) {
|
||||
updateCursor(dsde);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the cursor during drag operations using the presenter as the single source of truth.
|
||||
* If the presenter returns DEFAULT_CURSOR, uses MOVE_CURSOR to prevent "No" cursor issues.
|
||||
*/
|
||||
private void updateCursor(DragSourceDragEvent dsde) {
|
||||
Point mousePos = dsde.getDragSourceContext().getComponent().getMousePosition();
|
||||
if (mousePos != null) {
|
||||
// Use presenter as single source of truth for cursor
|
||||
Cursor requestedCursor = presenter.getCursorAt(mousePos, false, false, false);
|
||||
Cursor dragCursor = requestedCursor;
|
||||
// If DEFAULT_CURSOR, use MOVE_CURSOR to prevent "No" cursor during drag operations
|
||||
if (requestedCursor.getType() == Cursor.DEFAULT_CURSOR) {
|
||||
dragCursor = Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR);
|
||||
}
|
||||
dsde.getDragSourceContext().setCursor(null);
|
||||
dsde.getDragSourceContext().setCursor(dragCursor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+29
-6
@@ -21,15 +21,13 @@
|
||||
*/
|
||||
package org.diylc.swing.plugins.canvas;
|
||||
|
||||
import java.awt.Point;
|
||||
import java.awt.dnd.DnDConstants;
|
||||
import java.awt.dnd.DropTargetDragEvent;
|
||||
import java.awt.dnd.DropTargetDropEvent;
|
||||
import java.awt.dnd.DropTargetEvent;
|
||||
import java.awt.dnd.DropTargetListener;
|
||||
import java.awt.*;
|
||||
import java.awt.dnd.*;
|
||||
|
||||
import org.diylc.common.IPlugInPort;
|
||||
|
||||
import javax.swing.*;
|
||||
|
||||
|
||||
/**
|
||||
* {@link DropTargetListener} for {@link CanvasPanel}.
|
||||
@@ -106,10 +104,35 @@ class CanvasTargetListener implements DropTargetListener {
|
||||
@Override
|
||||
public void drop(DropTargetDropEvent dtde) {
|
||||
presenter.dragEnded(dtde.getLocation());
|
||||
updateCursor(dtde);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dropActionChanged(DropTargetDragEvent dtde) {
|
||||
presenter.dragActionChanged(dtde.getDropAction());
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the cursor during drag operations using the presenter as the single source of truth.
|
||||
* If the presenter returns DEFAULT_CURSOR, uses MOVE_CURSOR to prevent "No" cursor issues.
|
||||
*/
|
||||
private void updateCursor(DropTargetDropEvent dsde) {
|
||||
SwingUtilities.invokeLater(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
Point mousePos = dsde.getDropTargetContext().getComponent().getMousePosition();
|
||||
if (mousePos != null) {
|
||||
// Use presenter as single source of truth for cursor
|
||||
Cursor requestedCursor = presenter.getCursorAt(mousePos, false, false, false);
|
||||
Cursor dragCursor = requestedCursor;
|
||||
// If DEFAULT_CURSOR, use MOVE_CURSOR to prevent "No" cursor during drag operations
|
||||
if (requestedCursor.getType() == Cursor.DEFAULT_CURSOR) {
|
||||
dragCursor = Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR);
|
||||
}
|
||||
dsde.getDropTargetContext().getComponent().setCursor(null);
|
||||
dsde.getDropTargetContext().getComponent().setCursor(dragCursor);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
+225
@@ -0,0 +1,225 @@
|
||||
/*
|
||||
*
|
||||
* 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.swing.plugins.canvas;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.Component;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.Font;
|
||||
import java.awt.Graphics;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.Point;
|
||||
import java.awt.RenderingHints;
|
||||
import java.awt.Window;
|
||||
import java.text.DecimalFormat;
|
||||
import javax.swing.BorderFactory;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JWindow;
|
||||
import javax.swing.SwingUtilities;
|
||||
import javax.swing.UIManager;
|
||||
import org.diylc.appframework.miscutils.IConfigurationManager;
|
||||
import org.diylc.core.measures.ResizeDimensions;
|
||||
import org.diylc.core.measures.Size;
|
||||
|
||||
/**
|
||||
* A lightweight tooltip window that displays resize dimensions and tracks the mouse cursor.
|
||||
*/
|
||||
public class ResizeInfoTooltip {
|
||||
|
||||
private static final int OFFSET_X = 16;
|
||||
private static final int OFFSET_Y = 16;
|
||||
private static final DecimalFormat SIZE_FORMAT = new DecimalFormat("0.00");
|
||||
|
||||
private JWindow tooltipWindow;
|
||||
private JLabel tooltipLabel;
|
||||
private Component parentComponent;
|
||||
private ResizeDimensions currentDimensions;
|
||||
|
||||
public ResizeInfoTooltip(Component parentComponent, IConfigurationManager<?> configManager) {
|
||||
this.parentComponent = parentComponent;
|
||||
initializeTooltip();
|
||||
}
|
||||
|
||||
private void initializeTooltip() {
|
||||
Window parentWindow = SwingUtilities.getWindowAncestor(parentComponent);
|
||||
if (parentWindow == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
tooltipWindow = new JWindow(parentWindow);
|
||||
tooltipLabel = new JLabel() {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Override
|
||||
protected void paintComponent(Graphics g) {
|
||||
Graphics2D g2d = (Graphics2D) g.create();
|
||||
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
|
||||
super.paintComponent(g2d);
|
||||
g2d.dispose();
|
||||
}
|
||||
};
|
||||
|
||||
// Use system tooltip font and colors
|
||||
Font tooltipFont = UIManager.getFont("ToolTip.font");
|
||||
if (tooltipFont != null) {
|
||||
tooltipLabel.setFont(tooltipFont);
|
||||
}
|
||||
Color tooltipBackground = UIManager.getColor("ToolTip.background");
|
||||
Color tooltipForeground = UIManager.getColor("ToolTip.foreground");
|
||||
Color tooltipBorderColor = UIManager.getColor("ToolTip.border");
|
||||
if (tooltipBorderColor == null) {
|
||||
tooltipBorderColor = Color.GRAY;
|
||||
}
|
||||
|
||||
tooltipLabel.setBorder(BorderFactory.createCompoundBorder(
|
||||
BorderFactory.createLineBorder(tooltipBorderColor, 1),
|
||||
BorderFactory.createEmptyBorder(4, 6, 4, 6)));
|
||||
tooltipLabel.setOpaque(true);
|
||||
tooltipLabel.setBackground(tooltipBackground != null ? tooltipBackground : new Color(255, 255, 220));
|
||||
tooltipLabel.setForeground(tooltipForeground != null ? tooltipForeground : Color.BLACK);
|
||||
|
||||
tooltipWindow.add(tooltipLabel);
|
||||
tooltipWindow.setFocusableWindowState(false);
|
||||
tooltipWindow.setAlwaysOnTop(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the tooltip with new dimensions and mouse position.
|
||||
*/
|
||||
public void update(ResizeDimensions dimensions, Point mousePosition) {
|
||||
this.currentDimensions = dimensions;
|
||||
|
||||
if (dimensions == null || mousePosition == null) {
|
||||
hideTooltip();
|
||||
return;
|
||||
}
|
||||
|
||||
if (tooltipWindow == null) {
|
||||
initializeTooltip();
|
||||
if (tooltipWindow == null) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Format the dimensions
|
||||
String text = formatDimensions(dimensions);
|
||||
tooltipLabel.setText(text);
|
||||
tooltipLabel.setPreferredSize(null); // Reset to calculate preferred size
|
||||
tooltipWindow.pack();
|
||||
|
||||
// Position the tooltip near the mouse cursor
|
||||
updateTooltipPosition(mousePosition);
|
||||
tooltipWindow.setVisible(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the tooltip position based on the current mouse position.
|
||||
*/
|
||||
public void updatePosition(Point mousePosition) {
|
||||
if (currentDimensions != null && mousePosition != null && tooltipWindow != null && tooltipWindow.isVisible()) {
|
||||
updateTooltipPosition(mousePosition);
|
||||
}
|
||||
}
|
||||
|
||||
private void updateTooltipPosition(Point mousePosition) {
|
||||
if (tooltipWindow == null || parentComponent == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Convert mouse position to screen coordinates
|
||||
Point screenPoint = new Point(mousePosition);
|
||||
SwingUtilities.convertPointToScreen(screenPoint, parentComponent);
|
||||
|
||||
// Position tooltip 16 pixels down and right from cursor
|
||||
int x = screenPoint.x + OFFSET_X;
|
||||
int y = screenPoint.y + OFFSET_Y;
|
||||
|
||||
// Ensure tooltip stays on screen
|
||||
Dimension tooltipSize = tooltipWindow.getSize();
|
||||
if (tooltipSize == null) {
|
||||
tooltipWindow.pack();
|
||||
tooltipSize = tooltipWindow.getSize();
|
||||
}
|
||||
|
||||
if (tooltipSize != null) {
|
||||
java.awt.Toolkit toolkit = java.awt.Toolkit.getDefaultToolkit();
|
||||
Dimension screenSize = toolkit.getScreenSize();
|
||||
|
||||
// Adjust if tooltip would go off screen
|
||||
if (x + tooltipSize.width > screenSize.width) {
|
||||
x = screenPoint.x - tooltipSize.width - OFFSET_X;
|
||||
}
|
||||
if (y + tooltipSize.height > screenSize.height) {
|
||||
y = screenPoint.y - tooltipSize.height - OFFSET_Y;
|
||||
}
|
||||
if (x < 0) {
|
||||
x = 0;
|
||||
}
|
||||
if (y < 0) {
|
||||
y = 0;
|
||||
}
|
||||
}
|
||||
|
||||
tooltipWindow.setLocation(x, y);
|
||||
}
|
||||
|
||||
private String formatDimensions(ResizeDimensions dimensions) {
|
||||
StringBuilder sb = new StringBuilder("<html>");
|
||||
|
||||
// Format width
|
||||
Size width = dimensions.width();
|
||||
String widthUnit = width.getUnit().toString();
|
||||
sb.append(String.format("W: %s %s", SIZE_FORMAT.format(width.getValue()), widthUnit));
|
||||
|
||||
// Format height
|
||||
Size height = dimensions.height();
|
||||
String heightUnit = height.getUnit().toString();
|
||||
sb.append(String.format("<br>H: %s %s", SIZE_FORMAT.format(height.getValue()), heightUnit));
|
||||
|
||||
// Format length if available
|
||||
Size length = dimensions.length();
|
||||
if (length != null) {
|
||||
String lengthUnit = length.getUnit().toString();
|
||||
sb.append(String.format("<br>L: %s %s", SIZE_FORMAT.format(length.getValue()), lengthUnit));
|
||||
}
|
||||
|
||||
sb.append("</html>");
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Hides the tooltip.
|
||||
*/
|
||||
public void hideTooltip() {
|
||||
if (tooltipWindow != null) {
|
||||
tooltipWindow.setVisible(false);
|
||||
}
|
||||
currentDimensions = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Disposes the tooltip window.
|
||||
*/
|
||||
public void dispose() {
|
||||
if (tooltipWindow != null) {
|
||||
tooltipWindow.dispose();
|
||||
tooltipWindow = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -158,6 +158,10 @@ public class ConfigPlugin implements IPlugIn {
|
||||
ActionFactory.getInstance().createConfigAction(plugInPort, "Show Rulers", IPlugInPort.SHOW_RULERS_KEY, true),
|
||||
CONFIG_MENU);
|
||||
|
||||
swingUI.injectMenuAction(
|
||||
ActionFactory.getInstance().createConfigAction(plugInPort, "Show Resize Dimensions",
|
||||
IPlugInPort.SHOW_RESIZE_DIMENSIONS_TOOLTIP_KEY, true), CONFIG_MENU);
|
||||
|
||||
swingUI.injectSubmenu(SNAP_MENU, IconLoader.GraphEdgeDirected.getIcon(), CONFIG_MENU);
|
||||
swingUI.injectMenuAction(
|
||||
ActionFactory.getInstance().createToggleAction(IPlugInPort.SNAP_TO_NONE, IPlugInPort.SNAP_TO_KEY, SNAP_MENU, IPlugInPort.SNAP_TO_DEFAULT),
|
||||
|
||||
+96
@@ -0,0 +1,96 @@
|
||||
package org.diylc.swing.plugins.explorer;
|
||||
|
||||
import java.awt.Component;
|
||||
import java.awt.Dimension;
|
||||
import javax.swing.Icon;
|
||||
import javax.swing.JTree;
|
||||
import javax.swing.tree.DefaultMutableTreeNode;
|
||||
import javax.swing.tree.DefaultTreeCellRenderer;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.diylc.common.ComponentType;
|
||||
import org.diylc.core.IDIYComponent;
|
||||
|
||||
public class ComponentTreeCellRenderer extends DefaultTreeCellRenderer {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
// private static Color[] layerColors = new Color[] { Color.gray, Color.decode("#ff6363"),
|
||||
// Color.decode("#293462"), Color.decode("#138588"),
|
||||
// Color.decode("#c0ca03"), Color.decode("#990000") };
|
||||
|
||||
private Map<String, ComponentType> componentTypes;
|
||||
|
||||
public ComponentTreeCellRenderer(Map<String, List<ComponentType>> componentTypeMap) {
|
||||
this.componentTypes = componentTypeMap.values().stream().flatMap(x -> x.stream())
|
||||
.collect(Collectors.toMap(x -> x.getInstanceClass().getCanonicalName(), x -> x));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Component getTreeCellRendererComponent(JTree tree, Object value, boolean selected,
|
||||
boolean expanded, boolean leaf, int row, boolean hasFocus) {
|
||||
|
||||
super.getTreeCellRendererComponent(tree, value, selected, expanded, leaf, row, hasFocus);
|
||||
|
||||
if (!(value instanceof DefaultMutableTreeNode)) {
|
||||
return this;
|
||||
}
|
||||
|
||||
DefaultMutableTreeNode node = (DefaultMutableTreeNode) value;
|
||||
Object userObject = node.getUserObject();
|
||||
|
||||
// Handle group nodes
|
||||
if (userObject instanceof ComponentTreeModel.GroupNode) {
|
||||
ComponentTreeModel.GroupNode groupNode = (ComponentTreeModel.GroupNode) userObject;
|
||||
setText(groupNode.toString());
|
||||
setIcon(null); // No icon for group nodes
|
||||
setPreferredSize(new Dimension(250, 16));
|
||||
return this;
|
||||
}
|
||||
|
||||
if (!(userObject instanceof IDIYComponent)) {
|
||||
return this;
|
||||
}
|
||||
|
||||
IDIYComponent<?> component = (IDIYComponent<?>) userObject;
|
||||
ComponentType componentType = componentTypes.get(component.getClass().getCanonicalName());
|
||||
|
||||
if (componentType == null) {
|
||||
return this;
|
||||
}
|
||||
|
||||
// Set the component's icon
|
||||
Icon icon = componentType.getIcon();
|
||||
if (icon != null) {
|
||||
setIcon(icon);
|
||||
}
|
||||
|
||||
int layerId = (int) Math.round(componentType.getZOrder());
|
||||
|
||||
String valueForDisplay = component.getValueForDisplay();
|
||||
if (selected) {
|
||||
if (valueForDisplay == null || valueForDisplay.trim().isEmpty()) {
|
||||
setText(String.format("[L%s] %s", layerId, component.getName()));
|
||||
} else {
|
||||
setText(String.format("[L%s] %s (%s)", layerId, component.getName(), valueForDisplay));
|
||||
}
|
||||
} else {
|
||||
if (valueForDisplay == null || valueForDisplay.trim().isEmpty()) {
|
||||
setText(String.format("<html><font color='#c0c0c0'>[L%s]</font> %s</html>",
|
||||
layerId, component.getName()));
|
||||
} else {
|
||||
setText(String.format(
|
||||
"<html><font color='#c0c0c0'>[L%s]</font> %s (<font color='#666666'>%s</font>)</html>",
|
||||
layerId, component.getName(), valueForDisplay));
|
||||
}
|
||||
}
|
||||
|
||||
// Set preferred size after all properties are set (like TreePanel does)
|
||||
setPreferredSize(new Dimension(250, 16));
|
||||
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
+266
@@ -0,0 +1,266 @@
|
||||
package org.diylc.swing.plugins.explorer;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
import javax.swing.tree.DefaultMutableTreeNode;
|
||||
import javax.swing.tree.DefaultTreeModel;
|
||||
|
||||
import org.diylc.core.IDIYComponent;
|
||||
|
||||
public class ComponentTreeModel extends DefaultTreeModel {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private DefaultMutableTreeNode root;
|
||||
private List<IDIYComponent<?>> componentsRaw;
|
||||
private List<IDIYComponent<?>> components;
|
||||
private Set<Set<IDIYComponent<?>>> groups;
|
||||
|
||||
private String searchText;
|
||||
private ExplorerPane.Sort sort = ExplorerPane.Sort.Z_INDEX;
|
||||
|
||||
public ComponentTreeModel() {
|
||||
super(new DefaultMutableTreeNode("Root"));
|
||||
this.root = (DefaultMutableTreeNode) getRoot();
|
||||
}
|
||||
|
||||
public void setComponents(List<IDIYComponent<?>> components, Set<Set<IDIYComponent<?>>> groups) {
|
||||
this.componentsRaw = components;
|
||||
this.groups = groups != null ? groups : new HashSet<>();
|
||||
refresh();
|
||||
}
|
||||
|
||||
public void setComponents(List<IDIYComponent<?>> components) {
|
||||
setComponents(components, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the group that contains the given component, or null if not in a group.
|
||||
*/
|
||||
public Set<IDIYComponent<?>> getGroupForComponent(IDIYComponent<?> component) {
|
||||
if (groups == null) {
|
||||
return null;
|
||||
}
|
||||
for (Set<IDIYComponent<?>> group : groups) {
|
||||
if (group.contains(component)) {
|
||||
return group;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public List<IDIYComponent<?>> getComponents() {
|
||||
return components;
|
||||
}
|
||||
|
||||
public ExplorerPane.Sort getSort() {
|
||||
return sort;
|
||||
}
|
||||
|
||||
public void setSort(ExplorerPane.Sort sort) {
|
||||
this.sort = sort;
|
||||
refresh();
|
||||
}
|
||||
|
||||
public void setSearchText(String searchText) {
|
||||
this.searchText = searchText.toLowerCase();
|
||||
refresh();
|
||||
}
|
||||
|
||||
private void refresh() {
|
||||
// Clear existing children
|
||||
root.removeAllChildren();
|
||||
|
||||
// Sort components
|
||||
if (sort == ExplorerPane.Sort.NAME) {
|
||||
this.components =
|
||||
componentsRaw.stream().sorted(Comparator.comparing(x -> x.toString().toLowerCase()))
|
||||
.collect(Collectors.toList());
|
||||
} else if (sort == ExplorerPane.Sort.TYPE) {
|
||||
this.components = componentsRaw.stream()
|
||||
.sorted(Comparator.comparing(x -> x.getClass().getName().toLowerCase())
|
||||
.thenComparing(x -> x.toString().toLowerCase()))
|
||||
.collect(Collectors.toList());
|
||||
} else {
|
||||
List<IDIYComponent<?>> componentsClone = new ArrayList<IDIYComponent<?>>(componentsRaw);
|
||||
Collections.reverse(componentsClone);
|
||||
this.components = componentsClone;
|
||||
}
|
||||
|
||||
// Filter by search text
|
||||
if (searchText != null && !searchText.trim().isEmpty()) {
|
||||
this.components = this.components.stream().filter(component -> {
|
||||
return component.getName().toLowerCase().contains(searchText);
|
||||
}).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
// Build a map of components to their groups
|
||||
Set<IDIYComponent<?>> groupedComponents = new HashSet<>();
|
||||
if (groups != null) {
|
||||
for (Set<IDIYComponent<?>> group : groups) {
|
||||
groupedComponents.addAll(group);
|
||||
}
|
||||
}
|
||||
|
||||
// Create a list to hold all tree items (groups and ungrouped components) for sorting
|
||||
List<TreeItem> allItems = new ArrayList<>();
|
||||
|
||||
// Create group nodes
|
||||
if (groups != null && !groups.isEmpty()) {
|
||||
for (Set<IDIYComponent<?>> group : groups) {
|
||||
// Get ALL group components from raw list (for maxIndex calculation)
|
||||
List<IDIYComponent<?>> groupComponentsRaw = componentsRaw.stream()
|
||||
.filter(group::contains)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
// Apply search filter to get components to display
|
||||
List<IDIYComponent<?>> groupComponents = groupComponentsRaw.stream()
|
||||
.filter(component -> {
|
||||
if (searchText != null && !searchText.trim().isEmpty()) {
|
||||
return component.getName().toLowerCase().contains(searchText);
|
||||
}
|
||||
return true;
|
||||
})
|
||||
.collect(Collectors.toList());
|
||||
|
||||
if (!groupComponents.isEmpty()) {
|
||||
// Sort components within the group based on sort mode
|
||||
if (sort == ExplorerPane.Sort.NAME) {
|
||||
groupComponents.sort(Comparator.comparing(x -> x.toString().toLowerCase()));
|
||||
} else if (sort == ExplorerPane.Sort.TYPE) {
|
||||
groupComponents.sort(Comparator.comparing(x -> x.getClass().getName().toLowerCase())
|
||||
.thenComparing(x -> x.toString().toLowerCase()));
|
||||
} else {
|
||||
// For Z_INDEX, sort by index in raw list (reverse order - highest first)
|
||||
groupComponents.sort((c1, c2) -> {
|
||||
int idx1 = componentsRaw.indexOf(c1);
|
||||
int idx2 = componentsRaw.indexOf(c2);
|
||||
return Integer.compare(idx2, idx1); // Reverse: higher index first
|
||||
});
|
||||
}
|
||||
|
||||
// Store both the display components and all group components (for maxIndex calculation)
|
||||
allItems.add(new TreeItem(group, groupComponents, groupComponentsRaw, true));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add ungrouped components
|
||||
for (IDIYComponent<?> component : this.components) {
|
||||
if (!groupedComponents.contains(component)) {
|
||||
allItems.add(new TreeItem(null, Collections.singletonList(component), Collections.singletonList(component), false));
|
||||
}
|
||||
}
|
||||
|
||||
// Sort all items together based on sort mode
|
||||
if (sort == ExplorerPane.Sort.Z_INDEX) {
|
||||
// Sort by maximum z-order (index) - groups use max index of ALL their components (not just filtered)
|
||||
Collections.sort(allItems, (item1, item2) -> {
|
||||
int maxIndex1 = getMaxComponentIndex(item1.allComponents);
|
||||
int maxIndex2 = getMaxComponentIndex(item2.allComponents);
|
||||
// Reverse order: higher index first (since components are in reverse order)
|
||||
return Integer.compare(maxIndex2, maxIndex1);
|
||||
});
|
||||
} else if (sort == ExplorerPane.Sort.NAME) {
|
||||
Collections.sort(allItems, (item1, item2) -> {
|
||||
String name1 = item1.components.get(0).getName().toLowerCase();
|
||||
String name2 = item2.components.get(0).getName().toLowerCase();
|
||||
return name1.compareTo(name2);
|
||||
});
|
||||
} else if (sort == ExplorerPane.Sort.TYPE) {
|
||||
Collections.sort(allItems, (item1, item2) -> {
|
||||
String type1 = item1.components.get(0).getClass().getName().toLowerCase();
|
||||
String type2 = item2.components.get(0).getClass().getName().toLowerCase();
|
||||
int typeCompare = type1.compareTo(type2);
|
||||
if (typeCompare != 0) {
|
||||
return typeCompare;
|
||||
}
|
||||
String name1 = item1.components.get(0).getName().toLowerCase();
|
||||
String name2 = item2.components.get(0).getName().toLowerCase();
|
||||
return name1.compareTo(name2);
|
||||
});
|
||||
}
|
||||
|
||||
// Add sorted items to the tree
|
||||
for (TreeItem item : allItems) {
|
||||
if (item.isGroup) {
|
||||
DefaultMutableTreeNode groupNode = new DefaultMutableTreeNode(new GroupNode(item.group));
|
||||
for (IDIYComponent<?> component : item.components) {
|
||||
DefaultMutableTreeNode componentNode = new DefaultMutableTreeNode(component);
|
||||
groupNode.add(componentNode);
|
||||
}
|
||||
root.add(groupNode);
|
||||
} else {
|
||||
DefaultMutableTreeNode node = new DefaultMutableTreeNode(item.components.get(0));
|
||||
root.add(node);
|
||||
}
|
||||
}
|
||||
|
||||
// Notify tree model listeners
|
||||
nodeStructureChanged(root);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper class to represent a group node in the tree.
|
||||
*/
|
||||
public static class GroupNode {
|
||||
private final Set<IDIYComponent<?>> group;
|
||||
|
||||
public GroupNode(Set<IDIYComponent<?>> group) {
|
||||
this.group = group;
|
||||
}
|
||||
|
||||
public Set<IDIYComponent<?>> getGroup() {
|
||||
return group;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Group (" + group.size() + " components)";
|
||||
}
|
||||
}
|
||||
|
||||
public int getSize() {
|
||||
return this.components == null ? 0 : this.components.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the maximum index of components in the raw components list.
|
||||
* This represents the maximum z-order for the group.
|
||||
* Returns -1 if no valid index is found.
|
||||
*/
|
||||
private int getMaxComponentIndex(List<IDIYComponent<?>> groupComponents) {
|
||||
int maxIndex = -1;
|
||||
for (IDIYComponent<?> component : groupComponents) {
|
||||
int index = componentsRaw.indexOf(component);
|
||||
if (index >= 0 && index > maxIndex) {
|
||||
maxIndex = index;
|
||||
}
|
||||
}
|
||||
return maxIndex;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper class to hold either a group or a single ungrouped component for sorting.
|
||||
*/
|
||||
private static class TreeItem {
|
||||
final Set<IDIYComponent<?>> group;
|
||||
final List<IDIYComponent<?>> components; // Components to display (may be filtered)
|
||||
final List<IDIYComponent<?>> allComponents; // All components for maxIndex calculation
|
||||
final boolean isGroup;
|
||||
|
||||
TreeItem(Set<IDIYComponent<?>> group, List<IDIYComponent<?>> components,
|
||||
List<IDIYComponent<?>> allComponents, boolean isGroup) {
|
||||
this.group = group;
|
||||
this.components = components;
|
||||
this.allComponents = allComponents;
|
||||
this.isGroup = isGroup;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+213
@@ -0,0 +1,213 @@
|
||||
package org.diylc.swing.plugins.explorer;
|
||||
|
||||
import java.awt.datatransfer.DataFlavor;
|
||||
import java.awt.datatransfer.StringSelection;
|
||||
import java.awt.datatransfer.Transferable;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import javax.swing.JComponent;
|
||||
import javax.swing.JTree;
|
||||
import javax.swing.TransferHandler;
|
||||
import javax.swing.tree.DefaultMutableTreeNode;
|
||||
import javax.swing.tree.TreePath;
|
||||
|
||||
import org.diylc.common.IPlugInPort;
|
||||
import org.diylc.core.IDIYComponent;
|
||||
import org.diylc.lang.LangUtil;
|
||||
import org.diylc.swing.ISwingUI;
|
||||
|
||||
public class ComponentTreeTransferHandler extends TransferHandler {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private static final String REORDERING_NOT_POSSIBLE = LangUtil.translate(
|
||||
"Component reordering is possible only when Project Explorer is sorted by z-index.");
|
||||
|
||||
private final ComponentTreeModel treeModel;
|
||||
private final ISwingUI swingUI;
|
||||
private final IPlugInPort plugInPort;
|
||||
|
||||
public ComponentTreeTransferHandler(ComponentTreeModel treeModel, ISwingUI swingUI,
|
||||
IPlugInPort plugInPort) {
|
||||
this.treeModel = treeModel;
|
||||
this.swingUI = swingUI;
|
||||
this.plugInPort = plugInPort;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSourceActions(JComponent c) {
|
||||
return MOVE;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Transferable createTransferable(JComponent c) {
|
||||
if (!(c instanceof JTree)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
JTree tree = (JTree) c;
|
||||
TreePath[] selectionPaths = tree.getSelectionPaths();
|
||||
if (selectionPaths == null || selectionPaths.length == 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Get the first selected path
|
||||
TreePath firstPath = selectionPaths[0];
|
||||
DefaultMutableTreeNode node = (DefaultMutableTreeNode) firstPath.getLastPathComponent();
|
||||
Object userObject = node.getUserObject();
|
||||
|
||||
// Only allow dragging components, not group nodes
|
||||
if (!(userObject instanceof IDIYComponent)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Store the component's group info in the transferable
|
||||
IDIYComponent<?> component = (IDIYComponent<?>) userObject;
|
||||
Set<IDIYComponent<?>> group = treeModel.getGroupForComponent(component);
|
||||
String groupId = group != null ? Integer.toString(group.hashCode()) : "null";
|
||||
|
||||
int row = tree.getRowForPath(firstPath);
|
||||
return new StringSelection(row + ":" + groupId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canImport(TransferHandler.TransferSupport support) {
|
||||
if (!support.isDataFlavorSupported(DataFlavor.stringFlavor)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!(support.getComponent() instanceof JTree)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
JTree.DropLocation dl = (JTree.DropLocation) support.getDropLocation();
|
||||
TreePath dropPath = dl.getPath();
|
||||
if (dropPath == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Only allow reordering when sorted by Z_INDEX
|
||||
if (treeModel.getSort() != ExplorerPane.Sort.Z_INDEX) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if dropping on a group node - not allowed
|
||||
DefaultMutableTreeNode dropNode = (DefaultMutableTreeNode) dropPath.getLastPathComponent();
|
||||
if (dropNode.getUserObject() instanceof ComponentTreeModel.GroupNode) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get the source component's group from the transferable
|
||||
try {
|
||||
Transferable transferable = support.getTransferable();
|
||||
String data = (String) transferable.getTransferData(DataFlavor.stringFlavor);
|
||||
String[] parts = data.split(":");
|
||||
if (parts.length == 2) {
|
||||
String sourceGroupId = parts[1];
|
||||
|
||||
// Get the target component's group
|
||||
Object targetUserObject = dropNode.getUserObject();
|
||||
if (targetUserObject instanceof IDIYComponent) {
|
||||
IDIYComponent<?> targetComponent = (IDIYComponent<?>) targetUserObject;
|
||||
Set<IDIYComponent<?>> targetGroup = treeModel.getGroupForComponent(targetComponent);
|
||||
String targetGroupId = targetGroup != null ? Integer.toString(targetGroup.hashCode()) : "null";
|
||||
|
||||
// Only allow dragging within the same group (or both ungrouped)
|
||||
if (!sourceGroupId.equals(targetGroupId)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// If we can't parse the transferable, don't allow the drop
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean importData(TransferHandler.TransferSupport support) {
|
||||
if (!canImport(support)) {
|
||||
// Show error message if not sorted by Z_INDEX
|
||||
if (treeModel.getSort() != ExplorerPane.Sort.Z_INDEX) {
|
||||
swingUI.showMessage(REORDERING_NOT_POSSIBLE, "Error", ISwingUI.ERROR_MESSAGE);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!(support.getComponent() instanceof JTree)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
JTree.DropLocation dl = (JTree.DropLocation) support.getDropLocation();
|
||||
TreePath dropPath = dl.getPath();
|
||||
|
||||
if (dropPath == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
DefaultMutableTreeNode dropNode = (DefaultMutableTreeNode) dropPath.getLastPathComponent();
|
||||
Object dropUserObject = dropNode.getUserObject();
|
||||
|
||||
// Can't drop on group nodes
|
||||
if (dropUserObject instanceof ComponentTreeModel.GroupNode) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!(dropUserObject instanceof IDIYComponent)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get the parent - if it's a group node, we need to calculate index within the group
|
||||
DefaultMutableTreeNode parent = (DefaultMutableTreeNode) dropNode.getParent();
|
||||
|
||||
if (parent != null && parent.getUserObject() instanceof ComponentTreeModel.GroupNode) {
|
||||
// Dropping within a group - calculate index within the group's children
|
||||
int childIndex = dl.getChildIndex();
|
||||
int dropTargetIndex;
|
||||
|
||||
if (childIndex != -1) {
|
||||
dropTargetIndex = childIndex;
|
||||
} else {
|
||||
// Dropping on a component within the group
|
||||
dropTargetIndex = parent.getIndex(dropNode);
|
||||
}
|
||||
|
||||
// For groups, we need to calculate z-index based on the group's components
|
||||
// Get all components in the group in z-order (they're already sorted in the model)
|
||||
ComponentTreeModel.GroupNode groupNode = (ComponentTreeModel.GroupNode) parent.getUserObject();
|
||||
List<IDIYComponent<?>> groupComponents = treeModel.getComponents().stream()
|
||||
.filter(groupNode.getGroup()::contains)
|
||||
.collect(java.util.stream.Collectors.toList());
|
||||
|
||||
if (dropTargetIndex < groupComponents.size()) {
|
||||
IDIYComponent<?> targetComponent = groupComponents.get(dropTargetIndex);
|
||||
// Find the component's index in the full component list
|
||||
int componentIndex = treeModel.getComponents().indexOf(targetComponent);
|
||||
if (componentIndex >= 0) {
|
||||
int targetZIndex = treeModel.getSize() - componentIndex;
|
||||
plugInPort.moveSelectionToZIndex(targetZIndex);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Dropping on an ungrouped component at root level
|
||||
DefaultMutableTreeNode root = (DefaultMutableTreeNode) treeModel.getRoot();
|
||||
int childIndex = dl.getChildIndex();
|
||||
int dropTargetIndex;
|
||||
|
||||
if (childIndex != -1) {
|
||||
dropTargetIndex = childIndex;
|
||||
} else {
|
||||
dropTargetIndex = root.getIndex(dropNode);
|
||||
}
|
||||
|
||||
// Calculate the target z-index (components are in reverse order for z-index)
|
||||
int targetZIndex = treeModel.getSize() - dropTargetIndex;
|
||||
plugInPort.moveSelectionToZIndex(targetZIndex);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
+104
-207
@@ -1,6 +1,5 @@
|
||||
package org.diylc.swing.plugins.explorer;
|
||||
|
||||
import java.awt.Component;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.Font;
|
||||
import java.awt.GridBagConstraints;
|
||||
@@ -10,47 +9,36 @@ import java.awt.event.ItemListener;
|
||||
import java.awt.event.MouseAdapter;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
import javax.swing.AbstractListModel;
|
||||
import javax.swing.DefaultListCellRenderer;
|
||||
import javax.swing.DropMode;
|
||||
import javax.swing.JComboBox;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JList;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JScrollPane;
|
||||
import javax.swing.JTextField;
|
||||
import javax.swing.ListCellRenderer;
|
||||
import javax.swing.ListSelectionModel;
|
||||
import javax.swing.JTree;
|
||||
import javax.swing.SwingConstants;
|
||||
import javax.swing.SwingUtilities;
|
||||
import javax.swing.event.ListSelectionEvent;
|
||||
import javax.swing.event.ListSelectionListener;
|
||||
import javax.swing.tree.DefaultMutableTreeNode;
|
||||
import javax.swing.tree.TreePath;
|
||||
import javax.swing.tree.TreeSelectionModel;
|
||||
import javax.swing.event.TreeSelectionEvent;
|
||||
import javax.swing.event.TreeSelectionListener;
|
||||
|
||||
import org.diylc.common.ComponentType;
|
||||
import org.diylc.common.IPlugInPort;
|
||||
import org.diylc.core.IDIYComponent;
|
||||
import org.diylc.lang.LangUtil;
|
||||
import org.diylc.swing.ISwingUI;
|
||||
import org.diylc.swing.gui.ComponentPopupMenu;
|
||||
import org.diylc.swing.gui.DragDropList;
|
||||
import org.diylc.swing.gui.IDragDropListListener;
|
||||
import org.diylc.swing.gui.SearchTextField;
|
||||
|
||||
public class ExplorerPane extends JPanel {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private static final String REORDERING_NOT_POSSIBLE = LangUtil.translate(
|
||||
"Component reordering is possible only when Project Explorer is sorted by z-index.");
|
||||
|
||||
public static Font DEFAULT_FONT = new Font("Square721 BT", Font.PLAIN, 12);
|
||||
|
||||
private ISwingUI swingUI;
|
||||
@@ -59,8 +47,8 @@ public class ExplorerPane extends JPanel {
|
||||
private JLabel titleLabel;
|
||||
private JTextField searchField;
|
||||
private JScrollPane componentScrollPane;
|
||||
private ComponentListModel componentListModel;
|
||||
private JList<IDIYComponent<?>> componentList;
|
||||
private ComponentTreeModel componentTreeModel;
|
||||
private JTree componentTree;
|
||||
private ComponentPopupMenu popupMenu;
|
||||
private JComboBox<Sort> sortBox;
|
||||
|
||||
@@ -116,7 +104,7 @@ public class ExplorerPane extends JPanel {
|
||||
public JTextField getSearchField() {
|
||||
if (searchField == null) {
|
||||
searchField = new SearchTextField(true, 'X', text -> {
|
||||
getComponentListModel().setSearchText(text);
|
||||
getComponentTreeModel().setSearchText(text);
|
||||
});
|
||||
}
|
||||
return searchField;
|
||||
@@ -129,7 +117,7 @@ public class ExplorerPane extends JPanel {
|
||||
|
||||
@Override
|
||||
public void itemStateChanged(ItemEvent e) {
|
||||
getComponentListModel().setSort((Sort) e.getItem());
|
||||
getComponentTreeModel().setSort((Sort) e.getItem());
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -138,38 +126,37 @@ public class ExplorerPane extends JPanel {
|
||||
|
||||
public JScrollPane getComponentScrollPane() {
|
||||
if (componentScrollPane == null) {
|
||||
componentScrollPane = new JScrollPane(getComponentList(),
|
||||
componentScrollPane = new JScrollPane(getComponentTree(),
|
||||
JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
|
||||
}
|
||||
return componentScrollPane;
|
||||
}
|
||||
|
||||
public JList<IDIYComponent<?>> getComponentList() {
|
||||
if (componentList == null) {
|
||||
componentList = new DragDropList<IDIYComponent<?>>(new IDragDropListListener() {
|
||||
public JTree getComponentTree() {
|
||||
if (componentTree == null) {
|
||||
componentTree = new JTree(getComponentTreeModel());
|
||||
componentTree.setRootVisible(false);
|
||||
componentTree.setShowsRootHandles(true);
|
||||
componentTree.setFont(DEFAULT_FONT);
|
||||
componentTree.getSelectionModel().setSelectionMode(TreeSelectionModel.DISCONTIGUOUS_TREE_SELECTION);
|
||||
componentTree.setCellRenderer(new ComponentTreeCellRenderer(plugInPort.getComponentTypes()));
|
||||
// Set row height after renderer is set, using a fixed value
|
||||
componentTree.setRowHeight(24);
|
||||
|
||||
// Enable drag and drop
|
||||
componentTree.setDragEnabled(true);
|
||||
componentTree.setDropMode(DropMode.INSERT);
|
||||
componentTree.setTransferHandler(new ComponentTreeTransferHandler(getComponentTreeModel(),
|
||||
swingUI, plugInPort));
|
||||
|
||||
componentTree.addTreeSelectionListener(new TreeSelectionListener() {
|
||||
|
||||
@Override
|
||||
public boolean dropComplete(int selectedIndex, int dropTargetIndex) {
|
||||
if (getComponentListModel().getSort() != Sort.Z_INDEX) {
|
||||
swingUI.showMessage(REORDERING_NOT_POSSIBLE, "Error", ISwingUI.ERROR_MESSAGE);
|
||||
return false;
|
||||
}
|
||||
plugInPort.moveSelectionToZIndex(getComponentListModel().getSize() - dropTargetIndex);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
componentList.setModel(getComponentListModel());
|
||||
componentList.setFont(DEFAULT_FONT);
|
||||
componentList.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
|
||||
componentList.setCellRenderer(new ComponentListCellRenderer(plugInPort.getComponentTypes()));
|
||||
componentList.addListSelectionListener(new ListSelectionListener() {
|
||||
|
||||
@Override
|
||||
public void valueChanged(ListSelectionEvent e) {
|
||||
if (e.getValueIsAdjusting())
|
||||
public void valueChanged(TreeSelectionEvent e) {
|
||||
if (e.getNewLeadSelectionPath() == null)
|
||||
return;
|
||||
|
||||
List<IDIYComponent<?>> selectedValuesList = componentList.getSelectedValuesList();
|
||||
List<IDIYComponent<?>> selectedValuesList = getSelectedComponents();
|
||||
if (selectedValuesList.size() > 0 && !new HashSet<IDIYComponent<?>>(selectedValuesList)
|
||||
.equals(new HashSet<IDIYComponent<?>>(plugInPort.getSelectedComponents()))) {
|
||||
plugInPort.setSelection(selectedValuesList, true);
|
||||
@@ -177,7 +164,7 @@ public class ExplorerPane extends JPanel {
|
||||
}
|
||||
});
|
||||
|
||||
componentList.addMouseListener(new MouseAdapter() {
|
||||
componentTree.addMouseListener(new MouseAdapter() {
|
||||
|
||||
private MouseEvent pressedEvent;
|
||||
|
||||
@@ -190,7 +177,7 @@ public class ExplorerPane extends JPanel {
|
||||
|
||||
@Override
|
||||
public void mousePressed(MouseEvent e) {
|
||||
componentList.requestFocus();
|
||||
componentTree.requestFocus();
|
||||
pressedEvent = e;
|
||||
}
|
||||
|
||||
@@ -205,7 +192,7 @@ public class ExplorerPane extends JPanel {
|
||||
if (plugInPort.getNewComponentTypeSlot() == null && (e.isPopupTrigger()
|
||||
|| (pressedEvent != null && pressedEvent.isPopupTrigger()))) {
|
||||
|
||||
final List<IDIYComponent<?>> selectedValues = componentList.getSelectedValuesList();
|
||||
final List<IDIYComponent<?>> selectedValues = getSelectedComponents();
|
||||
getComponentPopupMenu().prepareAndShowAt(selectedValues, selectedValues, e.getX(),
|
||||
e.getY());
|
||||
}
|
||||
@@ -214,193 +201,103 @@ public class ExplorerPane extends JPanel {
|
||||
}
|
||||
});
|
||||
}
|
||||
return componentList;
|
||||
return componentTree;
|
||||
}
|
||||
|
||||
private List<IDIYComponent<?>> getSelectedComponents() {
|
||||
List<IDIYComponent<?>> selectedComponents = new ArrayList<>();
|
||||
TreePath[] selectionPaths = componentTree.getSelectionPaths();
|
||||
if (selectionPaths != null) {
|
||||
for (TreePath path : selectionPaths) {
|
||||
Object lastPathComponent = path.getLastPathComponent();
|
||||
if (lastPathComponent instanceof DefaultMutableTreeNode) {
|
||||
DefaultMutableTreeNode node = (DefaultMutableTreeNode) lastPathComponent;
|
||||
Object userObject = node.getUserObject();
|
||||
if (userObject instanceof IDIYComponent) {
|
||||
selectedComponents.add((IDIYComponent<?>) userObject);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return selectedComponents;
|
||||
}
|
||||
|
||||
public ComponentListModel getComponentListModel() {
|
||||
if (componentListModel == null) {
|
||||
componentListModel = new ComponentListModel();
|
||||
public ComponentTreeModel getComponentTreeModel() {
|
||||
if (componentTreeModel == null) {
|
||||
componentTreeModel = new ComponentTreeModel();
|
||||
}
|
||||
return componentListModel;
|
||||
return componentTreeModel;
|
||||
}
|
||||
|
||||
public ComponentPopupMenu getComponentPopupMenu() {
|
||||
if (popupMenu == null) {
|
||||
popupMenu = new ComponentPopupMenu(plugInPort, getComponentList(), false);
|
||||
popupMenu = new ComponentPopupMenu(plugInPort, getComponentTree(), false);
|
||||
}
|
||||
return popupMenu;
|
||||
}
|
||||
|
||||
public void setComponents(List<IDIYComponent<?>> components,
|
||||
Collection<IDIYComponent<?>> selectedComponents) {
|
||||
this.getComponentListModel().setComponents(components);
|
||||
// Get groups from the current project
|
||||
Set<Set<IDIYComponent<?>>> groups = null;
|
||||
if (plugInPort.getCurrentProject() != null) {
|
||||
groups = plugInPort.getCurrentProject().getGroups();
|
||||
}
|
||||
this.getComponentTreeModel().setComponents(components, groups);
|
||||
setSelection(new HashSet<IDIYComponent<?>>(selectedComponents));
|
||||
|
||||
// Expand all group nodes after model is updated
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
JTree tree = getComponentTree();
|
||||
for (int i = 0; i < tree.getRowCount(); i++) {
|
||||
TreePath path = tree.getPathForRow(i);
|
||||
if (path != null) {
|
||||
DefaultMutableTreeNode node = (DefaultMutableTreeNode) path.getLastPathComponent();
|
||||
if (node.getUserObject() instanceof ComponentTreeModel.GroupNode) {
|
||||
tree.expandPath(path);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void setSelection(Set<IDIYComponent<?>> selectedComponents) {
|
||||
List<IDIYComponent<?>> selectedValuesList = componentList.getSelectedValuesList();
|
||||
if (new HashSet<IDIYComponent<?>>(selectedValuesList)
|
||||
List<IDIYComponent<?>> currentSelected = getSelectedComponents();
|
||||
if (new HashSet<IDIYComponent<?>>(currentSelected)
|
||||
.equals(new HashSet<IDIYComponent<?>>(plugInPort.getSelectedComponents())))
|
||||
return;
|
||||
|
||||
final JList<IDIYComponent<?>> list = this.getComponentList();
|
||||
final JTree tree = this.getComponentTree();
|
||||
|
||||
if (selectedComponents.size() == 0) {
|
||||
list.clearSelection();
|
||||
tree.clearSelection();
|
||||
} else {
|
||||
|
||||
int[] indices = new int[selectedComponents.size()];
|
||||
int n = 0;
|
||||
List<IDIYComponent<?>> components = this.getComponentListModel().getComponents();
|
||||
for (int i = 0; i < components.size(); i++) {
|
||||
indices[n] = -1; // this should not happen
|
||||
if (selectedComponents.contains(components.get(i)))
|
||||
indices[n++] = i;
|
||||
if (n == indices.length)
|
||||
break;
|
||||
}
|
||||
|
||||
list.getSelectionModel().setValueIsAdjusting(true);
|
||||
list.setSelectedIndices(indices);
|
||||
list.getSelectionModel().setValueIsAdjusting(false);
|
||||
int firstVisibleIndex = list.getFirstVisibleIndex();
|
||||
int lastVisibleIndex = list.getFirstVisibleIndex();
|
||||
// if none of the selected items is visible, jump to the first one
|
||||
if (!Arrays.stream(indices)
|
||||
.anyMatch(index -> firstVisibleIndex <= index && index <= lastVisibleIndex))
|
||||
list.ensureIndexIsVisible(indices[0]);
|
||||
}
|
||||
}
|
||||
|
||||
private class ComponentListModel extends AbstractListModel<IDIYComponent<?>> {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private List<IDIYComponent<?>> componentsRaw;
|
||||
private List<IDIYComponent<?>> components;
|
||||
|
||||
private String searchText;
|
||||
private Sort sort = Sort.Z_INDEX;
|
||||
|
||||
public void setComponents(List<IDIYComponent<?>> components) {
|
||||
this.componentsRaw = components;
|
||||
int oldSize = getSize();
|
||||
|
||||
refresh(oldSize);
|
||||
}
|
||||
|
||||
public List<IDIYComponent<?>> getComponents() {
|
||||
return components;
|
||||
}
|
||||
|
||||
public Sort getSort() {
|
||||
return sort;
|
||||
}
|
||||
|
||||
public void setSort(Sort sort) {
|
||||
this.sort = sort;
|
||||
|
||||
refresh(getSize());
|
||||
}
|
||||
|
||||
public void setSearchText(String searchText) {
|
||||
this.searchText = searchText.toLowerCase();
|
||||
|
||||
refresh(getSize());
|
||||
}
|
||||
|
||||
private void refresh(int oldSize) {
|
||||
if (sort == Sort.NAME) {
|
||||
this.components =
|
||||
componentsRaw.stream().sorted(Comparator.comparing(x -> x.toString().toLowerCase()))
|
||||
.collect(Collectors.toList());
|
||||
} else if (sort == Sort.TYPE) {
|
||||
this.components = componentsRaw.stream()
|
||||
.sorted(Comparator.comparing(x -> x.getClass().getName().toLowerCase())
|
||||
.thenComparing(x -> x.toString().toLowerCase()))
|
||||
.collect(Collectors.toList());
|
||||
} else {
|
||||
List<IDIYComponent<?>> componentsClone = new ArrayList<IDIYComponent<?>>(componentsRaw);
|
||||
Collections.reverse(componentsClone);
|
||||
this.components = componentsClone;
|
||||
}
|
||||
|
||||
if (searchText != null && !searchText.trim().isEmpty()) {
|
||||
this.components = this.components.stream().filter(component -> {
|
||||
return component.getName().toLowerCase().contains(searchText);
|
||||
}).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
int newSize = this.components.size();
|
||||
|
||||
fireContentsChanged(this, 0, Math.min(newSize, oldSize));
|
||||
if (oldSize > newSize)
|
||||
fireIntervalRemoved(this, newSize, oldSize);
|
||||
else if (newSize > oldSize)
|
||||
fireIntervalAdded(this, oldSize, newSize);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSize() {
|
||||
return this.components == null ? 0 : this.components.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public IDIYComponent<?> getElementAt(int index) {
|
||||
return this.components.get(index);
|
||||
}
|
||||
}
|
||||
|
||||
private static class ComponentListCellRenderer implements ListCellRenderer<IDIYComponent<?>> {
|
||||
|
||||
// private static Color[] layerColors = new Color[] { Color.gray, Color.decode("#ff6363"),
|
||||
// Color.decode("#293462"), Color.decode("#138588"),
|
||||
// Color.decode("#c0ca03"), Color.decode("#990000") };
|
||||
|
||||
protected DefaultListCellRenderer defaultRenderer = new DefaultListCellRenderer();
|
||||
|
||||
private Map<String, ComponentType> componentTypes;
|
||||
|
||||
public ComponentListCellRenderer(Map<String, List<ComponentType>> componentTypeMap) {
|
||||
this.componentTypes = componentTypeMap.values().stream().flatMap(x -> x.stream())
|
||||
.collect(Collectors.toMap(x -> x.getInstanceClass().getCanonicalName(), x -> x));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Component getListCellRendererComponent(JList<? extends IDIYComponent<?>> list,
|
||||
IDIYComponent<?> value, int index, boolean isSelected, boolean cellHasFocus) {
|
||||
|
||||
ComponentType componentType = componentTypes.get(value.getClass().getCanonicalName());
|
||||
|
||||
JLabel renderer = (JLabel) defaultRenderer.getListCellRendererComponent(list, value, index,
|
||||
isSelected, cellHasFocus);
|
||||
|
||||
int layerId = (int) Math.round(componentType.getZOrder());
|
||||
|
||||
String valueForDisplay = value.getValueForDisplay();
|
||||
if (isSelected) {
|
||||
if (valueForDisplay == null || valueForDisplay.trim().isEmpty()) {
|
||||
renderer.setText(String.format("[L%s] %s", layerId, value.getName()));
|
||||
} else {
|
||||
renderer
|
||||
.setText(String.format("[L%s] %s (%s)", layerId, value.getName(), valueForDisplay));
|
||||
}
|
||||
} else {
|
||||
if (valueForDisplay == null || valueForDisplay.trim().isEmpty()) {
|
||||
renderer.setText(String.format("<html><font color='#c0c0c0'>[L%s]</font> %s</html>",
|
||||
layerId, value.getName()));
|
||||
} else {
|
||||
renderer.setText(String.format(
|
||||
"<html><font color='#c0c0c0'>[L%s]</font> %s (<font color='#666666'>%s</font>)</html>",
|
||||
layerId, value.getName(), valueForDisplay));
|
||||
List<TreePath> pathsToSelect = new ArrayList<>();
|
||||
DefaultMutableTreeNode root = (DefaultMutableTreeNode) getComponentTreeModel().getRoot();
|
||||
|
||||
for (int i = 0; i < root.getChildCount(); i++) {
|
||||
DefaultMutableTreeNode child = (DefaultMutableTreeNode) root.getChildAt(i);
|
||||
Object userObject = child.getUserObject();
|
||||
if (userObject instanceof IDIYComponent && selectedComponents.contains(userObject)) {
|
||||
pathsToSelect.add(new TreePath(child.getPath()));
|
||||
}
|
||||
}
|
||||
|
||||
return renderer;
|
||||
tree.getSelectionModel().setSelectionPaths(pathsToSelect.toArray(new TreePath[0]));
|
||||
|
||||
// If none of the selected items is visible, scroll to the first one
|
||||
if (!pathsToSelect.isEmpty()) {
|
||||
TreePath firstPath = pathsToSelect.get(0);
|
||||
if (tree.getRowForPath(firstPath) == -1) {
|
||||
tree.scrollPathToVisible(firstPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static enum Sort {
|
||||
|
||||
|
||||
static enum Sort {
|
||||
Z_INDEX("Sort by Z-index"), NAME("Sort by Name"), TYPE("Sort by Type");
|
||||
|
||||
private String label;
|
||||
|
||||
@@ -43,11 +43,13 @@ public class FileMenuPlugin implements IPlugIn, IDynamicSubmenuHandler {
|
||||
|
||||
private static final String FILE_TITLE = "File";
|
||||
private static final String TRACE_MASK_TITLE = "Trace Mask";
|
||||
private static final String SILK_SCREEN_TITLE = "Silkscreen";
|
||||
private static final String INTEGRATION_TITLE = "Integration";
|
||||
private static final String EXPORT_TITLE = "Export";
|
||||
private static final String PROJECT_TITLE = "Project";
|
||||
|
||||
private ProjectDrawingProvider drawingProvider;
|
||||
private TraceMaskDrawingProvider traceMaskDrawingProvider;
|
||||
private SilkScreenDrawingProvider silkScreenDrawingProvider;
|
||||
|
||||
private ISwingUI swingUI;
|
||||
private IPlugInPort plugInPort;
|
||||
@@ -62,6 +64,7 @@ public class FileMenuPlugin implements IPlugIn, IDynamicSubmenuHandler {
|
||||
this.plugInPort = plugInPort;
|
||||
this.drawingProvider = new ProjectDrawingProvider(plugInPort, false, true, false);
|
||||
this.traceMaskDrawingProvider = new TraceMaskDrawingProvider(plugInPort);
|
||||
this.silkScreenDrawingProvider = new SilkScreenDrawingProvider(plugInPort);
|
||||
|
||||
ActionFactory actionFactory = ActionFactory.getInstance();
|
||||
swingUI.injectMenuAction(actionFactory.createNewAction(plugInPort), FILE_TITLE);
|
||||
@@ -73,15 +76,17 @@ public class FileMenuPlugin implements IPlugIn, IDynamicSubmenuHandler {
|
||||
swingUI.injectMenuAction(actionFactory.createSaveAsAction(plugInPort, swingUI), FILE_TITLE);
|
||||
swingUI.injectDynamicSubmenu("Recent Files", IconLoader.History.getIcon(), FILE_TITLE, this);
|
||||
swingUI.injectMenuAction(null, FILE_TITLE);
|
||||
swingUI.injectSubmenu(EXPORT_TITLE, IconLoader.Export.getIcon(), FILE_TITLE);
|
||||
|
||||
swingUI.injectSubmenu(PROJECT_TITLE, IconLoader.Project.getIcon(), FILE_TITLE);
|
||||
swingUI.injectMenuAction(
|
||||
actionFactory.createExportPDFAction(plugInPort, drawingProvider, swingUI, ""), EXPORT_TITLE);
|
||||
actionFactory.createExportPDFAction(plugInPort, drawingProvider, swingUI, ""), PROJECT_TITLE);
|
||||
swingUI.injectMenuAction(
|
||||
actionFactory.createExportPNGAction(plugInPort, drawingProvider, swingUI, ""), EXPORT_TITLE);
|
||||
actionFactory.createExportPNGAction(plugInPort, drawingProvider, swingUI, ""), PROJECT_TITLE);
|
||||
swingUI.injectMenuAction(
|
||||
actionFactory.createExportGerberAction(plugInPort, swingUI), EXPORT_TITLE);
|
||||
actionFactory.createExportGerberAction(plugInPort, swingUI), PROJECT_TITLE);
|
||||
swingUI.injectMenuAction(actionFactory.createPrintAction(drawingProvider, swingUI,
|
||||
Toolkit.getDefaultToolkit().getMenuShortcutKeyMaskEx()), FILE_TITLE);
|
||||
Toolkit.getDefaultToolkit().getMenuShortcutKeyMaskEx()), PROJECT_TITLE);
|
||||
|
||||
swingUI.injectSubmenu(TRACE_MASK_TITLE, IconLoader.TraceMask.getIcon(), FILE_TITLE);
|
||||
swingUI.injectMenuAction(actionFactory.createExportPDFAction(plugInPort,
|
||||
traceMaskDrawingProvider, swingUI, " (mask)"), TRACE_MASK_TITLE);
|
||||
@@ -92,6 +97,17 @@ public class FileMenuPlugin implements IPlugIn, IDynamicSubmenuHandler {
|
||||
Toolkit.getDefaultToolkit().getMenuShortcutKeyMaskEx() | KeyEvent.SHIFT_DOWN_MASK),
|
||||
TRACE_MASK_TITLE);
|
||||
|
||||
swingUI.injectSubmenu(SILK_SCREEN_TITLE, IconLoader.Silkscreen.getIcon(), FILE_TITLE);
|
||||
swingUI.injectMenuAction(actionFactory.createExportPDFAction(plugInPort,
|
||||
silkScreenDrawingProvider, swingUI, " (silkscreen)"), SILK_SCREEN_TITLE);
|
||||
swingUI.injectMenuAction(actionFactory.createExportPNGAction(plugInPort,
|
||||
silkScreenDrawingProvider, swingUI, " (silkscreen)"), SILK_SCREEN_TITLE);
|
||||
swingUI.injectMenuAction(
|
||||
actionFactory.createPrintAction(silkScreenDrawingProvider, swingUI,
|
||||
Toolkit.getDefaultToolkit().getMenuShortcutKeyMaskEx() | KeyEvent.ALT_DOWN_MASK),
|
||||
SILK_SCREEN_TITLE);
|
||||
|
||||
|
||||
swingUI.injectMenuAction(null, FILE_TITLE);
|
||||
swingUI.injectSubmenu(INTEGRATION_TITLE, IconLoader.Node.getIcon(), FILE_TITLE);
|
||||
swingUI.injectMenuAction(actionFactory.createImportBlocksAction(swingUI, plugInPort),
|
||||
|
||||
+63
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
|
||||
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.swing.plugins.file;
|
||||
|
||||
import org.diylc.common.DrawOption;
|
||||
import org.diylc.common.IPlugInPort;
|
||||
import org.diylc.filter.PCBLayerFilter;
|
||||
import org.diylc.filter.SilkScreenFilter;
|
||||
import org.diylc.swingframework.IDrawingProvider;
|
||||
|
||||
import java.awt.*;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* {@link IDrawingProvider} implementation that uses {@link IPlugInPort} to draw a project onto the
|
||||
* canvas.
|
||||
*
|
||||
* @author Branislav Stojkovic
|
||||
*/
|
||||
public class SilkScreenDrawingProvider implements IDrawingProvider {
|
||||
|
||||
private IPlugInPort plugInPort;
|
||||
|
||||
public SilkScreenDrawingProvider(IPlugInPort plugInPort) {
|
||||
super();
|
||||
this.plugInPort = plugInPort;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Dimension getSize() {
|
||||
return plugInPort.getCanvasDimensions(false, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(int page, Graphics g, double zoomFactor) {
|
||||
plugInPort.draw((Graphics2D) g, EnumSet.of(DrawOption.ANTIALIASING, DrawOption.OUTLINE_MODE, DrawOption.MONOCHROMATIC),
|
||||
new SilkScreenFilter(), zoomFactor, null, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPageCount() {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -37,7 +37,7 @@ import org.diylc.common.DrawOption;
|
||||
import org.diylc.common.IPlugInPort;
|
||||
import org.diylc.common.PCBLayer;
|
||||
import org.diylc.core.IDIYComponent;
|
||||
import org.diylc.presenter.PCBLayerFilter;
|
||||
import org.diylc.filter.PCBLayerFilter;
|
||||
|
||||
/**
|
||||
* {@link IDrawingProvider} implementation that uses {@link IPlugInPort} to draw a project onto the
|
||||
|
||||
@@ -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,143 @@
|
||||
/*
|
||||
*
|
||||
* 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.util.Enumeration;
|
||||
import javax.swing.UIDefaults;
|
||||
import javax.swing.UIManager;
|
||||
import org.diylc.appframework.miscutils.ConfigurationManager;
|
||||
import org.diylc.common.IPlugInPort;
|
||||
|
||||
/**
|
||||
* Utility class for applying a configurable font scaling factor to all UI components.
|
||||
*
|
||||
* The scaling factor is stored in the application configuration and can be set
|
||||
* programmatically or through the configuration system.
|
||||
* For example, a factor of 1.5 will make all fonts 50% larger.
|
||||
*
|
||||
* @author Branislav Stojkovic
|
||||
*/
|
||||
public class FontScalingUtils {
|
||||
|
||||
private static final double DEFAULT_FONT_SCALE_FACTOR = 1.0;
|
||||
private static final double MIN_FONT_SCALE_FACTOR = 0.5;
|
||||
private static final double MAX_FONT_SCALE_FACTOR = 3.0;
|
||||
|
||||
/**
|
||||
* Gets the font scaling factor from ConfigurationManager.
|
||||
*
|
||||
* @return The scaling factor (defaults to 1.0 if not set or invalid)
|
||||
*/
|
||||
public static double getFontScaleFactor() {
|
||||
try {
|
||||
Object factorObj = ConfigurationManager.getInstance().readObject(
|
||||
IPlugInPort.FONT_SCALE_FACTOR_KEY, DEFAULT_FONT_SCALE_FACTOR);
|
||||
|
||||
if (factorObj instanceof Number) {
|
||||
double factor = ((Number) factorObj).doubleValue();
|
||||
// Validate the factor is reasonable (between 0.5 and 3.0)
|
||||
if (factor >= MIN_FONT_SCALE_FACTOR && factor <= MAX_FONT_SCALE_FACTOR) {
|
||||
return factor;
|
||||
} else {
|
||||
System.err.println("Warning: Font scale factor " + factor + " is out of range ("
|
||||
+ MIN_FONT_SCALE_FACTOR + "-" + MAX_FONT_SCALE_FACTOR + "). Using default "
|
||||
+ DEFAULT_FONT_SCALE_FACTOR);
|
||||
return DEFAULT_FONT_SCALE_FACTOR;
|
||||
}
|
||||
} else if (factorObj instanceof String) {
|
||||
// Handle string values (for backward compatibility or manual config editing)
|
||||
try {
|
||||
double factor = Double.parseDouble((String) factorObj);
|
||||
if (factor >= MIN_FONT_SCALE_FACTOR && factor <= MAX_FONT_SCALE_FACTOR) {
|
||||
return factor;
|
||||
}
|
||||
} catch (NumberFormatException e) {
|
||||
// Invalid string format, use default
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
System.err.println("Warning: Could not read font scale factor from configuration: " + e.getMessage());
|
||||
}
|
||||
|
||||
return DEFAULT_FONT_SCALE_FACTOR;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the font scaling factor in ConfigurationManager.
|
||||
*
|
||||
* @param factor The scaling factor (must be between 0.5 and 3.0)
|
||||
* @return true if the factor was set successfully, false if invalid
|
||||
*/
|
||||
public static boolean setFontScaleFactor(double factor) {
|
||||
if (factor < MIN_FONT_SCALE_FACTOR || factor > MAX_FONT_SCALE_FACTOR) {
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
ConfigurationManager.getInstance().writeValue(IPlugInPort.FONT_SCALE_FACTOR_KEY, factor);
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
System.err.println("Warning: Could not write font scale factor to configuration: " + e.getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies the font scaling factor to all UIManager font defaults.
|
||||
* This should be called once during application initialization, after setting the
|
||||
* Look and Feel but before creating any UI components.
|
||||
*
|
||||
* The scaling factor is read from ConfigurationManager using the key
|
||||
* IPlugInPort.FONT_SCALE_FACTOR_KEY. The factor should be a number between 0.5 and 3.0
|
||||
* (e.g., 1.5 for 50% larger fonts). Use setFontScaleFactor() to change the value
|
||||
* programmatically.
|
||||
*/
|
||||
public static void applyFontScaling() {
|
||||
double scaleFactor = getFontScaleFactor();
|
||||
|
||||
// If factor is 1.0, no scaling needed
|
||||
if (scaleFactor == 1.0) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
UIDefaults defaults = UIManager.getDefaults();
|
||||
Enumeration<Object> keys = defaults.keys();
|
||||
|
||||
while (keys.hasMoreElements()) {
|
||||
Object key = keys.nextElement();
|
||||
Object value = defaults.get(key);
|
||||
|
||||
// Check if this is a font property
|
||||
if (value instanceof Font font) {
|
||||
float newSize = (float) (font.getSize() * scaleFactor);
|
||||
Font scaledFont = font.deriveFont(newSize);
|
||||
defaults.put(key, scaledFont);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
System.err.println("Warning: Could not apply font scaling: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,7 +72,8 @@ public enum IconLoader {
|
||||
ComponentLarge("component_large.png"), ImportNetlist("import_netlist.png"), SplashIC("splash_ic.png"),
|
||||
ComponentPreferences("component_preferences.png"), ComponentReplace("component_replace.png"), Barcode("barcode.png"),
|
||||
DocumentGerber("document_gerber.png"), DocumentX2("document_x2.png"), Export("export.png"), Multimeter("multimeter.png"),
|
||||
Patreon("patreon.png"), Brain("brain.png"), Tables("tables.png");
|
||||
Patreon("patreon.png"), Brain("brain.png"), Tables("tables.png"), Silkscreen("silkscreen.png"),
|
||||
Project("project.png");
|
||||
|
||||
protected String name;
|
||||
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
*
|
||||
* 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.Color;
|
||||
import java.util.function.Function;
|
||||
import javax.swing.UIManager;
|
||||
|
||||
/**
|
||||
* Utility class for Swing UI-related operations.
|
||||
*
|
||||
* @author Branislav Stojkovic
|
||||
*/
|
||||
public class SwingUtils {
|
||||
|
||||
/**
|
||||
* Fixes tooltip colors for Linux systems where they may be unreadable.
|
||||
* Some Linux L&F themes set white text on light backgrounds, making tooltips invisible.
|
||||
* This method ensures tooltips always have dark text on light background for readability.
|
||||
*
|
||||
* Should be called after UIManager.setLookAndFeel() to override system defaults.
|
||||
*/
|
||||
public static void fixTooltipColors() {
|
||||
Color tooltipBg = UIManager.getColor("ToolTip.background");
|
||||
Color tooltipFg = UIManager.getColor("ToolTip.foreground");
|
||||
|
||||
// Helper function to calculate brightness (0.0 = black, 1.0 = white)
|
||||
Function<Color, Double> getBrightness = (c) -> {
|
||||
return (0.299 * c.getRed() + 0.587 * c.getGreen() + 0.114 * c.getBlue()) / 255.0;
|
||||
};
|
||||
|
||||
// Check if we have a problematic color combination (light text on light background)
|
||||
boolean needsFix = false;
|
||||
if (tooltipFg != null && tooltipBg != null) {
|
||||
double fgBrightness = getBrightness.apply(tooltipFg);
|
||||
double bgBrightness = getBrightness.apply(tooltipBg);
|
||||
// If both foreground and background are light, it's unreadable
|
||||
if (fgBrightness > 0.7 && bgBrightness > 0.5) {
|
||||
needsFix = true;
|
||||
}
|
||||
} else if (tooltipFg == null || tooltipBg == null) {
|
||||
// Missing color definitions - set safe defaults
|
||||
needsFix = true;
|
||||
}
|
||||
|
||||
if (needsFix) {
|
||||
UIManager.put("ToolTip.background", new Color(255, 255, 220)); // Light yellow
|
||||
UIManager.put("ToolTip.foreground", Color.BLACK);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 869 B |
Binary file not shown.
|
After Width: | Height: | Size: 906 B |
@@ -195,6 +195,7 @@ Image|Bild
|
||||
Immediate Only|Nur sofort
|
||||
Import|Importieren
|
||||
Import Building Blocks|Bausteine importieren
|
||||
Import Netlist|Netlist importieren
|
||||
Import Variants|Varianten importieren
|
||||
In-Line D-44|Inline D-44
|
||||
In-line|Inline
|
||||
@@ -231,6 +232,7 @@ Loadline Editor|Kennliniendiagramm
|
||||
Lock|Sperren
|
||||
Log|Log
|
||||
Log In|Anmelden
|
||||
Look for|Suchen
|
||||
Log Out|Abmelden
|
||||
Lug Size|Lötpadgröße
|
||||
Lugs|Lötpad
|
||||
@@ -303,6 +305,7 @@ Power Rating|Nennleistung
|
||||
Power Strip|Steckdosenleiste
|
||||
Primary CT|Primärer Mittelabgriff
|
||||
Print...|Drucken...
|
||||
Project|Projekt
|
||||
Project Cloud|Projekt Cloud
|
||||
Public Announcement|Öffentliche Ankündigung
|
||||
Radius|Radius
|
||||
@@ -360,6 +363,7 @@ Shaft Up|Achse nach oben
|
||||
Shape|Form
|
||||
Shapes|Formen
|
||||
Show Grid|Raster anzeigen
|
||||
Show Resize Dimensions|Größenänderungsdimensionen anzeigen
|
||||
Show Rulers|Lineale anzeigen
|
||||
Shroud|Wanne
|
||||
Shrouded|Abgeschirmt
|
||||
@@ -457,3 +461,34 @@ Zoom|Vergrößern
|
||||
base of 10|Zehntel
|
||||
base of 2|Achtel
|
||||
hold %s and drag to unstick from|Halten Sie X gedrückt und ziehen Sie, zum lösen von
|
||||
Are you sure you want to clear the chat history for this project? This action cannot be undone!|Sind Sie sicher, dass Sie den Chat-Verlauf für dieses Projekt löschen möchten? Diese Aktion kann nicht rückgängig gemacht werden!
|
||||
Component reordering is possible only when Project Explorer is sorted by z-index.|Komponenten können nur neu angeordnet werden, wenn der Projekt-Explorer nach z-index sortiert ist.
|
||||
Could not apply changes. Check the log for details.|Änderungen konnten nicht angewendet werden. Überprüfen Sie die Protokolldatei für Details.
|
||||
Exporting outline for board %s to file %s|Exportiere Umriss für Platte %s in Datei %s
|
||||
Failed to export the project to gerber. Please check the log for details|Fehler beim Exportieren des Projekts zu Gerber. Bitte überprüfen Sie die Protokolldatei für Details
|
||||
Forgot Password?|Passwort vergessen?
|
||||
Gerber Export|Gerber Export
|
||||
Gerber export completed successfully. The following files were created:%s|Gerber-Export erfolgreich abgeschlossen. Die folgenden Dateien wurden erstellt:%s
|
||||
Get Premium|Premium erhalten
|
||||
Netlist files|Netlist-Dateien
|
||||
No proximity issues detected.|Keine Näherungsprobleme erkannt.
|
||||
Nothing to export.|Nichts zu exportieren.
|
||||
Password|Passwort
|
||||
Send|Senden
|
||||
There are some components that are outside of the bounds of boards.%sThey will be ignored unless you add a board component underneath them.%sDo you want to continue?|Es gibt einige Komponenten, die außerhalb der Grenzen der Platten liegen.%sSie werden ignoriert, es sei denn, Sie fügen eine Plattenkomponente darunter hinzu.%sMöchten Sie fortfahren?
|
||||
User Name|Benutzername
|
||||
AI Circuit Analyzer|KI-Schaltungsanalysator
|
||||
Cloud|Cloud
|
||||
Compare With...|Vergleichen mit...
|
||||
Connect to Patreon|Mit Patreon verbinden
|
||||
Export|Exportieren
|
||||
Export to Gerber|Als Gerber exportieren
|
||||
Generate DIYLC Netlist (excl. Switches)|DIYLC-Netzliste erstellen (ohne Schalter)
|
||||
Generate DIYLC Netlist (incl. Switches)|DIYLC-Netzliste erstellen (mit Schaltern)
|
||||
Generate Spice Netlist (excl. Switches)|Spice-Netzliste erstellen (ohne Schalter)
|
||||
Generate Spice Netlist (incl. Switches)|Spice-Netzliste erstellen (mit Schaltern)
|
||||
Silkscreen|Siebdruck
|
||||
Virtual Multimeter (excl. Switches)|Virtuelles Multimeter (ohne Schalter)
|
||||
Virtual Multimeter (incl. Switches)|Virtuelles Multimeter (mit Schaltern)
|
||||
Become a Patreon and enjoy perks|Werden Sie Patreon und genießen Sie Vorteile
|
||||
Join Patreon|Patreon beitreten
|
||||
|
||||
@@ -300,6 +300,7 @@ Power Rating
|
||||
Power Strip
|
||||
Primary CT
|
||||
Print...
|
||||
Project
|
||||
Project Cloud
|
||||
Public Announcement
|
||||
Radius
|
||||
@@ -351,6 +352,7 @@ Shape
|
||||
Shapes
|
||||
Show Bracket
|
||||
Show Grid
|
||||
Show Resize Dimensions
|
||||
Show Rulers
|
||||
Shroud
|
||||
Shrouded
|
||||
@@ -462,4 +464,35 @@ Update Axis
|
||||
Apply Variant
|
||||
Save as Building Block
|
||||
Select
|
||||
Unlock
|
||||
Unlock
|
||||
Are you sure you want to clear the chat history for this project? This action cannot be undone!
|
||||
Component reordering is possible only when Project Explorer is sorted by z-index.
|
||||
Could not apply changes. Check the log for details.
|
||||
Exporting outline for board %s to file %s
|
||||
Failed to export the project to gerber. Please check the log for details
|
||||
Forgot Password?
|
||||
Gerber Export
|
||||
Gerber export completed successfully. The following files were created:%s
|
||||
Get Premium
|
||||
Netlist files
|
||||
No proximity issues detected.
|
||||
Nothing to export.
|
||||
Password
|
||||
Send
|
||||
There are some components that are outside of the bounds of boards.%sThey will be ignored unless you add a board component underneath them.%sDo you want to continue?
|
||||
User Name
|
||||
AI Circuit Analyzer
|
||||
Cloud
|
||||
Compare With...
|
||||
Connect to Patreon
|
||||
Export
|
||||
Export to Gerber
|
||||
Generate DIYLC Netlist (excl. Switches)
|
||||
Generate DIYLC Netlist (incl. Switches)
|
||||
Generate Spice Netlist (excl. Switches)
|
||||
Generate Spice Netlist (incl. Switches)
|
||||
Silkscreen
|
||||
Virtual Multimeter (excl. Switches)
|
||||
Virtual Multimeter (incl. Switches)
|
||||
Become a Patreon and enjoy perks
|
||||
Join Patreon
|
||||
@@ -193,6 +193,7 @@ Image|Imagen
|
||||
Immediate Only|Solo Inmediato
|
||||
Import|Importar
|
||||
Import Building Blocks|Importar Bloques
|
||||
Import Netlist|Importar Lista de Redes
|
||||
Import Variants|Importar variantes
|
||||
In-Line D-44|En Línea D-44
|
||||
In-line|En línea
|
||||
@@ -298,6 +299,7 @@ Power Rating|Potencia Nominal
|
||||
Power Strip|Regleta
|
||||
Primary CT|Toma Central en primario
|
||||
Print...|Imprimir...
|
||||
Project|Proyecto
|
||||
Project Cloud|Proyectos en la Nube
|
||||
Public Announcement|Anuncio Público
|
||||
Radius|Radio
|
||||
@@ -349,6 +351,7 @@ Shaft Up|Eje Arriba
|
||||
Shape|Forma
|
||||
Shapes|Formas
|
||||
Show Grid|Mostrar Cuadrícula
|
||||
Show Resize Dimensions|Mostrar Dimensiones de Redimensionamiento
|
||||
Show Rulers|Mostrar Reglas
|
||||
Shroud|Envoltura
|
||||
Shrouded|Con Envoltura
|
||||
@@ -457,4 +460,35 @@ Update Axis|Actualizar Ejes
|
||||
Apply Variant|Aplicar Variante
|
||||
Save as Building Block|Guardar como Bloque
|
||||
Select|Seleccionar
|
||||
Unlock|Desbloquear
|
||||
Unlock|Desbloquear
|
||||
Are you sure you want to clear the chat history for this project? This action cannot be undone!|¿Está seguro de que desea borrar el historial de chat de este proyecto? ¡Esta acción no se puede deshacer!
|
||||
Component reordering is possible only when Project Explorer is sorted by z-index.|El reordenamiento de componentes solo es posible cuando el Explorador de Proyectos está ordenado por z-index.
|
||||
Could not apply changes. Check the log for details.|No se pudieron aplicar los cambios. Verifique el registro para más detalles.
|
||||
Exporting outline for board %s to file %s|Exportando el contorno de la placa %s al archivo %s
|
||||
Failed to export the project to gerber. Please check the log for details|No se pudo exportar el proyecto a gerber. Por favor verifique el registro para más detalles
|
||||
Forgot Password?|¿Olvidó la Contraseña?
|
||||
Gerber Export|Exportar Gerber
|
||||
Gerber export completed successfully. The following files were created:%s|La exportación Gerber se completó exitosamente. Se crearon los siguientes archivos:%s
|
||||
Get Premium|Obtener Premium
|
||||
Netlist files|Archivos de lista de red
|
||||
No proximity issues detected.|No se detectaron problemas de proximidad.
|
||||
Nothing to export.|Nada que exportar.
|
||||
Password|Contraseña
|
||||
Send|Enviar
|
||||
There are some components that are outside of the bounds of boards.%sThey will be ignored unless you add a board component underneath them.%sDo you want to continue?|Hay algunos componentes que están fuera de los límites de las placas.%sSerán ignorados a menos que agregue un componente de placa debajo de ellos.%s¿Desea continuar?
|
||||
User Name|Nombre de Usuario
|
||||
AI Circuit Analyzer|Analizador de Circuitos IA
|
||||
Cloud|Nube
|
||||
Compare With...|Comparar con...
|
||||
Connect to Patreon|Conectar con Patreon
|
||||
Export|Exportar
|
||||
Export to Gerber|Exportar a Gerber
|
||||
Generate DIYLC Netlist (excl. Switches)|Generar Lista de Redes DIYLC (sin interruptores)
|
||||
Generate DIYLC Netlist (incl. Switches)|Generar Lista de Redes DIYLC (con interruptores)
|
||||
Generate Spice Netlist (excl. Switches)|Generar Lista Especial de Redes (sin interruptores)
|
||||
Generate Spice Netlist (incl. Switches)|Generar Lista Especial de Redes (con interruptores)
|
||||
Silkscreen|Serigrafía
|
||||
Virtual Multimeter (excl. Switches)|Multímetro Virtual (sin interruptores)
|
||||
Virtual Multimeter (incl. Switches)|Multímetro Virtual (con interruptores)
|
||||
Become a Patreon and enjoy perks|Conviértete en Patreon y disfruta de beneficios
|
||||
Join Patreon|Únete a Patreon
|
||||
@@ -193,6 +193,7 @@ Image|Image
|
||||
Immediate Only|Immédiatement proches seulement
|
||||
Import|Importer
|
||||
Import Building Blocks|Importer des blocs de construction
|
||||
Import Netlist|Importer la liste de connexions
|
||||
Import Variants|Importer des variantes
|
||||
In-Line D-44|En ligne D-44
|
||||
In-line|En ligne
|
||||
@@ -226,6 +227,7 @@ Light|Lumière
|
||||
Lock|Verrouiller
|
||||
Log In|Se connecter
|
||||
Log Out|Se déconnecter
|
||||
Look for|Rechercher
|
||||
Lug Size|Taille des cosses
|
||||
Lugs|Cosses
|
||||
Make a Donation|Faire un don
|
||||
@@ -294,6 +296,7 @@ Power Rating|Puissance nominale
|
||||
Power Strip|Multiprise
|
||||
Primary CT|Prise centrale primaire
|
||||
Print...|Imprimer...
|
||||
Project|Projet
|
||||
Project Cloud|Projet Cloud
|
||||
Public Announcement|Annonce publique
|
||||
Radius|Rayon
|
||||
@@ -345,6 +348,7 @@ Shaft Up|Tige vers le haut
|
||||
Shape|Forme
|
||||
Shapes|Formes
|
||||
Show Grid|Afficher la grille
|
||||
Show Resize Dimensions|Afficher les dimensions de redimensionnement
|
||||
Show Rulers|Afficher les règles
|
||||
Shroud|Enveloppe
|
||||
Shrouded|Enveloppé
|
||||
@@ -440,17 +444,48 @@ Zoom|Zoom
|
||||
base of 10|base de 10
|
||||
base of 2|base de 2
|
||||
hold %s and drag to unstick from|maintenez %s et faites glisser pour décoller
|
||||
Add a Curve
|
||||
Clear
|
||||
Insert Image
|
||||
Load From File
|
||||
Loadline Editor
|
||||
Max Current (mA)
|
||||
Max Dissipation (W)
|
||||
Max Voltage (V)
|
||||
Save To File
|
||||
Update Axis
|
||||
Apply Variant
|
||||
Save as Building Block
|
||||
Select
|
||||
Unlock
|
||||
Add a Curve|Ajouter une courbe
|
||||
Clear|Effacer
|
||||
Insert Image|Insérer une image
|
||||
Load From File|Charger depuis un fichier
|
||||
Loadline Editor|Éditeur de ligne de charge
|
||||
Max Current (mA)|Courant maximum (mA)
|
||||
Max Dissipation (W)|Dissipation maximale (W)
|
||||
Max Voltage (V)|Tension maximale (V)
|
||||
Save To File|Enregistrer dans un fichier
|
||||
Update Axis|Mettre à jour les axes
|
||||
Apply Variant|Appliquer la variante
|
||||
Save as Building Block|Enregistrer comme bloc de construction
|
||||
Select|Sélectionner
|
||||
Unlock|Déverrouiller
|
||||
Are you sure you want to clear the chat history for this project? This action cannot be undone!|Êtes-vous sûr de vouloir effacer l'historique de chat de ce projet? Cette action ne peut pas être annulée!
|
||||
Component reordering is possible only when Project Explorer is sorted by z-index.|Le réordonnancement des composants n'est possible que lorsque l'Explorateur de projet est trié par z-index.
|
||||
Could not apply changes. Check the log for details.|Impossible d'appliquer les modifications. Consultez le journal pour plus de détails.
|
||||
Exporting outline for board %s to file %s|Exportation du contour de la carte %s vers le fichier %s
|
||||
Failed to export the project to gerber. Please check the log for details|Échec de l'exportation du projet vers gerber. Veuillez consulter le journal pour plus de détails
|
||||
Forgot Password?|Mot de passe oublié?
|
||||
Gerber Export|Export Gerber
|
||||
Gerber export completed successfully. The following files were created:%s|L'export Gerber a été complété avec succès. Les fichiers suivants ont été créés:%s
|
||||
Get Premium|Obtenir Premium
|
||||
Netlist files|Fichiers de liste de connexions
|
||||
No proximity issues detected.|Aucun problème de proximité détecté.
|
||||
Nothing to export.|Rien à exporter.
|
||||
Password|Mot de passe
|
||||
Send|Envoyer
|
||||
There are some components that are outside of the bounds of boards.%sThey will be ignored unless you add a board component underneath them.%sDo you want to continue?|Il y a des composants qui sont en dehors des limites des cartes.%sIls seront ignorés sauf si vous ajoutez un composant de carte en dessous.%sVoulez-vous continuer?
|
||||
User Name|Nom d'utilisateur
|
||||
AI Circuit Analyzer|Analyseur de circuit IA
|
||||
Cloud|Cloud
|
||||
Compare With...|Comparer avec...
|
||||
Connect to Patreon|Se connecter à Patreon
|
||||
Export|Exporter
|
||||
Export to Gerber|Exporter vers Gerber
|
||||
Generate DIYLC Netlist (excl. Switches)|Générer la Netlist DIYLC (sans commutateurs)
|
||||
Generate DIYLC Netlist (incl. Switches)|Générer la Netlist DIYLC (avec commutateurs)
|
||||
Generate Spice Netlist (excl. Switches)|Générer la Netlist Spice (sans commutateurs)
|
||||
Generate Spice Netlist (incl. Switches)|Générer la Netlist Spice (avec commutateurs)
|
||||
Silkscreen|Sérigraphie
|
||||
Virtual Multimeter (excl. Switches)|Multimètre virtuel (sans commutateurs)
|
||||
Virtual Multimeter (incl. Switches)|Multimètre virtuel (avec commutateurs)
|
||||
Become a Patreon and enjoy perks|Devenez un Patreon et profitez d'avantages
|
||||
Join Patreon|Rejoindre Patreon
|
||||
@@ -196,6 +196,7 @@ Image|Immagine
|
||||
Immediate Only|Solo immediato
|
||||
Import|Importa
|
||||
Import Building Blocks|Importa blocchi di costruzione
|
||||
Import Netlist|Importa netlist
|
||||
Import Variants|Importa varianti
|
||||
In-Line D-44|In linea D-44
|
||||
In-line|In linea
|
||||
@@ -235,6 +236,7 @@ Lock|Blocco
|
||||
Log|Accesso
|
||||
Log In|Accedi
|
||||
Log Out|Esci
|
||||
Look for|Cerca
|
||||
Lug Size|Dimensione dell'aletta
|
||||
Lugs|Alette
|
||||
Make a Donation|Fai una donazione
|
||||
@@ -307,6 +309,7 @@ Power Rating|Potenza nominale
|
||||
Power Strip|Ciabatta
|
||||
Primary CT|CT primario
|
||||
Print...|Stampa ...
|
||||
Project|Progetto
|
||||
Project Cloud|Progetto Cloud
|
||||
Public Announcement|Annuncio pubblico
|
||||
Radius|raggio
|
||||
@@ -360,6 +363,7 @@ Shaft Up|Shaft Up
|
||||
Shape|forma
|
||||
Shapes|forme
|
||||
Show Grid|Mostra griglia
|
||||
Show Resize Dimensions|Mostra dimensioni ridimensionamento
|
||||
Show Rulers|Mostra righelli
|
||||
Shroud|Copri
|
||||
Shrouded|Coperto
|
||||
@@ -456,4 +460,35 @@ Y-axis|Asse Y
|
||||
Zoom|Ingrandimento
|
||||
base of 10|base di 10
|
||||
base of 2|base di 2
|
||||
hold %s and drag to unstick from|tieni premuto %s e trascina per sbloccare da
|
||||
hold %s and drag to unstick from|tieni premuto %s e trascina per sbloccare da
|
||||
Are you sure you want to clear the chat history for this project? This action cannot be undone!|Sei sicuro di voler cancellare la cronologia della chat per questo progetto? Questa azione non può essere annullata!
|
||||
Component reordering is possible only when Project Explorer is sorted by z-index.|Il riordinamento dei componenti è possibile solo quando Project Explorer è ordinato per z-index.
|
||||
Could not apply changes. Check the log for details.|Impossibile applicare le modifiche. Controlla il registro per i dettagli.
|
||||
Exporting outline for board %s to file %s|Esportazione del contorno per la scheda %s nel file %s
|
||||
Failed to export the project to gerber. Please check the log for details|Impossibile esportare il progetto in gerber. Controlla il registro per i dettagli
|
||||
Forgot Password?|Password dimenticata?
|
||||
Gerber Export|Esporta Gerber
|
||||
Gerber export completed successfully. The following files were created:%s|Esportazione Gerber completata con successo. Sono stati creati i seguenti file:%s
|
||||
Get Premium|Ottieni Premium
|
||||
Netlist files|File netlist
|
||||
No proximity issues detected.|Nessun problema di prossimità rilevato.
|
||||
Nothing to export.|Niente da esportare.
|
||||
Password|Password
|
||||
Send|Invia
|
||||
There are some components that are outside of the bounds of boards.%sThey will be ignored unless you add a board component underneath them.%sDo you want to continue?|Ci sono alcuni componenti che sono al di fuori dei limiti delle schede.%sVerranno ignorati a meno che non aggiungi un componente scheda sotto di loro.%sVuoi continuare?
|
||||
User Name|Nome utente
|
||||
AI Circuit Analyzer|Analizzatore di Circuiti IA
|
||||
Cloud|Cloud
|
||||
Compare With...|Confronta con...
|
||||
Connect to Patreon|Connetti a Patreon
|
||||
Export|Esporta
|
||||
Export to Gerber|Esporta in Gerber
|
||||
Generate DIYLC Netlist (excl. Switches)|Genera netlist DIYLC (escl. interruttori)
|
||||
Generate DIYLC Netlist (incl. Switches)|Genera netlist DIYLC (incl. interruttori)
|
||||
Generate Spice Netlist (excl. Switches)|Genera netlist Spice (escl. interruttori)
|
||||
Generate Spice Netlist (incl. Switches)|Genera netlist Spice (incl. interruttori)
|
||||
Silkscreen|Serigrafia
|
||||
Virtual Multimeter (excl. Switches)|Multimetro Virtuale (escl. interruttori)
|
||||
Virtual Multimeter (incl. Switches)|Multimetro Virtuale (incl. interruttori)
|
||||
Become a Patreon and enjoy perks|Diventa un Patreon e goditi i vantaggi
|
||||
Join Patreon|Unisciti a Patreon
|
||||
@@ -195,6 +195,7 @@ Image|Afbeelding
|
||||
Immediate Only|Alleen direct
|
||||
Import|Importeren
|
||||
Import Building Blocks|Bouwstenen importeren
|
||||
Import Netlist|Netlist importeren
|
||||
Import Variants|Varianten importeren
|
||||
In-Line D-44|Inline D-44
|
||||
In-line|Inline
|
||||
@@ -232,6 +233,7 @@ Lock|Vastpinnen
|
||||
Log|Logaritmisch
|
||||
Log In|Inloggen
|
||||
Log Out|Uitloggen
|
||||
Look for|Zoeken
|
||||
Lug Size|Solderooggrootte
|
||||
Lugs|Solderogen
|
||||
Make a Donation|Doneren
|
||||
@@ -303,6 +305,7 @@ Power Rating|Nominale vermogen
|
||||
Power Strip|Voedingslijn
|
||||
Primary CT|Primaire middenaftakking
|
||||
Print...|Afdrukken...
|
||||
Project|Project
|
||||
Project Cloud|Project cloud
|
||||
Public Announcement|Openbare aankondiging
|
||||
Radius|Straal
|
||||
@@ -360,6 +363,7 @@ Shaft Up|As omhoog
|
||||
Shape|Vorm
|
||||
Shapes|Vormen
|
||||
Show Grid|Raster weergeven
|
||||
Show Resize Dimensions|Grootte-aanpassingsafmetingen weergeven
|
||||
Show Rulers|Linialen weergeven
|
||||
Shroud|Beschermrand
|
||||
Shrouded|Afgeschermd
|
||||
@@ -456,4 +460,35 @@ Y-axis|Y-as
|
||||
Zoom|Zoom
|
||||
base of 10|Tienden
|
||||
base of 2|Achtsten
|
||||
hold %s and drag to unstick from|houd %s vast en sleep, om los te maken van
|
||||
hold %s and drag to unstick from|houd %s vast en sleep, om los te maken van
|
||||
Are you sure you want to clear the chat history for this project? This action cannot be undone!|Weet u zeker dat u de chatgeschiedenis voor dit project wilt wissen? Deze actie kan niet ongedaan worden gemaakt!
|
||||
Component reordering is possible only when Project Explorer is sorted by z-index.|Het opnieuw ordenen van componenten is alleen mogelijk wanneer Project Explorer is gesorteerd op z-index.
|
||||
Could not apply changes. Check the log for details.|Wijzigingen konden niet worden toegepast. Controleer het logbestand voor details.
|
||||
Exporting outline for board %s to file %s|Omtrek exporteren voor printplaat %s naar bestand %s
|
||||
Failed to export the project to gerber. Please check the log for details|Het exporteren van het project naar gerber is mislukt. Controleer het logbestand voor details
|
||||
Forgot Password?|Wachtwoord vergeten?
|
||||
Gerber Export|Gerber Export
|
||||
Gerber export completed successfully. The following files were created:%s|Gerber export succesvol voltooid. De volgende bestanden zijn aangemaakt:%s
|
||||
Get Premium|Premium krijgen
|
||||
Netlist files|Netlist bestanden
|
||||
No proximity issues detected.|Geen nabijheidsproblemen gedetecteerd.
|
||||
Nothing to export.|Niets te exporteren.
|
||||
Password|Wachtwoord
|
||||
Send|Verzenden
|
||||
There are some components that are outside of the bounds of boards.%sThey will be ignored unless you add a board component underneath them.%sDo you want to continue?|Er zijn enkele componenten die buiten de grenzen van printplaten liggen.%sZe worden genegeerd tenzij u een printplaatcomponent eronder toevoegt.%sWilt u doorgaan?
|
||||
User Name|Gebruikersnaam
|
||||
AI Circuit Analyzer|AI Circuit Analysator
|
||||
Cloud|Cloud
|
||||
Compare With...|Vergelijken met...
|
||||
Connect to Patreon|Verbinden met Patreon
|
||||
Export|Exporteren
|
||||
Export to Gerber|Exporteren naar Gerber
|
||||
Generate DIYLC Netlist (excl. Switches)|DIYLC netlist genereren (zonder schakelaars)
|
||||
Generate DIYLC Netlist (incl. Switches)|DIYLC netlist genereren (met schakelaars)
|
||||
Generate Spice Netlist (excl. Switches)|Spice netlist genereren (zonder schakelaars)
|
||||
Generate Spice Netlist (incl. Switches)|Spice netlist genereren (met schakelaars)
|
||||
Silkscreen|Zijdezeefdruk
|
||||
Virtual Multimeter (excl. Switches)|Virtuele Multimeter (zonder schakelaars)
|
||||
Virtual Multimeter (incl. Switches)|Virtuele Multimeter (met schakelaars)
|
||||
Become a Patreon and enjoy perks|Word een Patreon en geniet van voordelen
|
||||
Join Patreon|Lid worden van Patreon
|
||||
@@ -193,6 +193,7 @@ Image|Imagem
|
||||
Immediate Only|Apenas Imediato
|
||||
Import|Importar
|
||||
Import Building Blocks|Importar Blocos
|
||||
Import Netlist|Importar Lista de Redes
|
||||
Import Variants|Importar variações
|
||||
In-Line D-44|Em linha D-44
|
||||
In-line|Em linha
|
||||
@@ -298,6 +299,7 @@ Power Rating|Potência Nominal
|
||||
Power Strip|Listra Eletrificada
|
||||
Primary CT|CT Primário
|
||||
Print...|Imprimir...
|
||||
Project|Projeto
|
||||
Project Cloud|Projetos na Núvem
|
||||
Public Announcement|Anúncio Público
|
||||
Radius|Raio
|
||||
@@ -349,6 +351,7 @@ Shaft Up|Jogar para cima
|
||||
Shape|Forma
|
||||
Shapes|Formas
|
||||
Show Grid|Mostrar Grid
|
||||
Show Resize Dimensions|Mostrar Dimensões de Redimensionamento
|
||||
Show Rulers|Mostrar Réguas
|
||||
Shroud|Envoltura
|
||||
Shrouded|Envolto
|
||||
@@ -358,7 +361,7 @@ Size|Tamanho
|
||||
Sizing Mode|Modo de Dimensionamento
|
||||
Small-button B7G|Botão pequeno B7G
|
||||
Smooth|Suave
|
||||
Snap To...|Ajustarse a...
|
||||
Snap To|Ajustar a
|
||||
Soap Bar|Soap Bar
|
||||
Solid|Sólido
|
||||
South|Sul
|
||||
@@ -457,4 +460,35 @@ Update Axis|Atualizar Eixos
|
||||
Apply Variant|Aplicar Variante
|
||||
Save as Building Block|Salvar como Bloco
|
||||
Select|Selecionar
|
||||
Unlock|Desbloquear
|
||||
Unlock|Desbloquear
|
||||
Are you sure you want to clear the chat history for this project? This action cannot be undone!|Tem certeza de que deseja limpar o histórico de chat deste projeto? Esta ação não pode ser desfeita!
|
||||
Component reordering is possible only when Project Explorer is sorted by z-index.|A reordenação de componentes só é possível quando o Explorador de Projetos está ordenado por z-index.
|
||||
Could not apply changes. Check the log for details.|Não foi possível aplicar as alterações. Verifique o registro para mais detalhes.
|
||||
Exporting outline for board %s to file %s|Exportando o contorno da placa %s para o arquivo %s
|
||||
Failed to export the project to gerber. Please check the log for details|Falha ao exportar o projeto para gerber. Por favor, verifique o registro para mais detalhes
|
||||
Forgot Password?|Esqueceu a Senha?
|
||||
Gerber Export|Exportar Gerber
|
||||
Gerber export completed successfully. The following files were created:%s|A exportação Gerber foi concluída com sucesso. Os seguintes arquivos foram criados:%s
|
||||
Get Premium|Obter Premium
|
||||
Netlist files|Arquivos de lista de rede
|
||||
No proximity issues detected.|Nenhum problema de proximidade detectado.
|
||||
Nothing to export.|Nada para exportar.
|
||||
Password|Senha
|
||||
Send|Enviar
|
||||
There are some components that are outside of the bounds of boards.%sThey will be ignored unless you add a board component underneath them.%sDo you want to continue?|Há alguns componentes que estão fora dos limites das placas.%sEles serão ignorados a menos que você adicione um componente de placa abaixo deles.%sDeseja continuar?
|
||||
User Name|Nome de Usuário
|
||||
AI Circuit Analyzer|Analisador de Circuito IA
|
||||
Cloud|Nuvem
|
||||
Compare With...|Comparar com...
|
||||
Connect to Patreon|Conectar ao Patreon
|
||||
Export|Exportar
|
||||
Export to Gerber|Exportar para Gerber
|
||||
Generate DIYLC Netlist (excl. Switches)|Gerar Lista de Redes DIYLC (sem interruptores)
|
||||
Generate DIYLC Netlist (incl. Switches)|Gerar Lista de Redes DIYLC (com interruptores)
|
||||
Generate Spice Netlist (excl. Switches)|Gerar Lista Especial de Redes (sem interruptores)
|
||||
Generate Spice Netlist (incl. Switches)|Gerar Lista Especial de Redes (com interruptores)
|
||||
Silkscreen|Serigrafia
|
||||
Virtual Multimeter (excl. Switches)|Multímetro Virtual (sem interruptores)
|
||||
Virtual Multimeter (incl. Switches)|Multímetro Virtual (com interruptores)
|
||||
Become a Patreon and enjoy perks|Torne-se um Patreon e aproveite benefícios
|
||||
Join Patreon|Juntar-se ao Patreon
|
||||
@@ -191,6 +191,7 @@ Image|Slika
|
||||
Immediate Only|Samo Neposredne
|
||||
Import|Uvoz
|
||||
Import Building Blocks|Uvezi Blokove za Gradnju
|
||||
Import Netlist|Uvezi Netlistu
|
||||
Import Variants|Uvezi Varijacije
|
||||
In-Line D-44|U Redu D-44
|
||||
In-line|U Redu
|
||||
@@ -224,6 +225,7 @@ Light|Svetla
|
||||
Lock|Zaključaj
|
||||
Log In|Prijava
|
||||
Log Out|Odjava
|
||||
Look for|Traži
|
||||
Lug Size|Veličina Ušica
|
||||
Lugs|Ušice
|
||||
Make a Donation|Doniraj
|
||||
@@ -291,6 +293,7 @@ Power Rating|Snaga
|
||||
Power Strip|Naponska Magistrala
|
||||
Primary CT|Primarni Srednji Izvod
|
||||
Print...|Štampa...
|
||||
Project|Projekat
|
||||
Project Cloud|Projektni Oblak
|
||||
Public Announcement|Javna Objava
|
||||
Radius|Poluprečnik
|
||||
@@ -341,6 +344,7 @@ Shaft Up|Osovina Gore
|
||||
Shape|Oblik
|
||||
Shapes|Oblici
|
||||
Show Grid|Prikaz Rešetke
|
||||
Show Resize Dimensions|Prikaz Dimenzija Promene Veličine
|
||||
Show Rulers|Prikaz Lenjira
|
||||
Shroud|Kućište
|
||||
Shrouded|Sa Kućištem
|
||||
@@ -449,4 +453,35 @@ Update Axis|Osveži Ose
|
||||
Apply Variant|Primeni Varijaciju
|
||||
Save as Building Block|Sačuvaj Kao Blok
|
||||
Select|Izaberi
|
||||
Unlock|Otključaj
|
||||
Unlock|Otključaj
|
||||
Are you sure you want to clear the chat history for this project? This action cannot be undone!|Da li ste sigurni da želite da obrišete istoriju četa za ovaj projekat? Ova akcija se ne može poništiti!
|
||||
Component reordering is possible only when Project Explorer is sorted by z-index.|Preuređivanje komponenti je moguće samo kada je Project Explorer sortiran po z-index-u.
|
||||
Could not apply changes. Check the log for details.|Nije moguće primeniti izmene. Proverite log za više informacija.
|
||||
Exporting outline for board %s to file %s|Izvoz obrisa za pločicu %s u datoteku %s
|
||||
Failed to export the project to gerber. Please check the log for details|Neuspelo izvoženje projekta u gerber. Proverite log za više informacija
|
||||
Forgot Password?|Zaboravili ste lozinku?
|
||||
Gerber Export|Gerber Izvoz
|
||||
Gerber export completed successfully. The following files were created:%s|Gerber izvoz je uspešno završen. Sledeće datoteke su kreirane:%s
|
||||
Get Premium|Dobij Premium
|
||||
Netlist files|Netlist datoteke
|
||||
No proximity issues detected.|Nisu detektovani problemi sa blizinom.
|
||||
Nothing to export.|Nema ništa za izvoz.
|
||||
Password|Lozinka
|
||||
Send|Pošalji
|
||||
There are some components that are outside of the bounds of boards.%sThey will be ignored unless you add a board component underneath them.%sDo you want to continue?|Postoje neke komponente koje su van granica pločica.%sOne će biti ignorisane osim ako ne dodate komponentu pločice ispod njih.%sDa li želite da nastavite?
|
||||
User Name|Korisničko Ime
|
||||
AI Circuit Analyzer|AI Analizator Kola
|
||||
Cloud|Oblak
|
||||
Compare With...|Uporedi sa...
|
||||
Connect to Patreon|Poveži sa Patreon-om
|
||||
Export|Izvoz
|
||||
Export to Gerber|Izvoz u Gerber
|
||||
Generate DIYLC Netlist (excl. Switches)|Generiši DIYLC Netlistu (bez prekidača)
|
||||
Generate DIYLC Netlist (incl. Switches)|Generiši DIYLC Netlistu (sa prekidačima)
|
||||
Generate Spice Netlist (excl. Switches)|Generiši Spice Netlistu (bez prekidača)
|
||||
Generate Spice Netlist (incl. Switches)|Generiši Spice Netlistu (sa prekidačima)
|
||||
Silkscreen|Serigrafija
|
||||
Virtual Multimeter (excl. Switches)|Virtuelni Multimetar (bez prekidača)
|
||||
Virtual Multimeter (incl. Switches)|Virtuelni Multimetar (sa prekidačima)
|
||||
Become a Patreon and enjoy perks|Postanite Patreon i uživajte u prednostima
|
||||
Join Patreon|Pridružite se Patreon-u
|
||||
@@ -0,0 +1,499 @@
|
||||
#start-internal
|
||||
icon=india
|
||||
#end-internal
|
||||
(Building Blocks)|(बिल्डिंग ब्लॉक्स)
|
||||
(Favorites)|(पसंदीदा)
|
||||
(Recently Used)|(हाल ही में उपयोग किए गए)
|
||||
180 degrees clockwise|दक्षिणावर्त 180 डिग्री
|
||||
270 degrees clockwise|दक्षिणावर्त 270 डिग्री
|
||||
90 degrees clockwise|दक्षिणावर्त 90 डिग्री
|
||||
A variant with that name already exists. Overwrite?|उस नाम का एक वेरिएंट पहले से मौजूद है। अधिलेखित करें?
|
||||
About|के बारे में
|
||||
Above|ऊपर
|
||||
Access User Files|उपयोगकर्ता फ़ाइलें एक्सेस करें
|
||||
Add Flexible Leads|लचीली लीड जोड़ें
|
||||
Add Validation Step|सत्यापन चरण जोड़ें
|
||||
All Connected|सभी जुड़े हुए
|
||||
Alpha|अल्फा
|
||||
Analyze|विश्लेषण करें
|
||||
Analyze Guitar Diagrams|गिटार आरेखों का विश्लेषण करें
|
||||
Angle|कोण
|
||||
Anti-Aliasing|एंटी-अलियासिंग
|
||||
Arrow Size|तीर का आकार
|
||||
Author|लेखक
|
||||
Auto-Create Pads|ऑटो-क्रिएट पैड
|
||||
Auto-Edit Mode|ऑटो-एडिट मोड
|
||||
Base|आधार
|
||||
Below|नीचे
|
||||
Blueprint|ब्लूप्रिंट
|
||||
Board|बोर्ड
|
||||
Boards|बोर्ड
|
||||
Board Color|बोर्ड का रंग
|
||||
Board Width|बोर्ड की चौड़ाई
|
||||
Bobbin 1|बॉबिन 1
|
||||
Bobbin 2|बॉबिन 2
|
||||
Body|शरीर
|
||||
Border|बॉर्डर
|
||||
Border Thickness|बॉर्डर की मोटाई
|
||||
Both|दोनों
|
||||
Both Sides|दोनों तरफ
|
||||
Bottom|नीचे
|
||||
Bottom Left|नीचे बाएं
|
||||
Bottom Right|नीचे दाएं
|
||||
Bracket|ब्रैकेट
|
||||
Bring Forward|आगे लाएं
|
||||
Bring Selection to Front|चयन को सामने लाएं
|
||||
Buffer|बफर
|
||||
Center|केंद्र
|
||||
Center Negative|केंद्र नकारात्मक
|
||||
Center Positive|केंद्र सकारात्मक
|
||||
Center Terminal|केंद्र टर्मिनल
|
||||
Central|केंद्रीय
|
||||
Change Password|पासवर्ड बदलें
|
||||
Channel|चैनल
|
||||
Chassis|चेसिस
|
||||
Check Trace Proximity|ट्रेस निकटता जांचें
|
||||
Circular|गोलाकार
|
||||
Click on a component to highlight area directly or indirectly connected to the area below the cursor. To continue editing, this mode needs to be switched OFF|कर्सर के नीचे के क्षेत्र से सीधे या अप्रत्यक्ष रूप से जुड़े क्षेत्र को हाइलाइट करने के लिए एक घटक पर क्लिक करें। संपादन जारी रखने के लिए, इस मोड को बंद करना होगा
|
||||
Click on the canvas to create a new %s or press %s to cancel|एक नया %s बनाने के लिए कैनवास पर क्लिक करें या रद्द करने के लिए %s दबाएं
|
||||
Click on the canvas to set the %s control point of a new %s or press %s to cancel|एक नए %s के %s कंट्रोल पॉइंट सेट करने के लिए कैनवास पर क्लिक करें या रद्द करने के लिए %s दबाएं
|
||||
Click the lightbulb icon in the bottom-right corner of the window for more info.|अधिक जानकारी के लिए विंडो के निचले दाएं कोने में लाइटबल्ब आइकन पर क्लिक करें।
|
||||
Click to fetch the most recent public announcement|सबसे हाल का सार्वजनिक घोषणा प्राप्त करने के लिए क्लिक करें
|
||||
Click to show recent changes and updates|हाल के परिवर्तन और अपडेट दिखाने के लिए क्लिक करें
|
||||
Click to toggle to the next option|अगले विकल्प पर जाने के लिए क्लिक करें
|
||||
Coil|कुंडली
|
||||
Coil Border|कुंडली बॉर्डर
|
||||
Coil Length|कुंडली की लंबाई
|
||||
Coil Width|कुंडली की चौड़ाई
|
||||
Color|रंग
|
||||
Color 1|रंग 1
|
||||
Color 2|रंग 2
|
||||
Color code|रंग कोड
|
||||
Column Spacing|कॉलम स्पेसिंग
|
||||
Columns|कॉलम
|
||||
Component|घटक
|
||||
Component API|घटक API
|
||||
Components|घटक
|
||||
Config|कॉन्फ़िग
|
||||
Connector|कनेक्टर
|
||||
Connector (Mirrored)|कनेक्टर (मिरर किया गया)
|
||||
Connectivity|कनेक्टिविटी
|
||||
Contacts|संपर्क
|
||||
Continuous Creation|निरंतर निर्माण
|
||||
Coordinate Color|निर्देशांक रंग
|
||||
Coordinate Origin|निर्देशांक मूल
|
||||
Coordinates|निर्देशांक
|
||||
Copy|कॉपी करें
|
||||
Core|कोर
|
||||
Core Border|कोर बॉर्डर
|
||||
Core Thickness|कोर की मोटाई
|
||||
Core Width|कोर की चौड़ाई
|
||||
Could not create a new file. Check the log for details.|नई फ़ाइल नहीं बना सके। विवरण के लिए लॉग जांचें।
|
||||
Could not create component. Check log for details.|घटक नहीं बना सके। विवरण के लिए लॉग जांचें।
|
||||
Could not fetch public announcements.|सार्वजनिक घोषणाएं प्राप्त नहीं कर सके।
|
||||
Could not save the project to file. Check the log for details.|प्रोजेक्ट को फ़ाइल में सहेज नहीं सके। विवरण के लिए लॉग जांचें।
|
||||
Could not set component type slot. Check log for details.|घटक प्रकार स्लॉट सेट नहीं कर सके। विवरण के लिए लॉग जांचें।
|
||||
Cover|कवर
|
||||
Create B.O.M.|B.O.M. बनाएं
|
||||
Create New Account|नया खाता बनाएं
|
||||
Current|वर्तमान
|
||||
Cut|काटें
|
||||
Cut Between Holes|छेदों के बीच काटें
|
||||
Cut Orientation|कट ओरिएंटेशन
|
||||
DIP (Mirrored)|DIP (मिरर किया गया)
|
||||
DP3T (Standard 3-Position Strat)|DP3T (स्टैंडर्ड 3-पोजीशन Strat)
|
||||
DP3T (Standard 5-Position Strat)|DP3T (स्टैंडर्ड 5-पोजीशन Strat)
|
||||
DP4T (4-Position Tele)|DP4T (4-पोजीशन Tele)
|
||||
Dark|डार्क
|
||||
Dashed|डैश किया गया
|
||||
Decimals|दशमलव
|
||||
Default|डिफ़ॉल्ट
|
||||
Default Font|डिफ़ॉल्ट फ़ॉन्ट
|
||||
Default Font Size|डिफ़ॉल्ट फ़ॉन्ट साइज़
|
||||
Delete Selection|चयन हटाएं
|
||||
Description|विवरण
|
||||
Diameter|व्यास
|
||||
Directional|दिशात्मक
|
||||
Directly heated|सीधे गर्म
|
||||
Display|डिस्प्ले
|
||||
Display Pin #s|पिन #s दिखाएं
|
||||
Dog Ear|डॉग ईयर
|
||||
Dot Spacing|डॉट स्पेसिंग
|
||||
Donate|दान करें
|
||||
Dotted|बिंदीदार
|
||||
Drag control point(s) of|कंट्रोल पॉइंट खींचें
|
||||
Drag the mouse over the canvas to place a new|एक नया रखने के लिए कैनवास पर माउस खींचें
|
||||
Dual Rail Humbucker|ड्यूल रेल हम्बकर
|
||||
Dual Rods|ड्यूल रॉड्स
|
||||
Duodecar B12C|Duodecar B12C
|
||||
Duplicate|डुप्लिकेट
|
||||
Edges|किनारे
|
||||
Edit|संपादित करें
|
||||
Edit Project Settings|प्रोजेक्ट सेटिंग्स संपादित करें
|
||||
Edit Selection|चयन संपादित करें
|
||||
Electrode Labels|इलेक्ट्रोड लेबल
|
||||
Electro-Mechanical|इलेक्ट्रो-मैकेनिकल
|
||||
Enable Cache Boost|कैश बूस्ट सक्षम करें
|
||||
End Arrow|अंत तीर
|
||||
Enjoying DIYLC? Click here to buy me a coffee :)|DIYLC का आनंद ले रहे हैं? मुझे कॉफी खरीदने के लिए यहां क्लिक करें :)
|
||||
Error|त्रुटि
|
||||
Error occurred while editing selection. Check the log for details.|चयन संपादित करते समय त्रुटि हुई। विवरण के लिए लॉग जांचें।
|
||||
Exit|बाहर निकलें
|
||||
Expand Selection|चयन विस्तारित करें
|
||||
Explicit Dimensions|स्पष्ट आयाम
|
||||
Explicit Length|स्पष्ट लंबाई
|
||||
Explicit Width|स्पष्ट चौड़ाई
|
||||
Export Building Blocks|बिल्डिंग ब्लॉक्स निर्यात करें
|
||||
Export Grid|ग्रिड निर्यात करें
|
||||
Export Variants|वेरिएंट निर्यात करें
|
||||
Export to PDF|PDF में निर्यात करें
|
||||
Export to PNG|PNG में निर्यात करें
|
||||
Extra Working Area|अतिरिक्त कार्य क्षेत्र
|
||||
Eyelet color|आईलेट रंग
|
||||
FAQ|अक्सर पूछे जाने वाले प्रश्न
|
||||
File|फ़ाइल
|
||||
Filtertron|Filtertron
|
||||
Find|खोजें
|
||||
Five|पांच
|
||||
Flip|फ्लिप करें
|
||||
Folded|तह किया गया
|
||||
Font|फ़ॉन्ट
|
||||
Font Bold|फ़ॉन्ट बोल्ड
|
||||
Font Italic|फ़ॉन्ट इटैलिक
|
||||
Font Size|फ़ॉन्ट साइज़
|
||||
Font Size Override|फ़ॉन्ट साइज़ ओवरराइड
|
||||
Four|चार
|
||||
Full|पूर्ण
|
||||
Fuse|फ्यूज
|
||||
Generate DIYLC Netlist|DIYLC नेटलिस्ट जेनरेट करें
|
||||
Generate Spice Netlist|Spice नेटलिस्ट जेनरेट करें
|
||||
Grid|ग्रिड
|
||||
Grid Spacing|ग्रिड स्पेसिंग
|
||||
Group Selection|चयन समूहित करें
|
||||
Guitar|गिटार
|
||||
Half|आधा
|
||||
Hardware Acceleration|हार्डवेयर एक्सेलेरेशन
|
||||
Heaters|हीटर
|
||||
Height|ऊंचाई
|
||||
Help|मदद
|
||||
Hi-Quality Rendering|उच्च गुणवत्ता रेंडरिंग
|
||||
Highlight Connected Areas|जुड़े क्षेत्र हाइलाइट करें
|
||||
Hole|छेद
|
||||
Hole Size|छेद का आकार
|
||||
Hole Spacing|छेद स्पेसिंग
|
||||
Hole size|छेद का आकार
|
||||
Holes per strip|प्रति स्ट्रिप छेद
|
||||
Horizontal|क्षैतिज
|
||||
Horizontal Alignment|क्षैतिज संरेखण
|
||||
Horizontal Large|क्षैतिज बड़ा
|
||||
Horizontal Medium|क्षैतिज मध्यम
|
||||
Horizontal Small 1|क्षैतिज छोटा 1
|
||||
Horizontal Small 2|क्षैतिज छोटा 2
|
||||
Horizontal X-Small|क्षैतिज एक्स-छोटा
|
||||
Humbucking|हम्बकिंग
|
||||
If this box is checked application will use the current value as a<br>default when creating new components of the same type|यदि यह बॉक्स चेक किया गया है, तो एप्लिकेशन समान प्रकार के नए घटक बनाते समय वर्तमान मान को <br>डिफ़ॉल्ट के रूप में उपयोग करेगा
|
||||
Image|छवि
|
||||
Immediate Only|केवल तत्काल
|
||||
Import|आयात करें
|
||||
Import Building Blocks|बिल्डिंग ब्लॉक्स आयात करें
|
||||
Import Netlist|नेटलिस्ट आयात करें
|
||||
Import Variants|वेरिएंट आयात करें
|
||||
In-Line D-44|इन-लाइन D-44
|
||||
In-line|इन-लाइन
|
||||
Indent|इंडेंट
|
||||
Information|जानकारी
|
||||
Inline|इनलाइन
|
||||
Input error for "%s": %s|"%s" के लिए इनपुट त्रुटि: %s
|
||||
Inside color|अंदर का रंग
|
||||
Integration|एकीकरण
|
||||
Invert polarity|ध्रुवता उलटें
|
||||
Label|लेबल
|
||||
Label Color|लेबल रंग
|
||||
Label Orientation|लेबल ओरिएंटेशन
|
||||
Label Position|लेबल स्थिति
|
||||
Labels|लेबल
|
||||
Language|भाषा
|
||||
Language selection will be applied after the application is restarted.|भाषा चयन एप्लिकेशन रीस्टार्ट होने के बाद लागू होगा।
|
||||
Layer|लेयर
|
||||
Layers|लेयर
|
||||
Lead Color|लीड रंग
|
||||
Lead Count|लीड संख्या
|
||||
Lead Length|लीड लंबाई
|
||||
Lead Spacing|लीड स्पेसिंग
|
||||
Left|बाएं
|
||||
Left-to-Right|बाएं से दाएं
|
||||
Left click to instantiate this component, right click for more options|इस घटक को इंस्टेंट करने के लिए बाएं क्लिक करें, अधिक विकल्पों के लिए दाएं क्लिक करें
|
||||
Legs|पैर
|
||||
Length|लंबाई
|
||||
Letters|अक्षर
|
||||
Light|लाइट
|
||||
Lock|लॉक करें
|
||||
Log In|लॉग इन करें
|
||||
Log Out|लॉग आउट करें
|
||||
Look for|खोजें
|
||||
Lug Size|लग साइज़
|
||||
Lugs|लग्स
|
||||
Make a Donation|दान करें
|
||||
Manage Account|खाता प्रबंधित करें
|
||||
Manage My Uploads|मेरे अपलोड प्रबंधित करें
|
||||
Mark Common Lugs|सामान्य लग्स चिह्नित करें
|
||||
Marker|मार्कर
|
||||
Marker Size|मार्कर साइज़
|
||||
Mini|मिनी
|
||||
Miniature|लघु
|
||||
Mirror Horizontally|क्षैतिज रूप से मिरर करें
|
||||
Mirror Selection|चयन मिरर करें
|
||||
Mirror Vertically|ऊर्ध्वाधर रूप से मिरर करें
|
||||
Mirroring operation will change the circuit. Do you want to continue?|मिररिंग ऑपरेशन सर्किट को बदल देगा। क्या आप जारी रखना चाहते हैं?
|
||||
Misc|विविध
|
||||
Model|मॉडल
|
||||
Mono|मोनो
|
||||
Mount|माउंट
|
||||
Mounting Lugs|माउंटिंग लग्स
|
||||
Mouse Wheel Zoom|माउस व्हील ज़ूम
|
||||
Moveable Label|स्थानांतरणीय लेबल
|
||||
Name|नाम
|
||||
Negative|नकारात्मक
|
||||
New|नया
|
||||
New version available|नया संस्करण उपलब्ध है
|
||||
No new public announcements available.|कोई नई सार्वजनिक घोषणाएं उपलब्ध नहीं हैं।
|
||||
No|नहीं
|
||||
None|कोई नहीं
|
||||
North|उत्तर
|
||||
Noval B9A|Noval B9A
|
||||
Nudge|नज
|
||||
Numbers|संख्या
|
||||
Nut|नट
|
||||
Octal|ऑक्टल
|
||||
Offset|ऑफसेट
|
||||
One Side|एक तरफ
|
||||
Open|खोलें
|
||||
Opposing Points|विपरीत बिंदु
|
||||
Orientation|ओरिएंटेशन
|
||||
Outer Foil|बाहरी फॉइल
|
||||
Outer Foil Mark|बाहरी फॉइल मार्क
|
||||
Outline Mode|आउटलाइन मोड
|
||||
Outside|बाहर
|
||||
Oval horizontal|क्षैतिज अंडाकार
|
||||
Oval vertical|ऊर्ध्वाधर अंडाकार
|
||||
Pad Color|पैड रंग
|
||||
Panel|पैनल
|
||||
Passive|निष्क्रिय
|
||||
Paste|पेस्ट करें
|
||||
Pin Arrangement|पिन व्यवस्था
|
||||
Pin Color|पिन रंग
|
||||
Pin Spacing|पिन स्पेसिंग
|
||||
Pinout|पिनआउट
|
||||
Pins|पिन्स
|
||||
Play Test|टेस्ट प्ले करें
|
||||
Plugin API|प्लगइन API
|
||||
Point Count|बिंदु संख्या
|
||||
Polarity|ध्रुवता
|
||||
Polarized|ध्रुवीकृत
|
||||
Pole Color|पोल रंग
|
||||
Pole Pieces|पोल पीस
|
||||
Pole Pieces 1|पोल पीस 1
|
||||
Pole Pieces 2|पोल पीस 2
|
||||
Positions|स्थितियां
|
||||
Positive|सकारात्मक
|
||||
Power Rating|पावर रेटिंग
|
||||
Power Strip|पावर स्ट्रिप
|
||||
Primary CT|प्राइमरी CT
|
||||
Print...|प्रिंट...
|
||||
Project|प्रोजेक्ट
|
||||
Project Cloud|प्रोजेक्ट क्लाउड
|
||||
Public Announcement|सार्वजनिक घोषणा
|
||||
Radius|त्रिज्या
|
||||
Rail|रेल
|
||||
Recent Files|हाल की फ़ाइलें
|
||||
Recent Updates|हाल के अपडेट
|
||||
Redo|रीडू
|
||||
Renumber On Paste|पेस्ट पर री-नंबर करें
|
||||
Renumber Selection|चयन री-नंबर करें
|
||||
Resistance|प्रतिरोध
|
||||
Reverse (standing)|रिवर्स (खड़ा)
|
||||
Right|दाएं
|
||||
Rim|रिम
|
||||
Rods|रॉड्स
|
||||
Rotate Clockwise|दक्षिणावर्त घुमाएं
|
||||
Rotate Counterclockwise|वामावर्त घुमाएं
|
||||
Rotate Selection|चयन घुमाएं
|
||||
Round|गोल
|
||||
Round WOG A|राउंड WOG A
|
||||
Round WOG B|राउंड WOG B
|
||||
Row Spacing|पंक्ति स्पेसिंग
|
||||
Rows|पंक्तियां
|
||||
Ruler Inch Subdivision...|रूलर इंच सबडिवीजन...
|
||||
Same Type Only|केवल समान प्रकार
|
||||
Save|सहेजें
|
||||
Save As|इस रूप में सहेजें
|
||||
Save as Variant|वेरिएंट के रूप में सहेजें
|
||||
Scale|स्केल
|
||||
Schematic Symbols|स्कीमैटिक प्रतीक
|
||||
Screws|स्क्रू
|
||||
Selection|चयन
|
||||
Selection Size|चयन साइज़
|
||||
Selection is empty.|चयन खाली है।
|
||||
Search The Cloud|क्लाउड खोजें
|
||||
Searchable Tree|खोज योग्य ट्री
|
||||
Secondary CT|सेकेंडरी CT
|
||||
Select All|सभी चुनें
|
||||
Selected component(s) have reached the bottom of their layer. Do you want to force the selection to the back?|चयनित घटक अपनी लेयर के नीचे पहुंच गए हैं। क्या आप चयन को पीछे जबरदस्ती करना चाहते हैं?
|
||||
Selected component(s) have reached the top of their layer. Do you want to force the selection to the top?|चयनित घटक अपनी लेयर के ऊपर पहुंच गए हैं। क्या आप चयन को ऊपर जबरदस्ती करना चाहते हैं?
|
||||
Selection contains components that cannot be mirrored. Do you want to exclude them?|चयन में ऐसे घटक हैं जिन्हें मिरर नहीं किया जा सकता। क्या आप उन्हें बाहर करना चाहते हैं?
|
||||
Selection contains components that cannot be rotated. Do you want to exclude them?|चयन में ऐसे घटक हैं जिन्हें घुमाया नहीं जा सकता। क्या आप उन्हें बाहर करना चाहते हैं?
|
||||
Semiconductors|सेमीकंडक्टर्स
|
||||
Send Backward|पीछे भेजें
|
||||
Send Selection to Back|चयन को पीछे भेजें
|
||||
Seven|सात
|
||||
Shaft Down|शाफ्ट नीचे
|
||||
Shaft Up|शाफ्ट ऊपर
|
||||
Shape|आकार
|
||||
Shapes|आकार
|
||||
Show Bracket|ब्रैकेट दिखाएं
|
||||
Show Grid|ग्रिड दिखाएं
|
||||
Show Resize Dimensions|आकार बदलने के आयाम दिखाएं
|
||||
Show Rulers|रूलर दिखाएं
|
||||
Shroud|श्राउड
|
||||
Shrouded|श्राउडेड
|
||||
Single Rail|सिंगल रेल
|
||||
Single Rods|सिंगल रॉड्स
|
||||
Size|साइज़
|
||||
Sizing Mode|साइज़िंग मोड
|
||||
Small-button B7G|स्मॉल-बटन B7G
|
||||
Smooth|स्मूथ
|
||||
Snap To|स्नैप टू
|
||||
Soap Bar|सोप बार
|
||||
Solid|ठोस
|
||||
South|दक्षिण
|
||||
Spacing|स्पेसिंग
|
||||
Square|वर्ग
|
||||
Square BR-3|स्क्वायर BR-3
|
||||
Standard|स्टैंडर्ड
|
||||
Start Arrow|शुरुआत तीर
|
||||
Start Recording|रिकॉर्डिंग शुरू करें
|
||||
Stereo|स्टीरियो
|
||||
Sticky Points|स्टिकी पॉइंट्स
|
||||
Stop Recording|रिकॉर्डिंग रोकें
|
||||
Stratocaster|Stratocaster
|
||||
Strip Color|स्ट्रिप रंग
|
||||
Stripe|स्ट्राइप
|
||||
Stripe 1|स्ट्राइप 1
|
||||
Stripe 2|स्ट्राइप 2
|
||||
Stripe Color|स्ट्राइप रंग
|
||||
Stripe Color 1|स्ट्राइप रंग 1
|
||||
Stripe Color 2|स्ट्राइप रंग 2
|
||||
Style|स्टाइल
|
||||
Submit a Bug|बग सबमिट करें
|
||||
Suppressor grid|सप्रेसर ग्रिड
|
||||
Switched|स्विच किया गया
|
||||
Switches|स्विच
|
||||
Tab|टैब
|
||||
Tab Border|टैब बॉर्डर
|
||||
Tabbed Toolbar|टैब्ड टूलबार
|
||||
Taper|टेपर
|
||||
Telecaster|Telecaster
|
||||
Terminal Spacing|टर्मिनल स्पेसिंग
|
||||
Terminals|टर्मिनल
|
||||
Test|टेस्ट
|
||||
Text|टेक्स्ट
|
||||
Text Color|टेक्स्ट रंग
|
||||
Theme|थीम
|
||||
There are unsaved changes. Would you like to save them?|असहेजित परिवर्तन हैं। क्या आप उन्हें सहेजना चाहते हैं?
|
||||
Thickness|मोटाई
|
||||
Three|तीन
|
||||
Through Hole|थ्रू होल
|
||||
Tick|टिक
|
||||
Tick Width|टिक चौड़ाई
|
||||
Ticks|टिक्स
|
||||
Title|शीर्षक
|
||||
Toolbox|टूलबॉक्स
|
||||
Top|ऊपर
|
||||
Top Lead|टॉप लीड
|
||||
Top Left|ऊपर बाएं
|
||||
Top Right|ऊपर दाएं
|
||||
Top-to-Bottom|ऊपर से नीचे
|
||||
Trace|ट्रेस
|
||||
Trace Color|ट्रेस रंग
|
||||
Trace Mask|ट्रेस मास्क
|
||||
Transform Selection|चयन ट्रांसफॉर्म करें
|
||||
Triangle|त्रिभुज
|
||||
Tubular|ट्यूबुलर
|
||||
Tubes|ट्यूब्स
|
||||
Two|दो
|
||||
Type|प्रकार
|
||||
Ultra-miniature|अल्ट्रा-मिनिएचर
|
||||
Underside Pad Color|अंडरसाइड पैड रंग
|
||||
Underside Trace Color|अंडरसाइड ट्रेस रंग
|
||||
Undo|अनडू
|
||||
Ungroup Selection|चयन अनग्रुप करें
|
||||
Unit|यूनिट
|
||||
Upload A Project|एक प्रोजेक्ट अपलोड करें
|
||||
User Manual|उपयोगकर्ता मैनुअल
|
||||
Value|मान
|
||||
Version history is not available.|संस्करण इतिहास उपलब्ध नहीं है।
|
||||
Vertical|ऊर्ध्वाधर
|
||||
Vertical Alignment|ऊर्ध्वाधर संरेखण
|
||||
Vertical Inline|ऊर्ध्वाधर इनलाइन
|
||||
Vertical Offset 1|ऊर्ध्वाधर ऑफसेट 1
|
||||
Vertical Offset 2|ऊर्ध्वाधर ऑफसेट 2
|
||||
View|व्यू
|
||||
Visible|दृश्यमान
|
||||
Voltage|वोल्टेज
|
||||
Wafer|वेफर
|
||||
Warning|चेतावनी
|
||||
Width|चौड़ाई
|
||||
Winding Spacing|वाइंडिंग स्पेसिंग
|
||||
Wiring|वायरिंग
|
||||
X-axis|X-अक्ष
|
||||
Y-axis|Y-अक्ष
|
||||
Zoom|ज़ूम
|
||||
base of 10|10 का आधार
|
||||
base of 2|2 का आधार
|
||||
hold %s and drag to unstick from|%s दबाए रखें और खींचकर अलग करें
|
||||
Add a Curve|एक कर्व जोड़ें
|
||||
Clear|साफ करें
|
||||
Insert Image|छवि सम्मिलित करें
|
||||
Load From File|फ़ाइल से लोड करें
|
||||
Loadline Editor|लोडलाइन एडिटर
|
||||
Max Current (mA)|अधिकतम करंट (mA)
|
||||
Max Dissipation (W)|अधिकतम डिसिपेशन (W)
|
||||
Max Voltage (V)|अधिकतम वोल्टेज (V)
|
||||
Save To File|फ़ाइल में सहेजें
|
||||
Update Axis|अक्ष अपडेट करें
|
||||
Apply Variant|वेरिएंट लागू करें
|
||||
Save as Building Block|बिल्डिंग ब्लॉक के रूप में सहेजें
|
||||
Select|चुनें
|
||||
Unlock|अनलॉक करें
|
||||
Are you sure you want to clear the chat history for this project? This action cannot be undone!|क्या आप वाकई इस प्रोजेक्ट की चैट हिस्ट्री साफ करना चाहते हैं? यह कार्रवाई पूर्ववत नहीं की जा सकती!
|
||||
Component reordering is possible only when Project Explorer is sorted by z-index.|घटक पुन: क्रमबद्धता केवल तभी संभव है जब प्रोजेक्ट एक्सप्लोरर z-index द्वारा क्रमबद्ध हो।
|
||||
Could not apply changes. Check the log for details.|परिवर्तन लागू नहीं कर सके। विवरण के लिए लॉग जांचें।
|
||||
Exporting outline for board %s to file %s|बोर्ड %s का आउटलाइन फ़ाइल %s में निर्यात कर रहे हैं
|
||||
Failed to export the project to gerber. Please check the log for details|प्रोजेक्ट को gerber में निर्यात करने में विफल। कृपया विवरण के लिए लॉग जांचें
|
||||
Forgot Password?|पासवर्ड भूल गए?
|
||||
Gerber Export|Gerber निर्यात
|
||||
Gerber export completed successfully. The following files were created:%s|Gerber निर्यात सफलतापूर्वक पूर्ण हुआ। निम्नलिखित फ़ाइलें बनाई गईं:%s
|
||||
Get Premium|प्रीमियम प्राप्त करें
|
||||
Netlist files|नेटलिस्ट फ़ाइलें
|
||||
No proximity issues detected.|कोई निकटता समस्या का पता नहीं चला।
|
||||
Nothing to export.|निर्यात करने के लिए कुछ नहीं है।
|
||||
Password|पासवर्ड
|
||||
Send|भेजें
|
||||
There are some components that are outside of the bounds of boards.%sThey will be ignored unless you add a board component underneath them.%sDo you want to continue?|कुछ घटक बोर्ड की सीमाओं के बाहर हैं।%sजब तक आप उनके नीचे एक बोर्ड घटक नहीं जोड़ते, तब तक उन्हें अनदेखा किया जाएगा।%sक्या आप जारी रखना चाहते हैं?
|
||||
User Name|उपयोगकर्ता नाम
|
||||
AI Circuit Analyzer|AI सर्किट विश्लेषक
|
||||
Cloud|क्लाउड
|
||||
Compare With...|के साथ तुलना करें...
|
||||
Connect to Patreon|Patreon से कनेक्ट करें
|
||||
Export|निर्यात करें
|
||||
Export to Gerber|Gerber में निर्यात करें
|
||||
Generate DIYLC Netlist (excl. Switches)|DIYLC नेटलिस्ट जेनरेट करें (स्विच के बिना)
|
||||
Generate DIYLC Netlist (incl. Switches)|DIYLC नेटलिस्ट जेनरेट करें (स्विच के साथ)
|
||||
Generate Spice Netlist (excl. Switches)|Spice नेटलिस्ट जेनरेट करें (स्विच के बिना)
|
||||
Generate Spice Netlist (incl. Switches)|Spice नेटलिस्ट जेनरेट करें (स्विच के साथ)
|
||||
Silkscreen|सिल्कस्क्रीन
|
||||
Virtual Multimeter (excl. Switches)|वर्चुअल मल्टीमीटर (स्विच के बिना)
|
||||
Virtual Multimeter (incl. Switches)|वर्चुअल मल्टीमीटर (स्विच के साथ)
|
||||
Become a Patreon and enjoy perks|Patreon बनें और लाभ उठाएं
|
||||
Join Patreon|Patreon में शामिल हों
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user