Browse Source
Merge in ~CLOUD.LIU/design from feature/x to research/11.0 * commit '1a584a9a6b57e7f2c0330370ffa8a16d2a6ba79f': (1579 commits) 常量命名规范修复 REPORT-82787回退 REPORT-92147 REPORT-92159问题修复 REPORT-82787 图表空数据提示配置页面,默认图片需补充繁中版 REPORT-92134,REPORT-92120,REPORT-92123 问题修复 REPORT-89253 命名和读取规范化处理,回退右下角异常小标记 REPORT-89253 修正插入悬浮元素图标项文件路径 REPORT-89253 设计器替换高清svg图标第2次提交 REPORT-91610 数据脱敏-设计器内en下面板文案显示不全 【问题原因】en、ja、ko下面板文案显示不全 【改动思路】与产品沟通后,调大这几个语言下弹窗的宽度 【review建议】 REPORT-89253 设计器替换高清svg图标-第1次提交 REPORT-91657 设计器单元格属性-其他面板文本显示截断 【问题原因】不同语言显示长度不同 【改动思路】选项面板水平布局修改为垂直布局 REPORT-91743 设计器通过关闭模板A的方式触发保存,设计器页面显示还是模板A的内容 REPORT-91743 设计器通过关闭模板A的方式触发保存,设计器页面显示还是模板A的内容 REPORT-91486 不打开任何报表进入设计器,选项弹窗触发下保存,模板tab栏会自动弹出来 REPORT-91591 单元格控件的自定义样式设置--代码格式调整 REPORT-91316 不打开任何报表的情况下,编辑工具栏点不动 REPORT-91591 单元格控件的自定义样式设置 REPORT-91591 单元格控件的自定义样式设置 REPORT-80651 模板版本管理重构一期 - 优化代码结构 REPORT-80651 模板版本管理重构一期 - 修改代码 ...research/11.0
Cloud.Liu-刘学真
2 years ago
1743 changed files with 94487 additions and 27062 deletions
@ -0,0 +1,24 @@
|
||||
package com.fr.base.function; |
||||
|
||||
import com.fr.log.FineLoggerFactory; |
||||
|
||||
/** |
||||
* 可抛出异常的 Runnable |
||||
* |
||||
* created by Harrison on 2022/05/24 |
||||
**/ |
||||
public interface ThrowableRunnable<T extends Exception> { |
||||
|
||||
void run() throws T; |
||||
|
||||
static <T extends Exception> Runnable toRunnable(ThrowableRunnable<T> runnable) { |
||||
|
||||
return () -> { |
||||
try { |
||||
runnable.run(); |
||||
} catch (Exception e) { |
||||
FineLoggerFactory.getLogger().error(e.getMessage(), e); |
||||
} |
||||
}; |
||||
} |
||||
} |
@ -0,0 +1,24 @@
|
||||
package com.fr.base.function; |
||||
|
||||
import com.fr.design.utils.DesignUtils; |
||||
import com.fr.runtime.FineRuntime; |
||||
|
||||
/** |
||||
* UI 终止动作 |
||||
* |
||||
* created by Harrison on 2022/07/14 |
||||
**/ |
||||
public abstract class UITerminator { |
||||
|
||||
public void run() { |
||||
|
||||
// 先执行必须的逻辑
|
||||
FineRuntime.start(); |
||||
DesignUtils.initLookAndFeel(); |
||||
|
||||
// 在执行核心逻辑
|
||||
doRun(); |
||||
} |
||||
|
||||
protected abstract void doRun(); |
||||
} |
@ -1,92 +0,0 @@
|
||||
package com.fr.base.svg; |
||||
|
||||
import com.fr.general.IOUtils; |
||||
import org.apache.batik.transcoder.TranscoderException; |
||||
import org.apache.batik.transcoder.TranscoderInput; |
||||
import org.apache.xmlgraphics.java2d.Dimension2DDouble; |
||||
import org.jetbrains.annotations.NotNull; |
||||
import org.jetbrains.annotations.Nullable; |
||||
|
||||
import java.awt.Image; |
||||
import java.io.IOException; |
||||
import java.net.URL; |
||||
|
||||
/** |
||||
* SVG图标加载器 |
||||
* @author Yvan |
||||
* @version 10.0 |
||||
* Created by Yvan on 2020/12/17 |
||||
*/ |
||||
public class SVGLoader { |
||||
public static final int ICON_DEFAULT_SIZE = 16; |
||||
|
||||
public SVGLoader() { |
||||
} |
||||
|
||||
@Nullable |
||||
public static Image load(@NotNull String url) { |
||||
try { |
||||
URL resource = IOUtils.getResource(url, SVGLoader.class); |
||||
if (resource == null) { |
||||
return null; |
||||
} |
||||
return load(resource, SVGIcon.SYSTEM_SCALE); |
||||
} catch (IOException ignore) { |
||||
return null; |
||||
} |
||||
} |
||||
|
||||
@Nullable |
||||
public static Image load(@NotNull URL url) throws IOException { |
||||
return load(url, SVGIcon.SYSTEM_SCALE); |
||||
} |
||||
|
||||
@Nullable |
||||
public static Image load(@NotNull URL url, double scale) throws IOException { |
||||
try { |
||||
String svgUri = url.toString(); |
||||
TranscoderInput input = new TranscoderInput(svgUri); |
||||
return SVGTranscoder.createImage(scale, input).getImage(); |
||||
} catch (TranscoderException ignore) { |
||||
return null; |
||||
} |
||||
} |
||||
|
||||
@Nullable |
||||
public static Image load(@NotNull URL url, double scale, Dimension2DDouble dimension) throws IOException { |
||||
try { |
||||
String svgUri = url.toString(); |
||||
TranscoderInput input = new TranscoderInput(svgUri); |
||||
return SVGTranscoder.createImage(scale, input, |
||||
(float) (dimension.getWidth() * scale), (float) (dimension.getHeight() * scale)).getImage(); |
||||
} catch (TranscoderException ignore) { |
||||
return null; |
||||
} |
||||
} |
||||
|
||||
|
||||
@Nullable |
||||
public static Image load(@NotNull URL url, double scale, double overriddenWidth, double overriddenHeight) throws IOException { |
||||
try { |
||||
String svgUri = url.toString(); |
||||
TranscoderInput input = new TranscoderInput(svgUri); |
||||
return SVGTranscoder.createImage(scale, input, (float) (overriddenWidth * scale), (float) (overriddenHeight * scale)).getImage(); |
||||
} catch (TranscoderException ignore) { |
||||
return null; |
||||
} |
||||
} |
||||
|
||||
@Nullable |
||||
public static Image load(@NotNull String url, float width, float height) { |
||||
try { |
||||
URL resource = IOUtils.getResource(url, SVGLoader.class); |
||||
if (resource == null) { |
||||
return null; |
||||
} |
||||
TranscoderInput input = new TranscoderInput(resource.toString()); |
||||
return SVGTranscoder.createImage(SVGIcon.SYSTEM_SCALE, input, -1, -1, width, height).getImage(); |
||||
} catch (TranscoderException ignore) { |
||||
return null; |
||||
} |
||||
} |
||||
} |
@ -1,181 +0,0 @@
|
||||
package com.fr.base.svg; |
||||
|
||||
import com.fr.stable.AssistUtils; |
||||
import com.fr.value.AtomicNotNullLazyValue; |
||||
import org.apache.batik.anim.dom.SAXSVGDocumentFactory; |
||||
import org.apache.batik.anim.dom.SVGOMDocument; |
||||
import org.apache.batik.bridge.BridgeContext; |
||||
import org.apache.batik.bridge.UserAgent; |
||||
import org.apache.batik.transcoder.SVGAbstractTranscoder; |
||||
import org.apache.batik.transcoder.TranscoderException; |
||||
import org.apache.batik.transcoder.TranscoderInput; |
||||
import org.apache.batik.transcoder.TranscoderOutput; |
||||
import org.apache.batik.transcoder.image.ImageTranscoder; |
||||
import org.apache.batik.util.XMLResourceDescriptor; |
||||
import org.jetbrains.annotations.NotNull; |
||||
import org.jetbrains.annotations.Nullable; |
||||
import org.w3c.dom.Element; |
||||
import org.w3c.dom.svg.SVGDocument; |
||||
|
||||
import java.awt.GraphicsDevice; |
||||
import java.awt.GraphicsEnvironment; |
||||
import java.awt.Rectangle; |
||||
import java.awt.geom.AffineTransform; |
||||
import java.awt.image.BufferedImage; |
||||
import java.io.IOException; |
||||
import java.io.StringReader; |
||||
|
||||
/** |
||||
* 可以根据某个缩放倍数scale,将SVG图片转化为Image对象 |
||||
* @author Yvan |
||||
* @version 10.0 |
||||
* Created by Yvan on 2020/12/17 |
||||
*/ |
||||
public class SVGTranscoder extends ImageTranscoder { |
||||
|
||||
private static final float DEFAULT_VALUE = -1.0F; |
||||
public static final float ICON_DEFAULT_SIZE = 16F; |
||||
private float origDocWidth; |
||||
private float origDocHeight; |
||||
@Nullable |
||||
private BufferedImage image; |
||||
private final double scale; |
||||
|
||||
@NotNull |
||||
private static AtomicNotNullLazyValue<Double> iconMaxSize = new AtomicNotNullLazyValue<Double>() { |
||||
@NotNull |
||||
@Override |
||||
protected Double compute() { |
||||
double maxSize = Double.MAX_VALUE; |
||||
if (!GraphicsEnvironment.isHeadless()) { |
||||
GraphicsDevice defaultScreenDevice = GraphicsEnvironment |
||||
.getLocalGraphicsEnvironment() |
||||
.getDefaultScreenDevice(); |
||||
Rectangle bounds = defaultScreenDevice.getDefaultConfiguration().getBounds(); |
||||
AffineTransform tx = defaultScreenDevice |
||||
.getDefaultConfiguration() |
||||
.getDefaultTransform(); |
||||
maxSize = Math.max(bounds.width * tx.getScaleX(), bounds.height * tx.getScaleY()); |
||||
} |
||||
return maxSize; |
||||
} |
||||
}; |
||||
|
||||
public SVGTranscoder(double scale) { |
||||
this.scale = scale; |
||||
this.width = ICON_DEFAULT_SIZE; |
||||
this.height = ICON_DEFAULT_SIZE; |
||||
} |
||||
|
||||
public SVGTranscoder(double scale, float width, float height) { |
||||
this.scale = scale; |
||||
this.width = width; |
||||
this.height = height; |
||||
} |
||||
|
||||
public final float getOrigDocWidth() { |
||||
return this.origDocWidth; |
||||
} |
||||
|
||||
public final void setOrigDocWidth(float origDocWidth) { |
||||
this.origDocWidth = origDocWidth; |
||||
} |
||||
|
||||
public final float getOrigDocHeight() { |
||||
return this.origDocHeight; |
||||
} |
||||
|
||||
public final void setOrigDocHeight(float origDocHeight) { |
||||
this.origDocHeight = origDocHeight; |
||||
} |
||||
|
||||
public static double getIconMaxSize() { |
||||
return iconMaxSize.getValue(); |
||||
} |
||||
|
||||
@Nullable |
||||
public final BufferedImage getImage() { |
||||
return this.image; |
||||
} |
||||
|
||||
@NotNull |
||||
public static SVGTranscoder createImage(double scale, @NotNull TranscoderInput input) throws TranscoderException { |
||||
return createImage(scale, input, -1, -1); |
||||
} |
||||
|
||||
@NotNull |
||||
public static SVGTranscoder createImage(double scale, @NotNull TranscoderInput input, float overriddenWidth, float overriddenHeight) throws TranscoderException { |
||||
return createImage(scale, input, overriddenWidth, overriddenHeight, ICON_DEFAULT_SIZE, ICON_DEFAULT_SIZE); |
||||
} |
||||
|
||||
@NotNull |
||||
public static SVGTranscoder createImage(double scale, @NotNull TranscoderInput input, float overriddenWidth, float overriddenHeight, float width, float height) throws TranscoderException { |
||||
SVGTranscoder transcoder = new SVGTranscoder(scale, width, height); |
||||
if (!AssistUtils.equals(overriddenWidth, DEFAULT_VALUE)) { |
||||
transcoder.addTranscodingHint(SVGAbstractTranscoder.KEY_WIDTH, overriddenWidth); |
||||
} |
||||
|
||||
if (!AssistUtils.equals(overriddenHeight, DEFAULT_VALUE)) { |
||||
transcoder.addTranscodingHint(SVGAbstractTranscoder.KEY_HEIGHT, overriddenHeight); |
||||
} |
||||
|
||||
double iconMaxSize = SVGTranscoder.iconMaxSize.getValue(); |
||||
transcoder.addTranscodingHint(SVGAbstractTranscoder.KEY_MAX_WIDTH, (float) iconMaxSize); |
||||
transcoder.addTranscodingHint(SVGAbstractTranscoder.KEY_MAX_HEIGHT, (float) iconMaxSize); |
||||
transcoder.transcode(input, null); |
||||
return transcoder; |
||||
} |
||||
|
||||
private static SVGDocument createFallbackPlaceholder() { |
||||
try { |
||||
String fallbackIcon = "<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"16\" height=\"16\" viewBox=\"0 0 16 16\">\n" + |
||||
" <rect x=\"1\" y=\"1\" width=\"14\" height=\"14\" fill=\"none\" stroke=\"red\" stroke-width=\"2\"/>\n" + |
||||
" <line x1=\"1\" y1=\"1\" x2=\"15\" y2=\"15\" stroke=\"red\" stroke-width=\"2\"/>\n" + |
||||
" <line x1=\"1\" y1=\"15\" x2=\"15\" y2=\"1\" stroke=\"red\" stroke-width=\"2\"/>\n" + |
||||
"</svg>\n"; |
||||
|
||||
SAXSVGDocumentFactory factory = new SAXSVGDocumentFactory(XMLResourceDescriptor.getXMLParserClassName()); |
||||
return (SVGDocument) factory.createDocument(null, new StringReader(fallbackIcon)); |
||||
} catch (IOException e) { |
||||
throw new IllegalStateException(e); |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
protected void setImageSize(float docWidth, float docHeight) { |
||||
super.setImageSize((float) (docWidth * this.scale), (float) (docHeight * this.scale)); |
||||
this.origDocWidth = docWidth; |
||||
this.origDocHeight = docHeight; |
||||
} |
||||
|
||||
@Override |
||||
@NotNull |
||||
public BufferedImage createImage(int width, int height) { |
||||
return new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); |
||||
} |
||||
|
||||
@Override |
||||
public void writeImage(@NotNull BufferedImage image, @Nullable TranscoderOutput output) { |
||||
this.image = image; |
||||
} |
||||
|
||||
@Override |
||||
@NotNull |
||||
protected UserAgent createUserAgent() { |
||||
return new SVGAbstractTranscoderUserAgent() { |
||||
@Override |
||||
@NotNull |
||||
public SVGDocument getBrokenLinkDocument(@NotNull Element e, @NotNull String url, @NotNull String message) { |
||||
return createFallbackPlaceholder(); |
||||
} |
||||
}; |
||||
} |
||||
|
||||
/** |
||||
* 开放访问权限 |
||||
*/ |
||||
@Override |
||||
public BridgeContext createBridgeContext(SVGOMDocument doc) { |
||||
return super.createBridgeContext(doc); |
||||
} |
||||
} |
@ -1,101 +0,0 @@
|
||||
package com.fr.base.svg; |
||||
|
||||
import com.bulenkov.iconloader.util.UIUtil; |
||||
import com.fr.log.FineLoggerFactory; |
||||
import com.fr.stable.StableUtils; |
||||
import com.fr.stable.os.OperatingSystem; |
||||
import org.jetbrains.annotations.NotNull; |
||||
|
||||
import java.awt.GraphicsConfiguration; |
||||
import java.awt.GraphicsDevice; |
||||
import java.awt.GraphicsEnvironment; |
||||
import java.lang.reflect.Method; |
||||
import java.util.concurrent.atomic.AtomicReference; |
||||
|
||||
/** |
||||
* 获取系统Scale相关的工具类 |
||||
* @author Yvan |
||||
* @version 10.0 |
||||
* Created by Yvan on 2020/12/17 |
||||
*/ |
||||
public class SystemScaleUtils { |
||||
|
||||
private static final AtomicReference<Boolean> JRE_HIDPI = new AtomicReference<>(); |
||||
|
||||
private static final String HI_DPI = "hidpi"; |
||||
|
||||
/** |
||||
* 判断是否支持高清 |
||||
* @return |
||||
*/ |
||||
public static boolean isJreHiDPIEnabled() { |
||||
if (JRE_HIDPI.get() != null) { |
||||
return JRE_HIDPI.get(); |
||||
} |
||||
if (OperatingSystem.isMacos()) { |
||||
// 如果是mac os系统,直接返回true
|
||||
return true; |
||||
} |
||||
if (OperatingSystem.isWindows() && StableUtils.getMajorJavaVersion() <= 8) { |
||||
// 如果是jdk8 + Windows系统,直接返回false
|
||||
return false; |
||||
} |
||||
synchronized (JRE_HIDPI) { |
||||
if (JRE_HIDPI.get() != null) { |
||||
return JRE_HIDPI.get(); |
||||
} |
||||
boolean result = false; |
||||
if (getBooleanProperty(HI_DPI, true)) { |
||||
try { |
||||
GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment(); |
||||
Class<?> sunGraphicsEnvironmentClass = Class.forName("sun.java2d.SunGraphicsEnvironment"); |
||||
if (sunGraphicsEnvironmentClass.isInstance(ge)) { |
||||
try { |
||||
Method method = sunGraphicsEnvironmentClass.getDeclaredMethod("isUIScaleEnabled"); |
||||
method.setAccessible(true); |
||||
result = (Boolean)method.invoke(ge); |
||||
} |
||||
catch (NoSuchMethodException e) { |
||||
FineLoggerFactory.getLogger().error(e.getMessage()); |
||||
} |
||||
} |
||||
} |
||||
catch (Throwable ignore) { |
||||
} |
||||
} |
||||
JRE_HIDPI.set(result); |
||||
return result; |
||||
} |
||||
} |
||||
|
||||
public static boolean getBooleanProperty(@NotNull final String key, final boolean defaultValue) { |
||||
final String value = System.getProperty(key); |
||||
return value == null ? defaultValue : Boolean.parseBoolean(value); |
||||
} |
||||
|
||||
/** |
||||
* 获取系统Scale |
||||
* @return |
||||
*/ |
||||
public static float sysScale() { |
||||
// 如果检测到是retina,直接返回2
|
||||
if (UIUtil.isRetina()) { |
||||
return 2.0f; |
||||
} |
||||
float scale = 1.0f; |
||||
// 先判断是否支持高清,不支持代表此时是Windows + jdk8 的设计器,返回的scale值为1.0
|
||||
if (isJreHiDPIEnabled()) { |
||||
// 获取屏幕图形设备对象
|
||||
GraphicsDevice graphicsDevice = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice(); |
||||
if (graphicsDevice != null) { |
||||
// 获取图形配置对象
|
||||
GraphicsConfiguration configuration = graphicsDevice.getDefaultConfiguration(); |
||||
if (configuration != null && configuration.getDevice().getType() != GraphicsDevice.TYPE_PRINTER) { |
||||
// 获取屏幕缩放率,Windows+jdk11环境下会得到用户设置的dpi值
|
||||
scale = (float) configuration.getDefaultTransform().getScaleX(); |
||||
} |
||||
} |
||||
} |
||||
return scale; |
||||
} |
||||
} |
@ -0,0 +1,12 @@
|
||||
package com.fr.common.exception; |
||||
|
||||
/** |
||||
* @author hades |
||||
* @version 11.0 |
||||
* Created by hades on 2021/12/7 |
||||
*/ |
||||
public interface ThrowableHandler { |
||||
|
||||
boolean process(Throwable e); |
||||
|
||||
} |
@ -0,0 +1,193 @@
|
||||
package com.fr.design; |
||||
|
||||
import com.fr.concurrent.NamedThreadFactory; |
||||
import com.fr.general.CloudCenter; |
||||
import com.fr.general.CloudCenterConfig; |
||||
import com.fr.general.http.HttpToolbox; |
||||
import com.fr.log.FineLoggerFactory; |
||||
import com.fr.stable.ProductConstants; |
||||
import com.fr.stable.StableUtils; |
||||
import com.fr.stable.StringUtils; |
||||
import com.fr.stable.xml.XMLPrintWriter; |
||||
import com.fr.stable.xml.XMLReaderHelper; |
||||
import com.fr.stable.xml.XMLTools; |
||||
import com.fr.stable.xml.XMLable; |
||||
import com.fr.stable.xml.XMLableReader; |
||||
import com.fr.third.javax.xml.stream.XMLStreamException; |
||||
import com.fr.third.org.apache.commons.io.FileUtils; |
||||
|
||||
import java.io.ByteArrayOutputStream; |
||||
import java.io.File; |
||||
import java.io.FileInputStream; |
||||
import java.io.FileNotFoundException; |
||||
import java.io.IOException; |
||||
import java.io.InputStream; |
||||
import java.nio.charset.StandardCharsets; |
||||
import java.util.HashMap; |
||||
import java.util.Iterator; |
||||
import java.util.Map; |
||||
import java.util.concurrent.ExecutorService; |
||||
import java.util.concurrent.Executors; |
||||
|
||||
/** |
||||
* Created by kerry on 2021/10/22 |
||||
*/ |
||||
public class DesignerCloudURLManager implements XMLable { |
||||
private static final String CLOUD_URL_INFO = "cloudUrl.info"; |
||||
private static final String ROOT_XML_TAG = "CloudUrlInfoList"; |
||||
private static final String CHILD_XML_TAG = "CloudUrlInfo"; |
||||
private final Map<String, String> urlMap = new HashMap<>(); |
||||
|
||||
public static DesignerCloudURLManager getInstance() { |
||||
return DesignerCloudURLManager.HOLDER.singleton; |
||||
} |
||||
|
||||
private final ExecutorService executorService = Executors.newSingleThreadExecutor(new NamedThreadFactory("TestCloudConnectThread")); |
||||
|
||||
private volatile boolean testResult; |
||||
|
||||
|
||||
private static class HOLDER { |
||||
private static final DesignerCloudURLManager singleton = new DesignerCloudURLManager(); |
||||
} |
||||
|
||||
private DesignerCloudURLManager() { |
||||
loadURLXMLFile(); |
||||
} |
||||
|
||||
public String acquireUrlByKind(String key) { |
||||
String url = urlMap.getOrDefault(key, StringUtils.EMPTY); |
||||
if (StringUtils.isEmpty(url)) { |
||||
//本地缓存中为空时,直接从云中心获取,获取完成后异步更新本地缓存文件
|
||||
String latestUrl = CloudCenter.getInstance().acquireConf(key, StringUtils.EMPTY); |
||||
executorService.submit(() -> { |
||||
updateURLXMLFile(key, latestUrl); |
||||
}); |
||||
return latestUrl; |
||||
} |
||||
//本地缓存不为空时,直接返回对应 url,同时异步更新
|
||||
executorService.submit(() -> { |
||||
String latestUrl = CloudCenter.getInstance().acquireConf(key, StringUtils.EMPTY); |
||||
updateURLXMLFile(key, latestUrl); |
||||
}); |
||||
return url; |
||||
} |
||||
|
||||
private synchronized void updateURLXMLFile(String key, String url) { |
||||
if (StringUtils.isNotEmpty(url) && (!urlMap.containsKey(key) || !url.equals(urlMap.get(key)))) { |
||||
urlMap.put(key, url); |
||||
saveURLXMLFile(); |
||||
} |
||||
} |
||||
|
||||
|
||||
public void testConnect() { |
||||
executorService.submit(() -> { |
||||
testResult = isOnline(); |
||||
}); |
||||
} |
||||
|
||||
public boolean isConnected() { |
||||
return testResult; |
||||
} |
||||
|
||||
public boolean isOnline() { |
||||
if (CloudCenterConfig.getInstance().isOnline()) { |
||||
String ping = acquireUrlByKind("ping"); |
||||
if (StringUtils.isNotEmpty(ping)) { |
||||
try { |
||||
return StringUtils.isEmpty(HttpToolbox.get(ping)); |
||||
} catch (Exception ignore) { |
||||
} |
||||
} |
||||
} |
||||
return false; |
||||
} |
||||
|
||||
|
||||
/** |
||||
* 加载本地 url 管理文件 |
||||
*/ |
||||
private void loadURLXMLFile() { |
||||
if (!getInfoFile().exists()) { |
||||
return; |
||||
} |
||||
XMLableReader reader = null; |
||||
try (InputStream in = new FileInputStream(getInfoFile())) { |
||||
// XMLableReader 还是应该考虑实现 Closable 接口的,这样就能使用 try-with 语句了
|
||||
reader = XMLReaderHelper.createXMLableReader(in, XMLPrintWriter.XML_ENCODER); |
||||
if (reader == null) { |
||||
return; |
||||
} |
||||
reader.readXMLObject(this); |
||||
} catch (FileNotFoundException e) { |
||||
// do nothing
|
||||
} catch (XMLStreamException | IOException e) { |
||||
FineLoggerFactory.getLogger().error(e.getMessage(), e); |
||||
} finally { |
||||
try { |
||||
if (reader != null) { |
||||
reader.close(); |
||||
} |
||||
} catch (XMLStreamException e) { |
||||
FineLoggerFactory.getLogger().error(e.getMessage(), e); |
||||
} |
||||
} |
||||
} |
||||
|
||||
private File getInfoFile() { |
||||
|
||||
File file = new File(StableUtils.pathJoin(ProductConstants.getEnvHome(), CLOUD_URL_INFO)); |
||||
try { |
||||
if (!file.exists()) { |
||||
file.createNewFile(); |
||||
} |
||||
} catch (Exception ex) { |
||||
FineLoggerFactory.getLogger().error(ex.getMessage(), ex); |
||||
} |
||||
return file; |
||||
} |
||||
|
||||
/** |
||||
* 保存到本地 URL 管理文件中,存放在 .Finereport110 中 |
||||
*/ |
||||
void saveURLXMLFile() { |
||||
try { |
||||
ByteArrayOutputStream out = new ByteArrayOutputStream(); |
||||
XMLTools.writeOutputStreamXML(this, out); |
||||
out.flush(); |
||||
out.close(); |
||||
String fileContent = new String(out.toByteArray(), StandardCharsets.UTF_8); |
||||
FileUtils.writeStringToFile(getInfoFile(), fileContent, StandardCharsets.UTF_8); |
||||
} catch (Exception ex) { |
||||
FineLoggerFactory.getLogger().error(ex.getMessage()); |
||||
} |
||||
} |
||||
|
||||
|
||||
@Override |
||||
public void readXML(XMLableReader reader) { |
||||
String tagName = reader.getTagName(); |
||||
if (tagName.equals(CHILD_XML_TAG)) { |
||||
String key = reader.getAttrAsString("key", StringUtils.EMPTY); |
||||
String value = reader.getAttrAsString("url", StringUtils.EMPTY); |
||||
this.urlMap.put(key, value); |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
public void writeXML(XMLPrintWriter xmlPrintWriter) { |
||||
xmlPrintWriter.startTAG(ROOT_XML_TAG); |
||||
Iterator<Map.Entry<String, String>> iterable = urlMap.entrySet().iterator(); |
||||
while (iterable.hasNext()) { |
||||
Map.Entry<String, String> entry = iterable.next(); |
||||
xmlPrintWriter.startTAG(CHILD_XML_TAG).attr("key", entry.getKey()).attr("url", entry.getValue()).end(); |
||||
} |
||||
xmlPrintWriter.end(); |
||||
} |
||||
|
||||
@Override |
||||
public Object clone() throws CloneNotSupportedException { |
||||
return null; |
||||
} |
||||
} |
@ -0,0 +1,17 @@
|
||||
package com.fr.design.actions.community; |
||||
|
||||
import com.fr.design.i18n.Toolkit; |
||||
import com.fr.design.login.AbstractDesignerSSO; |
||||
import com.fr.general.CloudCenter; |
||||
|
||||
public class StudyPlanAction extends AbstractDesignerSSO { |
||||
public StudyPlanAction() { |
||||
this.setName(Toolkit.i18nText("Fine-Design_Study_Plan")); |
||||
this.setSmallIcon("/com/fr/design/images/bbs/studyPlan"); |
||||
} |
||||
|
||||
@Override |
||||
public String getJumpUrl() { |
||||
return CloudCenter.getInstance().acquireUrlByKind("bbs.studyPlan", "https://edu.fanruan.com/studypath/finereport"); |
||||
} |
||||
} |
@ -0,0 +1,50 @@
|
||||
package com.fr.design.actions.file; |
||||
|
||||
import com.fr.design.actions.UpdateAction; |
||||
import com.fr.design.dialog.FineJOptionPane; |
||||
import com.fr.design.file.FileOperations; |
||||
import com.fr.design.i18n.Toolkit; |
||||
import com.fr.design.mainframe.DesignerContext; |
||||
import com.fr.design.mainframe.DesignerFrameFileDealerPane; |
||||
import com.fr.design.utils.TemplateUtils; |
||||
|
||||
import javax.swing.JOptionPane; |
||||
import java.awt.event.ActionEvent; |
||||
|
||||
import static javax.swing.JOptionPane.WARNING_MESSAGE; |
||||
import static javax.swing.JOptionPane.YES_NO_OPTION; |
||||
|
||||
/* |
||||
* 删除指定文件 |
||||
*/ |
||||
public class DelFileAction extends UpdateAction { |
||||
|
||||
public DelFileAction() { |
||||
|
||||
this.setName(Toolkit.i18nText("Fine-Design_Basic_Remove")); |
||||
this.setSmallIcon("/com/fr/design/standard/remove/remove"); |
||||
} |
||||
|
||||
@Override |
||||
public void actionPerformed(ActionEvent evt) { |
||||
FileOperations selectedOperation = DesignerFrameFileDealerPane.getInstance().getSelectedOperation(); |
||||
if (!selectedOperation.access()) { |
||||
FineJOptionPane.showMessageDialog(DesignerContext.getDesignerFrame(), |
||||
Toolkit.i18nText("Fine-Design_Basic_Template_Permission_Denied"), |
||||
Toolkit.i18nText("Fine-Design_Basic_Alert"), |
||||
WARNING_MESSAGE); |
||||
return; |
||||
} |
||||
if (TemplateUtils.checkSelectedTemplateIsEditing()) { |
||||
if (FineJOptionPane.showConfirmDialog(DesignerContext.getDesignerFrame(), |
||||
Toolkit.i18nText("Fine-Design_Basic_Template_Is_Editing"), |
||||
Toolkit.i18nText("Fine-Design_Basic_Alert"), |
||||
YES_NO_OPTION) != JOptionPane.YES_OPTION) { |
||||
return; |
||||
} |
||||
} |
||||
selectedOperation.deleteFile(); |
||||
DesignerFrameFileDealerPane.getInstance().stateChange(); |
||||
DesignerContext.getDesignerFrame().setTitle(); |
||||
} |
||||
} |
@ -0,0 +1,163 @@
|
||||
package com.fr.design.actions.file; |
||||
|
||||
import com.fr.design.actions.UpdateAction; |
||||
import com.fr.design.file.HistoryTemplateListCache; |
||||
import com.fr.design.file.TemplateTreePane; |
||||
import com.fr.design.gui.itree.filetree.TemplateFileTree; |
||||
import com.fr.design.gui.itree.refreshabletree.ExpandMutableTreeNode; |
||||
import com.fr.design.gui.itree.refreshabletree.RefreshableJTree; |
||||
import com.fr.design.i18n.Toolkit; |
||||
import com.fr.design.mainframe.JTemplate; |
||||
import com.fr.design.mainframe.manager.search.TemplateTreeSearchManager; |
||||
import com.fr.file.filetree.FileNode; |
||||
import com.fr.general.ComparatorUtils; |
||||
import com.fr.stable.CoreConstants; |
||||
import com.fr.stable.StableUtils; |
||||
import com.fr.stable.project.ProjectConstants; |
||||
|
||||
import javax.swing.tree.DefaultTreeModel; |
||||
import javax.swing.tree.TreeNode; |
||||
import javax.swing.tree.TreePath; |
||||
import java.awt.event.ActionEvent; |
||||
import java.io.File; |
||||
|
||||
/** |
||||
* 模板定位功能 |
||||
*/ |
||||
public class LocateAction extends UpdateAction { |
||||
|
||||
public LocateAction() { |
||||
this.setName(Toolkit.i18nText("Fine-Design_Basic_Locate")); |
||||
this.setSmallIcon("/com/fr/design/standard/locate/locate"); |
||||
} |
||||
|
||||
@Override |
||||
public void actionPerformed(ActionEvent e) { |
||||
JTemplate<?, ?> current = HistoryTemplateListCache.getInstance().getCurrentEditingTemplate(); |
||||
gotoEditingTemplateLeaf(current.getEditingFILE().getPath(), false); |
||||
} |
||||
|
||||
/** |
||||
* 在左侧模板树定位到指定模板 |
||||
* |
||||
* @param locatedPath |
||||
*/ |
||||
public static void gotoEditingTemplateLeaf(String locatedPath) { |
||||
gotoEditingTemplateLeaf(locatedPath, true); |
||||
} |
||||
|
||||
private static void gotoEditingTemplateLeaf(String locatedPath, boolean needRefreshMode) { |
||||
if (locatedPath == null) { |
||||
return; |
||||
} |
||||
if (TemplateTreeSearchManager.getInstance().isInSearchMode()) { |
||||
TemplateTreeSearchManager.getInstance().outOfSearchMode(); |
||||
} |
||||
|
||||
DefaultTreeModel model = (DefaultTreeModel) getTemplateFileTree().getModel(); |
||||
ExpandMutableTreeNode treeNode = (ExpandMutableTreeNode) model.getRoot(); |
||||
if (needRefreshMode) { |
||||
treeNode.removeAllChildren(); |
||||
ExpandMutableTreeNode[] childTreeNodes = getTemplateFileTree().loadChildTreeNodes(treeNode); |
||||
treeNode.addChildTreeNodes(childTreeNodes); |
||||
model.reload(treeNode); |
||||
} |
||||
|
||||
recursiveSelectPath(treeNode, locatedPath, model); |
||||
TreePath selectedTreePath = getTemplateFileTree().getSelectionPath(); |
||||
if (selectedTreePath != null) { |
||||
getTemplateFileTree().scrollPathToVisible(selectedTreePath); |
||||
} |
||||
} |
||||
|
||||
private static TemplateFileTree getTemplateFileTree() { |
||||
return TemplateTreePane.getInstance().getTemplateFileTree(); |
||||
} |
||||
|
||||
private static void recursiveSelectPath(TreeNode treeNode, String currentPath, DefaultTreeModel m_model) { |
||||
for (int i = 0, len = treeNode.getChildCount(); i < len; i++) { |
||||
TreeNode node = treeNode.getChildAt(i); |
||||
// 取出当前的childTreeNode,并append到searchingPath后面
|
||||
ExpandMutableTreeNode childTreeNode = (ExpandMutableTreeNode) node; |
||||
if (selectFilePath(childTreeNode, ProjectConstants.REPORTLETS_NAME, currentPath, m_model)) { |
||||
break; |
||||
} |
||||
if (!node.isLeaf()) { |
||||
for (int j = 0; j < node.getChildCount(); j++) { |
||||
recursiveSelectPath(node.getChildAt(j), currentPath, m_model); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
/* |
||||
* 在currentTreeNode下找寻filePath |
||||
* |
||||
* prefix + currentTreeNode.getName() = currentTreeNode所对应的Path |
||||
* |
||||
* 返回currentTreeNode下是否找到了filePath |
||||
*/ |
||||
private static boolean selectFilePath(ExpandMutableTreeNode currentTreeNode, String prefix, String filePath, DefaultTreeModel model) { |
||||
Object userObj = currentTreeNode.getUserObject(); |
||||
if (!(userObj instanceof FileNode)) { |
||||
return false; |
||||
} |
||||
FileNode fileNode = (FileNode) userObj; |
||||
String nodePath = fileNode.getName(); |
||||
String currentTreePath = StableUtils.pathJoin(prefix, nodePath); |
||||
boolean result = false; |
||||
|
||||
// 如果equals,说明找到了,不必再找下去了
|
||||
if (ComparatorUtils.equals(new File(currentTreePath), new File(filePath))) { |
||||
getTemplateFileTree().setSelectionPath(new TreePath(model.getPathToRoot(currentTreeNode))); |
||||
result = true; |
||||
} |
||||
// 如果当前路径是currentFilePath的ParentFile,则expandTreeNode,并继续往下找
|
||||
else if (isParentFile(currentTreePath, filePath)) { |
||||
loadPendingChildTreeNode(currentTreeNode); |
||||
prefix = currentTreePath + CoreConstants.SEPARATOR; |
||||
for (int i = 0, len = currentTreeNode.getChildCount(); i < len; i++) { |
||||
ExpandMutableTreeNode childTreeNode = (ExpandMutableTreeNode) currentTreeNode.getChildAt(i); |
||||
|
||||
if (selectFilePath(childTreeNode, prefix, filePath, model)) { |
||||
result = true; |
||||
} |
||||
} |
||||
} |
||||
|
||||
return result; |
||||
} |
||||
|
||||
protected static void loadPendingChildTreeNode(ExpandMutableTreeNode currentTreeNode) { |
||||
if (currentTreeNode.isLeaf()) { |
||||
return; |
||||
} |
||||
|
||||
// 判断第一个孩子节点.UserObject是不是PENDING,如果是PENDING的话,需要重新加载这个TreeNode
|
||||
ExpandMutableTreeNode flag = (ExpandMutableTreeNode) currentTreeNode.getFirstChild(); |
||||
if (flag == null || !flag.getUserObject().toString().equals(RefreshableJTree.PENDING.toString())) { |
||||
return; |
||||
} |
||||
// 删除所有的节点.
|
||||
currentTreeNode.removeAllChildren(); |
||||
|
||||
ExpandMutableTreeNode[] children = getTemplateFileTree().loadChildTreeNodes(currentTreeNode); |
||||
for (ExpandMutableTreeNode c : children) { |
||||
currentTreeNode.add(c); |
||||
} |
||||
} |
||||
|
||||
private static boolean isParentFile(String var0, String var1) { |
||||
File var2 = new File(var0); |
||||
File var3 = new File(var1); |
||||
|
||||
while (!ComparatorUtils.equals(var2, var3)) { |
||||
var3 = var3.getParentFile(); |
||||
if (var3 == null) { |
||||
return false; |
||||
} |
||||
} |
||||
|
||||
return true; |
||||
} |
||||
} |
@ -0,0 +1,346 @@
|
||||
package com.fr.design.actions.file; |
||||
|
||||
import com.fr.base.BaseUtils; |
||||
import com.fr.chartx.TwoTuple; |
||||
import com.fr.design.DesignerEnvManager; |
||||
import com.fr.design.actions.UpdateAction; |
||||
import com.fr.design.dialog.FineJOptionPane; |
||||
import com.fr.design.file.FileOperations; |
||||
import com.fr.design.file.HistoryTemplateListCache; |
||||
import com.fr.design.file.MultiTemplateTabPane; |
||||
import com.fr.design.gui.ibutton.UIButton; |
||||
import com.fr.design.gui.ilable.UILabel; |
||||
import com.fr.design.gui.itextfield.UITextField; |
||||
import com.fr.design.i18n.Toolkit; |
||||
import com.fr.design.layout.TableLayout; |
||||
import com.fr.design.layout.TableLayoutHelper; |
||||
import com.fr.design.mainframe.DesignerContext; |
||||
import com.fr.design.mainframe.DesignerFrameFileDealerPane; |
||||
import com.fr.design.mainframe.vcs.common.VcsHelper; |
||||
import com.fr.design.utils.TemplateUtils; |
||||
import com.fr.design.utils.gui.GUICoreUtils; |
||||
import com.fr.event.EventDispatcher; |
||||
import com.fr.file.FileNodeFILE; |
||||
import com.fr.file.filetree.FileNode; |
||||
import com.fr.general.ComparatorUtils; |
||||
import com.fr.stable.CoreConstants; |
||||
import com.fr.stable.StringUtils; |
||||
import com.fr.stable.project.ProjectConstants; |
||||
import com.fr.third.org.apache.commons.io.FilenameUtils; |
||||
|
||||
import javax.swing.BorderFactory; |
||||
import javax.swing.JDialog; |
||||
import javax.swing.JPanel; |
||||
import javax.swing.JOptionPane; |
||||
import javax.swing.SwingConstants; |
||||
import javax.swing.event.DocumentEvent; |
||||
import javax.swing.event.DocumentListener; |
||||
import java.awt.BorderLayout; |
||||
import java.awt.Color; |
||||
import java.awt.Component; |
||||
import java.awt.Dimension; |
||||
import java.awt.FlowLayout; |
||||
import java.awt.event.ActionEvent; |
||||
import java.awt.event.ActionListener; |
||||
import java.awt.event.KeyAdapter; |
||||
import java.awt.event.KeyEvent; |
||||
import java.awt.event.KeyListener; |
||||
import java.util.regex.Pattern; |
||||
|
||||
import static javax.swing.JOptionPane.DEFAULT_OPTION; |
||||
import static javax.swing.JOptionPane.ERROR_MESSAGE; |
||||
import static javax.swing.JOptionPane.WARNING_MESSAGE; |
||||
import static javax.swing.JOptionPane.YES_NO_OPTION; |
||||
|
||||
/** |
||||
* 模板/目录重命名操作 |
||||
*/ |
||||
public class RenameAction extends UpdateAction { |
||||
|
||||
private FileOperations selectedOperation; |
||||
|
||||
public RenameAction() { |
||||
|
||||
this.setName(Toolkit.i18nText("Fine-Design_Basic_Rename")); |
||||
this.setSmallIcon("/com/fr/design/standard/rename/rename"); |
||||
} |
||||
|
||||
@Override |
||||
public void actionPerformed(ActionEvent evt) { |
||||
selectedOperation = DesignerFrameFileDealerPane.getInstance().getSelectedOperation(); |
||||
if (!selectedOperation.access()) { |
||||
FineJOptionPane.showMessageDialog(DesignerContext.getDesignerFrame(), |
||||
Toolkit.i18nText("Fine-Design_Basic_Template_Permission_Denied"), |
||||
Toolkit.i18nText("Fine-Design_Basic_Alert"), |
||||
WARNING_MESSAGE); |
||||
return; |
||||
} |
||||
|
||||
FileNode node = selectedOperation.getFileNode(); |
||||
String lock = node.getLock(); |
||||
if (lock != null && !lock.equals(node.getUserID())) { |
||||
// 提醒被锁定模板无法重命名
|
||||
FineJOptionPane.showMessageDialog(DesignerContext.getDesignerFrame(), |
||||
Toolkit.i18nText("Fine-Design_Basic_Unable_Rename_Locked_File"), |
||||
Toolkit.i18nText("Fine-Design_Basic_Alert"), |
||||
WARNING_MESSAGE); |
||||
return; |
||||
} |
||||
|
||||
new FileRenameDialog(node); |
||||
MultiTemplateTabPane.getInstance().repaint(); |
||||
DesignerFrameFileDealerPane.getInstance().stateChange(); |
||||
} |
||||
|
||||
/** |
||||
* 重命名对话框 |
||||
* 支持快捷键Enter,ESC |
||||
*/ |
||||
private class FileRenameDialog extends JDialog { |
||||
|
||||
private UITextField nameField; |
||||
|
||||
private UILabel warnLabel; |
||||
|
||||
private UIButton confirmButton; |
||||
|
||||
/** |
||||
* 操作的节点 |
||||
*/ |
||||
private FileNodeFILE fnf; |
||||
|
||||
private KeyListener keyListener = new KeyAdapter() { |
||||
@Override |
||||
public void keyPressed(KeyEvent e) { |
||||
if (e.getKeyCode() == KeyEvent.VK_ESCAPE) { |
||||
dispose(); |
||||
} else if (e.getKeyCode() == KeyEvent.VK_ENTER) { |
||||
if (confirmButton.isEnabled()) { |
||||
confirmClose(); |
||||
} |
||||
} |
||||
} |
||||
}; |
||||
|
||||
|
||||
private FileRenameDialog(FileNode node) { |
||||
super(DesignerContext.getDesignerFrame(), true); |
||||
if (node == null) { |
||||
return; |
||||
} |
||||
fnf = new FileNodeFILE(node); |
||||
String oldName = fnf.getName(); |
||||
String suffix = fnf.isDirectory() ? StringUtils.EMPTY : oldName.substring(oldName.lastIndexOf(CoreConstants.DOT), oldName.length()); |
||||
oldName = StringUtils.replaceLast(oldName, suffix, StringUtils.EMPTY); |
||||
|
||||
initPane(oldName); |
||||
} |
||||
|
||||
private void initPane(String oldName) { |
||||
this.setLayout(new BorderLayout()); |
||||
|
||||
// 输入框前提示
|
||||
UILabel newNameLabel = new UILabel(Toolkit.i18nText( |
||||
fnf.isDirectory() ? |
||||
"Fine-Design_Basic_Enter_New_Folder_Name" : "Fine-Design_Basic_Enter_New_File_Name") |
||||
); |
||||
newNameLabel.setHorizontalAlignment(SwingConstants.RIGHT); |
||||
newNameLabel.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 10)); |
||||
//newNameLabel.setPreferredSize(new Dimension(118, 15));
|
||||
|
||||
// 重命名输入框
|
||||
nameField = new UITextField(oldName); |
||||
nameField.getDocument().addDocumentListener(new DocumentListener() { |
||||
|
||||
@Override |
||||
public void changedUpdate(DocumentEvent e) { |
||||
validInput(); |
||||
} |
||||
|
||||
@Override |
||||
public void insertUpdate(DocumentEvent e) { |
||||
validInput(); |
||||
} |
||||
|
||||
@Override |
||||
public void removeUpdate(DocumentEvent e) { |
||||
validInput(); |
||||
} |
||||
}); |
||||
nameField.selectAll(); |
||||
nameField.setPreferredSize(new Dimension(170, 20)); |
||||
|
||||
JPanel topPanel = new JPanel(new FlowLayout(FlowLayout.LEFT, 0, 5)); |
||||
topPanel.setBorder(BorderFactory.createEmptyBorder(15, 15, 0, 15)); |
||||
topPanel.add(newNameLabel); |
||||
topPanel.add(nameField); |
||||
|
||||
// 增加enter以及esc快捷键的支持
|
||||
nameField.addKeyListener(keyListener); |
||||
// 重名提示
|
||||
warnLabel = new UILabel(); |
||||
warnLabel.setPreferredSize(new Dimension(300, 50)); |
||||
warnLabel.setHorizontalAlignment(SwingConstants.LEFT); |
||||
warnLabel.setVerticalAlignment(SwingConstants.TOP); |
||||
warnLabel.setForeground(Color.RED); |
||||
warnLabel.setVisible(false); |
||||
|
||||
JPanel midPanel = new JPanel(new BorderLayout()); |
||||
midPanel.setBorder(BorderFactory.createEmptyBorder(0, 15, 0, 15)); |
||||
midPanel.add(warnLabel, BorderLayout.WEST); |
||||
|
||||
// 确认按钮
|
||||
confirmButton = new UIButton(Toolkit.i18nText("Fine-Design_Basic_Confirm")); |
||||
confirmButton.addActionListener(new ActionListener() { |
||||
@Override |
||||
public void actionPerformed(ActionEvent e) { |
||||
confirmClose(); |
||||
} |
||||
}); |
||||
|
||||
// 取消按钮
|
||||
UIButton cancelButton = new UIButton(Toolkit.i18nText("Fine-Design_Basic_Cancel")); |
||||
|
||||
cancelButton.addActionListener(new ActionListener() { |
||||
|
||||
@Override |
||||
public void actionPerformed(ActionEvent e) { |
||||
dispose(); |
||||
} |
||||
}); |
||||
|
||||
|
||||
JPanel buttonsPane = new JPanel(new FlowLayout(FlowLayout.RIGHT, 10, 0)); |
||||
buttonsPane.setBorder(BorderFactory.createEmptyBorder(10, 15, 10, 10)); |
||||
buttonsPane.add(confirmButton); |
||||
buttonsPane.add(cancelButton); |
||||
|
||||
this.add( |
||||
TableLayoutHelper.createTableLayoutPane( |
||||
new Component[][]{ |
||||
new Component[]{topPanel}, |
||||
new Component[]{midPanel}, |
||||
new Component[]{buttonsPane} |
||||
}, |
||||
new double[]{TableLayout.FILL, TableLayout.PREFERRED, TableLayout.PREFERRED}, |
||||
new double[]{TableLayout.FILL} |
||||
), |
||||
BorderLayout.CENTER); |
||||
|
||||
this.setSize(340, 200); |
||||
this.setTitle(Toolkit.i18nText("Fine-Design_Basic_Rename")); |
||||
this.setResizable(false); |
||||
this.setIconImage(BaseUtils.readImage("/com/fr/base/images/oem/logo.png")); |
||||
this.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE); |
||||
GUICoreUtils.centerWindow(this); |
||||
this.setVisible(true); |
||||
} |
||||
|
||||
private void confirmClose() { |
||||
|
||||
if (TemplateUtils.checkSelectedTemplateIsEditing()) { |
||||
if (FineJOptionPane.showConfirmDialog(this, |
||||
Toolkit.i18nText("Fine-Design_Basic_Template_Is_Editing"), |
||||
Toolkit.i18nText("Fine-Design_Basic_Alert"), |
||||
YES_NO_OPTION) != JOptionPane.YES_OPTION) { |
||||
return; |
||||
} |
||||
} |
||||
|
||||
String userInput = nameField.getText().trim(); |
||||
|
||||
String path = FilenameUtils.standard(fnf.getPath()); |
||||
|
||||
String oldName = fnf.getName(); |
||||
String suffix = fnf.isDirectory() ? StringUtils.EMPTY : oldName.substring(oldName.lastIndexOf(CoreConstants.DOT), oldName.length()); |
||||
oldName = StringUtils.replaceLast(oldName, suffix, StringUtils.EMPTY); |
||||
|
||||
// 输入为空或者没有修改
|
||||
if (ComparatorUtils.equals(userInput, oldName)) { |
||||
this.dispose(); |
||||
return; |
||||
} |
||||
|
||||
String parentPath = FilenameUtils.standard(fnf.getParent().getPath()); |
||||
|
||||
// 简单执行old new 替换是不可行的,例如 /abc/abc/abc/abc/
|
||||
String newPath = parentPath + CoreConstants.SEPARATOR + userInput + suffix; |
||||
this.dispose(); |
||||
|
||||
//模版重命名
|
||||
boolean success = selectedOperation.rename(fnf, path, newPath); |
||||
|
||||
if (success) { |
||||
afterRename(path, newPath); |
||||
} else { |
||||
FineJOptionPane.showConfirmDialog(DesignerContext.getDesignerFrame(), |
||||
Toolkit.i18nText("Fine-Design_Basic_Rename_Failure"), |
||||
Toolkit.i18nText("Fine-Design_Basic_Error"), |
||||
DEFAULT_OPTION, |
||||
ERROR_MESSAGE); |
||||
} |
||||
} |
||||
|
||||
private void afterRename(String path, String newPath) { |
||||
EventDispatcher.fire(DesignerFrameFileDealerPane.TEMPLATE_RENAME, new TwoTuple<>(path, newPath)); |
||||
HistoryTemplateListCache.getInstance().rename(fnf, path, newPath); |
||||
DesignerEnvManager.getEnvManager().replaceRecentOpenedFilePath(fnf.isDirectory(), path, newPath); |
||||
selectedOperation.refreshParent(); |
||||
if (!fnf.isDirectory()) { |
||||
//版本控制:未打开模板时重命名,是个纯文件操作
|
||||
//不借助JTemplate的事件触发机制实现(需要新创建JTemplate,并添加监听,不确定会不会有问题)
|
||||
path = path.replaceFirst(ProjectConstants.REPORTLETS_NAME, StringUtils.EMPTY); |
||||
VcsHelper.getInstance().moveVcs(path, newPath.replaceFirst(ProjectConstants.REPORTLETS_NAME, StringUtils.EMPTY)); |
||||
} |
||||
DesignerContext.getDesignerFrame().setTitle(); |
||||
LocateAction.gotoEditingTemplateLeaf(newPath); |
||||
} |
||||
|
||||
|
||||
private void validInput() { |
||||
|
||||
String userInput = nameField.getText().trim(); |
||||
|
||||
String oldName = fnf.getName(); |
||||
String suffix = fnf.isDirectory() ? StringUtils.EMPTY : oldName.substring(oldName.lastIndexOf(CoreConstants.DOT), oldName.length()); |
||||
oldName = oldName.replaceAll(suffix, StringUtils.EMPTY); |
||||
|
||||
if (StringUtils.isEmpty(userInput)) { |
||||
confirmButton.setEnabled(false); |
||||
return; |
||||
} |
||||
|
||||
if (ComparatorUtils.equals(userInput, oldName)) { |
||||
warnLabel.setVisible(false); |
||||
confirmButton.setEnabled(true); |
||||
return; |
||||
} |
||||
|
||||
String errorMsg = doCheck(userInput, suffix, fnf.isDirectory()); |
||||
if (StringUtils.isNotEmpty(errorMsg)) { |
||||
nameField.selectAll(); |
||||
// 如果文件名已存在,则灰掉确认按钮
|
||||
warnLabel.setText(errorMsg); |
||||
warnLabel.setVisible(true); |
||||
confirmButton.setEnabled(false); |
||||
} else { |
||||
warnLabel.setVisible(false); |
||||
confirmButton.setEnabled(true); |
||||
} |
||||
} |
||||
|
||||
private String doCheck (String userInput, String suffix, boolean isDirectory) { |
||||
String errorMsg = StringUtils.EMPTY; |
||||
if (selectedOperation.duplicated(userInput, suffix, false)) { |
||||
errorMsg = Toolkit.i18nText(isDirectory ? |
||||
"Fine-Design_Basic_Folder_Name_Duplicate" : |
||||
"Fine-Design_Basic_Template_File_Name_Duplicate", |
||||
userInput); |
||||
} |
||||
if (!Pattern.compile(DesignerFrameFileDealerPane.FILE_NAME_LIMIT).matcher(userInput).matches()) { |
||||
errorMsg = Toolkit.i18nText("Fine-Design_Basic_Template_Name_Illegal"); |
||||
} |
||||
return errorMsg; |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,200 @@
|
||||
package com.fr.design.actions.help.alphafine; |
||||
|
||||
import com.fr.design.i18n.Toolkit; |
||||
import com.fr.general.CloudCenter; |
||||
import com.fr.json.JSONArray; |
||||
|
||||
import java.util.HashMap; |
||||
import java.util.Map; |
||||
|
||||
/** |
||||
* 云端变量统一管理 |
||||
* |
||||
* @author Link |
||||
* @version 11.0 |
||||
* Created by Link on 2022/9/21 |
||||
*/ |
||||
public class AlphaFineCloudConstants { |
||||
|
||||
private static final String PLUGIN_SEARCH_API = "plugin.searchAPI"; |
||||
private static final String PLUGIN_ALL_SEARCH_API = "plugin.all.searchAPI"; |
||||
private static final String AF_PLUGIN_INFO = "af.pluginInfo"; |
||||
private static final String AF_REUSE_INFO = "af.reuseInfo"; |
||||
private static final String AF_DOC_VIEW = "af.doc_view"; |
||||
private static final String AF_DOC_SEARCH = "af.doc_search"; |
||||
private static final String AF_DOC_INFO = "af.doc_info"; |
||||
private static final String AF_PLUGIN_IMAGE = "af.plugin_image"; |
||||
private static final String AF_RECORD = "af.record"; |
||||
private static final String AF_CLOUD_SEARCH = "af.cloud_search"; |
||||
private static final String AF_SIMILAR_SEARCH = "af.similar_search"; |
||||
private static final String AF_ADVICE_SEARCH = "af.advice_search"; |
||||
private static final String AF_HOT_SEARCH = "af.hot_search"; |
||||
private static final String AF_GO_FORUM = "af.go_fourm"; |
||||
private static final String AF_GO_WEB = "af.go_web"; |
||||
private static final String AF_PREVIEW = "af.preview"; |
||||
private static final String AF_CID_NEW = "af.cid.new"; |
||||
private static final String AF_CID_USER_GROUP_INFO = "af.cid.user.group.info"; |
||||
private static final String AF_HELP_QUICK_START = "af.help.quick.start"; |
||||
private static final String AF_HELP_REPORT_LEARNING_PATH = "af.help.report.learning.path"; |
||||
private static final String AF_HELP_PARAM_LEARNING_PATH = "af.help.param.learning.path"; |
||||
private static final String AF_HELP_FILL_LEARNING_PATH = "af.help.fill.learning.path"; |
||||
private static final String AF_HELP_API_SUMMARY = "af.help.api.summary"; |
||||
private static final String AF_HELP_MONTHLY_DOCUMENT = "af.help.monthly.document"; |
||||
private static final String AF_RECOMMEND = "af.recommend"; |
||||
|
||||
private static final String LINK_NAME = "name"; |
||||
private static final String LINK_URL = "link"; |
||||
|
||||
/** |
||||
* 获取插件搜索api |
||||
*/ |
||||
public static String getPluginSearchUrl() { |
||||
return CloudCenter.getInstance().acquireUrlByKind(PLUGIN_SEARCH_API); |
||||
}; |
||||
|
||||
/** |
||||
* 帆软市场里全部插件api |
||||
*/ |
||||
public static String getSearchAllPluginUrl() { |
||||
return CloudCenter.getInstance().acquireUrlByKind(PLUGIN_ALL_SEARCH_API); |
||||
} |
||||
|
||||
/** |
||||
* 获取插件信息api |
||||
*/ |
||||
public static String getPluginUrl() { |
||||
return CloudCenter.getInstance().acquireUrlByKind(AF_PLUGIN_INFO); |
||||
} |
||||
|
||||
/** |
||||
* 获取组件信息api |
||||
*/ |
||||
public static String getReuseUrl() { |
||||
return CloudCenter.getInstance().acquireUrlByKind(AF_REUSE_INFO); |
||||
} |
||||
|
||||
/** |
||||
* 获取帮助文档url |
||||
*/ |
||||
public static String getDocumentDocUrl() { |
||||
return CloudCenter.getInstance().acquireUrlByKind(AF_DOC_VIEW); |
||||
} |
||||
|
||||
/** |
||||
* 帮助文档搜索api |
||||
*/ |
||||
public static String getDocumentSearchUrl() { |
||||
return CloudCenter.getInstance().acquireUrlByKind(AF_DOC_SEARCH); |
||||
} |
||||
|
||||
/** |
||||
* 帮助文档信息api |
||||
*/ |
||||
public static String getDocumentInformationUrl() { |
||||
return CloudCenter.getInstance().acquireUrlByKind(AF_DOC_INFO); |
||||
} |
||||
|
||||
/** |
||||
* 插件图片api |
||||
*/ |
||||
public static String getPluginImageUrl() { |
||||
return CloudCenter.getInstance().acquireUrlByKind(AF_PLUGIN_IMAGE); |
||||
} |
||||
|
||||
/** |
||||
* 获取云端接口,用于上传alphafine搜索记录 |
||||
*/ |
||||
public static String getCloudServerUrl() { |
||||
return CloudCenter.getInstance().acquireUrlByKind(AF_RECORD); |
||||
} |
||||
|
||||
/** |
||||
* 获取搜索api,输入搜索词,返回fr的相关功能 |
||||
*/ |
||||
public static String getSearchApi() { |
||||
return CloudCenter.getInstance().acquireUrlByKind(AF_CLOUD_SEARCH); |
||||
} |
||||
|
||||
/** |
||||
* 获取模糊搜索api前缀,输入搜索词,返回alphaFine相关内容,插件,文档,功能等 |
||||
*/ |
||||
public static String getSimilarSearchUrlPrefix() { |
||||
return CloudCenter.getInstance().acquireUrlByKind(AF_SIMILAR_SEARCH); |
||||
} |
||||
|
||||
/** |
||||
* 补全建议搜索结果 api,与AF_SIMILAR_SEARCH接口类似,但是返回的信息更全 |
||||
*/ |
||||
public static String getComplementAdviceSearchUrlPrefix() { |
||||
return CloudCenter.getInstance().acquireUrlByKind(AF_ADVICE_SEARCH); |
||||
} |
||||
|
||||
/** |
||||
* 获取热门问题 |
||||
*/ |
||||
public static String getAlphaHotSearch() { |
||||
return CloudCenter.getInstance().acquireUrlByKind(AF_HOT_SEARCH); |
||||
} |
||||
|
||||
/** |
||||
* 跳转论坛url |
||||
*/ |
||||
public static String getAlphaGoToForum() { |
||||
return CloudCenter.getInstance().acquireUrlByKind(AF_GO_FORUM); |
||||
} |
||||
|
||||
/** |
||||
* 推荐搜索api,输入搜索词,返回猜你想搜的内容(html格式) |
||||
*/ |
||||
public static String getAlphaGoToWeb() { |
||||
return CloudCenter.getInstance().acquireUrlByKind(AF_GO_WEB); |
||||
} |
||||
|
||||
/** |
||||
* 帆软智能客服页面url |
||||
*/ |
||||
public static String getAlphaPreview() { |
||||
return CloudCenter.getInstance().acquireUrlByKind(AF_PREVIEW); |
||||
} |
||||
|
||||
/** |
||||
* cid系统的产品动态api |
||||
*/ |
||||
public static String getAlphaCid() { |
||||
return CloudCenter.getInstance().acquireUrlByKind(AF_CID_NEW); |
||||
} |
||||
|
||||
/** |
||||
* cid系统的 用户组信息api |
||||
*/ |
||||
public static String getAlphaCidUserGroupInfo() { |
||||
return CloudCenter.getInstance().acquireUrlByKind(AF_CID_USER_GROUP_INFO); |
||||
} |
||||
|
||||
private static String getDefaultRecommend() { |
||||
String[][] links = new String[][]{ |
||||
{Toolkit.i18nText("Fine-Design_Report_AlphaFine_Doc_Quick_Start"), CloudCenter.getInstance().acquireUrlByKind(AF_HELP_QUICK_START)}, |
||||
{Toolkit.i18nText("Fine-Design_Report_AlphaFine_Doc_Report_Learning"), CloudCenter.getInstance().acquireUrlByKind(AF_HELP_REPORT_LEARNING_PATH)}, |
||||
{Toolkit.i18nText("Fine-Design_Report_AlphaFine_Doc_Parameter_Learning"), CloudCenter.getInstance().acquireUrlByKind(AF_HELP_PARAM_LEARNING_PATH)}, |
||||
{Toolkit.i18nText("Fine-Design_Report_AlphaFine_Doc_Fill_Learning"), CloudCenter.getInstance().acquireUrlByKind(AF_HELP_FILL_LEARNING_PATH)}, |
||||
{Toolkit.i18nText("Fine-Design_Report_AlphaFine_Doc_Api_Summary"), CloudCenter.getInstance().acquireUrlByKind(AF_HELP_API_SUMMARY)}, |
||||
{Toolkit.i18nText("Fine-Design_Report_AlphaFine_Doc_Monthly_Document"), CloudCenter.getInstance().acquireUrlByKind(AF_HELP_MONTHLY_DOCUMENT)} |
||||
}; |
||||
JSONArray jsonArray = new JSONArray(); |
||||
for (String[] link : links) { |
||||
Map<String, String> map = new HashMap<>(); |
||||
map.put(LINK_NAME, link[0]); |
||||
map.put(LINK_URL, link[1]); |
||||
jsonArray.put(map); |
||||
} |
||||
|
||||
return jsonArray.toString(); |
||||
} |
||||
|
||||
/** |
||||
* 获取默认推荐帮助文档url |
||||
*/ |
||||
public static String getAlphaHelpRecommend() { |
||||
return CloudCenter.getInstance().acquireUrlByKind(AF_RECOMMEND, getDefaultRecommend()); |
||||
} |
||||
} |
@ -0,0 +1,44 @@
|
||||
package com.fr.design.actions.help.alphafine; |
||||
|
||||
/** |
||||
* @author hades |
||||
* @version 11.0 |
||||
* Created by hades on 2022/4/26 |
||||
*/ |
||||
public class AlphaFineShortCutUtil { |
||||
|
||||
private static final String TYPE = "pressed"; |
||||
private static final String DISPLAY_TYPE = "+"; |
||||
private static final String BACK_SLASH = "BACK_SLASH"; |
||||
private static final String DISPLAY_BACK_SLASH = "\\"; |
||||
private static final String SLASH = "SLASH"; |
||||
private static final String DISPLAY_SLASH = "/"; |
||||
private static final String CONTROL = "CONTROL"; |
||||
private static final String DISPLAY_CONTROL = "ctrl"; |
||||
private static final String OPEN_BRACKET = "OPEN_BRACKET"; |
||||
private static final String DISPLAY_OPEN_BRACKET = "{"; |
||||
private static final String CLOSE_BRACKET = "CLOSE_BRACKET"; |
||||
private static final String DISPLAY_CLOSE_BRACKET = "}"; |
||||
private static final String COMMA = "COMMA"; |
||||
private static final String DISPLAY_COMMA = ","; |
||||
private static final String PERIOD = "PERIOD"; |
||||
private static final String DISPLAY_PERIOD = "."; |
||||
private static final String SEMICOLON = "SEMICOLON"; |
||||
private static final String DISPLAY_SEMICOLON = ";"; |
||||
private static final String QUOTE = "QUOTE"; |
||||
private static final String DISPLAY_QUOTE = "'"; |
||||
private static final String EQUALS = "EQUALS"; |
||||
private static final String DISPLAY_EQUALS = "+"; |
||||
private static final String MINUS = "MINUS"; |
||||
private static final String DISPLAY_MINUS = "-"; |
||||
private static final String COMMAND = "META"; |
||||
private static final String SMALL_COMMAND = "meta"; |
||||
private static final String DISPLAY_COMMAND = "\u2318"; |
||||
|
||||
public static String getDisplayShortCut(String shortCut) { |
||||
return shortCut.replace(TYPE, DISPLAY_TYPE).replace(BACK_SLASH, DISPLAY_BACK_SLASH).replace(SLASH, DISPLAY_SLASH) |
||||
.replace(CONTROL, DISPLAY_CONTROL).replace(OPEN_BRACKET, DISPLAY_OPEN_BRACKET).replace(CLOSE_BRACKET, DISPLAY_CLOSE_BRACKET) |
||||
.replace(COMMA, DISPLAY_COMMA).replace(PERIOD, DISPLAY_PERIOD).replace(SEMICOLON, DISPLAY_SEMICOLON).replace(QUOTE, DISPLAY_QUOTE) |
||||
.replace(EQUALS, DISPLAY_EQUALS).replace(MINUS, DISPLAY_MINUS).replace(COMMAND, DISPLAY_COMMAND).replace(SMALL_COMMAND, DISPLAY_COMMAND); |
||||
} |
||||
} |
@ -0,0 +1,30 @@
|
||||
package com.fr.design.actions.help.alphafine; |
||||
|
||||
import java.util.Stack; |
||||
|
||||
/** |
||||
* @author hades |
||||
* @version 11.0 |
||||
* Created by hades on 2022/4/23 |
||||
*/ |
||||
public class SizedStack<T> extends Stack<T> { |
||||
|
||||
private final int maxSize; |
||||
|
||||
public SizedStack(int size) { |
||||
super(); |
||||
this.maxSize = size; |
||||
} |
||||
|
||||
@Override |
||||
public T push(T object) { |
||||
while (this.size() >= maxSize) { |
||||
this.remove(0); |
||||
} |
||||
// 不重复
|
||||
if (this.contains(object)) { |
||||
return object; |
||||
} |
||||
return super.push(object); |
||||
} |
||||
} |
@ -0,0 +1,229 @@
|
||||
package com.fr.design.actions.help.alphafine.component; |
||||
|
||||
import com.fr.base.svg.IconUtils; |
||||
import com.fr.design.actions.help.alphafine.AlphaFineConfigPane; |
||||
import com.fr.design.gui.ibutton.UIButton; |
||||
import com.fr.design.gui.icheckbox.UICheckBox; |
||||
import com.fr.stable.StringUtils; |
||||
|
||||
import javax.swing.BorderFactory; |
||||
import javax.swing.JPanel; |
||||
import javax.swing.SwingUtilities; |
||||
import javax.swing.plaf.PanelUI; |
||||
import java.awt.BorderLayout; |
||||
import java.awt.Color; |
||||
import java.awt.Component; |
||||
import java.awt.Dimension; |
||||
import java.awt.FlowLayout; |
||||
import java.util.ArrayList; |
||||
import java.util.Comparator; |
||||
import java.util.HashMap; |
||||
import java.util.HashSet; |
||||
import java.util.List; |
||||
import java.util.Map; |
||||
import java.util.function.Function; |
||||
|
||||
/** |
||||
* alphafine设置 - 搜索范围 - 自定义排序 - 弹出面板 |
||||
* |
||||
* @author Link |
||||
* @version 11.0 |
||||
* Created by Link on 2022/9/18 |
||||
*/ |
||||
public class CustomSortPane extends JPanel { |
||||
|
||||
|
||||
private static final int WIDTH = 147; |
||||
private static final int ITEM_HEIGHT = 23; |
||||
private static final int GAP = 1; |
||||
private static final Color BACKGROUND_COLOR = new Color(0xdadadd); |
||||
|
||||
private UIButton top; |
||||
private UIButton bottom; |
||||
private UIButton up; |
||||
private UIButton down; |
||||
private JPanel toolbarPane; |
||||
private MenuLabelPane sortItemPane; |
||||
private List<UICheckBox> sortItems; |
||||
private MenuLabel selectedLabel; |
||||
private AlphaFineConfigPane parentPane; |
||||
|
||||
public CustomSortPane(List<UICheckBox> items, AlphaFineConfigPane parentPane) { |
||||
this.sortItems = items; |
||||
this.parentPane = parentPane; |
||||
setLayout(new BorderLayout(GAP, GAP)); |
||||
int height = (sortItems.size() + 1) * (ITEM_HEIGHT + GAP) + GAP; |
||||
setPreferredSize(new Dimension(WIDTH, height)); |
||||
initComponent(); |
||||
add(toolbarPane, BorderLayout.NORTH); |
||||
add(sortItemPane, BorderLayout.CENTER); |
||||
revalidate(); |
||||
this.setVisible(true); |
||||
} |
||||
|
||||
@Override |
||||
public void setUI(PanelUI ui) { |
||||
super.setUI(ui); |
||||
setBackground(BACKGROUND_COLOR); |
||||
|
||||
} |
||||
|
||||
private void initComponent() { |
||||
createToolbarPane(); |
||||
createSortItemPane(); |
||||
} |
||||
|
||||
private void createToolbarPane() { |
||||
top = new UIButton(IconUtils.readIcon("com/fr/design/mainframe/alphafine/images/top.svg"), false); |
||||
bottom = new UIButton(IconUtils.readIcon("com/fr/design/mainframe/alphafine/images/bottom.svg"), false); |
||||
up = new UIButton(IconUtils.readIcon("com/fr/design/mainframe/alphafine/images/up.svg"), false); |
||||
down = new UIButton(IconUtils.readIcon("com/fr/design/mainframe/alphafine/images/down.svg"), false); |
||||
top.setDisabledIcon(IconUtils.readIcon("com/fr/design/mainframe/alphafine/images/top_disable.svg")); |
||||
bottom.setDisabledIcon(IconUtils.readIcon("com/fr/design/mainframe/alphafine/images/bottom_disable.svg")); |
||||
up.setDisabledIcon(IconUtils.readIcon("com/fr/design/mainframe/alphafine/images/up_disable.svg")); |
||||
down.setDisabledIcon(IconUtils.readIcon("com/fr/design/mainframe/alphafine/images/down_disable.svg")); |
||||
top.addActionListener(e -> { |
||||
SwingUtilities.invokeLater(() -> { |
||||
sortItemPane.setComponentZOrder(selectedLabel, 0); |
||||
setToolbarEnable(sortItemPane.getComponentZOrder(selectedLabel), sortItemPane.getComponentCount()); |
||||
CustomSortPane.this.revalidate(); |
||||
CustomSortPane.this.repaint(); |
||||
refreshCurrentOrder(); |
||||
}); |
||||
|
||||
}); |
||||
bottom.addActionListener(e -> { |
||||
SwingUtilities.invokeLater(() -> { |
||||
sortItemPane.setComponentZOrder(selectedLabel, sortItemPane.getComponentCount() - 1); |
||||
setToolbarEnable(sortItemPane.getComponentZOrder(selectedLabel), sortItemPane.getComponentCount()); |
||||
CustomSortPane.this.revalidate(); |
||||
CustomSortPane.this.repaint(); |
||||
refreshCurrentOrder(); |
||||
}); |
||||
|
||||
}); |
||||
up.addActionListener(e -> { |
||||
SwingUtilities.invokeLater(() -> { |
||||
sortItemPane.setComponentZOrder(selectedLabel, sortItemPane.getComponentZOrder(selectedLabel) - 1); |
||||
setToolbarEnable(sortItemPane.getComponentZOrder(selectedLabel), sortItemPane.getComponentCount()); |
||||
CustomSortPane.this.revalidate(); |
||||
CustomSortPane.this.repaint(); |
||||
refreshCurrentOrder(); |
||||
}); |
||||
|
||||
}); |
||||
down.addActionListener(e -> { |
||||
SwingUtilities.invokeLater(() -> { |
||||
sortItemPane.setComponentZOrder(selectedLabel, sortItemPane.getComponentZOrder(selectedLabel) + 1); |
||||
setToolbarEnable(sortItemPane.getComponentZOrder(selectedLabel), sortItemPane.getComponentCount()); |
||||
CustomSortPane.this.revalidate(); |
||||
CustomSortPane.this.repaint(); |
||||
refreshCurrentOrder(); |
||||
}); |
||||
|
||||
}); |
||||
toolbarPane = new JPanel(new FlowLayout(FlowLayout.TRAILING, GAP, GAP)); |
||||
toolbarPane.setBorder(BorderFactory.createEmptyBorder()); |
||||
toolbarPane.add(top); |
||||
toolbarPane.add(bottom); |
||||
toolbarPane.add(up); |
||||
toolbarPane.add(down); |
||||
} |
||||
|
||||
private void createSortItemPane() { |
||||
String[] currentTabOrder = parentPane.getCurrentOrder(); |
||||
Map<String, Integer> sortMap = new HashMap<>(); |
||||
for (int i = 0; i < currentTabOrder.length; i++) { |
||||
sortMap.put(currentTabOrder[i], i); |
||||
} |
||||
List<MenuLabel> sortLabels = new ArrayList<>(); |
||||
for (UICheckBox item : sortItems) { |
||||
MenuLabel label = new MenuLabel(item.getText(), (Function<MenuLabel, Object>) o -> { |
||||
selectedLabel = o; |
||||
disableButton(); |
||||
return null; |
||||
}); |
||||
sortLabels.add(label); |
||||
} |
||||
sortLabels.sort(Comparator.comparingInt(tab -> sortMap.get(tab.getText()))); |
||||
sortItemPane = new MenuLabelPane(sortLabels); |
||||
} |
||||
|
||||
/** |
||||
* 如果选中第一个和最后一个,则置灰向上和向下的按钮 |
||||
*/ |
||||
private void disableButton() { |
||||
int order = sortItemPane.getComponentZOrder(selectedLabel); |
||||
if (order == 0) { |
||||
setToolbarEnable(false, false, true, true); |
||||
} else if (order == sortItemPane.getComponentCount() - 1) { |
||||
setToolbarEnable(true, true, false, false); |
||||
} else { |
||||
setToolbarEnable(true, true, true, true); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* 设置 置顶,上移,下移,置底 按钮的状态 |
||||
* true:启用 |
||||
* false:关闭 |
||||
*/ |
||||
private void setToolbarEnable(boolean top, boolean up, boolean down, boolean bottom) { |
||||
this.top.setEnabled(top); |
||||
this.up.setEnabled(up); |
||||
this.down.setEnabled(down); |
||||
this.bottom.setEnabled(bottom); |
||||
} |
||||
|
||||
/** |
||||
* 根据选项当前位置以及菜单大小设置 置顶,上移,下移,置底 按钮的状态 |
||||
*/ |
||||
private void setToolbarEnable(int order, int maxOrder) { |
||||
this.top.setEnabled(true); |
||||
this.up.setEnabled(true); |
||||
this.down.setEnabled(true); |
||||
this.bottom.setEnabled(true); |
||||
// 选项处于顶端,则置灰上移和置顶按钮
|
||||
if (order == 0) { |
||||
this.top.setEnabled(false); |
||||
this.up.setEnabled(false); |
||||
} |
||||
// 选项处于底端,则置灰下移和置底按钮
|
||||
if (order == maxOrder - 1) { |
||||
this.down.setEnabled(false); |
||||
this.bottom.setEnabled(false); |
||||
} |
||||
} |
||||
|
||||
private void refreshCurrentOrder() { |
||||
String[] currentTabOrder = parentPane.getCurrentOrder(); |
||||
HashSet<String> selectedTab = new HashSet<>(); |
||||
for (UICheckBox item : sortItems) { |
||||
selectedTab.add(item.getText()); |
||||
} |
||||
|
||||
// 未选中的tab,保持原排序不变
|
||||
Map<String, Integer> exTab = new HashMap<>(); |
||||
for (int i = 0; i < currentTabOrder.length; i++) { |
||||
if (!selectedTab.contains(currentTabOrder[i])) { |
||||
exTab.put(currentTabOrder[i], i); |
||||
} |
||||
} |
||||
|
||||
// 计算当前排序
|
||||
String[] newOrder = new String[currentTabOrder.length]; |
||||
Component[] components = sortItemPane.getComponents(); |
||||
for (String s : exTab.keySet()) { |
||||
newOrder[exTab.get(s)] = s; |
||||
} |
||||
|
||||
int t = 0; |
||||
for (int i = 0; i < newOrder.length; i++) { |
||||
if (StringUtils.isEmpty(newOrder[i])) { |
||||
newOrder[i] = ((MenuLabel) components[t++]).getText(); |
||||
} |
||||
} |
||||
parentPane.setCurrentOrder(newOrder); |
||||
} |
||||
|
||||
} |
@ -0,0 +1,96 @@
|
||||
package com.fr.design.actions.help.alphafine.component; |
||||
|
||||
import com.fr.design.gui.ilable.UILabel; |
||||
import com.fr.design.utils.DesignUtils; |
||||
|
||||
import javax.swing.BorderFactory; |
||||
import javax.swing.plaf.LabelUI; |
||||
import java.awt.Color; |
||||
import java.awt.Dimension; |
||||
import java.awt.event.MouseAdapter; |
||||
import java.awt.event.MouseEvent; |
||||
import java.awt.event.MouseListener; |
||||
import java.util.function.Function; |
||||
|
||||
/** |
||||
* 菜单选项label |
||||
* |
||||
* @author Link |
||||
* @version 11.0 |
||||
* Created by Link on 2022/9/18 |
||||
*/ |
||||
public class MenuLabel extends UILabel { |
||||
|
||||
private static final Color BACKGROUND_COLOR = Color.white; |
||||
private static final Color SELECTED_COLOR = new Color(0x419BF9); |
||||
private static final Color HOVERED_COLOR = new Color(0xd9ebfe); |
||||
private static final int HEIGHT = 23; |
||||
private static final int WIDTH = 147; |
||||
|
||||
private MenuLabelPane parentMenu; |
||||
private final Function function; |
||||
private boolean selected; |
||||
|
||||
public MenuLabel(String text, Function function) { |
||||
super(text); |
||||
this.function = function; |
||||
setOpaque(true); |
||||
addMouseListener(createMouseListener()); |
||||
} |
||||
|
||||
public void setParentMenu(MenuLabelPane menu) { |
||||
this.parentMenu = menu; |
||||
} |
||||
|
||||
|
||||
@Override |
||||
public void setUI(LabelUI ui) { |
||||
super.setUI(ui); |
||||
this.setBackground(BACKGROUND_COLOR); |
||||
this.setBorder(BorderFactory.createEmptyBorder(2, 10, 1, 10)); |
||||
this.setPreferredSize(new Dimension(WIDTH, HEIGHT)); |
||||
this.setFont(DesignUtils.getDefaultGUIFont().applySize(12)); |
||||
} |
||||
|
||||
public boolean isSelected() { |
||||
return selected; |
||||
} |
||||
|
||||
public void setSelected(boolean selected) { |
||||
if (selected) { |
||||
parentMenu.setNoneSelected(); |
||||
setBackground(SELECTED_COLOR); |
||||
function.apply(this); |
||||
this.selected = true; |
||||
} else { |
||||
setBackground(BACKGROUND_COLOR); |
||||
this.selected = false; |
||||
} |
||||
} |
||||
|
||||
MouseListener createMouseListener() { |
||||
return new MouseAdapter() { |
||||
@Override |
||||
public void mouseClicked(MouseEvent e) { |
||||
super.mouseClicked(e); |
||||
setSelected(true); |
||||
} |
||||
|
||||
@Override |
||||
public void mouseEntered(MouseEvent e) { |
||||
super.mouseEntered(e); |
||||
if (!selected) { |
||||
setBackground(HOVERED_COLOR); |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
public void mouseExited(MouseEvent e) { |
||||
super.mouseExited(e); |
||||
if (!selected) { |
||||
setBackground(BACKGROUND_COLOR); |
||||
} |
||||
} |
||||
}; |
||||
} |
||||
} |
@ -0,0 +1,34 @@
|
||||
package com.fr.design.actions.help.alphafine.component; |
||||
|
||||
import javax.swing.JPanel; |
||||
import java.awt.FlowLayout; |
||||
import java.util.List; |
||||
|
||||
/** |
||||
* 简单菜单面板 |
||||
* |
||||
* @author Link |
||||
* @version 11.0 |
||||
* Created by Link on 2022/9/18 |
||||
*/ |
||||
public class MenuLabelPane extends JPanel { |
||||
|
||||
private static final int GAP = 1; |
||||
|
||||
private List<MenuLabel> labels; |
||||
|
||||
public MenuLabelPane(List<MenuLabel> labels) { |
||||
this.labels = labels; |
||||
setLayout(new FlowLayout(FlowLayout.CENTER, GAP, GAP)); |
||||
for (MenuLabel label : labels) { |
||||
label.setParentMenu(this); |
||||
add(label); |
||||
} |
||||
} |
||||
|
||||
public void setNoneSelected() { |
||||
for (MenuLabel label : labels) { |
||||
label.setSelected(false); |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,19 @@
|
||||
package com.fr.design.actions.help.replace; |
||||
|
||||
|
||||
/** |
||||
* 定义一些底层操作 |
||||
* |
||||
* @author Destiny.Lin |
||||
* @version 11.0 |
||||
* created by Destiny.Lin on 2022-09-27 |
||||
*/ |
||||
public interface ITReplaceOperator { |
||||
|
||||
|
||||
/** |
||||
* 关闭面板 |
||||
*/ |
||||
void close(); |
||||
|
||||
} |
@ -0,0 +1,28 @@
|
||||
package com.fr.design.carton; |
||||
|
||||
import java.io.File; |
||||
|
||||
public class CartonFiles { |
||||
private File easyCheckerFile; |
||||
private File timerCheckerFile; |
||||
|
||||
public CartonFiles() { |
||||
|
||||
} |
||||
|
||||
public File getEasyCheckerFile() { |
||||
return easyCheckerFile; |
||||
} |
||||
|
||||
public void setEasyCheckerFile(File easyCheckerFile) { |
||||
this.easyCheckerFile = easyCheckerFile; |
||||
} |
||||
|
||||
public File getTimerCheckerFile() { |
||||
return timerCheckerFile; |
||||
} |
||||
|
||||
public void setTimerCheckerFile(File timerCheckerFile) { |
||||
this.timerCheckerFile = timerCheckerFile; |
||||
} |
||||
} |
@ -0,0 +1,161 @@
|
||||
package com.fr.design.carton; |
||||
|
||||
import com.fr.base.SimpleDateFormatThreadSafe; |
||||
import com.fr.design.i18n.Toolkit; |
||||
import com.fr.json.JSONObject; |
||||
|
||||
import java.util.Map; |
||||
import java.util.Timer; |
||||
import java.util.TimerTask; |
||||
import java.util.concurrent.*; |
||||
import java.util.concurrent.atomic.AtomicLong; |
||||
|
||||
|
||||
public class CartonThreadExecutorPool extends ThreadPoolExecutor { |
||||
private static final int MAX_LIVE_TIME = 3000; |
||||
private static final int MAX_WORKER_THREADS = 10; |
||||
/** |
||||
* 开启间隔检测后两次检测的相隔时间ms |
||||
*/ |
||||
private static final long CHECK_INTERVAL_MS = 100; |
||||
private final ThreadLocal<StackTraceElement[]> startReportedStack = new ThreadLocal<>(); |
||||
private volatile static CartonThreadExecutorPool cartonThreadExecutorPool; |
||||
private static final ConcurrentHashMap<Long, ThreadInfo> concurrentHashMap = new ConcurrentHashMap<>(); |
||||
private static final SimpleDateFormatThreadSafe simpleDateFormatThreadSafe = new SimpleDateFormatThreadSafe(); |
||||
private final static AtomicLong hangCount = new AtomicLong(0); |
||||
private Timer timer; |
||||
/** |
||||
* 一个变量,用于控制easy监测模式的开关 |
||||
*/ |
||||
private boolean easyWitch = false; |
||||
|
||||
public boolean isEasyWitch() { |
||||
return easyWitch; |
||||
} |
||||
|
||||
public void setEasyWitch(boolean easyWitch) { |
||||
this.easyWitch = easyWitch; |
||||
} |
||||
|
||||
private static class ThreadInfo { |
||||
private ThreadInfo () { |
||||
hangNumber = hangCount.getAndAdd(1); |
||||
} |
||||
private long hangNumber; |
||||
private final Thread eventThread = Thread.currentThread(); |
||||
private StackTraceElement[] lastReportedStack; |
||||
private final long startTime = System.currentTimeMillis(); |
||||
public void checkForHang() { |
||||
if (timeSoFar() > MAX_LIVE_TIME) { |
||||
examineHang(); |
||||
} |
||||
} |
||||
private long timeSoFar() { |
||||
return (System.currentTimeMillis() - startTime); |
||||
} |
||||
|
||||
private void examineHang() { |
||||
StackTraceElement[] currentStack = eventThread.getStackTrace(); |
||||
|
||||
if (lastReportedStack!=null && EventDispatchThreadHangMonitor.stacksEqual(currentStack, lastReportedStack)) { |
||||
return; |
||||
} |
||||
lastReportedStack = currentStack; |
||||
String stackTrace = EventDispatchThreadHangMonitor.stackTraceToString(currentStack); |
||||
JSONObject jsonObject = new JSONObject(); |
||||
jsonObject.put(Toolkit.i18nText("Fine-Design_Basic_Carton_Output_Time"), simpleDateFormatThreadSafe.format(System.currentTimeMillis())); |
||||
jsonObject.put(Toolkit.i18nText("Fine-Design_Basic_Carton_Event_Number"), "swingWorker_" + hangNumber); |
||||
jsonObject.put(Toolkit.i18nText("Fine-Design_Basic_Carton_Duration_Task_Execute"), timeSoFar() + "ms"); |
||||
jsonObject.put(Toolkit.i18nText("Fine-Design_Basic_Carton_Stack_Info"), stackTrace); |
||||
EventDispatchThreadHangMonitor.outPutJournalLog(jsonObject.toString(), SwitchForSwingChecker.TIMER_CHECK_FLAG); |
||||
EventDispatchThreadHangMonitor.checkForDeadlock(); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* 来自SwingWorker类 |
||||
*/ |
||||
public static ThreadFactory threadFactory = |
||||
new ThreadFactory() { |
||||
final ThreadFactory defaultFactory = |
||||
Executors.defaultThreadFactory(); |
||||
@Override |
||||
public Thread newThread(final Runnable r) { |
||||
Thread thread = |
||||
defaultFactory.newThread(r); |
||||
thread.setName("SwingWorker-" |
||||
+ thread.getName()); |
||||
thread.setDaemon(true); |
||||
return thread; |
||||
} |
||||
}; |
||||
|
||||
public static CartonThreadExecutorPool getTimerThreadExecutorPool () { |
||||
if (cartonThreadExecutorPool == null) { |
||||
synchronized (CartonThreadExecutorPool.class) { |
||||
if (cartonThreadExecutorPool == null) { |
||||
cartonThreadExecutorPool = |
||||
new CartonThreadExecutorPool(MAX_WORKER_THREADS, MAX_WORKER_THREADS, |
||||
10L, TimeUnit.MINUTES, |
||||
new LinkedBlockingQueue<Runnable>(),threadFactory |
||||
); |
||||
} |
||||
} |
||||
} |
||||
return cartonThreadExecutorPool; |
||||
} |
||||
|
||||
private CartonThreadExecutorPool(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory) { |
||||
super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory); |
||||
simpleDateFormatThreadSafe.applyPattern("yyyy-MM-dd HH:mm:ss"); |
||||
} |
||||
|
||||
@Override |
||||
protected void beforeExecute(Thread t, Runnable r) { |
||||
super.beforeExecute(t, r); |
||||
startReportedStack.set(t.getStackTrace()); |
||||
concurrentHashMap.put(t.getId(), new ThreadInfo()); |
||||
} |
||||
|
||||
@Override |
||||
protected void afterExecute(Runnable r, Throwable t) { |
||||
super.afterExecute(r, t); |
||||
long currentThreadId = Thread.currentThread().getId(); |
||||
long runTime = (System.currentTimeMillis() - concurrentHashMap.get(currentThreadId).startTime); |
||||
//加~是为了之后输出的时候换行。
|
||||
if (isEasyWitch() && runTime > MAX_LIVE_TIME) { |
||||
JSONObject jsonObject = new JSONObject(); |
||||
jsonObject.put(Toolkit.i18nText("Fine-Design_Basic_Carton_Output_Time"), simpleDateFormatThreadSafe.format(System.currentTimeMillis())); |
||||
jsonObject.put(Toolkit.i18nText("Fine-Design_Basic_Carton_Event_Number"), "swingWorker_" + concurrentHashMap.get(currentThreadId).hangNumber); |
||||
jsonObject.put(Toolkit.i18nText("Fine-Design_Basic_Carton_Task_Start_Time"), simpleDateFormatThreadSafe.format(concurrentHashMap.get(currentThreadId).startTime)); |
||||
jsonObject.put(Toolkit.i18nText("Fine-Design_Basic_Carton_Task_Total_Time"), runTime + "ms"); |
||||
EventDispatchThreadHangMonitor.outPutJournalLog(jsonObject.toString(), SwitchForSwingChecker.EASY_CHECK_FLAG); |
||||
|
||||
} |
||||
concurrentHashMap.remove(currentThreadId); |
||||
} |
||||
|
||||
public class Checker extends TimerTask { |
||||
@Override |
||||
public void run() { |
||||
if (cartonThreadExecutorPool == null || concurrentHashMap.isEmpty()) { |
||||
return; |
||||
} |
||||
for (Map.Entry<Long, ThreadInfo> map : concurrentHashMap.entrySet()) { |
||||
map.getValue().checkForHang(); |
||||
} |
||||
} |
||||
} |
||||
|
||||
public void initTimer() { |
||||
timer = new Timer("CheckerSwingWorker",true); |
||||
timer.schedule(new Checker(), 0, CHECK_INTERVAL_MS); |
||||
} |
||||
|
||||
public void stopTimer() { |
||||
if (timer != null) { |
||||
timer.cancel(); |
||||
} |
||||
} |
||||
|
||||
} |
@ -0,0 +1,45 @@
|
||||
package com.fr.design.carton; |
||||
|
||||
|
||||
public class CartonUploadMessage { |
||||
private String hangCount; |
||||
private String slowTime; |
||||
private String threadTime; |
||||
private String info; |
||||
|
||||
public CartonUploadMessage() { |
||||
|
||||
} |
||||
|
||||
public String getHangCount() { |
||||
return hangCount; |
||||
} |
||||
|
||||
public void setHangCount(String hangCount) { |
||||
this.hangCount = hangCount; |
||||
} |
||||
|
||||
public String getSlowTime() { |
||||
return slowTime; |
||||
} |
||||
|
||||
public void setSlowTime(String slowTime) { |
||||
this.slowTime = slowTime; |
||||
} |
||||
|
||||
public String getThreadTime() { |
||||
return threadTime; |
||||
} |
||||
|
||||
public void setThreadTime(String threadTime) { |
||||
this.threadTime = threadTime; |
||||
} |
||||
|
||||
public String getInfo() { |
||||
return info; |
||||
} |
||||
|
||||
public void setInfo(String info) { |
||||
this.info = info; |
||||
} |
||||
} |
@ -0,0 +1,408 @@
|
||||
package com.fr.design.carton; |
||||
|
||||
import com.fr.concurrent.FineExecutors; |
||||
import com.fr.design.ui.util.UIUtil; |
||||
import com.fr.json.JSONObject; |
||||
import com.fr.log.FineLoggerFactory; |
||||
import com.fr.stable.ArrayUtils; |
||||
import com.fr.stable.ProductConstantsBase; |
||||
import com.fr.stable.StableUtils; |
||||
import com.fr.stable.StringUtils; |
||||
import org.jetbrains.annotations.NotNull; |
||||
|
||||
import javax.swing.SwingUtilities; |
||||
import java.awt.EventQueue; |
||||
import java.awt.Toolkit; |
||||
import java.awt.AWTEvent; |
||||
import java.awt.event.WindowEvent; |
||||
import java.io.BufferedWriter; |
||||
import java.io.File; |
||||
import java.io.FileWriter; |
||||
import java.io.IOException; |
||||
import java.lang.management.ManagementFactory; |
||||
import java.lang.management.ThreadInfo; |
||||
import java.lang.management.ThreadMXBean; |
||||
import java.text.SimpleDateFormat; |
||||
import java.util.LinkedList; |
||||
import java.util.Timer; |
||||
import java.util.TimerTask; |
||||
import java.util.concurrent.ScheduledExecutorService; |
||||
import java.util.concurrent.TimeUnit; |
||||
|
||||
/** |
||||
* 参考自git swinghelper |
||||
* 用于卡顿检测 |
||||
* 主要是两块内容 |
||||
* 1.获取eventQueue中每个事件的执行时间 |
||||
* 2.用一个Timer定时去检测当前执行任务的线程 |
||||
*/ |
||||
|
||||
public final class EventDispatchThreadHangMonitor extends EventQueue { |
||||
/** |
||||
* 日期事件格式 |
||||
*/ |
||||
private static final SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); |
||||
public static final EventDispatchThreadHangMonitor INSTANCE = new EventDispatchThreadHangMonitor(); |
||||
/** |
||||
* 一个timer |
||||
*/ |
||||
private Timer timer; |
||||
/** |
||||
* 开启间隔检测后两次检测的相隔时间ms |
||||
*/ |
||||
private static final long CHECK_INTERVAL_MS = 100; |
||||
/** |
||||
* 最大的事件允许执行时间,超过该时间则打印堆栈等相关信息 |
||||
*/ |
||||
private static final long UNREASONABLE_DISPATCH_DURATION_MS = 1500; |
||||
/** |
||||
* 事件唯一编码,用于方便日志的查看 |
||||
*/ |
||||
private static long hangCount = 0; |
||||
/** |
||||
* 输出日志所在地址 |
||||
*/ |
||||
private static final String JOURNAL_FILE_PATH = StableUtils.pathJoin(ProductConstantsBase.getEnvHome(), "journal_log"); |
||||
/** |
||||
* 类似于一个开关,当该值为默认的false启动时,定时任务在窗口开启前都不会对执行的事件进行检查 |
||||
*/ |
||||
private boolean haveShownSomeComponent = false; |
||||
/** |
||||
* 该链表为主要的实现定时任务的容器,在重写的dispatchEvent中由pre方法将DispatchInfo加入到链表,由post方法remove |
||||
*/ |
||||
private final LinkedList<DispatchInfo> dispatches = new LinkedList<DispatchInfo>(); |
||||
/** |
||||
* 一个变量,用于控制easy监测模式的开关 |
||||
*/ |
||||
private boolean easyWitch = false; |
||||
private static ScheduledExecutorService scheduledExecutorService; |
||||
|
||||
public boolean isEasyWitch() { |
||||
return easyWitch; |
||||
} |
||||
|
||||
public void setEasyWitch(boolean easyWitch) { |
||||
this.easyWitch = easyWitch; |
||||
} |
||||
|
||||
/** |
||||
* 一个变量,用于记录Timer的开关。 |
||||
*/ |
||||
|
||||
public boolean isTimerWitch() { |
||||
return timerWitch; |
||||
} |
||||
|
||||
public void setTimerWitch(boolean timerWitch) { |
||||
this.timerWitch = timerWitch; |
||||
} |
||||
|
||||
private boolean timerWitch = false; |
||||
|
||||
private synchronized static long getHangCount() { |
||||
return hangCount++; |
||||
} |
||||
|
||||
/** |
||||
* @param a can not be null |
||||
* @param b can not be null |
||||
* @return |
||||
*/ |
||||
public static boolean stacksEqual(@NotNull StackTraceElement[] a, @NotNull StackTraceElement[] b) { |
||||
|
||||
if (!ArrayUtils.isSameLength(a, b)) { |
||||
return false; |
||||
} |
||||
for (int i = 0; i < a.length; ++i) { |
||||
if (!a[i].equals(b[i])) { |
||||
return false; |
||||
} |
||||
} |
||||
return true; |
||||
} |
||||
|
||||
/** |
||||
* 用于判断是不是特定的堆栈 |
||||
*/ |
||||
public static boolean stackTraceElementIs(StackTraceElement e, String className, String methodName, boolean isNative) { |
||||
return e.getClassName().equals(className) && e.getMethodName().equals(methodName) && e.isNativeMethod() == isNative; |
||||
} |
||||
|
||||
/** |
||||
* 用于判断某个堆栈是否在等待另一个事件 |
||||
* 取当前堆栈前三层判断是是不是匹配等待堆栈的格式 |
||||
*/ |
||||
public static boolean isWaitingForNextEvent(StackTraceElement[] currentStack) { |
||||
|
||||
return currentStack != null && currentStack.length >= 3 && |
||||
stackTraceElementIs(currentStack[0], "java.lang.Object", "wait", true) |
||||
&& stackTraceElementIs(currentStack[1], "java.lang.Object", "wait", false) |
||||
&& stackTraceElementIs(currentStack[2], "java.awt.EventQueue", "getNextEvent", false); |
||||
} |
||||
|
||||
/** |
||||
* event事件的包装类 |
||||
*/ |
||||
public static class DispatchInfo { |
||||
// 上一次被打印的堆栈ou
|
||||
private StackTraceElement[] lastReportedStack; |
||||
//获取执行该事件的线程
|
||||
private final Thread eventDispatchThread = Thread.currentThread(); |
||||
//在队列中等待执行的事件最后未执行的时间,当有一个事件执行完后就遍历dispatches给该值赋当前时间
|
||||
private long lastDispatchTimeMillis = System.currentTimeMillis(); |
||||
//事件开始的时间
|
||||
private final long startDispatchTimeMillis = System.currentTimeMillis(); |
||||
//事件编号
|
||||
private long hangNumber; |
||||
//构造函数,给当前对象赋一个递增的唯一编号
|
||||
public DispatchInfo() { |
||||
hangNumber = getHangCount(); |
||||
} |
||||
//定时调度任务检测的入口,如果执行时间大于设定的值就进入examineHang()方法
|
||||
public void checkForHang() { |
||||
if (timeSoFar() > UNREASONABLE_DISPATCH_DURATION_MS) { |
||||
examineHang(); |
||||
} |
||||
} |
||||
//超时堆栈的具体处理
|
||||
private void examineHang() { |
||||
//获取执行线程的当前堆栈
|
||||
StackTraceElement[] currentStack = eventDispatchThread.getStackTrace(); |
||||
if (isWaitingForNextEvent(currentStack)) { |
||||
return; |
||||
} |
||||
//某个事件执行时间很长,定时处理时可能会连续打很多个堆栈,对同一个事件的相同堆栈只打一次
|
||||
if (lastReportedStack !=null && stacksEqual(lastReportedStack, currentStack)) { |
||||
return; |
||||
} |
||||
String stackTrace = stackTraceToString(currentStack); |
||||
lastReportedStack = currentStack; |
||||
JSONObject jsonObject = new JSONObject(); |
||||
jsonObject.put(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Carton_Output_Time"), simpleDateFormat.format(System.currentTimeMillis())); |
||||
jsonObject.put(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Carton_Event_Number"), "eventQueue_" + hangNumber); |
||||
jsonObject.put(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Carton_Duration_Task_Execute"), timeSoFar() + "ms"); |
||||
jsonObject.put(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Carton_Stack_Info"), stackTrace); |
||||
outPutJournalLog(jsonObject.toString(), SwitchForSwingChecker.TIMER_CHECK_FLAG); |
||||
checkForDeadlock(); |
||||
} |
||||
//记录连续运行了多长时间
|
||||
private long timeSoFar() { |
||||
return (System.currentTimeMillis() - lastDispatchTimeMillis); |
||||
} |
||||
//记录一个事件从被分发到结束的总运行时间
|
||||
private long totalTime() { |
||||
return (System.currentTimeMillis() - startDispatchTimeMillis); |
||||
} |
||||
//事件处理完后的时间判断
|
||||
public void dispose() { |
||||
if (timeSoFar() > UNREASONABLE_DISPATCH_DURATION_MS) { |
||||
exportCartonLog(true); |
||||
} else if (lastReportedStack != null){ |
||||
exportCartonLog(false); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* |
||||
* @param flag 判断一下输出日志时要输出哪个时间 |
||||
*/ |
||||
private void exportCartonLog(boolean flag) { |
||||
JSONObject jsonObject = new JSONObject(); |
||||
jsonObject.put(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Carton_Output_Time"), simpleDateFormat.format(System.currentTimeMillis())); |
||||
jsonObject.put(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Carton_Event_Number"), "eventQueue_" + hangNumber); |
||||
jsonObject.put(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Carton_Task_Start_Time"), simpleDateFormat.format(startDispatchTimeMillis)); |
||||
if (flag) { |
||||
jsonObject.put(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Carton_Task_Total_Time"), timeSoFar() + "ms"); |
||||
} else { |
||||
jsonObject.put(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Carton_Task_Total_Time"), totalTime() + "ms"); |
||||
} |
||||
outPutJournalLog(jsonObject.toString(), SwitchForSwingChecker.EASY_CHECK_FLAG); |
||||
} |
||||
} |
||||
|
||||
public static void outPutJournalLog(String message, int flag) { |
||||
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd"); |
||||
String date = simpleDateFormat.format(System.currentTimeMillis()); |
||||
String filename = flag == SwitchForSwingChecker.EASY_CHECK_FLAG ? SwitchForSwingChecker.EASY_CHECKER_FILE_NAME: SwitchForSwingChecker.TIMER_CHECKER_FILE_NAME; |
||||
String[] split = date.split("-"); |
||||
int month = StringUtils.isEmpty(split[1]) ? -1 : Integer.parseInt(split[1]); |
||||
String dirPath = StableUtils.pathJoin(JOURNAL_FILE_PATH, split[0], "month-" + month, date); |
||||
File dirFile = new File(dirPath); |
||||
File file = new File( StableUtils.pathJoin(dirPath, filename)); |
||||
try { |
||||
if (!file.exists()) { |
||||
if (!dirFile.exists()) { |
||||
dirFile.mkdirs(); |
||||
} |
||||
file.createNewFile(); |
||||
} |
||||
BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(file, true)); |
||||
String outputMessage = new StringBuilder(message.replaceAll("~", "\r\n")).append(",").append("\r\n").toString(); |
||||
bufferedWriter.write(outputMessage); |
||||
bufferedWriter.close(); |
||||
} catch (IOException e) { |
||||
FineLoggerFactory.getLogger().error("output fail", e); |
||||
} |
||||
} |
||||
|
||||
private EventDispatchThreadHangMonitor() { |
||||
|
||||
} |
||||
|
||||
/** |
||||
* 参考SwingExplorer,在处理模态框时没有做特殊处理,也不会输出卡顿堆栈 |
||||
* 原因是SwingExplorer窗口一直有一个监听事件,不断的add,remove。 |
||||
* 由于卡顿日志输出的是事件连续执行的时间,所以一个长时间存在的模态框被不断重复的监听事件刷新时间,就不会输出了。 |
||||
* 当检测开关打开后,在这里模拟一下监听事件,给个不耗时的任务就可以。 |
||||
*/ |
||||
public void startFilterModalWindow() { |
||||
scheduledExecutorService = FineExecutors.newSingleThreadScheduledExecutor(); |
||||
scheduledExecutorService.scheduleAtFixedRate(new Runnable() { |
||||
@Override |
||||
public void run() { |
||||
SwingUtilities.invokeLater(new Runnable() { |
||||
@Override |
||||
public void run() { |
||||
//不用干事,切个片就可以
|
||||
} |
||||
}); |
||||
} |
||||
}, 0, 500, TimeUnit.MILLISECONDS); |
||||
} |
||||
|
||||
public void stopFilterModalWindow() { |
||||
if (scheduledExecutorService != null) { |
||||
scheduledExecutorService.shutdown(); |
||||
} |
||||
} |
||||
/** |
||||
* Sets up a timer to check for hangs frequently. |
||||
* 初始化一个Timer |
||||
*/ |
||||
public void initTimer() { |
||||
final long initialDelayMs = 0; |
||||
final boolean daemon = true; |
||||
timer = new Timer("EventDispatchThreadHangMonitor", daemon); |
||||
timer.schedule(new HangChecker(), initialDelayMs, CHECK_INTERVAL_MS); |
||||
} |
||||
|
||||
/** |
||||
* /消除Timer |
||||
*/ |
||||
public void stopTimer() { |
||||
if (timer != null) { |
||||
timer.cancel(); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* /定时执行的任务 |
||||
*/ |
||||
public class HangChecker extends TimerTask { |
||||
@Override |
||||
public void run() { |
||||
synchronized (dispatches) { |
||||
//如果链表为空或者窗口还没启开,定时检测就不进行
|
||||
if (dispatches.isEmpty() || !haveShownSomeComponent) { |
||||
return; |
||||
} |
||||
dispatches.getLast().checkForHang(); |
||||
} |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* 将swing中默认的EventQueue换成自己的 |
||||
*/ |
||||
public static void initMonitoring() { |
||||
UIUtil.invokeLaterIfNeeded(() -> Toolkit.getDefaultToolkit().getSystemEventQueue().push(INSTANCE)); |
||||
} |
||||
|
||||
/** |
||||
* Overrides EventQueue.dispatchEvent to call our pre and post hooks either |
||||
* side of the system's event dispatch code. |
||||
* 重写 |
||||
*/ |
||||
@Override |
||||
protected void dispatchEvent(AWTEvent event) { |
||||
//如果两个开关都没开,那就不走重写方法了
|
||||
if (!isEasyWitch() && !isTimerWitch()) { |
||||
super.dispatchEvent(event); |
||||
} else { |
||||
try { |
||||
preDispatchEvent(); |
||||
super.dispatchEvent(event); |
||||
} finally { |
||||
postDispatchEvent(); |
||||
if (!haveShownSomeComponent && |
||||
event instanceof WindowEvent && event.getID() == WindowEvent.WINDOW_OPENED) { |
||||
haveShownSomeComponent = true; |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Starts tracking a dispatch. |
||||
*/ |
||||
private synchronized void preDispatchEvent() { |
||||
synchronized (dispatches) { |
||||
dispatches.addLast(new DispatchInfo()); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Stops tracking a dispatch. |
||||
*/ |
||||
private synchronized void postDispatchEvent() { |
||||
synchronized (dispatches) { |
||||
DispatchInfo justFinishedDispatch = dispatches.removeLast(); |
||||
if (isEasyWitch()) { |
||||
justFinishedDispatch.dispose(); |
||||
} |
||||
//嵌套最深的事件执行完毕后刷新链表中其他事件的lastDispatchTimeMillis
|
||||
Thread currentEventDispatchThread = Thread.currentThread(); |
||||
for (DispatchInfo dispatchInfo : dispatches) { |
||||
if (dispatchInfo.eventDispatchThread == currentEventDispatchThread) { |
||||
dispatchInfo.lastDispatchTimeMillis = System.currentTimeMillis(); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* 检查死锁 |
||||
*/ |
||||
public static void checkForDeadlock() { |
||||
ThreadMXBean threadBean = ManagementFactory.getThreadMXBean(); |
||||
long[] threadIds = threadBean.findDeadlockedThreads(); |
||||
if (threadIds == null) { |
||||
return; |
||||
} |
||||
FineLoggerFactory.getLogger().warn("deadlock detected involving the following threads:"); |
||||
ThreadInfo[] threadInfos = threadBean.getThreadInfo(threadIds, Integer.MAX_VALUE); |
||||
for (ThreadInfo info : threadInfos) { |
||||
FineLoggerFactory.getLogger().warn("Thread # {} {} ( {} ) waiting on {} held by {} {}", info.getThreadId(), info.getThreadName(), |
||||
info.getThreadState(), info.getLockName(), info.getLockOwnerName(), stackTraceToStringForConsole(info.getStackTrace())); |
||||
} |
||||
} |
||||
|
||||
public static String stackTraceToString(StackTraceElement[] stackTrace) { |
||||
StringBuilder result = new StringBuilder(); |
||||
for (StackTraceElement stackTraceElement : stackTrace) { |
||||
String indentation = " "; |
||||
result.append("~").append(indentation).append(stackTraceElement); |
||||
} |
||||
return result.toString(); |
||||
} |
||||
|
||||
public static String stackTraceToStringForConsole(StackTraceElement[] stackTrace) { |
||||
StringBuilder result = new StringBuilder(); |
||||
for (StackTraceElement stackTraceElement : stackTrace) { |
||||
String indentation = " "; |
||||
result.append("\r\n").append(indentation).append(stackTraceElement); |
||||
} |
||||
return result.toString(); |
||||
} |
||||
|
||||
} |
@ -0,0 +1,406 @@
|
||||
package com.fr.design.carton; |
||||
|
||||
import com.fr.decision.webservice.v10.log.download.utils.LogZipUtils; |
||||
import com.fr.design.DesignerEnvManager; |
||||
import com.fr.design.constants.UIConstants; |
||||
import com.fr.design.dialog.FineJOptionPane; |
||||
import com.fr.design.env.DesignerWorkspaceInfo; |
||||
import com.fr.design.gui.date.UIDatePicker; |
||||
import com.fr.design.gui.ibutton.UIButton; |
||||
import com.fr.design.gui.icheckbox.UICheckBox; |
||||
import com.fr.design.gui.ilable.UILabel; |
||||
import com.fr.design.i18n.Toolkit; |
||||
import com.fr.design.layout.FRGUIPaneFactory; |
||||
import com.fr.design.mainframe.DesignerContext; |
||||
import com.fr.design.utils.gui.GUICoreUtils; |
||||
import com.fr.env.detect.ui.EnvDetectorDialog; |
||||
import com.fr.file.FILE; |
||||
import com.fr.file.FILEChooserPane; |
||||
import com.fr.file.FILEFactory; |
||||
import com.fr.file.filter.ChooseFileFilter; |
||||
import com.fr.general.GeneralUtils; |
||||
import com.fr.log.FineLoggerFactory; |
||||
import com.fr.stable.StableUtils; |
||||
import com.fr.stable.StringUtils; |
||||
import org.jetbrains.annotations.Nullable; |
||||
import com.fr.workspace.WorkContext; |
||||
|
||||
|
||||
import javax.swing.BorderFactory; |
||||
import javax.swing.JDialog; |
||||
import javax.swing.JOptionPane; |
||||
import javax.swing.JPanel; |
||||
import javax.swing.UIManager; |
||||
import javax.swing.filechooser.FileSystemView; |
||||
import java.awt.BorderLayout; |
||||
import java.awt.Color; |
||||
import java.awt.Component; |
||||
import java.awt.Cursor; |
||||
import java.awt.Dimension; |
||||
import java.awt.FlowLayout; |
||||
import java.awt.Frame; |
||||
import java.awt.event.MouseAdapter; |
||||
import java.awt.event.MouseEvent; |
||||
import java.awt.event.WindowEvent; |
||||
import java.io.ByteArrayOutputStream; |
||||
import java.io.File; |
||||
import java.io.FileInputStream; |
||||
import java.text.ParseException; |
||||
import java.util.List; |
||||
import java.util.Objects; |
||||
|
||||
|
||||
public class FeedbackToolboxDialog extends JDialog { |
||||
private UIDatePicker uiDatePicker; |
||||
private JPanel generalSettingPanel = null; |
||||
private UICheckBox easyCheckerButton = null; |
||||
private UICheckBox timerCheckerButton = null; |
||||
private UIButton uploadButton = null; |
||||
private UILabel exportLogLabel = null; |
||||
private final Color backgroundColor = new Color(240, 240, 243, 1); |
||||
private final Color lineColor = new Color(192, 192, 192, 120); |
||||
private JPanel body = null; |
||||
private static final String WORK_SPACE_PATH = "reportlets"; |
||||
private static final int BUFFER_SIZE = 2 * 1024; |
||||
|
||||
public FeedbackToolboxDialog(Frame owner) { |
||||
super(owner, Toolkit.i18nText("Fine-Design_Basic_Carton_Feedback_ToolBox")); |
||||
setResizable(false); |
||||
this.setLayout(FRGUIPaneFactory.createBorderLayout()); |
||||
createBodyPanel(); |
||||
add(body); |
||||
setSize(body.getPreferredSize()); |
||||
setSwitches(!StringUtils.isEmpty(GeneralUtils.objectToString(uiDatePicker.getSelectedItem()))); |
||||
repaint(); |
||||
GUICoreUtils.centerWindow(this); |
||||
} |
||||
|
||||
public void createBodyPanel() { |
||||
JPanel body = FRGUIPaneFactory.createBorderLayout_L_Pane(); |
||||
body.setBackground(backgroundColor); |
||||
JPanel titlePane = createTitlePane(); |
||||
JPanel tailPane = createTailPane(); |
||||
JPanel midPane = createMidPane(); |
||||
JPanel infoPane = createInfoPane(); |
||||
body.add(titlePane, BorderLayout.NORTH); |
||||
body.add(tailPane, BorderLayout.SOUTH); |
||||
body.add(midPane, BorderLayout.CENTER); |
||||
midPane.add(infoPane, BorderLayout.NORTH); |
||||
Dimension dimension = new Dimension(662, 556); |
||||
body.setPreferredSize(dimension); |
||||
this.body = body; |
||||
} |
||||
|
||||
private JPanel createInfoPane() { |
||||
JPanel northPane = FRGUIPaneFactory.createNColumnGridInnerContainer_Pane(2, 10, 10); |
||||
UILabel title = new UILabel(); |
||||
title.setText(" " + Toolkit.i18nText("Fine-Design_Basic_Carton_Record_Lag_Time") + ": "); |
||||
//判断一下当天是否有卡顿日志记录,如果有将日期设置为当天,如果没有设置为空
|
||||
boolean cartonExists = SwitchForSwingChecker.isCartonExists(); |
||||
if (cartonExists) { |
||||
this.uiDatePicker = new UIDatePicker(UIDatePicker.STYLE_CN_DATE1, this); |
||||
} else { |
||||
this.uiDatePicker = new UIDatePicker(UIDatePicker.STYLE_CN_DATE1, null, this); |
||||
} |
||||
Dimension dimension = new Dimension(160, 100); |
||||
uiDatePicker.setPreferredSize(dimension); |
||||
northPane.add(GUICoreUtils.createFlowPane(new Component[]{title, uiDatePicker}, FlowLayout.LEFT)); |
||||
exportLogLabel = new UILabel(); |
||||
exportLogLabel.setText(Toolkit.i18nText("Fine-Design_Basic_Carton_Export_Carton_Log")); |
||||
exportLogLabel.setForeground(UIConstants.FLESH_BLUE); |
||||
exportLogLabel.addMouseListener(new MouseAdapter() { |
||||
@Override |
||||
public void mouseClicked(MouseEvent e) { |
||||
if (exportLogLabel.isEnabled()) { |
||||
exportLogFile(); |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
public void mouseEntered(MouseEvent evt) { |
||||
Object source = evt.getSource(); |
||||
if (source instanceof UILabel) { |
||||
((UILabel) source).setCursor(new Cursor(Cursor.HAND_CURSOR)); |
||||
} |
||||
} |
||||
}); |
||||
|
||||
northPane.add(GUICoreUtils.createFlowPane(exportLogLabel, FlowLayout.RIGHT)); |
||||
return northPane; |
||||
} |
||||
|
||||
private void exportLogFile() { |
||||
String selectDate = GeneralUtils.objectToString(uiDatePicker.getSelectedItem()); |
||||
FILEChooserPane fileChooserPane = FILEChooserPane.getInstance(); |
||||
StringBuilder fileName = new StringBuilder(); |
||||
fileName.append(selectDate).append(Toolkit.i18nText("Fine-Design_Basic_Carton_Carton_Log")); |
||||
fileChooserPane.setFileNameTextField(fileName.toString(), " "); |
||||
fileChooserPane.removeAllFilter(); |
||||
fileChooserPane.addChooseFILEFilter(new ChooseFileFilter("zip", Toolkit.i18nText("Fine-Design_Basic_Carton_Compile_File"))); |
||||
//默认选择桌面
|
||||
FILE desktop = FILEFactory.createFILE(FILEFactory.FILE_PREFIX + FileSystemView.getFileSystemView().getHomeDirectory().getPath()); |
||||
fileChooserPane.setCurrentDirectory(desktop); |
||||
int chooseResult = fileChooserPane.showSaveDialog(DesignerContext.getDesignerFrame()); |
||||
if (chooseResult == 0) { |
||||
FILE selectedFile = fileChooserPane.getSelectedFILE(); |
||||
String path = selectedFile.getPath(); |
||||
//selectDate 2002-03-09例子
|
||||
String[] split = selectDate.split("-"); |
||||
int month = Integer.parseInt(split[1]); |
||||
String sourceFilePath = StableUtils.pathJoin(SwitchForSwingChecker.JOURNAL_FILE_PATH, split[0], "month-" + month, selectDate); |
||||
File sourceFile = new File(sourceFilePath); |
||||
if (sourceFile.exists()) { |
||||
exportCartonLog(sourceFile, path, sourceFilePath); |
||||
} |
||||
fileChooserPane.removeAllFilter(); |
||||
} |
||||
} |
||||
|
||||
private JPanel createTailPane() { |
||||
JPanel tailPanel = FRGUIPaneFactory.createBorderLayout_S_Pane(); |
||||
tailPanel.setBorder(BorderFactory.createMatteBorder(1, 0, 0, 0, lineColor)); |
||||
JPanel actionsPanel = FRGUIPaneFactory.createBorderLayout_S_Pane(); |
||||
actionsPanel.setLayout(FRGUIPaneFactory.createM_BorderLayout()); |
||||
{ |
||||
uploadButton = new UIButton(Toolkit.i18nText("Fine-Design_Basic_Carton_Upload_Carton_Log")); |
||||
uploadButton.addActionListener((e) -> { |
||||
try { |
||||
List<CartonUploadMessage> list = SwitchForSwingChecker.uploadJournalLog(uiDatePicker.getSelectedDate()); |
||||
if (list.isEmpty()) { |
||||
FineJOptionPane.showMessageDialog(null, Toolkit.i18nText("Fine_Design_Basic_Upload_Fail"), UIManager.getString("OptionPane.messageDialogTitle"), JOptionPane.ERROR_MESSAGE); |
||||
} else { |
||||
FineJOptionPane.showMessageDialog(null, Toolkit.i18nText("Fine-Design_Basic_Upload_Success")); |
||||
} |
||||
} catch (ParseException parseException) { |
||||
FineLoggerFactory.getLogger().error("parse error", parseException); |
||||
} |
||||
|
||||
}); |
||||
actionsPanel.add(uploadButton, BorderLayout.WEST); |
||||
|
||||
UIButton cancelButton = new UIButton(Toolkit.i18nText("Fine-Design_Basic_Cancel")); |
||||
cancelButton.addActionListener((e) -> { |
||||
setVisible(false); |
||||
EnvDetectorDialog envDetectorDialog = new EnvDetectorDialog(DesignerContext.getDesignerFrame()); |
||||
envDetectorDialog.setVisible(true); |
||||
dispose(); |
||||
}); |
||||
actionsPanel.add(cancelButton, BorderLayout.EAST); |
||||
} |
||||
UIButton currencySetButton = new UIButton(Toolkit.i18nText("Fine-Design_Basic_Carton_General_Settings")); |
||||
currencySetButton.addActionListener((e -> { |
||||
createGeneralSettingPanel(); |
||||
this.remove(body); |
||||
this.add(generalSettingPanel); |
||||
setSize(generalSettingPanel.getPreferredSize()); |
||||
repaint(); |
||||
setVisible(true); |
||||
})); |
||||
tailPanel.add(actionsPanel, BorderLayout.EAST); |
||||
tailPanel.add(currencySetButton, BorderLayout.WEST); |
||||
return tailPanel; |
||||
} |
||||
|
||||
private JPanel createTitlePane() { |
||||
JPanel titlePane = FRGUIPaneFactory.createBorderLayout_M_Pane(); |
||||
titlePane.setBorder(BorderFactory.createMatteBorder(0, 0, 1, 0, lineColor)); |
||||
UILabel uiLabel = new UILabel(Toolkit.i18nText("Fine-Design_Basic_Carton_Carton_Journal_Record")); |
||||
uiLabel.setForeground(UIConstants.FLESH_BLUE); |
||||
titlePane.add(uiLabel, BorderLayout.WEST); |
||||
return titlePane; |
||||
} |
||||
|
||||
private JPanel createMidPane() { |
||||
JPanel midPanel = FRGUIPaneFactory.createBorderLayout_L_Pane(); |
||||
return midPanel; |
||||
} |
||||
|
||||
/** |
||||
* 下面是通用设置的面板 |
||||
*/ |
||||
private void createGeneralSettingPanel() { |
||||
JPanel generalSettingPanel = FRGUIPaneFactory.createBorderLayout_L_Pane(); |
||||
JPanel tailPaneInGeneralSettings = createTailPaneInGeneralSettings(); |
||||
generalSettingPanel.add(tailPaneInGeneralSettings, BorderLayout.SOUTH); |
||||
|
||||
JPanel titlePaneInGeneralSettings = createTitlePaneInGeneralSettings(); |
||||
generalSettingPanel.add(titlePaneInGeneralSettings, BorderLayout.NORTH); |
||||
|
||||
JPanel midPanel = FRGUIPaneFactory.createBorderLayout_L_Pane(); |
||||
generalSettingPanel.add(midPanel, BorderLayout.CENTER); |
||||
|
||||
JPanel infoPane = createInfoPanelInGeneralSettings(); |
||||
midPanel.add(infoPane, BorderLayout.NORTH); |
||||
|
||||
Dimension dimension = new Dimension(662, 556); |
||||
generalSettingPanel.setPreferredSize(dimension); |
||||
generalSettingPanel.setBackground(backgroundColor); |
||||
this.generalSettingPanel = generalSettingPanel; |
||||
} |
||||
|
||||
private JPanel createTitlePaneInGeneralSettings() { |
||||
JPanel titlePane = FRGUIPaneFactory.createBorderLayout_L_Pane(); |
||||
titlePane.setBorder(BorderFactory.createMatteBorder(0, 0, 1, 0, lineColor)); |
||||
UILabel uiLabel = new UILabel(Toolkit.i18nText("Fine-Design_Basic_Carton_Carton_Journal_Record") + "/"); |
||||
uiLabel.addMouseListener(new MouseAdapter() { |
||||
@Override |
||||
public void mouseClicked(MouseEvent e) { |
||||
createBodyPanel(); |
||||
remove(generalSettingPanel); |
||||
add(body); |
||||
setPreferredSize(body.getPreferredSize()); |
||||
setSwitches(!StringUtils.isEmpty(GeneralUtils.objectToString(uiDatePicker.getSelectedItem()))); |
||||
repaint(); |
||||
setVisible(true); |
||||
} |
||||
|
||||
@Override |
||||
public void mouseEntered(MouseEvent evt) { |
||||
Object source = evt.getSource(); |
||||
if (source instanceof UILabel) { |
||||
((UILabel) source).setCursor(new Cursor(Cursor.HAND_CURSOR)); |
||||
} |
||||
} |
||||
}); |
||||
UILabel uiCurrentLabel = new UILabel(Toolkit.i18nText("Fine-Design_Basic_Carton_General_Settings")); |
||||
uiCurrentLabel.setForeground(UIConstants.FLESH_BLUE); |
||||
titlePane.add(GUICoreUtils.createFlowPane(new Component[]{uiLabel, uiCurrentLabel}, FlowLayout.LEFT)); |
||||
return titlePane; |
||||
} |
||||
|
||||
private JPanel createTailPaneInGeneralSettings() { |
||||
JPanel tailPanel = FRGUIPaneFactory.createBorderLayout_S_Pane(); |
||||
tailPanel.setBorder(BorderFactory.createMatteBorder(1, 0, 0, 0, lineColor)); |
||||
JPanel actionsPanel = FRGUIPaneFactory.createBorderLayout_S_Pane(); |
||||
actionsPanel.setLayout(FRGUIPaneFactory.createM_BorderLayout()); |
||||
{ |
||||
UIButton confirmButton = new UIButton(Toolkit.i18nText("Fine-Design_Basic_Save")); |
||||
confirmButton.addActionListener((e) -> { |
||||
if (easyCheckerButton.isSelected()) { |
||||
SwitchForSwingChecker.startEasyChecker(); |
||||
} else { |
||||
SwitchForSwingChecker.stopEasyChecker(); |
||||
} |
||||
if (timerCheckerButton.isSelected()) { |
||||
SwitchForSwingChecker.startTimerChecker(); |
||||
} else { |
||||
SwitchForSwingChecker.stopTimerChecker(); |
||||
} |
||||
createBodyPanel(); |
||||
remove(generalSettingPanel); |
||||
add(body); |
||||
setPreferredSize(body.getPreferredSize()); |
||||
setSwitches(!StringUtils.isEmpty(GeneralUtils.objectToString(uiDatePicker.getSelectedItem()))); |
||||
repaint(); |
||||
setVisible(true); |
||||
}); |
||||
actionsPanel.add(confirmButton, BorderLayout.WEST); |
||||
|
||||
UIButton cancelButton = new UIButton(Toolkit.i18nText("Fine-Design_Basic_Cancel")); |
||||
cancelButton.addActionListener((e) -> { |
||||
createBodyPanel(); |
||||
remove(generalSettingPanel); |
||||
add(body); |
||||
setPreferredSize(body.getPreferredSize()); |
||||
repaint(); |
||||
setVisible(true); |
||||
|
||||
}); |
||||
actionsPanel.add(cancelButton, BorderLayout.EAST); |
||||
} |
||||
tailPanel.add(actionsPanel, BorderLayout.EAST); |
||||
return tailPanel; |
||||
} |
||||
|
||||
private JPanel createInfoPanelInGeneralSettings() { |
||||
JPanel infoPane = FRGUIPaneFactory.createNColumnGridInnerContainer_S_Pane(1); |
||||
easyCheckerButton = new UICheckBox(Toolkit.i18nText("Fine-Design_Basic_Carton_Operation_Time_Consuming_Detection")); |
||||
timerCheckerButton = new UICheckBox(Toolkit.i18nText("Fine-Design_Basic_Carton_Carton_Operation_Class_Capture")); |
||||
easyCheckerButton.setSelected(SwitchForSwingChecker.isEasyChecker()); |
||||
timerCheckerButton.setSelected(SwitchForSwingChecker.isCheckerTimerSwitch()); |
||||
infoPane.add(GUICoreUtils.createFlowPane(easyCheckerButton, FlowLayout.LEFT)); |
||||
infoPane.add(GUICoreUtils.createFlowPane(timerCheckerButton, FlowLayout.LEFT)); |
||||
return infoPane; |
||||
} |
||||
|
||||
@Override |
||||
protected void processWindowEvent(WindowEvent e) { |
||||
super.processWindowEvent(e); |
||||
if (e.getID() == WindowEvent.WINDOW_CLOSING) { |
||||
EnvDetectorDialog envDetectorDialog = new EnvDetectorDialog(DesignerContext.getDesignerFrame()); |
||||
envDetectorDialog.setVisible(true); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* 上传和导出卡顿日志的可用化处理,如果没有选择日期就不可用 |
||||
*/ |
||||
public void setSwitches(boolean flag) { |
||||
uploadButton.setEnabled(flag); |
||||
exportLogLabel.setEnabled(flag); |
||||
} |
||||
|
||||
/** |
||||
* 导出卡顿日志到本地或远程服务器WEB-INF下 |
||||
* |
||||
* @param sourceFile 导出的卡顿日志所在文件夹 |
||||
* @param path 文件需要导出到的路径 |
||||
* @param sourceFilePath 导出的卡顿日志所在文件夹的路径 |
||||
*/ |
||||
private void exportCartonLog(File sourceFile, String path, String sourceFilePath) { |
||||
File[] files = sourceFile.listFiles(); |
||||
if (!Objects.isNull(files)) { |
||||
try { |
||||
if (path.startsWith(WORK_SPACE_PATH)) { |
||||
if (WorkContext.getCurrent().isLocal()) { |
||||
String curEnvName = DesignerEnvManager.getEnvManager().getCurEnvName(); |
||||
DesignerWorkspaceInfo workspaceInfo = DesignerEnvManager.getEnvManager().getWorkspaceInfo(curEnvName); |
||||
String workspaceInfoPath = workspaceInfo.getPath(); |
||||
path = StableUtils.pathJoin(workspaceInfoPath, path); |
||||
LogZipUtils.compress(files, path, false); |
||||
} else { |
||||
String sourceFilePathZip = sourceFilePath + ".zip"; |
||||
LogZipUtils.compress(files, sourceFilePathZip, false); |
||||
byte[] bytesByFile = getBytesByFile(sourceFilePathZip); |
||||
WorkContext.getWorkResource().write(path, bytesByFile); |
||||
LogZipUtils.delDir(sourceFilePathZip); |
||||
} |
||||
} else { |
||||
LogZipUtils.compress(files, path, false); |
||||
} |
||||
FineJOptionPane.showMessageDialog(this, Toolkit.i18nText("Fine-Design_Report_Exported_Successfully")); |
||||
} catch (Exception exception) { |
||||
FineJOptionPane.showMessageDialog(this, Toolkit.i18nText("Fine-Design_Report_Export_Failed"), UIManager.getString("OptionPane.messageDialogTitle"), JOptionPane.ERROR_MESSAGE); |
||||
FineLoggerFactory.getLogger().error("export file fail", exception); |
||||
} |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* 根据文件地址将文件转换成byte[] |
||||
* |
||||
* @param pathStr 本地文件目录 |
||||
* @return 本地文件转成的byte[] |
||||
*/ |
||||
@Nullable |
||||
private static byte[] getBytesByFile(String pathStr) { |
||||
File file = new File(pathStr); |
||||
try { |
||||
FileInputStream fis = new FileInputStream(file); |
||||
ByteArrayOutputStream bos = new ByteArrayOutputStream(BUFFER_SIZE); |
||||
byte[] b = new byte[BUFFER_SIZE]; |
||||
int n; |
||||
while ((n = fis.read(b)) != -1) { |
||||
bos.write(b, 0, n); |
||||
} |
||||
fis.close(); |
||||
byte[] data = bos.toByteArray(); |
||||
bos.close(); |
||||
return data; |
||||
} catch (Exception e) { |
||||
FineLoggerFactory.getLogger().error("reading local file fail", e); |
||||
} |
||||
return null; |
||||
} |
||||
|
||||
} |
@ -0,0 +1,36 @@
|
||||
package com.fr.design.carton; |
||||
|
||||
import java.io.File; |
||||
|
||||
public class MonthlyCartonFile { |
||||
private File currentMonthFile; |
||||
private File lastMonthFile; |
||||
private File nextMonthFile; |
||||
public MonthlyCartonFile() { |
||||
|
||||
} |
||||
|
||||
public File getCurrentMonthFile() { |
||||
return currentMonthFile; |
||||
} |
||||
|
||||
public void setCurrentMonthFile(File currentMonthFile) { |
||||
this.currentMonthFile = currentMonthFile; |
||||
} |
||||
|
||||
public File getLastMonthFile() { |
||||
return lastMonthFile; |
||||
} |
||||
|
||||
public void setLastMonthFile(File lastMonthFile) { |
||||
this.lastMonthFile = lastMonthFile; |
||||
} |
||||
|
||||
public File getNextMonthFile() { |
||||
return nextMonthFile; |
||||
} |
||||
|
||||
public void setNextMonthFile(File nextMonthFile) { |
||||
this.nextMonthFile = nextMonthFile; |
||||
} |
||||
} |
@ -0,0 +1,312 @@
|
||||
package com.fr.design.carton; |
||||
|
||||
|
||||
import com.fr.design.i18n.Toolkit; |
||||
import com.fr.general.GeneralUtils; |
||||
import com.fr.json.JSON; |
||||
import com.fr.json.JSONArray; |
||||
import com.fr.json.JSONObject; |
||||
import com.fr.log.FineLoggerFactory; |
||||
import com.fr.stable.ProductConstantsBase; |
||||
import com.fr.stable.StableUtils; |
||||
import com.fr.stable.StringUtils; |
||||
import com.fr.stable.xml.XMLPrintWriter; |
||||
import com.fr.stable.xml.XMLReadable; |
||||
import com.fr.stable.xml.XMLWriter; |
||||
import com.fr.stable.xml.XMLableReader; |
||||
import sun.awt.AppContext; |
||||
|
||||
import javax.swing.SwingWorker; |
||||
import java.io.IOException; |
||||
import java.io.File; |
||||
import java.io.BufferedReader; |
||||
import java.io.FileReader; |
||||
import java.text.SimpleDateFormat; |
||||
import java.util.List; |
||||
import java.util.ArrayList; |
||||
import java.util.HashMap; |
||||
import java.util.Map; |
||||
import java.util.Date; |
||||
import java.util.Calendar; |
||||
|
||||
public class SwitchForSwingChecker implements XMLReadable, XMLWriter { |
||||
/** |
||||
* Designer4Debug类名 |
||||
*/ |
||||
private static final String DEBUG_MAIN_CLASS_NAME = "com.fr.start.Designer4Debug"; |
||||
/** |
||||
* XML标签 |
||||
*/ |
||||
public static final String XML_TAG = "SwitchForSwingChecker"; |
||||
/** |
||||
* 定时任务的开关 |
||||
*/ |
||||
private static boolean checkerTimerSwitch = false; |
||||
/** |
||||
* 简单记录事件执行时间的开关 |
||||
*/ |
||||
private static boolean easyChecker = false; |
||||
/** |
||||
* 一个标识位用于区分耗时任务时长检测(简单检测)和timer检测 |
||||
*/ |
||||
public static final int TIMER_CHECK_FLAG = 0; |
||||
public static final int EASY_CHECK_FLAG = 1; |
||||
|
||||
/** |
||||
* 日志存储地址 |
||||
*/ |
||||
public static final String JOURNAL_FILE_PATH = StableUtils.pathJoin(ProductConstantsBase.getEnvHome(), "journal_log"); |
||||
public static final String EASY_CHECKER_FILE_NAME = "easy_check_log.csv"; |
||||
public static final String TIMER_CHECKER_FILE_NAME = "timer_check_log.csv"; |
||||
public static boolean isCheckerTimerSwitch() { |
||||
return checkerTimerSwitch; |
||||
} |
||||
|
||||
public static boolean isEasyChecker() { |
||||
return easyChecker; |
||||
} |
||||
|
||||
public static volatile SwitchForSwingChecker switchForSwingChecker = new SwitchForSwingChecker(); |
||||
|
||||
public static SwitchForSwingChecker getInstance() { |
||||
return switchForSwingChecker; |
||||
} |
||||
|
||||
public static void startTimerChecker() { |
||||
if (!checkerTimerSwitch) { |
||||
EventDispatchThreadHangMonitor.INSTANCE.initTimer(); |
||||
CartonThreadExecutorPool.getTimerThreadExecutorPool().initTimer(); |
||||
EventDispatchThreadHangMonitor.INSTANCE.setTimerWitch(true); |
||||
checkerTimerSwitch = true; |
||||
if (!easyChecker) { |
||||
EventDispatchThreadHangMonitor.INSTANCE.startFilterModalWindow(); |
||||
} |
||||
} |
||||
} |
||||
|
||||
public static void stopTimerChecker() { |
||||
if (checkerTimerSwitch) { |
||||
EventDispatchThreadHangMonitor.INSTANCE.stopTimer(); |
||||
CartonThreadExecutorPool.getTimerThreadExecutorPool().stopTimer(); |
||||
EventDispatchThreadHangMonitor.INSTANCE.setTimerWitch(false); |
||||
checkerTimerSwitch = false; |
||||
if (!easyChecker) { |
||||
EventDispatchThreadHangMonitor.INSTANCE.stopFilterModalWindow(); |
||||
} |
||||
} |
||||
} |
||||
|
||||
public static void startEasyChecker() { |
||||
if (!easyChecker) { |
||||
EventDispatchThreadHangMonitor.INSTANCE.setEasyWitch(true); |
||||
CartonThreadExecutorPool.getTimerThreadExecutorPool().setEasyWitch(true); |
||||
easyChecker = true; |
||||
if (!checkerTimerSwitch) { |
||||
EventDispatchThreadHangMonitor.INSTANCE.startFilterModalWindow(); |
||||
} |
||||
} |
||||
} |
||||
|
||||
public static void stopEasyChecker() { |
||||
if (easyChecker) { |
||||
EventDispatchThreadHangMonitor.INSTANCE.setEasyWitch(false); |
||||
CartonThreadExecutorPool.getTimerThreadExecutorPool().setEasyWitch(false); |
||||
easyChecker = false; |
||||
if (!checkerTimerSwitch) { |
||||
EventDispatchThreadHangMonitor.INSTANCE.stopFilterModalWindow(); |
||||
} |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* 获取文件名字以及判断文件是否存在 |
||||
*/ |
||||
private static CartonFiles getFiles(String date) { |
||||
String[] split = date.split("-"); |
||||
int month = StringUtils.isEmpty(split[1]) ? -1 : Integer.parseInt(split[1]); |
||||
String dirPath = StableUtils.pathJoin(JOURNAL_FILE_PATH, split[0], "month-" + month, date); |
||||
File file1 = new File(StableUtils.pathJoin(dirPath, EASY_CHECKER_FILE_NAME)); |
||||
File file2 = new File(StableUtils.pathJoin(dirPath, TIMER_CHECKER_FILE_NAME)); |
||||
File[] files = new File[2]; |
||||
files[0] = file1; |
||||
files[1] = file2; |
||||
CartonFiles cartonFiles = new CartonFiles(); |
||||
cartonFiles.setEasyCheckerFile(file1); |
||||
cartonFiles.setTimerCheckerFile(file2); |
||||
return cartonFiles; |
||||
} |
||||
|
||||
/** |
||||
*处理文件 |
||||
* 一共四种情况, |
||||
* 两个文件都不存在 |
||||
* 文件一存在,文件二不存在 |
||||
* 文件二存在,文件一不存在 |
||||
* 两个文件都存在 |
||||
*/ |
||||
private static List<CartonUploadMessage> getCartonLog(File easyFile, File timerFile) { |
||||
List<CartonUploadMessage> res = new ArrayList<>(); |
||||
List<CartonUploadMessage> easyFileCartonLog = getEasyFileCartonLog(easyFile); |
||||
List<CartonUploadMessage> timerFileCartonLog = getTimerFileCartonLog(timerFile); |
||||
Map<String, CartonUploadMessage> easyFileMap = new HashMap<>(); |
||||
for (CartonUploadMessage cartonUploadMessage : easyFileCartonLog) { |
||||
easyFileMap.put(cartonUploadMessage.getHangCount(), cartonUploadMessage); |
||||
res.add(cartonUploadMessage); |
||||
} |
||||
for (CartonUploadMessage cartonUploadMessage : timerFileCartonLog) { |
||||
String hangCount = cartonUploadMessage.getHangCount(); |
||||
if (easyFileMap.containsKey(hangCount)) { |
||||
cartonUploadMessage.setThreadTime(easyFileMap.get(hangCount).getThreadTime()); |
||||
} |
||||
res.add(cartonUploadMessage); |
||||
} |
||||
return res; |
||||
} |
||||
|
||||
private static List<CartonUploadMessage> getTimerFileCartonLog(File file) { |
||||
List<CartonUploadMessage> res = new ArrayList<>(); |
||||
try { |
||||
StringBuilder stringBuilder = new StringBuilder(); |
||||
stringBuilder.append("["); |
||||
BufferedReader bufferedReader1 = new BufferedReader(new FileReader(file)); |
||||
String line1; |
||||
while ((line1 = bufferedReader1.readLine()) != null) { |
||||
stringBuilder.append(line1); |
||||
} |
||||
bufferedReader1.close(); |
||||
stringBuilder.append("]"); |
||||
JSONArray easyCheckerJSON = JSON.ARRAY.createJSON(GeneralUtils.objectToString(stringBuilder)); |
||||
for (Object jsonObject : easyCheckerJSON) { |
||||
CartonUploadMessage cartonUploadMessage = new CartonUploadMessage(); |
||||
JSONObject x = (JSONObject) jsonObject; |
||||
cartonUploadMessage.setHangCount(x.getString(Toolkit.i18nText("Fine-Design_Basic_Carton_Event_Number"))); |
||||
cartonUploadMessage.setSlowTime(x.getString(Toolkit.i18nText("Fine-Design_Basic_Carton_Output_Time"))); |
||||
cartonUploadMessage.setThreadTime("undefined"); |
||||
//这个跟输出到文件中的格式匹配,参考EventDis里的stackTraceToString方法
|
||||
String indentation = " "; |
||||
String logMessage = x.getString(Toolkit.i18nText("Fine-Design_Basic_Carton_Stack_Info")).replaceAll(indentation, "\r\n "); |
||||
cartonUploadMessage.setInfo(logMessage); |
||||
res.add(cartonUploadMessage); |
||||
} |
||||
} catch (IOException e) { |
||||
FineLoggerFactory.getLogger().error("upload fail", e); |
||||
} |
||||
return res; |
||||
} |
||||
|
||||
private static List<CartonUploadMessage> getEasyFileCartonLog(File file) { |
||||
List<CartonUploadMessage> res = new ArrayList<>(); |
||||
try { |
||||
StringBuilder stringBuilder = new StringBuilder(); |
||||
stringBuilder.append("["); |
||||
BufferedReader bufferedReader1 = new BufferedReader(new FileReader(file)); |
||||
String line1; |
||||
while ((line1 = bufferedReader1.readLine()) != null) { |
||||
stringBuilder.append(line1); |
||||
} |
||||
bufferedReader1.close(); |
||||
stringBuilder.append("]"); |
||||
JSONArray timerCheckerJSON = JSON.ARRAY.createJSON(GeneralUtils.objectToString(stringBuilder)); |
||||
for (Object jsonObject : timerCheckerJSON) { |
||||
JSONObject x = (JSONObject) jsonObject; |
||||
CartonUploadMessage cartonUploadMessage = new CartonUploadMessage(); |
||||
cartonUploadMessage.setHangCount(x.getString(Toolkit.i18nText("Fine-Design_Basic_Carton_Event_Number"))); |
||||
cartonUploadMessage.setSlowTime(x.getString(Toolkit.i18nText("Fine-Design_Basic_Carton_Output_Time"))); |
||||
cartonUploadMessage.setThreadTime(x.getString(Toolkit.i18nText("Fine-Design_Basic_Carton_Task_Total_Time"))); |
||||
cartonUploadMessage.setInfo("undefined"); |
||||
res.add(cartonUploadMessage); |
||||
} |
||||
} catch (IOException e) { |
||||
FineLoggerFactory.getLogger().error("upload fail", e); |
||||
} |
||||
return res; |
||||
} |
||||
|
||||
/** |
||||
* /埋点方法上传卡顿信息入口 |
||||
date为 2022-09-08的格式 |
||||
*/ |
||||
public static List<CartonUploadMessage> uploadJournalLog(Date dateTime) { |
||||
List<CartonUploadMessage> res = new ArrayList<>(); |
||||
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd"); |
||||
CartonFiles files = getFiles(simpleDateFormat.format(dateTime)); |
||||
File easyCheckerFile = files.getEasyCheckerFile(); |
||||
File timerCheckerFile = files.getTimerCheckerFile(); |
||||
if (easyCheckerFile.exists() && timerCheckerFile.exists()) { |
||||
return getCartonLog(easyCheckerFile, timerCheckerFile); |
||||
} else if (easyCheckerFile.exists()) { |
||||
return getEasyFileCartonLog(easyCheckerFile); |
||||
} else if (timerCheckerFile.exists()) { |
||||
return getTimerFileCartonLog(timerCheckerFile); |
||||
} else { |
||||
return res; |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* 初始化监控任务,主要是替换EventQueue以及SwingWorker执行任务的线程池 |
||||
* |
||||
*/ |
||||
public static void initThreadMonitoring () { |
||||
String mainClass = System.getProperty("sun.java.command"); |
||||
//判断一下,如果是以Designer4Debug启动,就不注册代码,不然会覆盖掉SwingExplorer,导致其无法使用
|
||||
if (!StringUtils.equals(mainClass, DEBUG_MAIN_CLASS_NAME)) { |
||||
EventDispatchThreadHangMonitor.initMonitoring(); |
||||
AppContext.getAppContext().put(SwingWorker.class, CartonThreadExecutorPool.getTimerThreadExecutorPool()); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* 判断是否有指定日期的卡顿日志,没有就返回false |
||||
*/ |
||||
public static boolean isCartonExists(Date date) { |
||||
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd"); |
||||
String format = simpleDateFormat.format(date); |
||||
Calendar calendar = Calendar.getInstance(); |
||||
int month = calendar.get(Calendar.MONTH) + 1; |
||||
int year = calendar.get(Calendar.YEAR); |
||||
File file = new File(StableUtils.pathJoin(JOURNAL_FILE_PATH, String.valueOf(year), "month-" + month, format)); |
||||
return file.exists(); |
||||
} |
||||
|
||||
public static boolean isCartonExists() { |
||||
return isCartonExists(new Date()); |
||||
} |
||||
|
||||
private void initSwitchChecker() { |
||||
if (easyChecker) { |
||||
EventDispatchThreadHangMonitor.INSTANCE.setEasyWitch(true); |
||||
CartonThreadExecutorPool.getTimerThreadExecutorPool().setEasyWitch(true); |
||||
} |
||||
if (checkerTimerSwitch) { |
||||
EventDispatchThreadHangMonitor.INSTANCE.initTimer(); |
||||
CartonThreadExecutorPool.getTimerThreadExecutorPool().initTimer(); |
||||
EventDispatchThreadHangMonitor.INSTANCE.setTimerWitch(true); |
||||
} |
||||
if (easyChecker || checkerTimerSwitch) { |
||||
EventDispatchThreadHangMonitor.INSTANCE.startFilterModalWindow(); |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
public void readXML(XMLableReader reader) { |
||||
if (reader.isAttr()) { |
||||
checkerTimerSwitch = reader.getAttrAsBoolean("checkerTimerSwitch", false); |
||||
easyChecker = reader.getAttrAsBoolean("easyChecker", false); |
||||
} |
||||
try { |
||||
initSwitchChecker(); |
||||
} catch (Throwable t) { |
||||
FineLoggerFactory.getLogger().error("read checker attr fail", t); |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
public void writeXML(XMLPrintWriter writer) { |
||||
writer.startTAG(XML_TAG); |
||||
writer.attr("checkerTimerSwitch", checkerTimerSwitch); |
||||
writer.attr("easyChecker", easyChecker); |
||||
writer.end(); |
||||
} |
||||
|
||||
} |
@ -0,0 +1,180 @@
|
||||
package com.fr.design.cell; |
||||
|
||||
import com.fr.base.CellBorderSourceFlag; |
||||
import com.fr.base.CellBorderStyle; |
||||
import com.fr.base.Style; |
||||
import com.fr.design.mainframe.theme.utils.DefaultThemedTemplateCellElementCase; |
||||
import com.fr.general.IOUtils; |
||||
import com.fr.report.cell.TemplateCellElement; |
||||
|
||||
import javax.swing.JPanel; |
||||
import java.awt.AlphaComposite; |
||||
import java.awt.Color; |
||||
import java.awt.Composite; |
||||
import java.awt.Dimension; |
||||
import java.awt.Graphics; |
||||
import java.awt.Graphics2D; |
||||
import java.awt.GridLayout; |
||||
import java.awt.Rectangle; |
||||
import java.awt.RenderingHints; |
||||
import java.awt.image.BufferedImage; |
||||
|
||||
/** |
||||
* @author Starryi |
||||
* @version 1.0 |
||||
* Created by Starryi on 2021/9/3 |
||||
*/ |
||||
public class CellRectangleStylePreviewPane extends JPanel { |
||||
|
||||
private static final BufferedImage transparentBackgroundImage = IOUtils.readImage("/com/fr/design/images/transparent_background.png"); |
||||
private final float transparentBackgroundWidth; |
||||
private final float transparentBackgroundHeight; |
||||
|
||||
private static final int ROW_COUNT = 2; |
||||
private static final int COLUMN_COUNT = 2; |
||||
|
||||
private final TemplateCellElement[][] cellElementGrid = new TemplateCellElement[ROW_COUNT][COLUMN_COUNT]; |
||||
private final int[][] borderSourceFlags = new int[ROW_COUNT][COLUMN_COUNT]; |
||||
private final CellStylePreviewPane[][] cellStylePreviewPaneGrid = new CellStylePreviewPane[ROW_COUNT][COLUMN_COUNT]; |
||||
|
||||
public CellRectangleStylePreviewPane(boolean supportInnerBorder) { |
||||
transparentBackgroundWidth = transparentBackgroundImage.getWidth(null); |
||||
transparentBackgroundHeight = transparentBackgroundImage.getHeight(null); |
||||
|
||||
setLayout(new GridLayout(2, 2)); |
||||
setOpaque(false); |
||||
setBackground(null); |
||||
|
||||
for (int r = 0; r < ROW_COUNT; r++) { |
||||
for (int c = 0; c < COLUMN_COUNT; c++) { |
||||
CellStylePreviewPane pane = new CellStylePreviewPane(c, r, COLUMN_COUNT, ROW_COUNT, false, false); |
||||
TemplateCellElement cellElement = DefaultThemedTemplateCellElementCase.createInstance(c, r); |
||||
int flags = CellBorderSourceFlag.INVALID_BORDER_SOURCE; |
||||
if (supportInnerBorder) { |
||||
flags = CellBorderSourceFlag.ALL_BORDER_SOURCE_OUTER; |
||||
if (r != 0) { |
||||
flags |= CellBorderSourceFlag.TOP_BORDER_SOURCE_INNER; |
||||
} |
||||
if (r != ROW_COUNT - 1) { |
||||
flags |= CellBorderSourceFlag.BOTTOM_BORDER_SOURCE_INNER; |
||||
} |
||||
if (c != 0) { |
||||
flags |= CellBorderSourceFlag.LEFT_BORDER_SOURCE_INNER; |
||||
} |
||||
if (c != COLUMN_COUNT - 1) { |
||||
flags |= CellBorderSourceFlag.RIGHT_BORDER_SOURCE_INNER; |
||||
} |
||||
} |
||||
|
||||
|
||||
pane.setStyle(cellElement.getStyle()); |
||||
add(pane); |
||||
|
||||
cellElementGrid[r][c] = cellElement; |
||||
borderSourceFlags[r][c] = flags; |
||||
cellStylePreviewPaneGrid[r][c] = pane; |
||||
} |
||||
} |
||||
} |
||||
|
||||
public void setPlainText(String text) { |
||||
cellStylePreviewPaneGrid[0][1].setPaintText(text); |
||||
cellStylePreviewPaneGrid[1][1].setPaintText(text); |
||||
repaint(); |
||||
} |
||||
|
||||
public void setStyle(Style style, CellBorderStyle borderStyle) { |
||||
for (int i = 0; i < ROW_COUNT; i++) { |
||||
for (int j = 0; j < COLUMN_COUNT; j++) { |
||||
CellStylePreviewPane pane = cellStylePreviewPaneGrid[i][j]; |
||||
TemplateCellElement cellElement = cellElementGrid[i][j]; |
||||
int flag = borderSourceFlags[i][j]; |
||||
cellElement.setStyle(CellBorderSourceFlag.deriveBorderedStyle(style, borderStyle, flag)); |
||||
|
||||
pane.setStyle(cellElement.getStyle()); |
||||
} |
||||
} |
||||
repaint(); |
||||
} |
||||
|
||||
@Override |
||||
public void setPreferredSize(Dimension preferredSize) { |
||||
super.setPreferredSize(preferredSize); |
||||
int hw = preferredSize.width / 2; |
||||
int hh = preferredSize.height / 2; |
||||
cellStylePreviewPaneGrid[0][0].setPreferredSize(new Dimension(hw, hh)); |
||||
cellStylePreviewPaneGrid[0][1].setPreferredSize(new Dimension(hw, hh)); |
||||
cellStylePreviewPaneGrid[1][0].setPreferredSize(new Dimension(hw, hh)); |
||||
cellStylePreviewPaneGrid[1][1].setPreferredSize(new Dimension(hw, hh)); |
||||
} |
||||
|
||||
@Override |
||||
public Dimension getPreferredSize() { |
||||
Dimension d00 = cellStylePreviewPaneGrid[0][0].getPreferredSize(); |
||||
Dimension d01 = cellStylePreviewPaneGrid[0][1].getPreferredSize(); |
||||
Dimension d10 = cellStylePreviewPaneGrid[1][0].getPreferredSize(); |
||||
Dimension d11 = cellStylePreviewPaneGrid[1][1].getPreferredSize(); |
||||
|
||||
int width = Math.max(d00.width + d01.width, d10.width + d11.width); |
||||
int height = Math.max(d00.height + d10.height, d01.height + d11.height); |
||||
|
||||
return new Dimension(width, height); |
||||
} |
||||
|
||||
@Override |
||||
public void paint(Graphics g) { |
||||
Graphics2D g2d = (Graphics2D) g; |
||||
g2d.clearRect(0, 0, getWidth(), getHeight()); |
||||
|
||||
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); |
||||
g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON); |
||||
|
||||
paintTransparentBackground((Graphics2D) g, cellElementGrid[0][0].getStyle()); |
||||
|
||||
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF); |
||||
g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_OFF); |
||||
|
||||
super.paint(g); |
||||
} |
||||
|
||||
@Override |
||||
public Rectangle getBounds() { |
||||
return super.getBounds(); |
||||
} |
||||
|
||||
private void paintTransparentBackground(Graphics2D g2d, Style style) { |
||||
float alpha = computeTransparentBackgroundAlpha(style); |
||||
|
||||
float scaleWidth = 1.0F * getWidth() / transparentBackgroundWidth; |
||||
float scaleHeight = 1.0F * getHeight() / transparentBackgroundHeight; |
||||
float maxScale = Math.max(scaleWidth, scaleHeight); |
||||
|
||||
if (maxScale <= 1) { |
||||
scaleWidth = scaleHeight = 1; |
||||
} else { |
||||
scaleHeight = scaleWidth = maxScale; |
||||
} |
||||
|
||||
Composite oldComposite = g2d.getComposite(); |
||||
g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_ATOP, alpha)); |
||||
g2d.drawImage(transparentBackgroundImage, 0, 0, (int) (transparentBackgroundWidth * scaleWidth), (int) (transparentBackgroundHeight * scaleHeight), null); |
||||
g2d.setComposite(oldComposite); |
||||
} |
||||
|
||||
private float computeTextColorBrightness(Style style) { |
||||
Color fontColor = style.getFRFont().getForeground(); |
||||
return fontColor.getRed() * 0.299F + fontColor.getGreen() * 0.587F + fontColor.getBlue() * 0.114F; |
||||
} |
||||
|
||||
private float computeTransparentBackgroundAlpha(Style style) { |
||||
float textBrightness = computeTextColorBrightness(style); |
||||
|
||||
float alpha = 1.0F; |
||||
if (textBrightness < 50) { |
||||
alpha = 0.2F; |
||||
} else if (textBrightness < 160){ |
||||
alpha = 0.5F; |
||||
} |
||||
return alpha; |
||||
} |
||||
} |
@ -0,0 +1,148 @@
|
||||
package com.fr.design.components.loading; |
||||
|
||||
import javax.swing.JComponent; |
||||
import javax.swing.Timer; |
||||
import java.awt.AlphaComposite; |
||||
import java.awt.BasicStroke; |
||||
import java.awt.Color; |
||||
import java.awt.Component; |
||||
import java.awt.Composite; |
||||
import java.awt.Container; |
||||
import java.awt.Dimension; |
||||
import java.awt.Graphics; |
||||
import java.awt.Graphics2D; |
||||
import java.awt.LayoutManager; |
||||
import java.awt.RenderingHints; |
||||
import java.awt.event.ActionEvent; |
||||
import java.awt.event.ActionListener; |
||||
import java.awt.event.MouseAdapter; |
||||
import java.awt.event.MouseEvent; |
||||
|
||||
/** |
||||
* @author hades |
||||
* @version 10.0 |
||||
* Created by hades on 2021/4/12 |
||||
*/ |
||||
public class LoadingPane extends JComponent implements ActionListener { |
||||
|
||||
private AlphaComposite composite = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.3f); |
||||
private volatile boolean mIsRunning; |
||||
private volatile boolean mIsFadingOut; |
||||
private Timer mTimer; |
||||
private int mAngle; |
||||
private int mFadeCount; |
||||
private int mFadeLimit = 15; |
||||
private int lines = 12; |
||||
private int maxAngle = 360; |
||||
private int angleAdd = 30; |
||||
|
||||
public LoadingPane() { |
||||
|
||||
addMouseListener(new MouseAdapter() { |
||||
@Override |
||||
public void mouseClicked(MouseEvent e) { |
||||
// do nothing
|
||||
} |
||||
}); |
||||
|
||||
setLayout(getCoverLayout()); |
||||
setBackground(null); |
||||
setOpaque(false); |
||||
} |
||||
|
||||
protected LayoutManager getCoverLayout() { |
||||
return new LayoutManager() { |
||||
|
||||
@Override |
||||
public void removeLayoutComponent(Component comp) { |
||||
} |
||||
|
||||
@Override |
||||
public Dimension preferredLayoutSize(Container parent) { |
||||
return parent.getPreferredSize(); |
||||
} |
||||
|
||||
@Override |
||||
public Dimension minimumLayoutSize(Container parent) { |
||||
return null; |
||||
} |
||||
|
||||
@Override |
||||
public void layoutContainer(Container parent) { |
||||
} |
||||
|
||||
@Override |
||||
public void addLayoutComponent(String name, Component comp) { |
||||
} |
||||
}; |
||||
} |
||||
|
||||
|
||||
@Override |
||||
public void paint(Graphics g) { |
||||
int w = this.getWidth(); |
||||
int h = this.getHeight(); |
||||
super.paint(g); |
||||
if (!mIsRunning) { |
||||
return; |
||||
} |
||||
Graphics2D g2 = (Graphics2D) g.create(); |
||||
float fade = (float) mFadeCount / (float) mFadeLimit; |
||||
Composite urComposite = g2.getComposite(); |
||||
g2.setComposite(composite); |
||||
g2.fillRect(0, 0, w, h); |
||||
g2.setComposite(urComposite); |
||||
int s = Math.min(w, h) / 50; |
||||
int cx = w / 2; |
||||
int cy = h / 2; |
||||
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); |
||||
g2.setStroke(new BasicStroke(s / 4, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND)); |
||||
g2.setPaint(Color.BLACK); |
||||
g2.rotate(Math.PI * mAngle / 180, cx, cy); |
||||
for (int i = 0; i < lines; i++) { |
||||
float scale = (11.0f - (float) i) / 11.0f; |
||||
g2.drawLine(cx + s, cy, cx + s * 2, cy); |
||||
g2.rotate(-Math.PI / 6, cx, cy); |
||||
g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, scale * fade)); |
||||
} |
||||
g2.dispose(); |
||||
} |
||||
|
||||
@Override |
||||
public void actionPerformed(ActionEvent e) { |
||||
if (mIsRunning) { |
||||
repaint(); |
||||
mAngle += angleAdd; |
||||
if (mAngle >= maxAngle) { |
||||
mAngle = 0; |
||||
} |
||||
if (mIsFadingOut) { |
||||
if (--mFadeCount == 0) { |
||||
mIsRunning = false; |
||||
mTimer.stop(); |
||||
} |
||||
} else if (mFadeCount < mFadeLimit) { |
||||
mFadeCount++; |
||||
} |
||||
} |
||||
} |
||||
|
||||
public void start() { |
||||
if (mIsRunning) { |
||||
return; |
||||
} |
||||
mIsRunning = true; |
||||
mIsFadingOut = false; |
||||
mFadeCount = 0; |
||||
int fps = 24; |
||||
int tick = 1000 / fps; |
||||
mTimer = new Timer(tick, this); |
||||
mTimer.start(); |
||||
} |
||||
|
||||
public void stop() { |
||||
mIsRunning = false; |
||||
mIsFadingOut = true; |
||||
} |
||||
|
||||
} |
@ -0,0 +1,21 @@
|
||||
package com.fr.design.components.notification; |
||||
|
||||
/** |
||||
* created by Harrison on 2022/05/24 |
||||
**/ |
||||
public interface NotificationAction { |
||||
|
||||
/** |
||||
* 行为名 |
||||
* |
||||
* @return 名称 |
||||
*/ |
||||
String name(); |
||||
|
||||
/** |
||||
* 行为动作 |
||||
* |
||||
* @param args 参数 |
||||
*/ |
||||
void run(Object... args); |
||||
} |
@ -0,0 +1,391 @@
|
||||
package com.fr.design.components.notification; |
||||
|
||||
import com.fr.base.function.ThrowableRunnable; |
||||
import com.fr.base.svg.IconUtils; |
||||
import com.fr.design.components.page.PageControlModel; |
||||
import com.fr.design.components.page.PageControlPanel; |
||||
import com.fr.design.dialog.link.MessageWithLink; |
||||
import com.fr.design.gui.ibutton.UIButton; |
||||
import com.fr.design.gui.ilable.UILabel; |
||||
import com.fr.design.i18n.Toolkit; |
||||
import com.fr.design.layout.FRGUIPaneFactory; |
||||
import com.fr.design.layout.VerticalFlowLayout; |
||||
import com.fr.design.utils.LinkStrUtils; |
||||
import com.fr.env.detect.base.EnvDetectorConfig; |
||||
|
||||
import javax.swing.BorderFactory; |
||||
import javax.swing.Icon; |
||||
import javax.swing.JComponent; |
||||
import javax.swing.JDialog; |
||||
import javax.swing.JPanel; |
||||
import javax.swing.JScrollPane; |
||||
import javax.swing.ScrollPaneConstants; |
||||
import java.awt.BorderLayout; |
||||
import java.awt.Color; |
||||
import java.awt.Container; |
||||
import java.awt.Desktop; |
||||
import java.awt.Dimension; |
||||
import java.awt.event.ActionEvent; |
||||
import java.awt.event.ActionListener; |
||||
import java.awt.event.MouseAdapter; |
||||
import java.awt.event.MouseEvent; |
||||
import java.net.URI; |
||||
import java.util.Arrays; |
||||
import java.util.List; |
||||
import java.util.function.Function; |
||||
import java.util.function.Supplier; |
||||
import java.util.stream.Collectors; |
||||
|
||||
/** |
||||
* 右下角的提醒 <a href="https://kms.fineres.com/pages/viewpage.action?pageId=388333688">异常提醒</a> |
||||
* 相关使用方式见 <a href="https://kms.fineres.com/pages/viewpage.action?pageId=415212013">提醒组件</a> |
||||
* |
||||
* created by Harrison on 2022/05/24 |
||||
**/ |
||||
public class NotificationDialog extends JDialog { |
||||
|
||||
/** |
||||
* 通知框的内部高度 |
||||
*/ |
||||
private static final int CONTENT_INNER_HEIGHT = 60; |
||||
/** |
||||
* 通知框如果出现滚动条后的内部宽度 |
||||
*/ |
||||
private static final int CONTENT_SCROLL_WIDTH = 280; |
||||
|
||||
private static final int CONTENT_WIDTH = 300; |
||||
private static final int CONTENT_HEIGHT = 100; |
||||
/** |
||||
* 通知框的外部宽高 |
||||
*/ |
||||
private static final Dimension CONTENT_SIZE = new Dimension(CONTENT_WIDTH, CONTENT_HEIGHT); |
||||
private static final Dimension BUTTON_DIMENSION = new Dimension(68, 20); |
||||
|
||||
/** |
||||
* 标记 LABEL, 没有作用 |
||||
*/ |
||||
private static final UILabel SIGN_LABEL = new UILabel("#"); |
||||
/** |
||||
* 确认一个 LABEL 的宽高 |
||||
*/ |
||||
private static final Dimension SIGN_LABEL_DIMENSION = SIGN_LABEL.getPreferredSize(); |
||||
|
||||
private NotificationDialogProperties properties; |
||||
|
||||
/* 数据 model */ |
||||
|
||||
private List<NotificationModel> notificationModels; |
||||
private PageControlModel pageControlModel; |
||||
|
||||
private JPanel body; |
||||
private JPanel headerPanel; |
||||
private JPanel contentPanel; |
||||
private JPanel tailPanel; |
||||
|
||||
public NotificationDialog(NotificationDialogProperties properties, List<NotificationModel> notificationModels) { |
||||
|
||||
super(properties.getOwner()); |
||||
setTitle(properties.getTitle()); |
||||
this.properties = properties; |
||||
|
||||
this.notificationModels = notificationModels; |
||||
this.pageControlModel = new PageControlModel(0, this.notificationModels.size()); |
||||
|
||||
initComponents(); |
||||
} |
||||
|
||||
public void initComponents() { |
||||
|
||||
//UI 配置
|
||||
configProperties(); |
||||
|
||||
this.body = FRGUIPaneFactory.createBorderLayout_L_Pane(); |
||||
body.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0)); |
||||
|
||||
//首行
|
||||
layoutHeaderPanel(); |
||||
|
||||
//消息内容
|
||||
layoutContentPanel(); |
||||
|
||||
//查看详情
|
||||
layoutTailPanel(); |
||||
|
||||
add(body); |
||||
|
||||
Dimension dimension = body.getPreferredSize(); |
||||
setSize(dimension.width, dimension.height); |
||||
|
||||
Container parent = getParent(); |
||||
setLocation((parent.getWidth() - dimension.width - 30 + parent.getX()), |
||||
parent.getY() + parent.getHeight() - dimension.height - 30); |
||||
|
||||
} |
||||
|
||||
public void open() { |
||||
|
||||
setVisible(true); |
||||
} |
||||
|
||||
private void configProperties() { |
||||
|
||||
setModal(properties.isModal()); |
||||
setFocusable(false); |
||||
setAutoRequestFocus(false); |
||||
setResizable(false); |
||||
} |
||||
|
||||
protected JPanel createHeaderPanel() { |
||||
|
||||
return null; |
||||
} |
||||
|
||||
/** |
||||
* 内容 |
||||
* |
||||
* @return 内容面板 |
||||
*/ |
||||
protected JPanel createContentPanel() { |
||||
|
||||
JPanel contentPanel = FRGUIPaneFactory.createBorderLayout_S_Pane(); |
||||
contentPanel.setBorder(BorderFactory.createEmptyBorder(8, 10, 8, 10)); |
||||
contentPanel.setName("contentPanel"); |
||||
|
||||
NotificationModel model = getCurrentModel(); |
||||
|
||||
UILabel icon = new UILabel(getIconForType(model.getType())); |
||||
icon.setPreferredSize(new Dimension(16, 16)); |
||||
JPanel iconPanel = FRGUIPaneFactory.createBorderLayout_L_Pane(); |
||||
iconPanel.setBorder(BorderFactory.createEmptyBorder(0, 5, 10, 8)); |
||||
iconPanel.add(icon, BorderLayout.NORTH); |
||||
|
||||
contentPanel.add(iconPanel, BorderLayout.WEST); |
||||
|
||||
JPanel centerPanel = FRGUIPaneFactory.createBorderLayout_S_Pane(); |
||||
centerPanel.setBorder(BorderFactory.createEmptyBorder(0, 0, 5, 5)); |
||||
|
||||
NotificationMessage[] messages = model.getMessages(); |
||||
List<? extends JComponent> messageComponents = Arrays.stream(messages) |
||||
.map((messageModel) -> { |
||||
if (messageModel.getType() == NotificationMessage.Type.LINK) { |
||||
NotificationMessage.LinkMessage linkMessage = (NotificationMessage.LinkMessage) messageModel; |
||||
return new MessageWithLink(linkMessage.format(), ThrowableRunnable.toRunnable(() -> { |
||||
Desktop.getDesktop().browse(URI.create(linkMessage.getLink())); |
||||
})); |
||||
} |
||||
return new UILabel(LinkStrUtils.generateHtmlTag(messageModel.format())); |
||||
}) |
||||
.collect(Collectors.toList()); |
||||
|
||||
// 当高度 大于 60 时,就会出现滚动条。
|
||||
// 当出现滚动条时,需要将内部的宽度限制为 280, 否则会展示不出来
|
||||
Function<Double, Integer> calStandardWidth = height -> height > CONTENT_INNER_HEIGHT ? CONTENT_SCROLL_WIDTH : CONTENT_WIDTH; |
||||
|
||||
int widthUnit = messageComponents.stream() |
||||
.map((component) -> { |
||||
Dimension preferredSize = component.getPreferredSize(); |
||||
double width = preferredSize.getWidth(); |
||||
double widthFactor = Math.ceil(width / CONTENT_WIDTH); |
||||
// 这里的高度是没有限制宽度的,如果限制宽度,高度会变更,所以这里需要加上宽度的影响
|
||||
return preferredSize.getHeight() + widthFactor * SIGN_LABEL_DIMENSION.getHeight(); |
||||
}) |
||||
.reduce(Double::sum) |
||||
.map(calStandardWidth) |
||||
.orElse(CONTENT_WIDTH); |
||||
|
||||
messageComponents = messageComponents.stream() |
||||
.peek((component) -> { |
||||
Dimension preferredSize = component.getPreferredSize(); |
||||
double componentWidth = preferredSize.getWidth(); |
||||
double componentHeight = preferredSize.getHeight(); |
||||
double heightFactor = Math.ceil(componentHeight / SIGN_LABEL_DIMENSION.getHeight()); |
||||
double widthFactor = Math.ceil(componentWidth / widthUnit); |
||||
int realHeight = (int)Math.ceil(heightFactor + widthFactor - 1) * (int)(Math.ceil(SIGN_LABEL_DIMENSION.getHeight())); |
||||
component.setPreferredSize(new Dimension(widthUnit, realHeight)); |
||||
}) |
||||
.collect(Collectors.toList()); |
||||
|
||||
// 竖向排列
|
||||
JPanel messageSummaryPanel = FRGUIPaneFactory.createVerticalFlowLayout_Pane(true, VerticalFlowLayout.TOP, 0, 0); |
||||
messageComponents.forEach(messageSummaryPanel::add); |
||||
|
||||
JScrollPane jScrollPane = new JScrollPane(messageSummaryPanel, ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED, ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER); |
||||
jScrollPane.setBorder(BorderFactory.createEmptyBorder()); |
||||
|
||||
centerPanel.add(jScrollPane, BorderLayout.CENTER); |
||||
centerPanel.setPreferredSize(CONTENT_SIZE); |
||||
|
||||
contentPanel.add(centerPanel, BorderLayout.CENTER); |
||||
|
||||
return contentPanel; |
||||
} |
||||
|
||||
/** |
||||
* 行动 |
||||
* |
||||
* UI布局 |
||||
* /翻页/不再提醒/提醒行为/我知道了 |
||||
* |
||||
* @return 行动面板 |
||||
*/ |
||||
protected JPanel createTailPanel() { |
||||
|
||||
JPanel tailPanel = FRGUIPaneFactory.createBorderLayout_L_Pane(); |
||||
tailPanel.setName("tailPanel"); |
||||
|
||||
// 翻页按钮效果
|
||||
PageControlPanel pageControlPanel = new PageControlPanel(pageControlModel); |
||||
|
||||
pageControlPanel.actions(new Runnable() { |
||||
@Override |
||||
public void run() { |
||||
pageControlModel = pageControlPanel.performPrevious(); |
||||
refresh(); |
||||
} |
||||
}, new Runnable() { |
||||
@Override |
||||
public void run() { |
||||
pageControlModel = pageControlPanel.performNext(); |
||||
refresh(); |
||||
} |
||||
}); |
||||
|
||||
tailPanel.add(pageControlPanel, BorderLayout.WEST); |
||||
|
||||
// 行为效果
|
||||
JPanel actionsPanel = FRGUIPaneFactory.createBorderLayout_M_Pane(); |
||||
|
||||
{ |
||||
actionsPanel.setBorder(BorderFactory.createEmptyBorder()); |
||||
actionsPanel.setName("actionsPanel"); |
||||
|
||||
UILabel notReminder = new UILabel(); |
||||
notReminder.setText(Toolkit.i18nText("Fine-Design_Basic_Not_Reminder")); |
||||
notReminder.addMouseListener(new MouseAdapter() { |
||||
@Override |
||||
public void mouseClicked(MouseEvent e) { |
||||
// 配置处理
|
||||
EnvDetectorConfig.getInstance().setEnabled(false); |
||||
// 点击事件
|
||||
destroy(); |
||||
} |
||||
}); |
||||
Color color = new Color(65, 155, 249); |
||||
notReminder.setForeground(color); |
||||
actionsPanel.add(notReminder, BorderLayout.WEST); |
||||
|
||||
JPanel buttonPanel = FRGUIPaneFactory.createBorderLayout_M_Pane(); |
||||
buttonPanel.setBorder(BorderFactory.createEmptyBorder()); |
||||
|
||||
// real-action
|
||||
NotificationModel currentModel = getCurrentModel(); |
||||
NotificationAction action = currentModel.getAction(); |
||||
if (action != null) { |
||||
UIButton actionButton = new UIButton(action.name()); |
||||
actionButton.setPreferredSize(BUTTON_DIMENSION); |
||||
actionButton.addActionListener(new ActionListener() { |
||||
@Override |
||||
public void actionPerformed(ActionEvent e) { |
||||
action.run(); |
||||
} |
||||
}); |
||||
buttonPanel.add(actionButton, BorderLayout.WEST); |
||||
} |
||||
|
||||
UIButton knowButton = new UIButton(Toolkit.i18nText("Fine-Design_Basic_Know")); |
||||
knowButton.setPreferredSize(BUTTON_DIMENSION); |
||||
knowButton.addActionListener(new ActionListener() { |
||||
@Override |
||||
public void actionPerformed(ActionEvent e) { |
||||
if (pageControlModel.isLast()) { |
||||
destroy(); |
||||
return; |
||||
} |
||||
pageControlModel = pageControlPanel.performNext(); |
||||
refresh(); |
||||
} |
||||
}); |
||||
buttonPanel.add(knowButton, BorderLayout.EAST); |
||||
|
||||
actionsPanel.add(buttonPanel, BorderLayout.EAST); |
||||
} |
||||
|
||||
tailPanel.add(actionsPanel, BorderLayout.EAST); |
||||
|
||||
return tailPanel; |
||||
} |
||||
|
||||
private void refresh() { |
||||
|
||||
layoutContentPanel(); |
||||
|
||||
layoutTailPanel(); |
||||
|
||||
this.repaint(); |
||||
} |
||||
|
||||
private void layoutHeaderPanel() { |
||||
|
||||
this.headerPanel = layoutPanel(this.headerPanel, this::createHeaderPanel, BorderLayout.NORTH); |
||||
} |
||||
|
||||
private void layoutTailPanel() { |
||||
|
||||
this.tailPanel = layoutPanel(this.tailPanel, this::createTailPanel, BorderLayout.SOUTH); |
||||
} |
||||
|
||||
private void layoutContentPanel() { |
||||
|
||||
this.contentPanel = layoutPanel(this.contentPanel, this::createContentPanel, BorderLayout.CENTER); |
||||
} |
||||
|
||||
private JPanel layoutPanel(JPanel oldPanel, Supplier<JPanel> supplier, Object constraints){ |
||||
|
||||
if (oldPanel != null) { |
||||
this.body.remove(oldPanel); |
||||
} |
||||
JPanel newPanel = supplier.get(); |
||||
if (newPanel != null) { |
||||
this.body.add(newPanel, constraints); |
||||
} |
||||
return newPanel; |
||||
} |
||||
|
||||
private NotificationModel getCurrentModel() { |
||||
|
||||
int index = pageControlModel.getIndex(); |
||||
return notificationModels.get(index); |
||||
} |
||||
|
||||
protected Icon getIconForType(NotificationType type) { |
||||
|
||||
String iconPath; |
||||
switch (type) { |
||||
case ERROR: |
||||
iconPath = "/com/fr/design/standard/reminder/reminder_error.svg"; |
||||
break; |
||||
case INFO: |
||||
iconPath = "/com/fr/design/standard/reminder/reminder_success.svg"; |
||||
break; |
||||
case WARNING: |
||||
iconPath = "/com/fr/design/standard/reminder/reminder_warning.svg"; |
||||
break; |
||||
default: |
||||
return null; |
||||
} |
||||
return IconUtils.readIcon(iconPath); |
||||
} |
||||
|
||||
private void destroy() { |
||||
|
||||
setVisible(false); |
||||
dispose(); |
||||
} |
||||
|
||||
@Override |
||||
public void dispose() { |
||||
|
||||
super.dispose(); |
||||
// todo
|
||||
} |
||||
} |
@ -0,0 +1,39 @@
|
||||
package com.fr.design.components.notification; |
||||
|
||||
import java.awt.Frame; |
||||
|
||||
/** |
||||
* 通知会话的属性 |
||||
* |
||||
* created by Harrison on 2022/05/24 |
||||
**/ |
||||
public class NotificationDialogProperties { |
||||
|
||||
private Frame owner; |
||||
|
||||
private String title; |
||||
|
||||
private boolean modal; |
||||
|
||||
public NotificationDialogProperties(Frame owner, String title) { |
||||
this.owner = owner; |
||||
this.title = title; |
||||
this.modal = false; |
||||
} |
||||
|
||||
public void setModal(boolean modal) { |
||||
this.modal = modal; |
||||
} |
||||
|
||||
public Frame getOwner() { |
||||
return owner; |
||||
} |
||||
|
||||
public String getTitle() { |
||||
return title; |
||||
} |
||||
|
||||
public boolean isModal() { |
||||
return modal; |
||||
} |
||||
} |
@ -0,0 +1,88 @@
|
||||
package com.fr.design.components.notification; |
||||
|
||||
import com.fr.design.utils.LinkStrUtils; |
||||
|
||||
/** |
||||
* created by Harrison on 2022/05/24 |
||||
**/ |
||||
public interface NotificationMessage { |
||||
|
||||
/** |
||||
* 格式化 |
||||
* |
||||
* @return 通知信息 |
||||
*/ |
||||
String format(); |
||||
|
||||
/** |
||||
* 类型 |
||||
* |
||||
* @return 类型 |
||||
*/ |
||||
Type getType(); |
||||
|
||||
enum Type { |
||||
|
||||
/** |
||||
* 简单型 |
||||
*/ |
||||
SIMPLE, |
||||
|
||||
/** |
||||
* 链接 |
||||
*/ |
||||
LINK |
||||
} |
||||
|
||||
class SimpleMessage implements NotificationMessage { |
||||
|
||||
private String text; |
||||
|
||||
public SimpleMessage(String text) { |
||||
this.text = text; |
||||
} |
||||
|
||||
@Override |
||||
public String format() { |
||||
return text; |
||||
} |
||||
|
||||
@Override |
||||
public Type getType() { |
||||
return Type.SIMPLE; |
||||
} |
||||
} |
||||
|
||||
class LinkMessage implements NotificationMessage { |
||||
|
||||
private String text; |
||||
|
||||
private String link; |
||||
|
||||
public LinkMessage(String text, String link) { |
||||
|
||||
this.text = text; |
||||
this.link = link; |
||||
} |
||||
|
||||
public String getText() { |
||||
return text; |
||||
} |
||||
|
||||
public String getLink() { |
||||
return link; |
||||
} |
||||
|
||||
@Override |
||||
public String format() { |
||||
|
||||
return LinkStrUtils.generateHtmlTag(text); |
||||
} |
||||
|
||||
@Override |
||||
public Type getType() { |
||||
|
||||
return Type.LINK; |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,39 @@
|
||||
package com.fr.design.components.notification; |
||||
|
||||
import org.jetbrains.annotations.Nullable; |
||||
|
||||
import java.util.List; |
||||
|
||||
public class NotificationModel { |
||||
|
||||
private final NotificationType type; |
||||
private final NotificationMessage[] messages; |
||||
|
||||
@Nullable |
||||
private final NotificationAction action; |
||||
|
||||
public NotificationModel(NotificationType type, @Nullable NotificationAction action, List<NotificationMessage> messages) { |
||||
this(type, action, messages.toArray(new NotificationMessage[0])); |
||||
} |
||||
|
||||
public NotificationModel(NotificationType type, @Nullable NotificationAction action, NotificationMessage... messages) { |
||||
|
||||
this.type = type; |
||||
this.messages = messages; |
||||
this.action = action; |
||||
} |
||||
|
||||
public NotificationType getType() { |
||||
return type; |
||||
} |
||||
|
||||
public NotificationMessage[] getMessages() { |
||||
return messages == null ? new NotificationMessage[0] : messages; |
||||
} |
||||
|
||||
public @Nullable NotificationAction getAction() { |
||||
|
||||
return action; |
||||
} |
||||
|
||||
} |
@ -0,0 +1,16 @@
|
||||
package com.fr.design.components.notification; |
||||
|
||||
/** |
||||
* 提醒类型 |
||||
* 决定图标种类 |
||||
* |
||||
* created by Harrison on 2022/05/27 |
||||
**/ |
||||
public enum NotificationType { |
||||
|
||||
INFO, |
||||
|
||||
ERROR, |
||||
|
||||
WARNING |
||||
} |
@ -0,0 +1,78 @@
|
||||
package com.fr.design.components.page; |
||||
|
||||
/** |
||||
* created by Harrison on 2022/05/26 |
||||
**/ |
||||
public class PageControlModel { |
||||
|
||||
/** |
||||
* 当前索引 |
||||
* |
||||
* = (页数-1) |
||||
*/ |
||||
private int index; |
||||
|
||||
/** |
||||
* 总页数 |
||||
*/ |
||||
private int summary; |
||||
|
||||
public PageControlModel(int index, int summary) { |
||||
this.index = index; |
||||
this.summary = summary; |
||||
} |
||||
|
||||
public PageControlModel() { |
||||
} |
||||
|
||||
public PageControlModel previous() { |
||||
|
||||
this.index--; |
||||
return this; |
||||
} |
||||
|
||||
public PageControlModel next() { |
||||
|
||||
this.index++; |
||||
return this; |
||||
} |
||||
|
||||
/** |
||||
* 页数 |
||||
* index+1 |
||||
* |
||||
* @return 页数 |
||||
*/ |
||||
public int getNumber() { |
||||
return index + 1; |
||||
} |
||||
|
||||
public boolean isFirst() { |
||||
return getNumber() == 1; |
||||
} |
||||
|
||||
public boolean isLast() { |
||||
return getNumber() == getSummary(); |
||||
} |
||||
|
||||
public int getIndex() { |
||||
return index; |
||||
} |
||||
|
||||
public void setIndex(int index) { |
||||
this.index = index; |
||||
} |
||||
|
||||
public int getSummary() { |
||||
return summary; |
||||
} |
||||
|
||||
public void setSummary(int summary) { |
||||
this.summary = summary; |
||||
} |
||||
|
||||
public String toContent() { |
||||
|
||||
return getNumber() + "/" + this.summary; |
||||
} |
||||
} |
@ -0,0 +1,151 @@
|
||||
package com.fr.design.components.page; |
||||
|
||||
import com.fr.base.svg.IconUtils; |
||||
import com.fr.design.gui.ibutton.UIButton; |
||||
import com.fr.design.gui.ilable.UILabel; |
||||
|
||||
import javax.swing.BorderFactory; |
||||
import javax.swing.JPanel; |
||||
import java.awt.BorderLayout; |
||||
import java.awt.Dimension; |
||||
import java.awt.event.ActionEvent; |
||||
import java.awt.event.ActionListener; |
||||
import java.util.function.Function; |
||||
|
||||
/** |
||||
* 翻页组件 |
||||
* 相关使用方式见 <a href="https://kms.fineres.com/pages/viewpage.action?pageId=415212013">翻页组件</a> |
||||
* |
||||
* created by Harrison on 2022/05/26 |
||||
**/ |
||||
public class PageControlPanel extends JPanel { |
||||
|
||||
private static final long serialVersionUID = 8140501834691131305L; |
||||
|
||||
private static final Dimension PAGE_CONTROL_BUTTON_DIMENSION = new Dimension(20, 20); |
||||
|
||||
private UIButton previous; |
||||
private Runnable previousAction; |
||||
|
||||
private UILabel content; |
||||
private PageControlModel model; |
||||
|
||||
private UIButton next; |
||||
private Runnable nextAction; |
||||
|
||||
public PageControlPanel(PageControlModel model) { |
||||
|
||||
this.model = model; |
||||
setBorder(BorderFactory.createEmptyBorder()); |
||||
setLayout(new BorderLayout(6, 0)); |
||||
|
||||
this.previous = new UIButton(); |
||||
previous.setPreferredSize(PAGE_CONTROL_BUTTON_DIMENSION); |
||||
previous.setIcon(IconUtils.readIcon("/com/fr/design/standard/arrow/arrow_enable_left.svg")); |
||||
previous.setDisabledIcon(IconUtils.readIcon("/com/fr/design/standard/arrow/arrow_disable_left.svg")); |
||||
previous.addActionListener(new ActionListener() { |
||||
@Override |
||||
public void actionPerformed(ActionEvent e) { |
||||
performAction(previousAction); |
||||
} |
||||
}); |
||||
|
||||
this.add(previous, BorderLayout.WEST); |
||||
|
||||
this.content = new UILabel(model.toContent()); |
||||
this.add(content, BorderLayout.CENTER); |
||||
|
||||
next = new UIButton(); |
||||
next.setPreferredSize(PAGE_CONTROL_BUTTON_DIMENSION); |
||||
next.setIcon(IconUtils.readIcon("/com/fr/design/standard/arrow/arrow_enable_right.svg")); |
||||
next.setDisabledIcon(IconUtils.readIcon("/com/fr/design/standard/arrow/arrow_disable_right.svg")); |
||||
next.addActionListener(new ActionListener() { |
||||
@Override |
||||
public void actionPerformed(ActionEvent e) { |
||||
performAction(nextAction); |
||||
} |
||||
}); |
||||
this.add(next, BorderLayout.EAST); |
||||
|
||||
refresh(); |
||||
} |
||||
|
||||
public PageControlModel performPrevious() { |
||||
|
||||
update(PageControlModel::previous); |
||||
return this.model; |
||||
} |
||||
|
||||
public PageControlModel performNext() { |
||||
|
||||
update(PageControlModel::next); |
||||
return this.model; |
||||
} |
||||
|
||||
public PageControlModel getModel() { |
||||
|
||||
return this.model; |
||||
} |
||||
|
||||
public void update(PageControlModel model) { |
||||
|
||||
this.model.setIndex(model.getIndex()); |
||||
this.model.setSummary(model.getSummary()); |
||||
refresh(); |
||||
} |
||||
|
||||
public void update(Function<PageControlModel, PageControlModel> updateAction) { |
||||
|
||||
PageControlModel newModel = updateAction.apply(this.model); |
||||
update(newModel); |
||||
refresh(); |
||||
} |
||||
|
||||
public void refresh() { |
||||
|
||||
this.content.setText(this.model.toContent()); |
||||
this.content.repaint(); |
||||
|
||||
this.previous.setEnabled(true); |
||||
this.next.setEnabled(true); |
||||
if (model.getNumber() == 1) { |
||||
// 禁用上一个
|
||||
disableButton(this.previous); |
||||
// 禁用next
|
||||
if (model.getNumber() == model.getSummary()) { |
||||
disableButton(this.next); |
||||
} |
||||
return; |
||||
} |
||||
|
||||
// 禁用next
|
||||
if (model.getNumber() == model.getSummary()) { |
||||
disableButton(this.next); |
||||
} |
||||
} |
||||
|
||||
private void enable(UIButton button) { |
||||
|
||||
button.setEnabled(true); |
||||
} |
||||
|
||||
private void disableButton(UIButton button) { |
||||
|
||||
button.setEnabled(false); |
||||
} |
||||
|
||||
private void performAction(Runnable action) { |
||||
|
||||
if (action != null) { |
||||
action.run(); |
||||
refresh(); |
||||
} |
||||
} |
||||
|
||||
public void actions(Runnable previousAction, Runnable nextAction) { |
||||
|
||||
this.previousAction = previousAction; |
||||
this.nextAction = nextAction; |
||||
} |
||||
|
||||
} |
@ -0,0 +1,184 @@
|
||||
package com.fr.design.components.table; |
||||
|
||||
import com.fr.design.gui.ilable.UILabel; |
||||
import com.fr.design.layout.FRGUIPaneFactory; |
||||
import com.fr.design.utils.ColorUtils; |
||||
import com.fr.third.org.apache.commons.lang3.ArrayUtils; |
||||
|
||||
import javax.swing.BorderFactory; |
||||
import javax.swing.JPanel; |
||||
import javax.swing.JSeparator; |
||||
import java.awt.BorderLayout; |
||||
import java.awt.Color; |
||||
import java.awt.Component; |
||||
import java.awt.Dimension; |
||||
import java.awt.GridLayout; |
||||
|
||||
/** |
||||
* 表头 |
||||
* 内容 |
||||
* |
||||
* 适用于需要一个表格的 Panel |
||||
* |
||||
* created by Harrison on 2022/05/26 |
||||
**/ |
||||
public class TablePanel extends JPanel { |
||||
|
||||
private static final Color DEFAULT_HEADER_COLOR = new Color(232, 232, 233); |
||||
|
||||
private static final Color DEFAULT_ODD_ROW_COLOR = new Color(245, 245, 247); |
||||
|
||||
private static final Color DEFAULT_EVEN_ROW_COLOR = Color.WHITE; |
||||
|
||||
private JPanel headerPanel; |
||||
|
||||
private JPanel[] headerItemPanels; |
||||
|
||||
private JPanel contentPanel; |
||||
|
||||
private JPanel[][] cellPanels; |
||||
|
||||
public TablePanel(int row, int column) { |
||||
|
||||
setLayout(FRGUIPaneFactory.createBorderLayout()); |
||||
|
||||
/* header 部分 */ |
||||
|
||||
this.headerPanel = new JPanel(); |
||||
headerPanel.setLayout(FRGUIPaneFactory.createNColumnGridLayout(column)); |
||||
headerPanel.setName("header-panel"); |
||||
headerPanel.setPreferredSize(new Dimension(640, 24)); |
||||
|
||||
// border
|
||||
headerPanel.setBorder(BorderFactory.createLineBorder(new Color(218, 218, 221))); |
||||
syncHeaderColor(headerPanel); |
||||
|
||||
headerItemPanels = new JPanel[column]; |
||||
for (int i = 0; i < column; i++) { |
||||
JPanel headerItemWrapper = FRGUIPaneFactory.createBorderLayout_S_Pane(); |
||||
syncHeaderColor(headerItemWrapper); |
||||
headerItemWrapper.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 10)); |
||||
|
||||
JPanel headerItemPanel = FRGUIPaneFactory.createBorderLayout_S_Pane(); |
||||
headerItemPanel.setBorder(BorderFactory.createEmptyBorder(0, 10, 0, 0)); |
||||
headerItemPanels[i] = headerItemPanel; |
||||
|
||||
UILabel label = new UILabel(); |
||||
syncHeaderColor(label); |
||||
|
||||
headerItemPanel.add(new UILabel(), BorderLayout.CENTER); |
||||
|
||||
headerItemWrapper.add(headerItemPanel, BorderLayout.WEST); |
||||
if (i != column - 1) { |
||||
JSeparator separator = new JSeparator(JSeparator.VERTICAL); |
||||
separator.setBackground(new Color(218, 218, 221)); |
||||
headerItemWrapper.add(separator, BorderLayout.EAST); |
||||
} |
||||
headerPanel.add(headerItemWrapper); |
||||
} |
||||
|
||||
/* content 部分 */ |
||||
|
||||
contentPanel = new JPanel(); |
||||
|
||||
contentPanel.setLayout(new GridLayout(row, 1)); |
||||
contentPanel.setBorder(BorderFactory.createLineBorder(new Color(218, 218, 221))); |
||||
|
||||
cellPanels = new JPanel[row][column]; |
||||
for (int i = 0; i < row; i++) { |
||||
|
||||
JPanel rowPanel = new JPanel(); |
||||
// 获取行号
|
||||
Color rowColor = getRowColorByRowNumber(i + 1); |
||||
rowPanel.setBackground(rowColor); |
||||
rowPanel.setLayout(FRGUIPaneFactory.createNColumnGridLayout(column)); |
||||
rowPanel.setName("row-" + i); |
||||
rowPanel.setBorder(BorderFactory.createEmptyBorder()); |
||||
rowPanel.setPreferredSize(new Dimension(640, 24)); |
||||
|
||||
for (int j = 0; j < column; j++) { |
||||
|
||||
JPanel rowItemPanel = FRGUIPaneFactory.createBorderLayout_S_Pane(); |
||||
rowItemPanel.setBorder(BorderFactory.createEmptyBorder(0, 10, 0, 0)); |
||||
rowItemPanel.setName("rowItemPanel-"+ i + "-" + j); |
||||
final UILabel empty = new UILabel(); |
||||
empty.setPreferredSize(new Dimension(210, 24)); |
||||
rowItemPanel.setBackground(rowPanel.getBackground()); |
||||
rowItemPanel.add(empty, BorderLayout.CENTER); |
||||
|
||||
rowPanel.add(rowItemPanel); |
||||
cellPanels[i][j] = rowItemPanel; |
||||
} |
||||
|
||||
contentPanel.add(rowPanel); |
||||
} |
||||
|
||||
add(headerPanel, BorderLayout.NORTH); |
||||
add(contentPanel, BorderLayout.SOUTH); |
||||
} |
||||
|
||||
/** |
||||
* 获取行的颜色 |
||||
* |
||||
* @param row 行号 |
||||
* @return 颜色 |
||||
*/ |
||||
private Color getRowColorByRowNumber(int row) { |
||||
|
||||
Color rowColor; |
||||
if (row % 2 != 0) { |
||||
rowColor = DEFAULT_EVEN_ROW_COLOR; |
||||
} else { |
||||
rowColor = DEFAULT_ODD_ROW_COLOR; |
||||
} |
||||
return rowColor; |
||||
} |
||||
|
||||
public void updateHeaders(String[] headers) { |
||||
|
||||
for (int i = 0; i < headers.length; i++) { |
||||
String header = headers[i]; |
||||
UILabel headerContent = new UILabel(header); |
||||
JPanel headerItemPanel = headerItemPanels[i]; |
||||
if (ArrayUtils.getLength(headerItemPanel.getComponents()) == 1) { |
||||
headerItemPanel.remove(0); |
||||
} |
||||
headerItemPanel.add(headerContent); |
||||
syncHeaderColor(headerItemPanel); |
||||
} |
||||
} |
||||
|
||||
public void updateCell(int row, int column, Component component) { |
||||
|
||||
int x = row - 1; |
||||
int y = column - 1; |
||||
|
||||
syncCellColor(row, component); |
||||
|
||||
JPanel cellPanel = this.cellPanels[x][y]; |
||||
if (ArrayUtils.getLength(cellPanel.getComponents()) == 1) { |
||||
cellPanel.remove(0); |
||||
} |
||||
cellPanel.add(component); |
||||
} |
||||
|
||||
public void updateCell(int row, int column, String value) { |
||||
|
||||
UILabel cellContent = new UILabel(value); |
||||
syncCellColor(row, cellContent); |
||||
this.updateCell(row, column, cellContent); |
||||
} |
||||
|
||||
private void syncHeaderColor(Component component) { |
||||
|
||||
ColorUtils.syncBackground(component, DEFAULT_HEADER_COLOR); |
||||
} |
||||
|
||||
private void syncCellColor(int row, Component component) { |
||||
|
||||
Color rowColor = getRowColorByRowNumber(row); |
||||
ColorUtils.syncBackground(component, rowColor); |
||||
component.setBackground(rowColor); |
||||
} |
||||
|
||||
} |
@ -0,0 +1,105 @@
|
||||
package com.fr.design.components.tooltip; |
||||
|
||||
import com.fr.base.GraphHelper; |
||||
import com.fr.design.gui.itooltip.UIToolTip; |
||||
import com.fr.log.FineLoggerFactory; |
||||
|
||||
import javax.swing.Icon; |
||||
import javax.swing.JComponent; |
||||
import javax.swing.JToolTip; |
||||
import javax.swing.SwingUtilities; |
||||
import javax.swing.plaf.ToolTipUI; |
||||
import java.awt.Color; |
||||
import java.awt.Dimension; |
||||
import java.awt.FontMetrics; |
||||
import java.awt.Graphics; |
||||
import java.awt.Graphics2D; |
||||
import java.awt.RenderingHints; |
||||
import java.io.BufferedReader; |
||||
import java.io.IOException; |
||||
import java.io.StringReader; |
||||
import java.util.Enumeration; |
||||
import java.util.Vector; |
||||
|
||||
/** |
||||
* 现代化的 UIToolTip |
||||
* 见 <a href="https://kms.fineres.com/pages/viewpage.action?pageId=416850313">设计文档</a> |
||||
* |
||||
* created by Harrison on 2022/07/09 |
||||
**/ |
||||
public class ModernToolTip extends UIToolTip { |
||||
|
||||
public ModernToolTip() { |
||||
super(); |
||||
setUI(new ModernToolTipUI()); |
||||
} |
||||
|
||||
private class ModernToolTipUI extends ToolTipUI { |
||||
|
||||
private String[] strs; |
||||
private Icon icon; |
||||
private boolean needPaint; |
||||
public void paint(Graphics g, JComponent c) { |
||||
if (!needPaint) { |
||||
return; |
||||
} |
||||
FontMetrics metrics = GraphHelper.getFontMetrics(c.getFont()); |
||||
Dimension size = c.getSize(); |
||||
int width = size.width; |
||||
int height = size.height; |
||||
Graphics2D g2 = (Graphics2D) g; |
||||
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); |
||||
g2.setColor(new Color(51, 51, 52, (int) Math.round(0.7 * 255))); |
||||
g2.fillRoundRect(0, 0, width, height, 4, 4); |
||||
|
||||
g2.setColor(Color.WHITE); |
||||
if (strs != null) { |
||||
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_DEFAULT); |
||||
for (int i = 0; i < strs.length; i++) { |
||||
g2.drawString(strs[i], icon.getIconWidth() + 6, (metrics.getHeight()) * (i + 1)); |
||||
} |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
public Dimension getPreferredSize(JComponent c) { |
||||
FontMetrics metrics = GraphHelper.getFontMetrics(c.getFont()); |
||||
String tipText = ((JToolTip) c).getTipText(); |
||||
icon = ((UIToolTip)c).getIcon(); |
||||
needPaint = true; |
||||
if (tipText == null) { |
||||
if(icon.getIconWidth() == -1) { |
||||
needPaint = false; |
||||
} |
||||
tipText = " "; |
||||
} |
||||
BufferedReader br = new BufferedReader(new StringReader(tipText)); |
||||
String line; |
||||
int maxWidth = 0; |
||||
Vector<String> v = new Vector<String>(); |
||||
try { |
||||
while ((line = br.readLine()) != null) { |
||||
int width = SwingUtilities.computeStringWidth(metrics, line); |
||||
maxWidth = (maxWidth < width) ? width : maxWidth; |
||||
v.addElement(line); |
||||
} |
||||
} catch (IOException ex) { |
||||
FineLoggerFactory.getLogger().error(ex.getMessage(), ex); |
||||
} |
||||
int lines = v.size(); |
||||
if (lines < 1) { |
||||
strs = null; |
||||
lines = 1; |
||||
} else { |
||||
strs = new String[lines]; |
||||
int i = 0; |
||||
for (Enumeration<String> e = v.elements(); e.hasMoreElements(); i++) { |
||||
strs[i] = e.nextElement(); |
||||
} |
||||
} |
||||
int height = metrics.getHeight() * lines; |
||||
return new Dimension(maxWidth + icon.getIconWidth() + 10, Math.max(height, icon.getIconHeight()) + 6); |
||||
} |
||||
|
||||
} |
||||
} |
@ -0,0 +1,52 @@
|
||||
package com.fr.design.config; |
||||
|
||||
import com.fr.general.IOUtils; |
||||
import com.fr.log.FineLoggerFactory; |
||||
import com.fr.stable.StableUtils; |
||||
import com.fr.stable.StringUtils; |
||||
|
||||
import java.io.BufferedInputStream; |
||||
import java.io.FileInputStream; |
||||
import java.io.FileNotFoundException; |
||||
import java.io.InputStream; |
||||
import java.util.Properties; |
||||
|
||||
public class DesignerProperties { |
||||
private static DesignerProperties holder = null; |
||||
private boolean supportLoginEntry = true; |
||||
|
||||
public DesignerProperties() { |
||||
String filePath = StableUtils.pathJoin(StableUtils.getInstallHome(), "/config/config.properties"); |
||||
InputStream is = null; |
||||
try { |
||||
is = new BufferedInputStream(new FileInputStream(filePath)); |
||||
Properties ps = new Properties(); |
||||
ps.load(is); |
||||
this.initProperties(ps); |
||||
} catch (FileNotFoundException e) { |
||||
// ignore
|
||||
} catch (Exception e) { |
||||
FineLoggerFactory.getLogger().error(e, e.getMessage()); |
||||
} finally { |
||||
IOUtils.close(is); |
||||
} |
||||
} |
||||
|
||||
public static DesignerProperties getInstance() { |
||||
if (holder == null) { |
||||
holder = new DesignerProperties(); |
||||
} |
||||
return holder; |
||||
} |
||||
|
||||
private void initProperties(Properties ps) { |
||||
String supportLoginEntry = ps.getProperty("supportLoginEntry"); |
||||
if (StringUtils.isNotEmpty(supportLoginEntry)) { |
||||
this.supportLoginEntry = Boolean.valueOf(supportLoginEntry); |
||||
} |
||||
} |
||||
|
||||
public boolean isSupportLoginEntry() { |
||||
return supportLoginEntry; |
||||
} |
||||
} |
@ -0,0 +1,24 @@
|
||||
package com.fr.design.constants; |
||||
|
||||
import java.awt.Color; |
||||
|
||||
/** |
||||
* 见 <a href="https://www.figma.com/file/G2f40rv7cY8zpDDYbt6pY2/%E8%AE%BE%E8%AE%A1%E5%99%A811.0%E8%A7%84%E8%8C%83%E6%95%B4%E7%90%86">设计器规范</a> |
||||
* 将相关的逻辑抽象过来 |
||||
* 如果后面更改的话, 可以统一修改 |
||||
* 如果换版本,可以换成 v2 这种类推 |
||||
* |
||||
* created by Harrison on 2022/05/26 |
||||
**/ |
||||
public interface DesignerColor { |
||||
|
||||
interface Button { |
||||
|
||||
interface Primary { |
||||
|
||||
Color PRESSED = new Color(29, 122, 220); |
||||
Color HOVER = new Color(84, 165, 249); |
||||
Color NORMAL = new Color(65, 155, 249); |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,5 @@
|
||||
package com.fr.design.constants; |
||||
|
||||
public class TableDataConstants { |
||||
public static final String SEPARATOR = "_"; |
||||
} |
@ -0,0 +1,93 @@
|
||||
package com.fr.design.data; |
||||
|
||||
import org.jetbrains.annotations.NotNull; |
||||
|
||||
import java.util.LinkedHashMap; |
||||
import java.util.Map; |
||||
|
||||
/** |
||||
* @author rinoux |
||||
* @version 10.0 |
||||
* Created by rinoux on 2022/3/28 |
||||
*/ |
||||
public final class MapCompareUtils { |
||||
|
||||
|
||||
/** |
||||
* 对比两个map 查找出相比orig,other中有哪些是新增的、删除的或者被修改的,并分别进行处理 |
||||
* |
||||
* 对比时默认用equals方法来判断是否为 EntryEventKind#UPDATED |
||||
* |
||||
* @param orig 原始map |
||||
* @param other 参考的新map |
||||
* @param eventHandler 有区别时的事件处理器 |
||||
* @param <K> K |
||||
* @param <V> V |
||||
*/ |
||||
public static <K, V> void contrastMapEntries(@NotNull Map<K, V> orig, @NotNull Map<K, V> other, @NotNull EventHandler<K, V> eventHandler) { |
||||
|
||||
contrastMapEntries(orig, other, eventHandler, UpdateRule.DEFAULT); |
||||
} |
||||
|
||||
/** |
||||
* 对比两个map 查找出相比orig,other中有哪些是新增的、删除的或者被修改的,并分别进行处理 |
||||
* |
||||
* 对比时用自定义的规则来判断是否为 EntryEventKind#UPDATED |
||||
* |
||||
* @param orig 原始map |
||||
* @param other 参考的新map |
||||
* @param eventHandler 有区别时的事件处理器 |
||||
* @param updateRule 自定义的Update事件判定规则 |
||||
* @param <K> |
||||
* @param <V> |
||||
*/ |
||||
public static <K, V> void contrastMapEntries(@NotNull Map<K, V> orig, @NotNull Map<K, V> other, @NotNull EventHandler<K, V> eventHandler, @NotNull UpdateRule<K, V> updateRule) { |
||||
|
||||
Map<K, V> copiedOrig = new LinkedHashMap<>(orig); |
||||
|
||||
other.forEach((k, v) -> { |
||||
V existedV = copiedOrig.remove(k); |
||||
if (existedV != null) { |
||||
if (updateRule.needUpdate(existedV, v)) { |
||||
eventHandler.on(EntryEventKind.UPDATED, k, v); |
||||
} |
||||
} else { |
||||
eventHandler.on(EntryEventKind.ADDED, k, v); |
||||
} |
||||
}); |
||||
|
||||
copiedOrig.forEach((k, v) -> eventHandler.on(EntryEventKind.REMOVED, k, v)); |
||||
} |
||||
|
||||
|
||||
/** |
||||
* 事件处理器,对应比较后的三种结果的事件处理 |
||||
* @param <K> |
||||
* @param <V> |
||||
*/ |
||||
public interface EventHandler<K, V> { |
||||
void on(EntryEventKind entryEventKind, K k, V v); |
||||
} |
||||
|
||||
/** |
||||
* 数据被修改(EntryEventKind.UPDATED) 的判定规则 |
||||
* @param <K> |
||||
* @param <V> |
||||
*/ |
||||
public interface UpdateRule<K, V> { |
||||
|
||||
EntryEventKind eventKind = EntryEventKind.UPDATED; |
||||
|
||||
UpdateRule DEFAULT = new UpdateRule() {}; |
||||
|
||||
default boolean needUpdate(V origin, V v) { |
||||
return !v.equals(origin); |
||||
} |
||||
} |
||||
|
||||
public enum EntryEventKind { |
||||
ADDED, |
||||
REMOVED, |
||||
UPDATED; |
||||
} |
||||
} |
@ -0,0 +1,73 @@
|
||||
package com.fr.design.data.datapane; |
||||
|
||||
import com.fr.data.util.function.AbstractDataFunction; |
||||
import com.fr.data.util.function.AverageFunction; |
||||
import com.fr.data.util.function.CountFunction; |
||||
import com.fr.data.util.function.MaxFunction; |
||||
import com.fr.data.util.function.MinFunction; |
||||
import com.fr.data.util.function.NoneFunction; |
||||
import com.fr.data.util.function.SumFunction; |
||||
import com.fr.design.gui.icombobox.UIComboBox; |
||||
import com.fr.design.i18n.Toolkit; |
||||
import com.fr.general.ComparatorUtils; |
||||
import com.fr.log.FineLoggerFactory; |
||||
import com.fr.plugin.chart.base.FirstFunction; |
||||
|
||||
/** |
||||
* 图表数据汇总方式下拉框 |
||||
* |
||||
* 支持首个,最后一个,求和,平均,最大值,最小值,个数 |
||||
* |
||||
*/ |
||||
public class SummaryMethodComboBox extends UIComboBox { |
||||
public static final String[] CALCULATE_ARRAY = {Toolkit.i18nText("Fine-Design_Chart_Data_Function_First"), Toolkit.i18nText("Fine-Design_Chart_Data_Function_Last"), |
||||
Toolkit.i18nText("Fine-Design_Chart_Data_Function_Sum"), Toolkit.i18nText("Fine-Design_Chart_Data_Function_Average"), |
||||
Toolkit.i18nText("Fine-Design_Chart_Data_Function_Max"), Toolkit.i18nText("Fine-Design_Chart_Data_Function_Min"), |
||||
Toolkit.i18nText("Fine-Design_Chart_Data_Function_Count")}; |
||||
public static final Class[] CLASS_ARRAY = {FirstFunction.class, NoneFunction.class, SumFunction.class, AverageFunction.class, |
||||
MaxFunction.class, MinFunction.class, CountFunction.class}; |
||||
|
||||
public SummaryMethodComboBox() { |
||||
super(CALCULATE_ARRAY); |
||||
setSelectedIndex(2); |
||||
} |
||||
|
||||
public void reset() { |
||||
this.setSelectedItem(Toolkit.i18nText("Fine-Design_Chart_Data_Function_Sum")); |
||||
} |
||||
|
||||
/** |
||||
* 更新公式选择. |
||||
*/ |
||||
public void populateBean(AbstractDataFunction function) { |
||||
if (function != null) { |
||||
for (int i = 0; i < CLASS_ARRAY.length; i++) { |
||||
if (this.getModel() != null && this.getModel().getSize() > i |
||||
&& ComparatorUtils.equals(function.getClass(), CLASS_ARRAY[i])) { |
||||
setSelectedIndex(i); |
||||
break; |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* 返回当前选择的公式 |
||||
*/ |
||||
public AbstractDataFunction updateBean() { |
||||
try { |
||||
int selectIndex = getSelectedIndex(); |
||||
if (selectIndex >= 0 && selectIndex < CLASS_ARRAY.length) { |
||||
return (AbstractDataFunction) CLASS_ARRAY[selectIndex].newInstance(); |
||||
} |
||||
} catch (InstantiationException e) { |
||||
FineLoggerFactory.getLogger().error("Function Error"); |
||||
return null; |
||||
} catch (IllegalAccessException e) { |
||||
FineLoggerFactory.getLogger().error("Function Error"); |
||||
return null; |
||||
} |
||||
|
||||
return null; |
||||
} |
||||
} |
@ -0,0 +1,57 @@
|
||||
package com.fr.design.data.datapane.auth; |
||||
|
||||
import com.fr.base.TableData; |
||||
import com.fr.data.impl.Connection; |
||||
import com.fr.data.impl.DBTableData; |
||||
import com.fr.data.impl.NameDatabaseConnection; |
||||
import com.fr.stable.StringUtils; |
||||
import com.fr.workspace.WorkContext; |
||||
import com.fr.workspace.server.connection.DBConnectAuth; |
||||
|
||||
import java.util.Collection; |
||||
import java.util.Collections; |
||||
|
||||
/** |
||||
* 数据连接权限相关的工具类 |
||||
* @author Yvan |
||||
*/ |
||||
public class TableDataAuthHelper { |
||||
|
||||
/** |
||||
* 编辑数据集时是否需要检查权限 |
||||
* @param tableData |
||||
* @return |
||||
*/ |
||||
public static boolean needCheckAuthWhenEdit(TableData tableData) { |
||||
// 远程设计下,编辑DBTableData时需要判断权限
|
||||
return !WorkContext.getCurrent().isLocal() && tableData instanceof DBTableData; |
||||
} |
||||
|
||||
/** |
||||
* 获取无权限数据连接集合 |
||||
* 远程下需要调用RPC,为耗时操作,谨慎使用 |
||||
* @return |
||||
*/ |
||||
public static Collection<String> getNoAuthConnections() { |
||||
// 获取无权限连接集合
|
||||
Collection<String> noAuthConnections = WorkContext.getCurrent().get(DBConnectAuth.class).getNoAuthConnections(); |
||||
return noAuthConnections == null ? Collections.emptyList() : noAuthConnections; |
||||
} |
||||
|
||||
/** |
||||
* 通过数据集获取其数据连接的名称 |
||||
* |
||||
* 注意: |
||||
* 1. Connection接口本身是不提供名称的,只有我们内部为了使用方便,将其包装成了NameDataBaseConnection |
||||
* 如果不是NameDataBaseConnection类型,则无名称,因此这里只能用判断类型的方式获取名称 |
||||
* 2. 仅支持DBTableData获取连接名 |
||||
* @return |
||||
*/ |
||||
public static String getConnectionNameByDBTableData(DBTableData tableData) { |
||||
Connection database = tableData.getDatabase(); |
||||
if (database instanceof NameDatabaseConnection) { |
||||
return ((NameDatabaseConnection) database).getName(); |
||||
} |
||||
return StringUtils.EMPTY; |
||||
} |
||||
} |
@ -0,0 +1,145 @@
|
||||
package com.fr.design.data.datapane.connect; |
||||
|
||||
import com.fr.data.impl.JDBCDatabaseConnection; |
||||
import com.fr.data.pool.DBCPConnectionPoolAttr; |
||||
import com.fr.design.dialog.BasicPane; |
||||
import com.fr.design.editor.editor.IntegerEditor; |
||||
import com.fr.design.gui.icombobox.UIComboBox; |
||||
import com.fr.design.gui.ilable.UILabel; |
||||
import com.fr.design.gui.itextfield.UITextField; |
||||
import com.fr.design.i18n.Toolkit; |
||||
import com.fr.design.layout.FRGUIPaneFactory; |
||||
import com.fr.design.layout.TableLayout; |
||||
import com.fr.design.layout.TableLayoutHelper; |
||||
import com.fr.stable.StringUtils; |
||||
|
||||
import javax.swing.JPanel; |
||||
import javax.swing.SwingConstants; |
||||
import java.awt.BorderLayout; |
||||
import java.awt.Color; |
||||
import java.awt.Component; |
||||
import java.awt.event.FocusEvent; |
||||
import java.awt.event.FocusListener; |
||||
|
||||
/** |
||||
* @author xiqiu |
||||
* @date 2021/11/22 |
||||
* @description |
||||
*/ |
||||
public class AdvancePane extends BasicPane { |
||||
private IntegerEditor DBCP_MAX_ACTIVE = new IntegerEditor(); |
||||
private UIComboBox DBCP_TESTONBORROW = new UIComboBox(new String[]{Toolkit.i18nText("Fine-Design_Basic_No"), Toolkit.i18nText("Fine-Design_Basic_Yes")}); |
||||
private IntegerEditor DBCP_MAX_WAIT = new IntegerEditor(); |
||||
private SpecialUITextField DBCP_VALIDATION_QUERY = new SpecialUITextField(); |
||||
|
||||
|
||||
public AdvancePane() { |
||||
JPanel jPanel = FRGUIPaneFactory.createBorderLayout_S_Pane(); |
||||
DBCP_VALIDATION_QUERY.addFocusListener(new JTextFieldHintListener(DBCP_VALIDATION_QUERY)); |
||||
double p = TableLayout.PREFERRED; |
||||
DBCP_VALIDATION_QUERY.setColumns(20); |
||||
double[] rowSizeDbcp = {p, p, p, p}; |
||||
double[] columnDbcp = {190, p}; |
||||
Component[][] comps = { |
||||
{new UILabel(Toolkit.i18nText("Fine-Design_Basic_Dbcp_Max_Active") + ":", SwingConstants.RIGHT), DBCP_MAX_ACTIVE}, |
||||
{new UILabel(Toolkit.i18nText("Fine-Design_Basic_Dbcp_Validation_Query") + ":", SwingConstants.RIGHT), DBCP_VALIDATION_QUERY}, |
||||
{new UILabel(Toolkit.i18nText("Fine-Design_Basic_Dbcp_Test_On_Borrow") + ":", SwingConstants.RIGHT), DBCP_TESTONBORROW}, |
||||
{new UILabel(Toolkit.i18nText("Fine-Design_Basic_Connection_Pool_Max_Wait_Time") + ":", SwingConstants.RIGHT), DBCP_MAX_WAIT} |
||||
}; |
||||
|
||||
JPanel contextPane = TableLayoutHelper.createGapTableLayoutPane(comps, rowSizeDbcp, columnDbcp, 11, 11); |
||||
jPanel.add(contextPane, BorderLayout.CENTER); |
||||
this.add(jPanel); |
||||
} |
||||
|
||||
|
||||
public void populate(JDBCDatabaseConnection jdbcDatabase) { |
||||
DBCPConnectionPoolAttr dbcpAttr = jdbcDatabase.getDbcpAttr(); |
||||
if (dbcpAttr == null) { |
||||
dbcpAttr = new DBCPConnectionPoolAttr(); |
||||
jdbcDatabase.setDbcpAttr(dbcpAttr); |
||||
} |
||||
this.DBCP_MAX_ACTIVE.setValue(dbcpAttr.getMaxActive()); |
||||
this.DBCP_MAX_WAIT.setValue(dbcpAttr.getMaxWait()); |
||||
this.DBCP_VALIDATION_QUERY.setText(dbcpAttr.getValidationQuery()); |
||||
this.DBCP_TESTONBORROW.setSelectedIndex(dbcpAttr.isTestOnBorrow() ? 1 : 0); |
||||
|
||||
} |
||||
|
||||
public void update(JDBCDatabaseConnection jdbcDatabase) { |
||||
DBCPConnectionPoolAttr dbcpAttr = jdbcDatabase.getDbcpAttr(); |
||||
if (dbcpAttr == null) { |
||||
dbcpAttr = new DBCPConnectionPoolAttr(); |
||||
jdbcDatabase.setDbcpAttr(dbcpAttr); |
||||
} |
||||
dbcpAttr.setMaxActive(this.DBCP_MAX_ACTIVE.getValue().intValue()); |
||||
dbcpAttr.setMaxWait(this.DBCP_MAX_WAIT.getValue().intValue()); |
||||
dbcpAttr.setValidationQuery(this.DBCP_VALIDATION_QUERY.getText()); |
||||
dbcpAttr.setTestOnBorrow(this.DBCP_TESTONBORROW.getSelectedIndex() == 0 ? false : true); |
||||
} |
||||
|
||||
@Override |
||||
protected String title4PopupWindow() { |
||||
return Toolkit.i18nText("Fine-Design_Basic_Advanced_Setup"); |
||||
} |
||||
|
||||
|
||||
private class JTextFieldHintListener implements FocusListener { |
||||
private SpecialUITextField textField; |
||||
|
||||
public JTextFieldHintListener(SpecialUITextField jTextField) { |
||||
this.textField = jTextField; |
||||
} |
||||
|
||||
@Override |
||||
public void focusGained(FocusEvent e) { |
||||
//获取焦点时,清空提示内容
|
||||
String temp = textField.getText(); |
||||
textField.setForeground(Color.BLACK); |
||||
textField.setTextOrigin(temp); |
||||
|
||||
} |
||||
|
||||
@Override |
||||
public void focusLost(FocusEvent e) { |
||||
//失去焦点时,没有输入内容,显示提示内容
|
||||
String temp = textField.getTextOrigin(); |
||||
textField.setText(temp); |
||||
} |
||||
} |
||||
|
||||
private class SpecialUITextField extends UITextField { |
||||
|
||||
@Override |
||||
public String getText() { |
||||
String text = super.getText(); |
||||
if (isUseless(text)) { |
||||
return StringUtils.EMPTY; |
||||
} |
||||
return text; |
||||
} |
||||
|
||||
@Override |
||||
public void setText(String text) { |
||||
if (isUseless(text)) { |
||||
this.setForeground(Color.GRAY); |
||||
super.setText(Toolkit.i18nText("Fine-Design_Dbcp_Default_Query")); |
||||
} else { |
||||
this.setForeground(Color.BLACK); |
||||
super.setText(text); |
||||
} |
||||
} |
||||
|
||||
public String getTextOrigin() { |
||||
return super.getText(); |
||||
} |
||||
|
||||
public void setTextOrigin(String text) { |
||||
super.setText(text); |
||||
} |
||||
|
||||
private boolean isUseless(String text) { |
||||
return text == null || text.isEmpty() || Toolkit.i18nText("Fine-Design_Dbcp_Default_Query").equals(text); |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,301 @@
|
||||
package com.fr.design.data.datapane.connect; |
||||
|
||||
import com.fr.data.impl.JDBCDatabaseConnection; |
||||
import com.fr.data.security.ssh.BaseSsh; |
||||
import com.fr.data.security.ssh.Ssh; |
||||
import com.fr.data.security.ssh.SshException; |
||||
import com.fr.data.security.ssh.SshType; |
||||
import com.fr.data.security.ssh.impl.KeyVerifySsh; |
||||
import com.fr.data.security.ssh.impl.NormalSsh; |
||||
import com.fr.data.security.ssl.SslUtils; |
||||
import com.fr.design.border.UITitledBorder; |
||||
import com.fr.design.constants.UIConstants; |
||||
import com.fr.design.dialog.BasicPane; |
||||
import com.fr.design.editor.editor.NotNegativeIntegerEditor; |
||||
import com.fr.design.gui.ibutton.UIButton; |
||||
import com.fr.design.gui.icheckbox.UICheckBox; |
||||
import com.fr.design.gui.icombobox.UIComboBox; |
||||
import com.fr.design.gui.ilable.UILabel; |
||||
import com.fr.design.gui.ipasswordfield.UIPasswordFieldWithFixedLength; |
||||
import com.fr.design.gui.itextfield.UITextField; |
||||
import com.fr.design.i18n.Toolkit; |
||||
import com.fr.design.layout.FRGUIPaneFactory; |
||||
import com.fr.design.layout.TableLayout; |
||||
import com.fr.design.layout.TableLayoutHelper; |
||||
import com.fr.file.FILE; |
||||
import com.fr.file.FILEChooserPane; |
||||
import com.fr.file.filter.ChooseFileFilter; |
||||
import com.fr.stable.StringUtils; |
||||
import com.fr.third.guava.collect.HashBiMap; |
||||
|
||||
import javax.swing.ImageIcon; |
||||
import javax.swing.JPanel; |
||||
import javax.swing.JPasswordField; |
||||
import javax.swing.SwingConstants; |
||||
import java.awt.BorderLayout; |
||||
import java.awt.Component; |
||||
import java.awt.Dimension; |
||||
import java.awt.event.ActionEvent; |
||||
import java.awt.event.ActionListener; |
||||
import java.awt.event.KeyAdapter; |
||||
import java.awt.event.KeyEvent; |
||||
import java.util.regex.Matcher; |
||||
import java.util.regex.Pattern; |
||||
|
||||
import static com.fr.design.i18n.Toolkit.i18nText; |
||||
|
||||
/** |
||||
* @author xiqiu |
||||
* @date 2021/12/23 |
||||
* @description |
||||
*/ |
||||
public class SshPane extends BasicPane { |
||||
private static HashBiMap<String, SshType> typeMap; |
||||
|
||||
static { |
||||
typeMap = HashBiMap.create(); |
||||
typeMap.put(Toolkit.i18nText("Fine-Design_Basic_Password"), SshType.NORMAL); |
||||
typeMap.put(Toolkit.i18nText("Fine-Design_Basic_Ssh_Public_Key"), SshType.KEY); |
||||
} |
||||
|
||||
private UICheckBox usingSsh = new UICheckBox(i18nText("Fine-Design_Basic_Ssh_Using")); |
||||
private NotNegativeIntegerEditor port = new NotNegativeIntegerEditor(20); |
||||
private UITextField ip = new UITextField(20); |
||||
private UIComboBox type = new UIComboBox(); |
||||
private UITextField user = new UITextField(20); |
||||
private JPasswordField password = new UIPasswordFieldWithFixedLength(20); |
||||
private JPasswordField secret = new UIPasswordFieldWithFixedLength(20); |
||||
private KeyFileUITextField keyPath = new KeyFileUITextField(18); |
||||
private JPanel contextPane; |
||||
private Component[][] passwordComps; |
||||
private Component[][] keyComps; |
||||
private double p = TableLayout.PREFERRED; |
||||
private double f = TableLayout.FILL; |
||||
private JPanel jPanel; |
||||
private UIButton fileChooserButton = new UIButton(); |
||||
private double[] columnSize = new double[]{195, p}; |
||||
|
||||
public SshPane() { |
||||
fileChooserButton.setIcon(new ImageIcon(UIConstants.ACCESSIBLE_EDITOR_DOT)); |
||||
this.setBorder(UITitledBorder.createBorderWithTitle(Toolkit.i18nText("Fine-Design_Basic_Ssh_Settings"))); |
||||
this.setLayout(FRGUIPaneFactory.createLabelFlowLayout()); |
||||
typeMap.keySet().forEach(key -> type.addItem(key)); |
||||
type.setSelectedItem(typeMap.inverse().get(SshType.KEY)); |
||||
jPanel = FRGUIPaneFactory.createBorderLayout_S_Pane(); |
||||
fileChooserButton.setPreferredSize(new Dimension(20, 20)); |
||||
type.setEditable(false); |
||||
type.setSelectedItem(Toolkit.i18nText("Fine-Design_Basic_Ssh_Private_Key")); |
||||
JPanel filePanel = TableLayoutHelper.createCommonTableLayoutPane(new Component[][]{{keyPath, fileChooserButton}}, new double[]{p}, new double[]{f, 20}, 0); |
||||
Component[] compIp = {new UILabel(Toolkit.i18nText("Fine-Design_Basic_Host") + ":", SwingConstants.RIGHT), ip}; |
||||
Component[] compPort = {new UILabel(Toolkit.i18nText("Fine-Design_Basic_Port") + ":", SwingConstants.RIGHT), port}; |
||||
Component[] compUserName = {new UILabel(Toolkit.i18nText("Fine-Design_Report_UserName") + ":", SwingConstants.RIGHT), user}; |
||||
Component[] compMethod = {new UILabel(Toolkit.i18nText("Fine-Design_Basic_Ssh_Verify_Method") + ":", SwingConstants.RIGHT), type}; |
||||
Component[] compPassword = {new UILabel(Toolkit.i18nText("Fine-Design_Basic_Password") + ":", SwingConstants.RIGHT), password}; |
||||
Component[] compKey = {new UILabel(Toolkit.i18nText("Fine-Design_Basic_Ssh_Private_Key") + ":", SwingConstants.RIGHT), filePanel}; |
||||
Component[] comSecret = {new UILabel(Toolkit.i18nText("Fine-Design_Basic_Ssh_Secret") + ":", SwingConstants.RIGHT), secret}; |
||||
|
||||
passwordComps = new Component[][]{ |
||||
compIp, |
||||
compPort, |
||||
compUserName, |
||||
compMethod, |
||||
compPassword |
||||
}; |
||||
keyComps = new Component[][]{ |
||||
compIp, |
||||
compPort, |
||||
compUserName, |
||||
compMethod, |
||||
compKey, |
||||
comSecret |
||||
}; |
||||
usingSsh.setSelected(true); |
||||
contextPane = TableLayoutHelper.createGapTableLayoutPane(keyComps, new double[]{p, p, p, p, p, p}, columnSize, 11, 11); |
||||
jPanel.add(usingSsh, BorderLayout.NORTH); |
||||
jPanel.add(contextPane, BorderLayout.CENTER); |
||||
this.add(jPanel); |
||||
|
||||
usingSsh.addActionListener(new ActionListener() { |
||||
@Override |
||||
public void actionPerformed(ActionEvent e) { |
||||
changePane(); |
||||
} |
||||
}); |
||||
|
||||
type.addActionListener(new ActionListener() { |
||||
@Override |
||||
public void actionPerformed(ActionEvent e) { |
||||
changePaneForType(); |
||||
} |
||||
}); |
||||
fileChooserButton.addActionListener(new ActionListener() { |
||||
@Override |
||||
public void actionPerformed(ActionEvent e) { |
||||
FILEChooserPane fileChooser = FILEChooserPane.getInstanceWithDesignatePath(SslUtils.PREFIX, new ChooseFileFilter(true), SslUtils.CERTIFICATES); |
||||
int type = fileChooser.showOpenDialog(SshPane.this, StringUtils.EMPTY); |
||||
if (type == FILEChooserPane.OK_OPTION) { |
||||
final FILE file = fileChooser.getSelectedFILE(); |
||||
if (file == null) { |
||||
keyPath.setText(StringUtils.EMPTY); |
||||
} else { |
||||
keyPath.setText(file.getPath()); |
||||
} |
||||
} |
||||
fileChooser.removeAllFilter(); |
||||
fileChooser.removeTopPath(); |
||||
} |
||||
}); |
||||
} |
||||
|
||||
|
||||
private void changePane() { |
||||
contextPane.setVisible(usingSsh.isSelected()); |
||||
} |
||||
|
||||
private void changePaneForType() { |
||||
contextPane.removeAll(); |
||||
switch (typeMap.get(type.getSelectedItem())) { |
||||
case NORMAL: |
||||
TableLayoutHelper.addComponent2ResultPane(passwordComps, new double[]{p, p, p, p, p}, columnSize, contextPane); |
||||
break; |
||||
case KEY: |
||||
TableLayoutHelper.addComponent2ResultPane(keyComps, new double[]{p, p, p, p, p, p}, columnSize, contextPane); |
||||
break; |
||||
default: |
||||
throw new SshException("un support ssh type"); |
||||
} |
||||
jPanel.revalidate(); |
||||
jPanel.repaint(); |
||||
} |
||||
|
||||
|
||||
@Override |
||||
protected String title4PopupWindow() { |
||||
return Toolkit.i18nText("Fine-Design_Basic_Ssh_Settings"); |
||||
} |
||||
|
||||
|
||||
public void populate(JDBCDatabaseConnection jdbcDatabase) { |
||||
if (jdbcDatabase.getSsh() == null) { |
||||
jdbcDatabase.setSsh(new NormalSsh()); |
||||
} |
||||
Ssh ssh = jdbcDatabase.getSsh(); |
||||
switch (ssh.getSshType()) { |
||||
case KEY: |
||||
type.setSelectedItem(typeMap.inverse().get(ssh.getSshType())); |
||||
KeyVerifySsh keyVerifySsh = (KeyVerifySsh) ssh; |
||||
keyPath.setText(keyVerifySsh.getPrivateKeyPath()); |
||||
secret.setText(keyVerifySsh.getSecret()); |
||||
password.setText(StringUtils.EMPTY); |
||||
setCommonConfig(keyVerifySsh); |
||||
break; |
||||
case NORMAL: |
||||
type.setSelectedItem(typeMap.inverse().get(ssh.getSshType())); |
||||
NormalSsh normalSsh = (NormalSsh) ssh; |
||||
password.setText(normalSsh.getSecret()); |
||||
keyPath.setText(StringUtils.EMPTY); |
||||
secret.setText(StringUtils.EMPTY); |
||||
setCommonConfig(normalSsh); |
||||
break; |
||||
default: |
||||
throw new SshException("un support ssh type"); |
||||
} |
||||
usingSsh.setSelected(ssh.isUsingSsh()); |
||||
changePane(); |
||||
} |
||||
|
||||
private void setCommonConfig(BaseSsh baseSsh) { |
||||
ip.setText(baseSsh.getIp()); |
||||
port.setValue(baseSsh.getPort()); |
||||
user.setText(baseSsh.getUser()); |
||||
} |
||||
|
||||
public void update(JDBCDatabaseConnection jdbcDatabase) { |
||||
Ssh ssh; |
||||
switch (typeMap.get(type.getSelectedItem())) { |
||||
case NORMAL: |
||||
NormalSsh normalSsh = new NormalSsh(); |
||||
normalSsh.setSecret(new String(password.getPassword()).trim()); |
||||
getCommonConfig(normalSsh); |
||||
ssh = normalSsh; |
||||
break; |
||||
case KEY: |
||||
KeyVerifySsh keyVerifySsh = new KeyVerifySsh(); |
||||
keyVerifySsh.setPrivateKeyPath(keyPath.getText().trim()); |
||||
keyVerifySsh.setSecret(new String(secret.getPassword()).trim()); |
||||
getCommonConfig(keyVerifySsh); |
||||
ssh = keyVerifySsh; |
||||
break; |
||||
default: |
||||
throw new SshException("un support ssh type"); |
||||
} |
||||
jdbcDatabase.setSsh(ssh); |
||||
} |
||||
|
||||
private void getCommonConfig(BaseSsh baseSsh) { |
||||
baseSsh.setUsingSsh(usingSsh.isSelected()); |
||||
baseSsh.setIp(ip.getText().trim()); |
||||
baseSsh.setPort(port.getValue()); |
||||
baseSsh.setUser(user.getText().trim()); |
||||
} |
||||
|
||||
public static class KeyFileUITextField extends UITextField { |
||||
private static final Pattern ERROR_START = Pattern.compile("^([/\\\\.]+).*"); |
||||
private static final Pattern MUTI_DOT = Pattern.compile("\\.+"); |
||||
private static final String UPPER = ".."; |
||||
|
||||
public KeyFileUITextField(int columns) { |
||||
this(); |
||||
this.setColumns(columns); |
||||
} |
||||
|
||||
|
||||
public KeyFileUITextField() { |
||||
super(); |
||||
this.addKeyListener(new KeyAdapter() { |
||||
@Override |
||||
public void keyReleased(KeyEvent e) { |
||||
String text = KeyFileUITextField.this.getTextOrigin(); |
||||
if (!StringUtils.isEmpty(text)) { |
||||
if (text.contains(UPPER)) { |
||||
text = MUTI_DOT.matcher(text).replaceAll("."); |
||||
KeyFileUITextField.this.setTextOrigin(text); |
||||
} |
||||
Matcher matcher = ERROR_START.matcher(text); |
||||
if (matcher.matches()) { |
||||
text = text.substring(matcher.group(1).length()); |
||||
KeyFileUITextField.this.setTextOrigin(text); |
||||
} |
||||
} |
||||
} |
||||
}); |
||||
} |
||||
|
||||
public String getTextOrigin() { |
||||
return super.getText(); |
||||
} |
||||
|
||||
public void setTextOrigin(String text) { |
||||
super.setText(text); |
||||
} |
||||
|
||||
@Override |
||||
public String getText() { |
||||
// 获取的时候,不为空,给他加上前缀就好了,否则还是空
|
||||
if (!StringUtils.isEmpty(super.getText())) { |
||||
return SslUtils.PREFIX + super.getText(); |
||||
} |
||||
return StringUtils.EMPTY; |
||||
} |
||||
|
||||
@Override |
||||
public void setText(String text) { |
||||
// 设置的时候,不为空,说明文件指定了(文件需要是resource下),替换掉前缀
|
||||
if (!StringUtils.isEmpty(text) && text.startsWith(SslUtils.PREFIX)) { |
||||
super.setText(text.replaceFirst(SslUtils.PREFIX, "")); |
||||
} else { |
||||
super.setText(text); |
||||
} |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,165 @@
|
||||
package com.fr.design.data.datapane.connect; |
||||
|
||||
import com.fr.data.impl.JDBCDatabaseConnection; |
||||
import com.fr.data.security.ssl.Ssl; |
||||
import com.fr.data.security.ssl.SslException; |
||||
import com.fr.data.security.ssl.SslType; |
||||
import com.fr.data.security.ssl.SslUtils; |
||||
import com.fr.data.security.ssl.impl.NormalSsl; |
||||
import com.fr.design.border.UITitledBorder; |
||||
import com.fr.design.constants.UIConstants; |
||||
import com.fr.design.data.datapane.connect.SshPane.KeyFileUITextField; |
||||
import com.fr.design.dialog.BasicPane; |
||||
import com.fr.design.gui.ibutton.UIButton; |
||||
import com.fr.design.gui.icheckbox.UICheckBox; |
||||
import com.fr.design.gui.ilable.UILabel; |
||||
import com.fr.design.gui.itextfield.UITextField; |
||||
import com.fr.design.i18n.Toolkit; |
||||
import com.fr.design.layout.FRGUIPaneFactory; |
||||
import com.fr.design.layout.TableLayout; |
||||
import com.fr.design.layout.TableLayoutHelper; |
||||
import com.fr.file.FILE; |
||||
import com.fr.file.FILEChooserPane; |
||||
import com.fr.file.filter.ChooseFileFilter; |
||||
import com.fr.stable.StringUtils; |
||||
|
||||
import javax.swing.ImageIcon; |
||||
import javax.swing.JPanel; |
||||
import javax.swing.SwingConstants; |
||||
import java.awt.BorderLayout; |
||||
import java.awt.Component; |
||||
import java.awt.Dimension; |
||||
import java.awt.event.ActionEvent; |
||||
import java.awt.event.ActionListener; |
||||
|
||||
import static com.fr.design.i18n.Toolkit.i18nText; |
||||
|
||||
/** |
||||
* @author xiqiu |
||||
* @date 2022/1/4 |
||||
* @description |
||||
*/ |
||||
public class SslPane extends BasicPane { |
||||
UICheckBox usingSsl = new UICheckBox(i18nText("Fine-Design_Basic_Ssl_Using")); |
||||
private KeyFileUITextField keyPathCa = new KeyFileUITextField(18); |
||||
private UIButton fileChooserButtonCa = new UIButton(); |
||||
private KeyFileUITextField keyPathClientCert = new KeyFileUITextField(18); |
||||
private UIButton fileChooserButtonClientCert = new UIButton(); |
||||
private KeyFileUITextField keyPathClientKey = new KeyFileUITextField(18); |
||||
private UIButton fileChooserButtonClientKey = new UIButton(); |
||||
private UICheckBox verifyCa = new UICheckBox(i18nText("Fine-Design_Basic_Ssl_Verify_Ca")); |
||||
// private UITextField cipher = new UITextField(20);
|
||||
private JPanel jPanel; |
||||
private Component[][] usingComps; |
||||
private double p = TableLayout.PREFERRED; |
||||
private double f = TableLayout.FILL; |
||||
private JPanel contextPane; |
||||
private double[] columnSize = new double[]{195, p}; |
||||
|
||||
public SslPane() { |
||||
fileChooserButtonCa.setIcon(new ImageIcon(UIConstants.ACCESSIBLE_EDITOR_DOT)); |
||||
fileChooserButtonClientCert.setIcon(new ImageIcon(UIConstants.ACCESSIBLE_EDITOR_DOT)); |
||||
fileChooserButtonClientKey.setIcon(new ImageIcon(UIConstants.ACCESSIBLE_EDITOR_DOT)); |
||||
this.setBorder(UITitledBorder.createBorderWithTitle(Toolkit.i18nText("Fine-Design_Basic_Ssl_Settings"))); |
||||
this.setLayout(FRGUIPaneFactory.createLabelFlowLayout()); |
||||
jPanel = FRGUIPaneFactory.createBorderLayout_S_Pane(); |
||||
Dimension dimension = new Dimension(20, 20); |
||||
fileChooserButtonCa.setPreferredSize(dimension); |
||||
fileChooserButtonClientCert.setPreferredSize(dimension); |
||||
fileChooserButtonClientKey.setPreferredSize(dimension); |
||||
JPanel filePanelCa = TableLayoutHelper.createCommonTableLayoutPane(new Component[][]{{keyPathCa, fileChooserButtonCa}}, new double[]{p}, new double[]{f, 20}, 0); |
||||
Component[] compCa = {new UILabel(Toolkit.i18nText("Fine-Design_Basic_Ssl_Ca") + ":", SwingConstants.RIGHT), filePanelCa}; |
||||
Component[] compVerifyCa = {null, verifyCa}; |
||||
JPanel filePanelClientKey = TableLayoutHelper.createCommonTableLayoutPane(new Component[][]{{keyPathClientKey, fileChooserButtonClientKey}}, new double[]{p}, new double[]{f, 20}, 0); |
||||
Component[] compClientKey = {new UILabel(Toolkit.i18nText("Fine-Design_Basic_Ssl_Client_Key") + ":", SwingConstants.RIGHT), filePanelClientKey}; |
||||
JPanel filePanelClientCert = TableLayoutHelper.createCommonTableLayoutPane(new Component[][]{{keyPathClientCert, fileChooserButtonClientCert}}, new double[]{p}, new double[]{f, 20}, 0); |
||||
Component[] compClientCert = {new UILabel(Toolkit.i18nText("Fine-Design_Basic_Ssl_Client_Cert") + ":", SwingConstants.RIGHT), filePanelClientCert}; |
||||
// Component[] comCipher = {new UILabel(Toolkit.i18nText("Fine-Design_Basic_Ssl_Cipher") + ":", SwingConstants.RIGHT), cipher};
|
||||
usingComps = new Component[][]{ |
||||
compCa, |
||||
compVerifyCa, |
||||
compClientKey, |
||||
compClientCert |
||||
// comCipher
|
||||
}; |
||||
usingSsl.setSelected(true); |
||||
contextPane = TableLayoutHelper.createGapTableLayoutPane(usingComps, new double[]{p, p, p, p}, columnSize, 11, 11); |
||||
jPanel.add(usingSsl, BorderLayout.NORTH); |
||||
jPanel.add(contextPane, BorderLayout.CENTER); |
||||
this.add(jPanel); |
||||
usingSsl.addActionListener(new ActionListener() { |
||||
@Override |
||||
public void actionPerformed(ActionEvent e) { |
||||
changePane(); |
||||
} |
||||
}); |
||||
fileChooserButtonCa.addActionListener(new TextFieldActionListener(keyPathCa)); |
||||
fileChooserButtonClientCert.addActionListener(new TextFieldActionListener(keyPathClientCert)); |
||||
fileChooserButtonClientKey.addActionListener(new TextFieldActionListener(keyPathClientKey)); |
||||
} |
||||
|
||||
private void changePane() { |
||||
contextPane.setVisible(usingSsl.isSelected()); |
||||
} |
||||
|
||||
|
||||
@Override |
||||
protected String title4PopupWindow() { |
||||
return Toolkit.i18nText("Fine-Design_Basic_Ssl_Settings"); |
||||
} |
||||
|
||||
public void populate(JDBCDatabaseConnection jdbcDatabase) { |
||||
Ssl ssl = jdbcDatabase.getSsl(); |
||||
if (ssl == null) { |
||||
ssl = new NormalSsl(); |
||||
jdbcDatabase.setSsl(ssl); |
||||
} |
||||
if (ssl.getSslType() == SslType.NORMAL) { |
||||
NormalSsl normalSsl = (NormalSsl) ssl; |
||||
keyPathCa.setText(normalSsl.getCaCertificate()); |
||||
keyPathClientCert.setText(normalSsl.getClientCertificate()); |
||||
keyPathClientKey.setText(normalSsl.getClientPrivateKey()); |
||||
verifyCa.setSelected(normalSsl.isVerifyCa()); |
||||
// cipher.setText(normalSsl.getCipher());
|
||||
} else { |
||||
throw new SslException("un support ssl type"); |
||||
} |
||||
usingSsl.setSelected(ssl.isUsingSsl()); |
||||
changePane(); |
||||
} |
||||
|
||||
public void update(JDBCDatabaseConnection jdbcDatabase) { |
||||
NormalSsl normalSsl = new NormalSsl(); |
||||
// normalSsl.setCipher(cipher.getText().trim());
|
||||
normalSsl.setVerifyCa(verifyCa.isSelected()); |
||||
normalSsl.setCaCertificate(keyPathCa.getText().trim()); |
||||
normalSsl.setClientCertificate(keyPathClientCert.getText().trim()); |
||||
normalSsl.setClientPrivateKey(keyPathClientKey.getText().trim()); |
||||
normalSsl.setUsingSsl(usingSsl.isSelected()); |
||||
jdbcDatabase.setSsl(normalSsl); |
||||
} |
||||
|
||||
private class TextFieldActionListener implements ActionListener { |
||||
private UITextField textField; |
||||
|
||||
public TextFieldActionListener(UITextField textField) { |
||||
this.textField = textField; |
||||
} |
||||
|
||||
@Override |
||||
public void actionPerformed(ActionEvent e) { |
||||
FILEChooserPane fileChooser = FILEChooserPane.getInstanceWithDesignatePath(SslUtils.PREFIX, new ChooseFileFilter(true), SslUtils.CERTIFICATES); |
||||
int type = fileChooser.showOpenDialog(SslPane.this, StringUtils.EMPTY); |
||||
if (type == FILEChooserPane.OK_OPTION) { |
||||
final FILE file = fileChooser.getSelectedFILE(); |
||||
if (file == null) { |
||||
textField.setText(StringUtils.EMPTY); |
||||
} else { |
||||
textField.setText(file.getPath()); |
||||
} |
||||
} |
||||
fileChooser.removeAllFilter(); |
||||
fileChooser.removeTopPath(); |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,74 @@
|
||||
package com.fr.design.data.datapane.management.clip; |
||||
|
||||
import com.fr.base.TableData; |
||||
import com.fr.design.data.tabledata.paste.TableDataFollowingPasteUtils; |
||||
import com.fr.design.data.tabledata.wrapper.AbstractTableDataWrapper; |
||||
import com.fr.design.data.tabledata.wrapper.TemplateTableDataWrapper; |
||||
import com.fr.general.NameObject; |
||||
|
||||
import java.util.HashMap; |
||||
import java.util.Map; |
||||
|
||||
/** |
||||
* 用于数据集的复制粘贴 |
||||
* |
||||
* @author Yvan |
||||
*/ |
||||
public class TableDataTreeClipboard { |
||||
|
||||
/** |
||||
* 数据集名称 - 数据集Wrapper |
||||
*/ |
||||
private Map<String, AbstractTableDataWrapper> clip = new HashMap<>(); |
||||
|
||||
private static class Holder { |
||||
private static final TableDataTreeClipboard INSTANCE = new TableDataTreeClipboard(); |
||||
} |
||||
|
||||
private TableDataTreeClipboard() { |
||||
} |
||||
|
||||
public static TableDataTreeClipboard getInstance() { |
||||
return Holder.INSTANCE; |
||||
} |
||||
|
||||
/** |
||||
* 添加选中的数据集数据到剪切板,覆盖原本剪切板内数据 |
||||
* |
||||
* @param copyMap |
||||
* @return |
||||
*/ |
||||
public void addToClip(Map<String, AbstractTableDataWrapper> copyMap) { |
||||
this.clip = copyMap; |
||||
} |
||||
|
||||
public Map<String, AbstractTableDataWrapper> transferNameObjectArray2Map(NameObject[] selectedNameObjects) { |
||||
Map<String, AbstractTableDataWrapper> resultMap = new HashMap<>(); |
||||
if (selectedNameObjects == null) { |
||||
return resultMap; |
||||
} |
||||
for (NameObject selectedNameObject : selectedNameObjects) { |
||||
TableData cloned = TableDataFollowingPasteUtils.cloneTableData(((AbstractTableDataWrapper) selectedNameObject.getObject()).getTableData()); |
||||
if (cloned != null) { |
||||
resultMap.put(selectedNameObject.getName(), new TemplateTableDataWrapper(cloned)); |
||||
} |
||||
} |
||||
return resultMap; |
||||
} |
||||
|
||||
/** |
||||
* 取出剪切板内的所有数据集数据,剪切板不清空 |
||||
* |
||||
* @return |
||||
*/ |
||||
public Map<String, AbstractTableDataWrapper> takeFromClip() { |
||||
return clip; |
||||
} |
||||
|
||||
/** |
||||
* 清空剪切板 |
||||
*/ |
||||
public void reset() { |
||||
clip.clear(); |
||||
} |
||||
} |
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue