mirror of
https://github.com/bancika/diy-layout-creator
synced 2026-11-07 12:24:58 +10:00
Merge branch 'master' into controllers
This commit is contained in:
@@ -7,7 +7,7 @@
|
||||
<parent>
|
||||
<groupId>org.diylc</groupId>
|
||||
<artifactId>diylc-parent</artifactId>
|
||||
<version>6.3.0</version>
|
||||
<version>6.4.0</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>diylc-core</artifactId>
|
||||
|
||||
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
|
||||
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.announcements;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* Utility methods for detecting and extracting URLs from announcements and HTML.
|
||||
*/
|
||||
public class AnnouncementUtils {
|
||||
|
||||
private static final Pattern HREF_PATTERN = Pattern.compile(
|
||||
"href=[\"']([^\"']+)[\"']", Pattern.CASE_INSENSITIVE);
|
||||
|
||||
/**
|
||||
* Extracts the first URL found in the text or HTML.
|
||||
*
|
||||
* @param html text or HTML content
|
||||
* @return first URL found, or null if none
|
||||
*/
|
||||
public static String extractFirstUrl(String html) {
|
||||
List<String> urls = extractUrls(html);
|
||||
return urls.isEmpty() ? null : urls.get(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts all unique URLs found in the HTML href attributes in order of appearance.
|
||||
*
|
||||
* @param html text or HTML content
|
||||
* @return list of unique URLs
|
||||
*/
|
||||
public static List<String> extractUrls(String html) {
|
||||
List<String> urls = new ArrayList<String>();
|
||||
if (html == null || html.isEmpty()) {
|
||||
return urls;
|
||||
}
|
||||
|
||||
Matcher hrefMatcher = HREF_PATTERN.matcher(html);
|
||||
while (hrefMatcher.find()) {
|
||||
String url = hrefMatcher.group(1).trim();
|
||||
url = normalizeUrl(url);
|
||||
if (!urls.contains(url)) {
|
||||
urls.add(url);
|
||||
}
|
||||
}
|
||||
|
||||
return urls;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the HTML contains any link.
|
||||
*
|
||||
* @param html text or HTML content
|
||||
* @return true if at least one URL is detected, false otherwise
|
||||
*/
|
||||
public static boolean containsLink(String html) {
|
||||
return extractFirstUrl(html) != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalizes a URL to ensure it has a valid protocol if starting with www.
|
||||
*/
|
||||
public static String normalizeUrl(String url) {
|
||||
if (url == null) {
|
||||
return null;
|
||||
}
|
||||
String trimmed = url.trim();
|
||||
if (trimmed.startsWith("www.")) {
|
||||
return "http://" + trimmed;
|
||||
}
|
||||
return trimmed;
|
||||
}
|
||||
}
|
||||
@@ -22,22 +22,31 @@
|
||||
package org.diylc.common;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.math.BigDecimal;
|
||||
|
||||
public class Percentage implements Serializable, Cloneable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private int value;
|
||||
private BigDecimal value;
|
||||
|
||||
public Percentage(int value) {
|
||||
public Percentage(BigDecimal value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public int getValue() {
|
||||
public Percentage(double value) {
|
||||
this.value = BigDecimal.valueOf(value);
|
||||
}
|
||||
|
||||
public Percentage(int value) {
|
||||
this.value = BigDecimal.valueOf((long) value);
|
||||
}
|
||||
|
||||
public BigDecimal getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public void setValue(int value) {
|
||||
public void setValue(BigDecimal value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
@@ -45,7 +54,7 @@ public class Percentage implements Serializable, Cloneable {
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + value;
|
||||
result = prime * result + ((value == null) ? 0 : value.stripTrailingZeros().hashCode());
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -58,14 +67,19 @@ public class Percentage implements Serializable, Cloneable {
|
||||
if (getClass() != obj.getClass())
|
||||
return false;
|
||||
Percentage other = (Percentage) obj;
|
||||
if (value != other.value)
|
||||
if (value == null) {
|
||||
if (other.value != null)
|
||||
return false;
|
||||
} else if (value.compareTo(other.value) != 0)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return value + "%";
|
||||
if (value == null) return "null%";
|
||||
BigDecimal stripped = value.stripTrailingZeros();
|
||||
return String.format("%s%%", stripped.toPlainString());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+5
-5
@@ -59,19 +59,19 @@ public abstract class AbstractTransparentComponent<T> extends AbstractComponent<
|
||||
|
||||
public Composite applyAlpha(Graphics2D g2d, ComponentState componentState) {
|
||||
Composite oldComposite = g2d.getComposite();
|
||||
int alpha = (OUTLINE_MODE_WHILE_DRAGGING && componentState == ComponentState.DRAGGING) ? 0 :
|
||||
getAlpha().getValue();
|
||||
double alpha = (OUTLINE_MODE_WHILE_DRAGGING && componentState == ComponentState.DRAGGING) ? 0 :
|
||||
getAlpha().getValue().doubleValue();
|
||||
if (alpha < MAX_ALPHA) {
|
||||
g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, alpha / 100f));
|
||||
g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, (float)(alpha / 100d)));
|
||||
}
|
||||
return oldComposite;
|
||||
}
|
||||
|
||||
public Composite applyAlpha(Graphics2D g2d) {
|
||||
Composite oldComposite = g2d.getComposite();
|
||||
int alpha = getAlpha().getValue();
|
||||
double alpha = getAlpha().getValue().doubleValue();
|
||||
if (alpha < MAX_ALPHA) {
|
||||
g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, alpha / 100f));
|
||||
g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, (float)(alpha / 100d)));
|
||||
}
|
||||
return oldComposite;
|
||||
}
|
||||
|
||||
@@ -594,7 +594,7 @@ public class DrawingManager {
|
||||
try {
|
||||
g2dWrapper.transform(mirrorTx);
|
||||
if (Boolean.TRUE.equals(board.getUndersideTransparency())) {
|
||||
g2dWrapper.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, MIRROR_ALPHA.getValue() / 100f));
|
||||
g2dWrapper.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, (float)(MIRROR_ALPHA.getValue().doubleValue() / 100d)));
|
||||
}
|
||||
component.draw(g2dWrapper, state, outlineMode, project, g2dWrapper);
|
||||
} catch (Exception e) {
|
||||
|
||||
@@ -1,73 +1,5 @@
|
||||
<java.util.Arrays_-ArrayList>
|
||||
<a class="org.diylc.appframework.update.Version-array">
|
||||
<org.diylc.appframework.update.Version>
|
||||
<versionNumber>
|
||||
<major>5</major>
|
||||
<minor>9</minor>
|
||||
<build>0</build>
|
||||
</versionNumber>
|
||||
<releaseDate>2026-01-10 00:00:00.000 CET</releaseDate>
|
||||
<name></name>
|
||||
<changes>
|
||||
<org.diylc.appframework.update.Change>
|
||||
<changeType>NEW_FEATURE</changeType>
|
||||
<description>Added ability to name groups of components</description>
|
||||
</org.diylc.appframework.update.Change>
|
||||
<org.diylc.appframework.update.Change>
|
||||
<changeType>NEW_FEATURE</changeType>
|
||||
<description>Show groups of components as single items in the Project Explorer list</description>
|
||||
</org.diylc.appframework.update.Change>
|
||||
<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>
|
||||
<org.diylc.appframework.update.Version>
|
||||
<versionNumber>
|
||||
<major>5</major>
|
||||
@@ -428,5 +360,25 @@
|
||||
</changes>
|
||||
<url>https://github.com/bancika/diy-layout-creator/releases</url>
|
||||
</org.diylc.appframework.update.Version>
|
||||
<org.diylc.appframework.update.Version>
|
||||
<versionNumber>
|
||||
<major>6</major>
|
||||
<minor>4</minor>
|
||||
<build>0</build>
|
||||
</versionNumber>
|
||||
<releaseDate>2026-08-22 00:00:00.000 CET</releaseDate>
|
||||
<name></name>
|
||||
<changes>
|
||||
<org.diylc.appframework.update.Change>
|
||||
<changeType>IMPROVEMENT</changeType>
|
||||
<description>Percentage values such as transparency Alpha, image Scale, etc, can have decimal values and not just integers</description>
|
||||
</org.diylc.appframework.update.Change>
|
||||
<org.diylc.appframework.update.Change>
|
||||
<changeType>IMPROVEMENT</changeType>
|
||||
<description>Made announcement bubbles and dialogs that contain links clickable</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>
|
||||
|
||||
@@ -5257,5 +5257,73 @@
|
||||
</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>2026-01-10 00:00:00.000 CET</releaseDate>
|
||||
<name></name>
|
||||
<changes>
|
||||
<org.diylc.appframework.update.Change>
|
||||
<changeType>NEW_FEATURE</changeType>
|
||||
<description>Added ability to name groups of components</description>
|
||||
</org.diylc.appframework.update.Change>
|
||||
<org.diylc.appframework.update.Change>
|
||||
<changeType>NEW_FEATURE</changeType>
|
||||
<description>Show groups of components as single items in the Project Explorer list</description>
|
||||
</org.diylc.appframework.update.Change>
|
||||
<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>
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
<parent>
|
||||
<groupId>org.diylc</groupId>
|
||||
<artifactId>diylc-parent</artifactId>
|
||||
<version>6.3.0</version>
|
||||
<version>6.4.0</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>diylc-library</artifactId>
|
||||
|
||||
@@ -122,7 +122,7 @@ public class Image extends AbstractTransparentComponent<Void> {
|
||||
double x;
|
||||
double y;
|
||||
if (getSizingMode() == ImageSizingMode.Scale) {
|
||||
scaleX = scaleY = 1d * getScale().getValue() / 100d;
|
||||
scaleX = scaleY = 1d * getScale().getValue().doubleValue() / 100d;
|
||||
x = point.getX();
|
||||
y = point.getY();
|
||||
} else {
|
||||
@@ -299,7 +299,7 @@ public class Image extends AbstractTransparentComponent<Void> {
|
||||
if (getSizingMode() == ImageSizingMode.TwoPoints) {
|
||||
finalSecondPoint = getControlPoint(1);
|
||||
} else {
|
||||
double scale = 1d * getScale().getValue() / 100d;
|
||||
double scale = 1d * getScale().getValue().doubleValue() / 100d;
|
||||
ImageIcon iconImage = getImage();
|
||||
double w = (iconImage != null && iconImage.getIconWidth() > 0) ? iconImage.getIconWidth() * scale : 0;
|
||||
double h = (iconImage != null && iconImage.getIconHeight() > 0) ? iconImage.getIconHeight() * scale : 0;
|
||||
|
||||
@@ -105,7 +105,7 @@ public class SVGImage extends AbstractTransparentComponent<Void> {
|
||||
double x;
|
||||
double y;
|
||||
if (getSizingMode() == ImageSizingMode.Scale) {
|
||||
scaleX = scaleY = 1d * getScale().getValue() / 100d;
|
||||
scaleX = scaleY = 1d * getScale().getValue().doubleValue() / 100d;
|
||||
x = point.getX();
|
||||
y = point.getY();
|
||||
} else {
|
||||
@@ -290,7 +290,7 @@ public class SVGImage extends AbstractTransparentComponent<Void> {
|
||||
if (getSizingMode() == ImageSizingMode.TwoPoints) {
|
||||
finalSecondPoint = getControlPoint(1);
|
||||
} else {
|
||||
double scale = 1d * getScale().getValue() / 100d;
|
||||
double scale = 1d * getScale().getValue().doubleValue() / 100d;
|
||||
SVGDiagram diagram = getSvgDiagram();
|
||||
double w = (diagram != null && diagram.getWidth() > 0) ? diagram.getWidth() * scale : 0;
|
||||
double h = (diagram != null && diagram.getHeight() > 0) ? diagram.getHeight() * scale : 0;
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
<parent>
|
||||
<groupId>org.diylc</groupId>
|
||||
<artifactId>diylc-parent</artifactId>
|
||||
<version>6.3.0</version>
|
||||
<version>6.4.0</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>diylc-swing</artifactId>
|
||||
|
||||
@@ -0,0 +1,148 @@
|
||||
/*
|
||||
|
||||
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.gui;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.Cursor;
|
||||
import java.awt.Font;
|
||||
import java.awt.event.MouseAdapter;
|
||||
import java.awt.event.MouseEvent;
|
||||
import javax.swing.BorderFactory;
|
||||
import javax.swing.JComponent;
|
||||
import javax.swing.JEditorPane;
|
||||
import javax.swing.UIManager;
|
||||
import javax.swing.event.HyperlinkEvent;
|
||||
import javax.swing.event.HyperlinkListener;
|
||||
import org.apache.log4j.Logger;
|
||||
import org.diylc.announcements.AnnouncementUtils;
|
||||
import org.diylc.appframework.miscutils.Utils;
|
||||
|
||||
/**
|
||||
* Utility for creating interactive Swing HTML components with clickable links.
|
||||
*/
|
||||
public class ClickableHtmlUtils {
|
||||
|
||||
private static final Logger LOG = Logger.getLogger(ClickableHtmlUtils.class);
|
||||
|
||||
/**
|
||||
* Creates a JEditorPane configured to display HTML with clickable links and whole-text click navigation.
|
||||
*
|
||||
* @param html the HTML content to display
|
||||
* @param defaultUrlToOpen optional default URL to open when clicking non-hyperlink area of text (if null, extracted from html)
|
||||
* @param onLinkOpened optional callback executed when a link is opened
|
||||
* @return configured JEditorPane
|
||||
*/
|
||||
public static JEditorPane createClickableHtmlPane(String html, final String defaultUrlToOpen,
|
||||
final Runnable onLinkOpened) {
|
||||
JEditorPane editorPane = new JEditorPane();
|
||||
editorPane.setContentType("text/html");
|
||||
editorPane.setText(html != null ? html : "");
|
||||
editorPane.setEditable(false);
|
||||
editorPane.setOpaque(false);
|
||||
editorPane.setBackground(new Color(0, 0, 0, 0));
|
||||
editorPane.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0));
|
||||
editorPane.putClientProperty(JEditorPane.HONOR_DISPLAY_PROPERTIES, Boolean.TRUE);
|
||||
|
||||
final String targetUrl =
|
||||
defaultUrlToOpen != null ? defaultUrlToOpen : AnnouncementUtils.extractFirstUrl(html);
|
||||
final long[] lastClickTime = new long[1];
|
||||
|
||||
editorPane.addHyperlinkListener(new HyperlinkListener() {
|
||||
|
||||
@Override
|
||||
public void hyperlinkUpdate(HyperlinkEvent e) {
|
||||
if (HyperlinkEvent.EventType.ACTIVATED.equals(e.getEventType())) {
|
||||
lastClickTime[0] = System.currentTimeMillis();
|
||||
String url = e.getURL() != null ? e.getURL().toString() : e.getDescription();
|
||||
if (url != null) {
|
||||
openUrl(url);
|
||||
if (onLinkOpened != null) {
|
||||
onLinkOpened.run();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (targetUrl != null) {
|
||||
editorPane.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
|
||||
editorPane.addMouseListener(new MouseAdapter() {
|
||||
|
||||
@Override
|
||||
public void mouseClicked(MouseEvent e) {
|
||||
if (System.currentTimeMillis() - lastClickTime[0] > 500) {
|
||||
lastClickTime[0] = System.currentTimeMillis();
|
||||
openUrl(targetUrl);
|
||||
if (onLinkOpened != null) {
|
||||
onLinkOpened.run();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return editorPane;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a JComponent suitable for display inside JOptionPane message dialogs with clickable links.
|
||||
*
|
||||
* @param message HTML or text message
|
||||
* @return JComponent with clickable links
|
||||
*/
|
||||
public static JComponent createClickableMessageComponent(String message) {
|
||||
if (message == null || message.isEmpty()) {
|
||||
return new JEditorPane();
|
||||
}
|
||||
String html = message;
|
||||
if (!html.trim().toLowerCase().startsWith("<html>")) {
|
||||
html = "<html>" + html + "</html>";
|
||||
}
|
||||
JEditorPane pane = createClickableHtmlPane(html, null, null);
|
||||
Font font = UIManager.getFont("Label.font");
|
||||
if (font != null) {
|
||||
pane.setFont(font);
|
||||
}
|
||||
Color fg = UIManager.getColor("Label.foreground");
|
||||
if (fg != null) {
|
||||
pane.setForeground(fg);
|
||||
}
|
||||
return pane;
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens a URL in the system browser.
|
||||
*
|
||||
* @param url URL to open
|
||||
*/
|
||||
public static void openUrl(String url) {
|
||||
if (url == null || url.trim().isEmpty()) {
|
||||
return;
|
||||
}
|
||||
String normalized = AnnouncementUtils.normalizeUrl(url);
|
||||
try {
|
||||
Utils.openURL(normalized);
|
||||
} catch (Exception ex) {
|
||||
LOG.error("Could not open URL: " + normalized, ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -56,6 +56,7 @@ import javax.swing.Timer;
|
||||
import javax.swing.event.MenuEvent;
|
||||
import javax.swing.event.MenuListener;
|
||||
import org.apache.log4j.Logger;
|
||||
import org.diylc.announcements.AnnouncementUtils;
|
||||
import org.diylc.appframework.miscutils.ConfigurationManager;
|
||||
import org.diylc.appframework.miscutils.IConfigurationManager;
|
||||
import org.diylc.swing.IDynamicSubmenuHandler;
|
||||
@@ -288,7 +289,12 @@ public class MainFrame extends JFrame implements ISwingUI {
|
||||
|
||||
@Override
|
||||
public void showMessage(String message, String title, int messageType) {
|
||||
JOptionPane.showMessageDialog(this, message, title, messageType);
|
||||
if (message != null && (message.toLowerCase().contains("<html>") || AnnouncementUtils.containsLink(message))) {
|
||||
JComponent component = ClickableHtmlUtils.createClickableMessageComponent(message);
|
||||
JOptionPane.showMessageDialog(this, component, title, messageType);
|
||||
} else {
|
||||
JOptionPane.showMessageDialog(this, message, title, messageType);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -25,6 +25,7 @@ import java.awt.Color;
|
||||
import java.awt.FlowLayout;
|
||||
import java.awt.event.KeyAdapter;
|
||||
import java.awt.event.KeyEvent;
|
||||
import java.math.BigDecimal;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JSlider;
|
||||
@@ -46,6 +47,7 @@ public class PercentageEditor extends JPanel {
|
||||
|
||||
private int minValue = 0;
|
||||
private int maxValue = 100;
|
||||
private boolean isUpdating = false;
|
||||
|
||||
public PercentageEditor(final PropertyWrapper property) {
|
||||
super();
|
||||
@@ -69,39 +71,71 @@ public class PercentageEditor extends JPanel {
|
||||
valueField.setEnabled(false);
|
||||
}
|
||||
|
||||
slider.setMinimum(minValue);
|
||||
slider.setMaximum(maxValue);
|
||||
slider.setMinimum(minValue * 100);
|
||||
slider.setMaximum(maxValue * 100);
|
||||
slider.setMinorTickSpacing(100);
|
||||
|
||||
// Initialize values BEFORE adding the listener to avoid overwriting the property with integer
|
||||
Object val = property.getValue();
|
||||
if (val != null) {
|
||||
BigDecimal bdVal = ((Percentage) val).getValue();
|
||||
if (bdVal == null) bdVal = BigDecimal.ZERO;
|
||||
slider.setValue((int) Math.round(bdVal.doubleValue() * 100));
|
||||
valueField.setText(bdVal.stripTrailingZeros().toPlainString());
|
||||
} else {
|
||||
slider.setValue(minValue * 100);
|
||||
valueField.setText(Integer.toString(minValue));
|
||||
}
|
||||
|
||||
slider.addMouseListener(new java.awt.event.MouseAdapter() {
|
||||
@Override
|
||||
public void mousePressed(java.awt.event.MouseEvent e) {
|
||||
slider.setSnapToTicks(true);
|
||||
}
|
||||
@Override
|
||||
public void mouseReleased(java.awt.event.MouseEvent e) {
|
||||
slider.setSnapToTicks(false);
|
||||
}
|
||||
});
|
||||
|
||||
slider.addChangeListener(new ChangeListener() {
|
||||
|
||||
@Override
|
||||
public void stateChanged(ChangeEvent e) {
|
||||
property.setChanged(true);
|
||||
setBackground(oldBg);
|
||||
slider.setBackground(oldBg);
|
||||
|
||||
property.setValue(new Percentage(slider.getValue()));
|
||||
valueField.setText(Integer.toString(slider.getValue()));
|
||||
if (isUpdating) return;
|
||||
isUpdating = true;
|
||||
try {
|
||||
property.setChanged(true);
|
||||
setBackground(oldBg);
|
||||
slider.setBackground(oldBg);
|
||||
BigDecimal bd = BigDecimal.valueOf(slider.getValue()).divide(BigDecimal.valueOf(100)).stripTrailingZeros();
|
||||
property.setValue(new Percentage(bd));
|
||||
valueField.setText(bd.toPlainString());
|
||||
} finally {
|
||||
isUpdating = false;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
Object val = property.getValue();
|
||||
if (val != null) {
|
||||
slider.setValue(((Percentage) val).getValue());
|
||||
} else {
|
||||
slider.setValue(minValue);
|
||||
}
|
||||
|
||||
valueField.setText(Integer.toString(slider.getValue()));
|
||||
valueField.setColumns(3);
|
||||
valueField.setColumns(4);
|
||||
valueField.addKeyListener(new KeyAdapter() {
|
||||
|
||||
@Override
|
||||
public void keyReleased(KeyEvent e) {
|
||||
try {
|
||||
int newPosition = (int) Double.parseDouble(valueField.getText());
|
||||
double newPosition = Double.parseDouble(valueField.getText());
|
||||
if (newPosition >= minValue && newPosition <= maxValue) {
|
||||
slider.setValue(newPosition);
|
||||
isUpdating = true;
|
||||
try {
|
||||
slider.setValue((int) Math.round(newPosition * 100));
|
||||
property.setChanged(true);
|
||||
setBackground(oldBg);
|
||||
slider.setBackground(oldBg);
|
||||
// Use string parsing directly to avoid double floating point inaccuracy
|
||||
property.setValue(new Percentage(new BigDecimal(valueField.getText().trim())));
|
||||
} finally {
|
||||
isUpdating = false;
|
||||
}
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
}
|
||||
|
||||
@@ -59,14 +59,19 @@ import org.diylc.swingframework.MemoryBar;
|
||||
import org.diylc.swingframework.miscutils.PercentageListCellRenderer;
|
||||
import org.diylc.swingframework.update.UpdateDialog;
|
||||
import org.diylc.swingframework.update.UpdateLabel;
|
||||
import java.awt.Color;
|
||||
import java.awt.Font;
|
||||
import javax.swing.JEditorPane;
|
||||
import net.java.balloontip.BalloonTip;
|
||||
import net.java.balloontip.styles.EdgedBalloonStyle;
|
||||
import org.diylc.announcements.AnnouncementProvider;
|
||||
import org.diylc.announcements.AnnouncementUtils;
|
||||
import org.diylc.core.IDIYComponent;
|
||||
import org.diylc.core.IView;
|
||||
import org.diylc.lang.LangUtil;
|
||||
import org.diylc.presenter.Presenter;
|
||||
import org.diylc.swing.ISwingUI;
|
||||
import org.diylc.swing.gui.ClickableHtmlUtils;
|
||||
import org.diylc.utils.IconLoader;
|
||||
|
||||
public class StatusBar extends JPanel implements IPlugIn {
|
||||
@@ -172,8 +177,7 @@ public class StatusBar extends JPanel implements IPlugIn {
|
||||
@Override
|
||||
public void complete(String result) {
|
||||
if (result != null && result.length() > 0) {
|
||||
new BalloonTip(getUpdateLabel(), result, new EdgedBalloonStyle(UIManager.getColor("ToolTip.background"),
|
||||
UIManager.getColor("ToolTip.foreground")), true);
|
||||
showAnnouncementBalloon(result);
|
||||
announcementProvider.dismissed();
|
||||
}
|
||||
}
|
||||
@@ -568,4 +572,51 @@ public class StatusBar extends JPanel implements IPlugIn {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void showAnnouncementBalloon(String result) {
|
||||
String html = result;
|
||||
final String firstUrl = AnnouncementUtils.extractFirstUrl(html);
|
||||
final BalloonTip[] balloonTipHolder = new BalloonTip[1];
|
||||
|
||||
JEditorPane pane = ClickableHtmlUtils.createClickableHtmlPane(html, firstUrl, new Runnable() {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
if (balloonTipHolder[0] != null) {
|
||||
balloonTipHolder[0].closeBalloon();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
Font font = UIManager.getFont("ToolTip.font");
|
||||
if (font == null) {
|
||||
font = UIManager.getFont("Label.font");
|
||||
}
|
||||
if (font != null) {
|
||||
pane.setFont(font);
|
||||
}
|
||||
Color fg = UIManager.getColor("ToolTip.foreground");
|
||||
if (fg != null) {
|
||||
pane.setForeground(fg);
|
||||
}
|
||||
|
||||
EdgedBalloonStyle style = new EdgedBalloonStyle(
|
||||
UIManager.getColor("ToolTip.background"),
|
||||
UIManager.getColor("ToolTip.foreground"));
|
||||
|
||||
final BalloonTip balloonTip = new BalloonTip(getUpdateLabel(), pane, style, true);
|
||||
balloonTipHolder[0] = balloonTip;
|
||||
|
||||
if (firstUrl != null) {
|
||||
balloonTip.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
|
||||
balloonTip.addMouseListener(new MouseAdapter() {
|
||||
|
||||
@Override
|
||||
public void mouseClicked(MouseEvent e) {
|
||||
ClickableHtmlUtils.openUrl(firstUrl);
|
||||
balloonTip.closeBalloon();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -6,7 +6,7 @@
|
||||
|
||||
<groupId>org.diylc</groupId>
|
||||
<artifactId>diylc-parent</artifactId>
|
||||
<version>6.3.0</version>
|
||||
<version>6.4.0</version>
|
||||
<packaging>pom</packaging>
|
||||
|
||||
<name>DIY Layout Creator</name>
|
||||
|
||||
Reference in New Issue
Block a user