Harrison
3 years ago
495 changed files with 18868 additions and 18384 deletions
@ -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,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,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,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,5 @@ |
|||||||
|
package com.fr.design.constants; |
||||||
|
|
||||||
|
public class TableDataConstants { |
||||||
|
public static final String SEPARATOR = "_"; |
||||||
|
} |
@ -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.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.stable.project.ProjectConstants; |
||||||
|
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(ProjectConstants.RESOURCES_NAME, new ChooseFileFilter(true)); |
||||||
|
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(); |
||||||
|
} |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
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 PREFIX = ProjectConstants.RESOURCES_NAME + "/"; |
||||||
|
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 PREFIX + super.getText(); |
||||||
|
} |
||||||
|
return StringUtils.EMPTY; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void setText(String text) { |
||||||
|
// 设置的时候,不为空,说明文件指定了(文件需要是resource下),替换掉前缀
|
||||||
|
if (!StringUtils.isEmpty(text) && text.startsWith(PREFIX)) { |
||||||
|
super.setText(text.replaceFirst(PREFIX, "")); |
||||||
|
} else { |
||||||
|
super.setText(text); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,164 @@ |
|||||||
|
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.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 com.fr.stable.project.ProjectConstants; |
||||||
|
|
||||||
|
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 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}; |
||||||
|
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}; |
||||||
|
// Component[] comCipher = {new UILabel(Toolkit.i18nText("Fine-Design_Basic_Ssl_Cipher") + ":", SwingConstants.RIGHT), cipher};
|
||||||
|
usingComps = new Component[][]{ |
||||||
|
compCa, |
||||||
|
compVerifyCa, |
||||||
|
compClientCert, |
||||||
|
compClientKey, |
||||||
|
// 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(ProjectConstants.RESOURCES_NAME, new ChooseFileFilter(true)); |
||||||
|
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(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,15 @@ |
|||||||
|
package com.fr.design.deeplink; |
||||||
|
|
||||||
|
import java.util.Map; |
||||||
|
|
||||||
|
/** |
||||||
|
* @author Starryi |
||||||
|
* @version 1.0 |
||||||
|
* Created by Starryi on 2022/1/6 |
||||||
|
*/ |
||||||
|
public abstract class DeepLink { |
||||||
|
|
||||||
|
public abstract boolean accept(String url, String host, String path, Map<String, Object> params); |
||||||
|
|
||||||
|
public abstract void run(String url, String host, String path, Map<String, Object> params); |
||||||
|
} |
@ -0,0 +1,184 @@ |
|||||||
|
package com.fr.design.deeplink; |
||||||
|
|
||||||
|
import com.fr.design.constants.DesignerLaunchStatus; |
||||||
|
import com.fr.design.startup.FineStartupNotificationFactory; |
||||||
|
import com.fr.design.startup.FineStartupNotificationProvider; |
||||||
|
import com.fr.event.Event; |
||||||
|
import com.fr.event.EventDispatcher; |
||||||
|
import com.fr.event.Listener; |
||||||
|
import com.fr.event.Null; |
||||||
|
import com.fr.log.FineLoggerFactory; |
||||||
|
import com.fr.stable.StringUtils; |
||||||
|
import com.fr.stable.os.OperatingSystem; |
||||||
|
import com.fr.third.org.apache.http.NameValuePair; |
||||||
|
import com.fr.web.URLUtils; |
||||||
|
|
||||||
|
import javax.swing.SwingUtilities; |
||||||
|
import java.awt.Color; |
||||||
|
import java.awt.Frame; |
||||||
|
import java.awt.event.WindowAdapter; |
||||||
|
import java.awt.event.WindowEvent; |
||||||
|
import java.io.IOException; |
||||||
|
import java.net.MalformedURLException; |
||||||
|
import java.net.URL; |
||||||
|
import java.net.URLConnection; |
||||||
|
import java.net.URLStreamHandler; |
||||||
|
import java.util.ArrayList; |
||||||
|
import java.util.HashMap; |
||||||
|
import java.util.List; |
||||||
|
import java.util.Map; |
||||||
|
|
||||||
|
/** |
||||||
|
* @author Starryi |
||||||
|
* @version 1.0 |
||||||
|
* Created by Starryi on 2022/1/6 |
||||||
|
*/ |
||||||
|
public class DeepLinkCore { |
||||||
|
|
||||||
|
protected DeepLinkCore(){} |
||||||
|
private static final DeepLinkCore instance = new DeepLinkCore(); |
||||||
|
public static DeepLinkCore getInstance(){ |
||||||
|
return instance; |
||||||
|
} |
||||||
|
|
||||||
|
private String pendingURL; |
||||||
|
|
||||||
|
private final List<DeepLink> deepLinkList = new ArrayList<>(); |
||||||
|
|
||||||
|
private boolean isDesignerStartupCompleted = false; |
||||||
|
|
||||||
|
public void register(DeepLink deepLink) { |
||||||
|
if (deepLink != null) { |
||||||
|
deepLinkList.add(deepLink); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public void start(String[] args) { |
||||||
|
if (OperatingSystem.isWindows()) { |
||||||
|
if (args.length > 0) { |
||||||
|
receiveDeeplink(args[0]); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if (OperatingSystem.isWindows() && args.length > 0) { |
||||||
|
receiveDeeplink(args[0]); |
||||||
|
} |
||||||
|
|
||||||
|
FineStartupNotificationFactory.getNotification() |
||||||
|
.registerStartupListener(new FineStartupNotificationProvider.Listener() { |
||||||
|
@Override |
||||||
|
public void startupPerformed(String parameters) { |
||||||
|
receiveDeeplink(parameters); |
||||||
|
} |
||||||
|
}); |
||||||
|
|
||||||
|
EventDispatcher.listen(DesignerLaunchStatus.STARTUP_COMPLETE, new Listener<Null>() { |
||||||
|
@Override |
||||||
|
public void on(Event event, Null param) { |
||||||
|
EventDispatcher.stopListen(this); |
||||||
|
isDesignerStartupCompleted = true; |
||||||
|
if (canConsumePendingURL()) { |
||||||
|
consumePendingURL(); |
||||||
|
} |
||||||
|
} |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
public void receiveDeeplink(String url) { |
||||||
|
if (canAcceptNewURL()) { |
||||||
|
acceptNewURL(url); |
||||||
|
if (canConsumePendingURL()) { |
||||||
|
consumePendingURL(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public void receiveDeeplink2(String url) { |
||||||
|
if (canAcceptNewURL()) { |
||||||
|
acceptNewURL(url); |
||||||
|
if (canConsumePendingURL()) { |
||||||
|
consumePendingURL(); |
||||||
|
} else { |
||||||
|
Frame frame = new Frame("can not ConsumePendingURL"); |
||||||
|
frame.setSize(400, 400); |
||||||
|
frame.setBackground(Color.BLACK); |
||||||
|
frame.addWindowListener(new WindowAdapter() { |
||||||
|
public void windowClosing(WindowEvent windowEvent){ |
||||||
|
frame.dispose(); |
||||||
|
} |
||||||
|
}); |
||||||
|
frame.setVisible(true); |
||||||
|
} |
||||||
|
} else { |
||||||
|
Frame frame = new Frame("can not AcceptNewURL"); |
||||||
|
frame.setSize(400, 400); |
||||||
|
frame.setBackground(Color.BLACK); |
||||||
|
frame.addWindowListener(new WindowAdapter() { |
||||||
|
public void windowClosing(WindowEvent windowEvent){ |
||||||
|
frame.dispose(); |
||||||
|
} |
||||||
|
}); |
||||||
|
frame.setVisible(true); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private boolean canAcceptNewURL() { |
||||||
|
return StringUtils.isEmpty(this.pendingURL); |
||||||
|
} |
||||||
|
|
||||||
|
private void acceptNewURL(String url) { |
||||||
|
this.pendingURL = url; |
||||||
|
} |
||||||
|
|
||||||
|
private boolean canConsumePendingURL() { |
||||||
|
return StringUtils.isNotEmpty(this.pendingURL) && isDesignerStartupCompleted; |
||||||
|
} |
||||||
|
|
||||||
|
private void consumePendingURL() { |
||||||
|
String host = null; |
||||||
|
String path = null; |
||||||
|
Map<String, Object> params = new HashMap<>(); |
||||||
|
|
||||||
|
URL url = null; |
||||||
|
try { |
||||||
|
url = new URL(null, this.pendingURL, new URLStreamHandler() { |
||||||
|
@Override |
||||||
|
protected URLConnection openConnection(URL u) throws IOException { |
||||||
|
return null; |
||||||
|
} |
||||||
|
}); |
||||||
|
} catch (MalformedURLException ignored) {} |
||||||
|
|
||||||
|
if (url != null) { |
||||||
|
host = url.getHost(); |
||||||
|
path = url.getPath(); |
||||||
|
|
||||||
|
List<NameValuePair> pairs = URLUtils.parse(url.getQuery()); |
||||||
|
for (NameValuePair pair: pairs) { |
||||||
|
params.put(pair.getName(), pair.getValue()); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
FineLoggerFactory.getLogger().info("consume deep link: " + this.pendingURL); |
||||||
|
performDeepLinks(this.pendingURL, host, path, params); |
||||||
|
|
||||||
|
markPendingURLConsumed(); |
||||||
|
} |
||||||
|
|
||||||
|
private void performDeepLinks(String url, String host, String path, Map<String, Object> params) { |
||||||
|
SwingUtilities.invokeLater(new Runnable() { |
||||||
|
@Override |
||||||
|
public void run() { |
||||||
|
for (DeepLink deepLink: deepLinkList) { |
||||||
|
if (deepLink.accept(url, host, path, params)) { |
||||||
|
deepLink.run(url, host, path, params); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
private void markPendingURLConsumed() { |
||||||
|
this.pendingURL = null; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,33 @@ |
|||||||
|
package com.fr.design.editor.editor; |
||||||
|
|
||||||
|
import java.awt.event.KeyAdapter; |
||||||
|
import java.awt.event.KeyEvent; |
||||||
|
|
||||||
|
/** |
||||||
|
* @author xiqiu |
||||||
|
* @date 2022/1/10 |
||||||
|
* @description 一个简单的非负整数框 |
||||||
|
*/ |
||||||
|
public class NotNegativeIntegerEditor extends IntegerEditor { |
||||||
|
private static final String NEG = "-"; |
||||||
|
|
||||||
|
public NotNegativeIntegerEditor(int columns) { |
||||||
|
this(); |
||||||
|
this.setColumns(columns); |
||||||
|
} |
||||||
|
|
||||||
|
public NotNegativeIntegerEditor() { |
||||||
|
super(); |
||||||
|
this.numberField.addKeyListener(new KeyAdapter() { |
||||||
|
|
||||||
|
@Override |
||||||
|
public void keyReleased(KeyEvent evt) { |
||||||
|
char keyChar = evt.getKeyChar(); |
||||||
|
if (NEG.equals(keyChar + "")) { |
||||||
|
numberField.setText(numberField.getTextValue().replace(NEG, "")); |
||||||
|
} |
||||||
|
} |
||||||
|
}); |
||||||
|
} |
||||||
|
} |
||||||
|
|
@ -0,0 +1,7 @@ |
|||||||
|
package com.fr.design.event; |
||||||
|
|
||||||
|
import java.awt.*; |
||||||
|
|
||||||
|
public interface ComponentChangeListener { |
||||||
|
void initListener(Container changedComponent); |
||||||
|
} |
@ -0,0 +1,6 @@ |
|||||||
|
package com.fr.design.event; |
||||||
|
|
||||||
|
|
||||||
|
public interface ComponentChangeObserver { |
||||||
|
void registerChangeListener(ComponentChangeListener uiChangeableListener); |
||||||
|
} |
@ -0,0 +1,96 @@ |
|||||||
|
package com.fr.design.formula.exception.function; |
||||||
|
|
||||||
|
import com.fr.design.i18n.Toolkit; |
||||||
|
|
||||||
|
/** |
||||||
|
* @author Hoky |
||||||
|
* @date 2021/11/30 |
||||||
|
*/ |
||||||
|
public class TranslateTokenUtils { |
||||||
|
public static String translateToken(String token) { |
||||||
|
switch (token) { |
||||||
|
case ("RPAREN"): |
||||||
|
return ")"; |
||||||
|
case ("LPAREN"): |
||||||
|
return "("; |
||||||
|
case ("COMMA"): |
||||||
|
return ","; |
||||||
|
case ("COLON"): |
||||||
|
return ":"; |
||||||
|
case ("EOF"): |
||||||
|
return Toolkit.i18nText("Fine-Design_Basic_Formula_Check_Mismatched_EOF"); |
||||||
|
case ("DOT"): |
||||||
|
return "."; |
||||||
|
case ("FLOT_NUM"): |
||||||
|
return Toolkit.i18nText("Fine-Design_Basic_Formula_Float_Number"); |
||||||
|
case ("LOR"): |
||||||
|
return "||"; |
||||||
|
case ("LAND"): |
||||||
|
return "&&"; |
||||||
|
case ("EQUAL"): |
||||||
|
return "="; |
||||||
|
case ("EQUAL2"): |
||||||
|
return "="; |
||||||
|
case ("NOT_EQUAL"): |
||||||
|
return "!="; |
||||||
|
case ("NOT_EQUAL2"): |
||||||
|
return "!="; |
||||||
|
case ("GE"): |
||||||
|
return ">="; |
||||||
|
case ("LE"): |
||||||
|
return "<="; |
||||||
|
case ("LT"): |
||||||
|
return "<"; |
||||||
|
case ("PLUS"): |
||||||
|
return "+"; |
||||||
|
case ("MINUS"): |
||||||
|
return "-"; |
||||||
|
case ("STAR"): |
||||||
|
return "*"; |
||||||
|
case ("DIV"): |
||||||
|
return "/"; |
||||||
|
case ("MOD"): |
||||||
|
return "%"; |
||||||
|
case ("POWER"): |
||||||
|
return "^"; |
||||||
|
case ("LNOT"): |
||||||
|
return "!"; |
||||||
|
case ("WAVE"): |
||||||
|
return "~"; |
||||||
|
case ("LBRACK"): |
||||||
|
return "["; |
||||||
|
case ("SEMI"): |
||||||
|
return ";"; |
||||||
|
case ("RBRACK"): |
||||||
|
return "]"; |
||||||
|
case ("LCURLY"): |
||||||
|
return "{"; |
||||||
|
case ("RCURLY"): |
||||||
|
return "}"; |
||||||
|
case ("DCOLON"): |
||||||
|
return ";"; |
||||||
|
case ("INT_NUM"): |
||||||
|
return Toolkit.i18nText("Fine-Design_Basic_Formula_Integer"); |
||||||
|
case ("CR_ADRESS"): |
||||||
|
return "\n"; |
||||||
|
case ("SHARP"): |
||||||
|
return "#"; |
||||||
|
case ("AT"): |
||||||
|
return "@"; |
||||||
|
case ("QUESTION"): |
||||||
|
return "?"; |
||||||
|
case ("BOR"): |
||||||
|
return "||"; |
||||||
|
case ("BAND"): |
||||||
|
return "&&"; |
||||||
|
case ("Char"): |
||||||
|
return Toolkit.i18nText("Fine-Design_Basic_Formula_Character"); |
||||||
|
case ("DIGIT"): |
||||||
|
return Toolkit.i18nText("Fine-Design_Basic_Formula_Digital"); |
||||||
|
case ("XDIGIT"): |
||||||
|
return Toolkit.i18nText("Fine-Design_Basic_Formula_Hexadecimal_Digital"); |
||||||
|
default: |
||||||
|
return token; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,5 @@ |
|||||||
|
package com.fr.design.gui.autocomplete; |
||||||
|
|
||||||
|
public interface AutoCompleteExtraRefreshComponent { |
||||||
|
void refresh(String replacementText); |
||||||
|
} |
@ -0,0 +1,982 @@ |
|||||||
|
package com.fr.design.gui.autocomplete; |
||||||
|
|
||||||
|
import com.fr.design.gui.syntax.ui.rsyntaxtextarea.PopupWindowDecorator; |
||||||
|
import com.fr.log.FineLoggerFactory; |
||||||
|
import java.awt.BorderLayout; |
||||||
|
import java.awt.ComponentOrientation; |
||||||
|
import java.awt.Dimension; |
||||||
|
import java.awt.Point; |
||||||
|
import java.awt.Rectangle; |
||||||
|
import java.awt.Toolkit; |
||||||
|
import java.awt.Window; |
||||||
|
import java.awt.event.ActionEvent; |
||||||
|
import java.awt.event.KeyEvent; |
||||||
|
import java.awt.event.MouseEvent; |
||||||
|
import java.awt.event.MouseListener; |
||||||
|
import java.util.List; |
||||||
|
import javax.swing.AbstractAction; |
||||||
|
import javax.swing.Action; |
||||||
|
import javax.swing.ActionMap; |
||||||
|
import javax.swing.InputMap; |
||||||
|
import javax.swing.JComponent; |
||||||
|
import javax.swing.JList; |
||||||
|
import javax.swing.JPanel; |
||||||
|
import javax.swing.JScrollPane; |
||||||
|
import javax.swing.JWindow; |
||||||
|
import javax.swing.KeyStroke; |
||||||
|
import javax.swing.ListCellRenderer; |
||||||
|
import javax.swing.SwingUtilities; |
||||||
|
import javax.swing.event.CaretEvent; |
||||||
|
import javax.swing.event.CaretListener; |
||||||
|
import javax.swing.event.ListSelectionEvent; |
||||||
|
import javax.swing.event.ListSelectionListener; |
||||||
|
import javax.swing.plaf.ListUI; |
||||||
|
import javax.swing.text.Caret; |
||||||
|
import javax.swing.text.JTextComponent; |
||||||
|
|
||||||
|
public abstract class AutoCompleteWithExtraRefreshPopupWindow extends JWindow implements CaretListener, |
||||||
|
ListSelectionListener, MouseListener { |
||||||
|
private final static int DIS = 5; |
||||||
|
/** |
||||||
|
* The parent AutoCompletion instance. |
||||||
|
*/ |
||||||
|
private AutoCompletion ac; |
||||||
|
|
||||||
|
/** |
||||||
|
* The list of completion choices. |
||||||
|
*/ |
||||||
|
private JList list; |
||||||
|
|
||||||
|
/** |
||||||
|
* The contents of {@link #list()}. |
||||||
|
*/ |
||||||
|
private CompletionListModel model; |
||||||
|
|
||||||
|
/** |
||||||
|
* A hack to work around the fact that we clear our completion model (and |
||||||
|
* our selection) when hiding the completion window. This allows us to |
||||||
|
* still know what the user selected after the popup is hidden. |
||||||
|
*/ |
||||||
|
private Completion lastSelection; |
||||||
|
|
||||||
|
/** |
||||||
|
* Optional popup window containing a description of the currently |
||||||
|
* selected completion. |
||||||
|
*/ |
||||||
|
private AutoCompleteDescWindow descWindow; |
||||||
|
|
||||||
|
/** |
||||||
|
* The preferred size of the optional description window. This field |
||||||
|
* only exists because the user may (and usually will) set the size of |
||||||
|
* the description window before it exists (it must be parented to a |
||||||
|
* Window). |
||||||
|
*/ |
||||||
|
private Dimension preferredDescWindowSize; |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* Whether the completion window and the optional description window |
||||||
|
* should be displayed above the current caret position (as opposed to |
||||||
|
* underneath it, which is preferred unless there is not enough space). |
||||||
|
*/ |
||||||
|
private boolean aboveCaret; |
||||||
|
|
||||||
|
private int lastLine; |
||||||
|
|
||||||
|
private KeyActionPair escapeKap; |
||||||
|
private KeyActionPair upKap; |
||||||
|
private KeyActionPair downKap; |
||||||
|
private KeyActionPair leftKap; |
||||||
|
private KeyActionPair rightKap; |
||||||
|
private KeyActionPair enterKap; |
||||||
|
private KeyActionPair tabKap; |
||||||
|
private KeyActionPair homeKap; |
||||||
|
private KeyActionPair endKap; |
||||||
|
private KeyActionPair pageUpKap; |
||||||
|
private KeyActionPair pageDownKap; |
||||||
|
private KeyActionPair ctrlCKap; |
||||||
|
|
||||||
|
private KeyActionPair oldEscape, oldUp, oldDown, oldLeft, oldRight, |
||||||
|
oldEnter, oldTab, oldHome, oldEnd, oldPageUp, oldPageDown, |
||||||
|
oldCtrlC; |
||||||
|
|
||||||
|
/** |
||||||
|
* The space between the caret and the completion popup. |
||||||
|
*/ |
||||||
|
private static final int VERTICAL_SPACE = 1; |
||||||
|
|
||||||
|
/** |
||||||
|
* The class name of the Substance List UI. |
||||||
|
*/ |
||||||
|
private static final String SUBSTANCE_LIST_UI = |
||||||
|
"org.pushingpixels.substance.internal.ui.SubstanceListUI"; |
||||||
|
|
||||||
|
|
||||||
|
protected AutoCompleteExtraRefreshComponent component; |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* Constructor. |
||||||
|
* |
||||||
|
* @param parent The parent window (hosting the text component). |
||||||
|
* @param ac The auto-completion instance. |
||||||
|
*/ |
||||||
|
public AutoCompleteWithExtraRefreshPopupWindow(Window parent, final AutoCompletion ac) { |
||||||
|
|
||||||
|
super(parent); |
||||||
|
ComponentOrientation o = ac.getTextComponentOrientation(); |
||||||
|
|
||||||
|
this.ac = ac; |
||||||
|
model = new CompletionListModel(); |
||||||
|
list = new PopupList(model); |
||||||
|
|
||||||
|
list.setCellRenderer(new DelegatingCellRenderer()); |
||||||
|
list.addListSelectionListener(this); |
||||||
|
list.addMouseListener(this); |
||||||
|
|
||||||
|
JPanel contentPane = new JPanel(new BorderLayout()); |
||||||
|
JScrollPane sp = new JScrollPane(list, |
||||||
|
JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, |
||||||
|
JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS); |
||||||
|
|
||||||
|
// In 1.4, JScrollPane.setCorner() has a bug where it won't accept
|
||||||
|
// JScrollPane.LOWER_TRAILING_CORNER, even though that constant is
|
||||||
|
// defined. So we have to put the logic added in 1.5 to handle it
|
||||||
|
// here.
|
||||||
|
JPanel corner = new SizeGrip(); |
||||||
|
//sp.setCorner(JScrollPane.LOWER_TRAILING_CORNER, corner);
|
||||||
|
boolean isLeftToRight = o.isLeftToRight(); |
||||||
|
String str = isLeftToRight ? JScrollPane.LOWER_RIGHT_CORNER : |
||||||
|
JScrollPane.LOWER_LEFT_CORNER; |
||||||
|
sp.setCorner(str, corner); |
||||||
|
|
||||||
|
contentPane.add(sp); |
||||||
|
setContentPane(contentPane); |
||||||
|
applyComponentOrientation(o); |
||||||
|
|
||||||
|
// Give apps a chance to decorate us with drop shadows, etc.
|
||||||
|
if (Util.getShouldAllowDecoratingMainAutoCompleteWindows()) { |
||||||
|
PopupWindowDecorator decorator = PopupWindowDecorator.get(); |
||||||
|
if (decorator != null) { |
||||||
|
decorator.decorate(this); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
pack(); |
||||||
|
|
||||||
|
setFocusableWindowState(false); |
||||||
|
|
||||||
|
lastLine = -1; |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
public void caretUpdate(CaretEvent e) { |
||||||
|
if (isVisible()) { // Should always be true
|
||||||
|
int line = ac.getLineOfCaret(); |
||||||
|
if (line != lastLine) { |
||||||
|
lastLine = -1; |
||||||
|
setVisible(false); |
||||||
|
} else { |
||||||
|
doAutocomplete(); |
||||||
|
} |
||||||
|
} else if (AutoCompletion.isDebug()) { |
||||||
|
Thread.dumpStack(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* Creates the description window. |
||||||
|
* |
||||||
|
* @return The description window. |
||||||
|
*/ |
||||||
|
private AutoCompleteDescWindow createDescriptionWindow() { |
||||||
|
AutoCompleteDescWindow dw = new AutoCompleteDescWindow(this, ac); |
||||||
|
dw.applyComponentOrientation(ac.getTextComponentOrientation()); |
||||||
|
Dimension size = preferredDescWindowSize; |
||||||
|
if (size == null) { |
||||||
|
size = getSize(); |
||||||
|
} |
||||||
|
dw.setSize(size); |
||||||
|
return dw; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* Creates the mappings from keys to Actions we'll be putting into the |
||||||
|
* text component's ActionMap and InputMap. |
||||||
|
*/ |
||||||
|
private void createKeyActionPairs() { |
||||||
|
|
||||||
|
// Actions we'll install.
|
||||||
|
EnterAction enterAction = new EnterAction(); |
||||||
|
escapeKap = new KeyActionPair("Escape", new EscapeAction()); |
||||||
|
upKap = new KeyActionPair("Up", new UpAction()); |
||||||
|
downKap = new KeyActionPair("Down", new DownAction()); |
||||||
|
leftKap = new KeyActionPair("Left", new LeftAction()); |
||||||
|
rightKap = new KeyActionPair("Right", new RightAction()); |
||||||
|
enterKap = new KeyActionPair("Enter", enterAction); |
||||||
|
tabKap = new KeyActionPair("Tab", enterAction); |
||||||
|
homeKap = new KeyActionPair("Home", new HomeAction()); |
||||||
|
endKap = new KeyActionPair("End", new EndAction()); |
||||||
|
pageUpKap = new KeyActionPair("PageUp", new PageUpAction()); |
||||||
|
pageDownKap = new KeyActionPair("PageDown", new PageDownAction()); |
||||||
|
ctrlCKap = new KeyActionPair("CtrlC", new CopyAction()); |
||||||
|
|
||||||
|
// Buffers for the actions we replace.
|
||||||
|
oldEscape = new KeyActionPair(); |
||||||
|
oldUp = new KeyActionPair(); |
||||||
|
oldDown = new KeyActionPair(); |
||||||
|
oldLeft = new KeyActionPair(); |
||||||
|
oldRight = new KeyActionPair(); |
||||||
|
oldEnter = new KeyActionPair(); |
||||||
|
oldTab = new KeyActionPair(); |
||||||
|
oldHome = new KeyActionPair(); |
||||||
|
oldEnd = new KeyActionPair(); |
||||||
|
oldPageUp = new KeyActionPair(); |
||||||
|
oldPageDown = new KeyActionPair(); |
||||||
|
oldCtrlC = new KeyActionPair(); |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
protected void doAutocomplete() { |
||||||
|
lastLine = ac.refreshPopupWindow(); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* Returns the copy keystroke to use for this platform. |
||||||
|
* |
||||||
|
* @return The copy keystroke. |
||||||
|
*/ |
||||||
|
private static final KeyStroke getCopyKeyStroke() { |
||||||
|
int key = KeyEvent.VK_C; |
||||||
|
int mask = Toolkit.getDefaultToolkit().getMenuShortcutKeyMask(); |
||||||
|
return KeyStroke.getKeyStroke(key, mask); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* Returns the default list cell renderer used when a completion provider |
||||||
|
* does not supply its own. |
||||||
|
* |
||||||
|
* @return The default list cell renderer. |
||||||
|
* @see #setListCellRenderer(ListCellRenderer) |
||||||
|
*/ |
||||||
|
public ListCellRenderer getListCellRenderer() { |
||||||
|
DelegatingCellRenderer dcr = (DelegatingCellRenderer) list. |
||||||
|
getCellRenderer(); |
||||||
|
return dcr.getFallbackCellRenderer(); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* Returns the selected value, or <code>null</code> if nothing is selected. |
||||||
|
* |
||||||
|
* @return The selected value. |
||||||
|
*/ |
||||||
|
public Completion getSelection() { |
||||||
|
return isShowing() ? (Completion) list.getSelectedValue() : lastSelection; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* Inserts the currently selected completion. |
||||||
|
* |
||||||
|
* @see #getSelection() |
||||||
|
*/ |
||||||
|
private void insertSelectedCompletion() { |
||||||
|
Completion comp = getSelection(); |
||||||
|
ac.insertCompletion(comp); |
||||||
|
} |
||||||
|
|
||||||
|
public void installComp(AutoCompleteExtraRefreshComponent component){ |
||||||
|
this.component = component; |
||||||
|
} |
||||||
|
|
||||||
|
public void refreshInstallComp() { |
||||||
|
component.refresh(getSelection().getReplacementText()); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Registers keyboard actions to listen for in the text component and |
||||||
|
* intercept. |
||||||
|
* |
||||||
|
* @see #uninstallKeyBindings() |
||||||
|
*/ |
||||||
|
private void installKeyBindings() { |
||||||
|
|
||||||
|
if (AutoCompletion.isDebug()) { |
||||||
|
FineLoggerFactory.getLogger().debug("PopupWindow: Installing keybindings"); |
||||||
|
} |
||||||
|
|
||||||
|
if (escapeKap == null) { // Lazily create actions.
|
||||||
|
createKeyActionPairs(); |
||||||
|
} |
||||||
|
|
||||||
|
JTextComponent comp = ac.getTextComponent(); |
||||||
|
InputMap im = comp.getInputMap(); |
||||||
|
ActionMap am = comp.getActionMap(); |
||||||
|
|
||||||
|
replaceAction(im, am, KeyEvent.VK_ESCAPE, escapeKap, oldEscape); |
||||||
|
if (AutoCompletion.isDebug() && oldEscape.action == escapeKap.action) { |
||||||
|
Thread.dumpStack(); |
||||||
|
} |
||||||
|
replaceAction(im, am, KeyEvent.VK_UP, upKap, oldUp); |
||||||
|
replaceAction(im, am, KeyEvent.VK_LEFT, leftKap, oldLeft); |
||||||
|
replaceAction(im, am, KeyEvent.VK_DOWN, downKap, oldDown); |
||||||
|
replaceAction(im, am, KeyEvent.VK_RIGHT, rightKap, oldRight); |
||||||
|
replaceAction(im, am, KeyEvent.VK_ENTER, enterKap, oldEnter); |
||||||
|
replaceAction(im, am, KeyEvent.VK_TAB, tabKap, oldTab); |
||||||
|
replaceAction(im, am, KeyEvent.VK_HOME, homeKap, oldHome); |
||||||
|
replaceAction(im, am, KeyEvent.VK_END, endKap, oldEnd); |
||||||
|
replaceAction(im, am, KeyEvent.VK_PAGE_UP, pageUpKap, oldPageUp); |
||||||
|
replaceAction(im, am, KeyEvent.VK_PAGE_DOWN, pageDownKap, oldPageDown); |
||||||
|
|
||||||
|
// Make Ctrl+C copy from description window. This isn't done
|
||||||
|
// automagically because the desc. window is not focusable, and copying
|
||||||
|
// from text components can only be done from focused components.
|
||||||
|
KeyStroke ks = getCopyKeyStroke(); |
||||||
|
oldCtrlC.key = im.get(ks); |
||||||
|
im.put(ks, ctrlCKap.key); |
||||||
|
oldCtrlC.action = am.get(ctrlCKap.key); |
||||||
|
am.put(ctrlCKap.key, ctrlCKap.action); |
||||||
|
|
||||||
|
comp.addCaretListener(this); |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
public void mouseClicked(MouseEvent e) { |
||||||
|
if (e.getClickCount() == 2 && e.getButton() == 1) { |
||||||
|
insertSelectedCompletion(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
public void mouseEntered(MouseEvent e) { |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
public void mouseExited(MouseEvent e) { |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
public void mousePressed(MouseEvent e) { |
||||||
|
refreshInstallComp(); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
public void mouseReleased(MouseEvent e) { |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* Positions the description window relative to the completion choices |
||||||
|
* window. We assume there is room on one side of the other for this |
||||||
|
* entire window to fit. |
||||||
|
*/ |
||||||
|
private void positionDescWindow() { |
||||||
|
|
||||||
|
boolean showDescWindow = descWindow != null && ac.isShowDescWindow(); |
||||||
|
if (!showDescWindow) { |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
// Don't use getLocationOnScreen() as this throws an exception if
|
||||||
|
// window isn't visible yet, but getLocation() doesn't, and is in
|
||||||
|
// screen coordinates!
|
||||||
|
Point p = getLocation(); |
||||||
|
Rectangle screenBounds = Util.getScreenBoundsForPoint(p.x, p.y); |
||||||
|
//Dimension screenSize = getToolkit().getScreenSize();
|
||||||
|
//int totalH = Math.max(getHeight(), descWindow.getHeight());
|
||||||
|
|
||||||
|
// Try to position to the right first (LTR)
|
||||||
|
int x; |
||||||
|
if (ac.getTextComponentOrientation().isLeftToRight()) { |
||||||
|
x = getX() + getWidth() + DIS; |
||||||
|
if (x + descWindow.getWidth() > screenBounds.x + screenBounds.width) { // doesn't fit
|
||||||
|
x = getX() - DIS - descWindow.getWidth(); |
||||||
|
} |
||||||
|
} else { // RTL
|
||||||
|
x = getX() - DIS - descWindow.getWidth(); |
||||||
|
if (x < screenBounds.x) { // Doesn't fit
|
||||||
|
x = getX() + getWidth() + DIS; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
int y = getY(); |
||||||
|
if (aboveCaret) { |
||||||
|
y = y + getHeight() - descWindow.getHeight(); |
||||||
|
} |
||||||
|
|
||||||
|
if (x != descWindow.getX() || y != descWindow.getY()) { |
||||||
|
descWindow.setLocation(x, y); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* "Puts back" the original key/Action pair for a keystroke. This is used |
||||||
|
* when this popup is hidden. |
||||||
|
* |
||||||
|
* @param im The input map. |
||||||
|
* @param am The action map. |
||||||
|
* @param key The keystroke whose key/Action pair to change. |
||||||
|
* @param kap The (original) key/Action pair. |
||||||
|
* @see #replaceAction(InputMap, ActionMap, int, KeyActionPair, KeyActionPair) |
||||||
|
*/ |
||||||
|
private void putBackAction(InputMap im, ActionMap am, int key, |
||||||
|
KeyActionPair kap) { |
||||||
|
KeyStroke ks = KeyStroke.getKeyStroke(key, 0); |
||||||
|
am.put(im.get(ks), kap.action); // Original action for the "new" key
|
||||||
|
im.put(ks, kap.key); // Original key for the keystroke.
|
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* Replaces a key/Action pair in an InputMap and ActionMap with a new |
||||||
|
* pair. |
||||||
|
* |
||||||
|
* @param im The input map. |
||||||
|
* @param am The action map. |
||||||
|
* @param key The keystroke whose information to replace. |
||||||
|
* @param kap The new key/Action pair for <code>key</code>. |
||||||
|
* @param old A buffer in which to place the old key/Action pair. |
||||||
|
* @see #putBackAction(InputMap, ActionMap, int, KeyActionPair) |
||||||
|
*/ |
||||||
|
private void replaceAction(InputMap im, ActionMap am, int key, |
||||||
|
KeyActionPair kap, KeyActionPair old) { |
||||||
|
KeyStroke ks = KeyStroke.getKeyStroke(key, 0); |
||||||
|
old.key = im.get(ks); |
||||||
|
im.put(ks, kap.key); |
||||||
|
old.action = am.get(kap.key); |
||||||
|
am.put(kap.key, kap.action); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* Selects the first item in the completion list. |
||||||
|
* |
||||||
|
* @see #selectLastItem() |
||||||
|
*/ |
||||||
|
private void selectFirstItem() { |
||||||
|
if (model.getSize() > 0) { |
||||||
|
list.setSelectedIndex(0); |
||||||
|
list.ensureIndexIsVisible(0); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* Selects the last item in the completion list. |
||||||
|
* |
||||||
|
* @see #selectFirstItem() |
||||||
|
*/ |
||||||
|
private void selectLastItem() { |
||||||
|
int index = model.getSize() - 1; |
||||||
|
if (index > -1) { |
||||||
|
list.setSelectedIndex(index); |
||||||
|
list.ensureIndexIsVisible(index); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* Selects the next item in the completion list. |
||||||
|
* |
||||||
|
* @see #selectPreviousItem() |
||||||
|
*/ |
||||||
|
private void selectNextItem() { |
||||||
|
int index = list.getSelectedIndex(); |
||||||
|
if (index > -1) { |
||||||
|
index = (index + 1) % model.getSize(); |
||||||
|
list.setSelectedIndex(index); |
||||||
|
list.ensureIndexIsVisible(index); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* Selects the completion item one "page down" from the currently selected |
||||||
|
* one. |
||||||
|
* |
||||||
|
* @see #selectPageUpItem() |
||||||
|
*/ |
||||||
|
private void selectPageDownItem() { |
||||||
|
int visibleRowCount = list.getVisibleRowCount(); |
||||||
|
int i = Math.min(list.getModel().getSize() - 1, |
||||||
|
list.getSelectedIndex() + visibleRowCount); |
||||||
|
list.setSelectedIndex(i); |
||||||
|
list.ensureIndexIsVisible(i); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* Selects the completion item one "page up" from the currently selected |
||||||
|
* one. |
||||||
|
* |
||||||
|
* @see #selectPageDownItem() |
||||||
|
*/ |
||||||
|
private void selectPageUpItem() { |
||||||
|
int visibleRowCount = list.getVisibleRowCount(); |
||||||
|
int i = Math.max(0, list.getSelectedIndex() - visibleRowCount); |
||||||
|
list.setSelectedIndex(i); |
||||||
|
list.ensureIndexIsVisible(i); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* Selects the previous item in the completion list. |
||||||
|
* |
||||||
|
* @see #selectNextItem() |
||||||
|
*/ |
||||||
|
private void selectPreviousItem() { |
||||||
|
int index = list.getSelectedIndex(); |
||||||
|
switch (index) { |
||||||
|
case 0: |
||||||
|
index = list.getModel().getSize() - 1; |
||||||
|
break; |
||||||
|
case -1: // Check for an empty list (would be an error)
|
||||||
|
index = list.getModel().getSize() - 1; |
||||||
|
if (index == -1) { |
||||||
|
return; |
||||||
|
} |
||||||
|
break; |
||||||
|
default: |
||||||
|
index = index - 1; |
||||||
|
break; |
||||||
|
} |
||||||
|
list.setSelectedIndex(index); |
||||||
|
list.ensureIndexIsVisible(index); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* Sets the completions to display in the choices list. The first |
||||||
|
* completion is selected. |
||||||
|
* |
||||||
|
* @param completions The completions to display. |
||||||
|
*/ |
||||||
|
public void setCompletions(List<Completion> completions) { |
||||||
|
model.setContents(completions); |
||||||
|
selectFirstItem(); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* Sets the size of the description window. |
||||||
|
* |
||||||
|
* @param size The new size. This cannot be <code>null</code>. |
||||||
|
*/ |
||||||
|
public void setDescriptionWindowSize(Dimension size) { |
||||||
|
if (descWindow != null) { |
||||||
|
descWindow.setSize(size); |
||||||
|
} else { |
||||||
|
preferredDescWindowSize = size; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* Sets the default list cell renderer to use when a completion provider |
||||||
|
* does not supply its own. |
||||||
|
* |
||||||
|
* @param renderer The renderer to use. If this is <code>null</code>, |
||||||
|
* a default renderer is used. |
||||||
|
* @see #getListCellRenderer() |
||||||
|
*/ |
||||||
|
public void setListCellRenderer(ListCellRenderer renderer) { |
||||||
|
DelegatingCellRenderer dcr = (DelegatingCellRenderer) list. |
||||||
|
getCellRenderer(); |
||||||
|
dcr.setFallbackCellRenderer(renderer); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* Sets the location of this window to be "good" relative to the specified |
||||||
|
* rectangle. That rectangle should be the location of the text |
||||||
|
* component's caret, in screen coordinates. |
||||||
|
* |
||||||
|
* @param r The text component's caret position, in screen coordinates. |
||||||
|
*/ |
||||||
|
public void setLocationRelativeTo(Rectangle r) { |
||||||
|
|
||||||
|
// Multi-monitor support - make sure the completion window (and
|
||||||
|
// description window, if applicable) both fit in the same window in
|
||||||
|
// a multi-monitor environment. To do this, we decide which monitor
|
||||||
|
// the rectangle "r" is in, and use that one (just pick top-left corner
|
||||||
|
// as the defining point).
|
||||||
|
Rectangle screenBounds = Util.getScreenBoundsForPoint(r.x, r.y); |
||||||
|
//Dimension screenSize = getToolkit().getScreenSize();
|
||||||
|
|
||||||
|
boolean showDescWindow = descWindow != null && ac.isShowDescWindow(); |
||||||
|
int totalH = getHeight(); |
||||||
|
if (showDescWindow) { |
||||||
|
totalH = Math.max(totalH, descWindow.getHeight()); |
||||||
|
} |
||||||
|
|
||||||
|
// Try putting our stuff "below" the caret first. We assume that the
|
||||||
|
// entire height of our stuff fits on the screen one way or the other.
|
||||||
|
aboveCaret = false; |
||||||
|
int y = r.y + r.height + VERTICAL_SPACE; |
||||||
|
if (y + totalH > screenBounds.height) { |
||||||
|
y = r.y - VERTICAL_SPACE - getHeight(); |
||||||
|
aboveCaret = true; |
||||||
|
} |
||||||
|
|
||||||
|
// Get x-coordinate of completions. Try to align left edge with the
|
||||||
|
// caret first.
|
||||||
|
int x = r.x; |
||||||
|
if (!ac.getTextComponentOrientation().isLeftToRight()) { |
||||||
|
x -= getWidth(); // RTL => align right edge
|
||||||
|
} |
||||||
|
if (x < screenBounds.x) { |
||||||
|
x = screenBounds.x; |
||||||
|
} else if (x + getWidth() > screenBounds.x + screenBounds.width) { // completions don't fit
|
||||||
|
x = screenBounds.x + screenBounds.width - getWidth(); |
||||||
|
} |
||||||
|
|
||||||
|
setLocation(x, y); |
||||||
|
|
||||||
|
// Position the description window, if necessary.
|
||||||
|
if (showDescWindow) { |
||||||
|
positionDescWindow(); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* Toggles the visibility of this popup window. |
||||||
|
* |
||||||
|
* @param visible Whether this window should be visible. |
||||||
|
*/ |
||||||
|
@Override |
||||||
|
public void setVisible(boolean visible) { |
||||||
|
|
||||||
|
if (visible != isVisible()) { |
||||||
|
|
||||||
|
if (visible) { |
||||||
|
installKeyBindings(); |
||||||
|
lastLine = ac.getLineOfCaret(); |
||||||
|
selectFirstItem(); |
||||||
|
if (descWindow == null && ac.isShowDescWindow()) { |
||||||
|
descWindow = createDescriptionWindow(); |
||||||
|
positionDescWindow(); |
||||||
|
} |
||||||
|
// descWindow needs a kick-start the first time it's displayed.
|
||||||
|
// Also, the newly-selected item in the choices list is
|
||||||
|
// probably different from the previous one anyway.
|
||||||
|
if (descWindow != null) { |
||||||
|
Completion c = (Completion) list.getSelectedValue(); |
||||||
|
if (c != null) { |
||||||
|
descWindow.setDescriptionFor(c); |
||||||
|
} |
||||||
|
} |
||||||
|
} else { |
||||||
|
uninstallKeyBindings(); |
||||||
|
} |
||||||
|
|
||||||
|
super.setVisible(visible); |
||||||
|
|
||||||
|
// Some languages, such as Java, can use quite a lot of memory
|
||||||
|
// when displaying hundreds of completion choices. We pro-actively
|
||||||
|
// clear our list model here to make them available for GC.
|
||||||
|
// Otherwise, they stick around, and consider the following: a
|
||||||
|
// user starts code-completion for Java 5 SDK classes, then hides
|
||||||
|
// the dialog, then changes the "class path" to use a Java 6 SDK
|
||||||
|
// instead. On pressing Ctrl+space, a new array of Completions is
|
||||||
|
// created. If this window holds on to the previous Completions,
|
||||||
|
// you're getting roughly 2x the necessary Completions in memory
|
||||||
|
// until the Completions are actually passed to this window.
|
||||||
|
if (!visible) { // Do after super.setVisible(false)
|
||||||
|
lastSelection = (Completion) list.getSelectedValue(); |
||||||
|
model.clear(); |
||||||
|
} |
||||||
|
|
||||||
|
// Must set descWindow's visibility one way or the other each time,
|
||||||
|
// because of the way child JWindows' visibility is handled - in
|
||||||
|
// some ways it's dependent on the parent, in other ways it's not.
|
||||||
|
if (descWindow != null) { |
||||||
|
descWindow.setVisible(visible && ac.isShowDescWindow()); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* Stops intercepting certain keystrokes from the text component. |
||||||
|
* |
||||||
|
* @see #installKeyBindings() |
||||||
|
*/ |
||||||
|
private void uninstallKeyBindings() { |
||||||
|
|
||||||
|
if (AutoCompletion.isDebug()) { |
||||||
|
FineLoggerFactory.getLogger().debug("PopupWindow: Removing keybindings"); |
||||||
|
} |
||||||
|
|
||||||
|
JTextComponent comp = ac.getTextComponent(); |
||||||
|
InputMap im = comp.getInputMap(); |
||||||
|
ActionMap am = comp.getActionMap(); |
||||||
|
|
||||||
|
putBackAction(im, am, KeyEvent.VK_ESCAPE, oldEscape); |
||||||
|
putBackAction(im, am, KeyEvent.VK_UP, oldUp); |
||||||
|
putBackAction(im, am, KeyEvent.VK_DOWN, oldDown); |
||||||
|
putBackAction(im, am, KeyEvent.VK_LEFT, oldLeft); |
||||||
|
putBackAction(im, am, KeyEvent.VK_RIGHT, oldRight); |
||||||
|
putBackAction(im, am, KeyEvent.VK_ENTER, oldEnter); |
||||||
|
putBackAction(im, am, KeyEvent.VK_TAB, oldTab); |
||||||
|
putBackAction(im, am, KeyEvent.VK_HOME, oldHome); |
||||||
|
putBackAction(im, am, KeyEvent.VK_END, oldEnd); |
||||||
|
putBackAction(im, am, KeyEvent.VK_PAGE_UP, oldPageUp); |
||||||
|
putBackAction(im, am, KeyEvent.VK_PAGE_DOWN, oldPageDown); |
||||||
|
|
||||||
|
// Ctrl+C
|
||||||
|
KeyStroke ks = getCopyKeyStroke(); |
||||||
|
am.put(im.get(ks), oldCtrlC.action); // Original action
|
||||||
|
im.put(ks, oldCtrlC.key); // Original key
|
||||||
|
|
||||||
|
comp.removeCaretListener(this); |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* Updates the <tt>LookAndFeel</tt> of this window and the description |
||||||
|
* window. |
||||||
|
*/ |
||||||
|
public void updateUI() { |
||||||
|
SwingUtilities.updateComponentTreeUI(this); |
||||||
|
if (descWindow != null) { |
||||||
|
descWindow.updateUI(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* Called when a new item is selected in the popup list. |
||||||
|
* |
||||||
|
* @param e The event. |
||||||
|
*/ |
||||||
|
public void valueChanged(ListSelectionEvent e) { |
||||||
|
if (!e.getValueIsAdjusting()) { |
||||||
|
Object value = list.getSelectedValue(); |
||||||
|
if (value != null && descWindow != null) { |
||||||
|
descWindow.setDescriptionFor((Completion) value); |
||||||
|
positionDescWindow(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
class CopyAction extends AbstractAction { |
||||||
|
|
||||||
|
public void actionPerformed(ActionEvent e) { |
||||||
|
boolean doNormalCopy = false; |
||||||
|
if (descWindow != null && descWindow.isVisible()) { |
||||||
|
doNormalCopy = !descWindow.copy(); |
||||||
|
} |
||||||
|
if (doNormalCopy) { |
||||||
|
ac.getTextComponent().copy(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
class DownAction extends AbstractAction { |
||||||
|
|
||||||
|
public void actionPerformed(ActionEvent e) { |
||||||
|
if (isVisible()) { |
||||||
|
selectNextItem(); |
||||||
|
refreshInstallComp(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
class EndAction extends AbstractAction { |
||||||
|
|
||||||
|
public void actionPerformed(ActionEvent e) { |
||||||
|
if (isVisible()) { |
||||||
|
selectLastItem(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
class EnterAction extends AbstractAction { |
||||||
|
|
||||||
|
public void actionPerformed(ActionEvent e) { |
||||||
|
if (isVisible()) { |
||||||
|
insertSelectedCompletion(); |
||||||
|
refreshInstallComp(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
class EscapeAction extends AbstractAction { |
||||||
|
|
||||||
|
public void actionPerformed(ActionEvent e) { |
||||||
|
if (isVisible()) { |
||||||
|
setVisible(false); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
class HomeAction extends AbstractAction { |
||||||
|
|
||||||
|
public void actionPerformed(ActionEvent e) { |
||||||
|
if (isVisible()) { |
||||||
|
selectFirstItem(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* A mapping from a key (an Object) to an Action. |
||||||
|
*/ |
||||||
|
private static class KeyActionPair { |
||||||
|
|
||||||
|
public Object key; |
||||||
|
public Action action; |
||||||
|
|
||||||
|
public KeyActionPair() { |
||||||
|
} |
||||||
|
|
||||||
|
public KeyActionPair(Object key, Action a) { |
||||||
|
this.key = key; |
||||||
|
this.action = a; |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
class LeftAction extends AbstractAction { |
||||||
|
|
||||||
|
public void actionPerformed(ActionEvent e) { |
||||||
|
if (isVisible()) { |
||||||
|
JTextComponent comp = ac.getTextComponent(); |
||||||
|
Caret c = comp.getCaret(); |
||||||
|
int dot = c.getDot(); |
||||||
|
if (dot > 0) { |
||||||
|
c.setDot(--dot); |
||||||
|
// Ensure moving left hasn't moved us up a line, thus
|
||||||
|
// hiding the popup window.
|
||||||
|
if (comp.isVisible()) { |
||||||
|
if (lastLine != -1) { |
||||||
|
doAutocomplete(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
class PageDownAction extends AbstractAction { |
||||||
|
|
||||||
|
public void actionPerformed(ActionEvent e) { |
||||||
|
if (isVisible()) { |
||||||
|
selectPageDownItem(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
class PageUpAction extends AbstractAction { |
||||||
|
|
||||||
|
public void actionPerformed(ActionEvent e) { |
||||||
|
if (isVisible()) { |
||||||
|
selectPageUpItem(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* The actual list of completion choices in this popup window. |
||||||
|
*/ |
||||||
|
private class PopupList extends JList { |
||||||
|
|
||||||
|
public PopupList(CompletionListModel model) { |
||||||
|
super(model); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void setUI(ListUI ui) { |
||||||
|
if (Util.getUseSubstanceRenderers() && |
||||||
|
SUBSTANCE_LIST_UI.equals(ui.getClass().getName())) { |
||||||
|
// Substance requires its special ListUI be installed for
|
||||||
|
// its renderers to actually render (!), but long completion
|
||||||
|
// lists (e.g. PHPCompletionProvider in RSTALanguageSupport)
|
||||||
|
// will simply populate too slowly on initial display (when
|
||||||
|
// calculating preferred size of all items), so in this case
|
||||||
|
// we give a prototype cell value.
|
||||||
|
CompletionProvider p = ac.getCompletionProvider(); |
||||||
|
BasicCompletion bc = new BasicCompletion(p, "Hello world"); |
||||||
|
setPrototypeCellValue(bc); |
||||||
|
} else { |
||||||
|
// Our custom UI that is faster for long HTML completion lists.
|
||||||
|
ui = new FastListUI(); |
||||||
|
setPrototypeCellValue(null); |
||||||
|
} |
||||||
|
super.setUI(ui); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
class RightAction extends AbstractAction { |
||||||
|
|
||||||
|
public void actionPerformed(ActionEvent e) { |
||||||
|
if (isVisible()) { |
||||||
|
JTextComponent comp = ac.getTextComponent(); |
||||||
|
Caret c = comp.getCaret(); |
||||||
|
int dot = c.getDot(); |
||||||
|
if (dot < comp.getDocument().getLength()) { |
||||||
|
c.setDot(++dot); |
||||||
|
// Ensure moving right hasn't moved us up a line, thus
|
||||||
|
// hiding the popup window.
|
||||||
|
if (comp.isVisible()) { |
||||||
|
if (lastLine != -1) { |
||||||
|
doAutocomplete(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
class UpAction extends AbstractAction { |
||||||
|
|
||||||
|
public void actionPerformed(ActionEvent e) { |
||||||
|
if (isVisible()) { |
||||||
|
selectPreviousItem(); |
||||||
|
refreshInstallComp(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
} |
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,16 @@ |
|||||||
|
package com.fr.design.gui.autocomplete; |
||||||
|
|
||||||
|
import java.awt.Window; |
||||||
|
|
||||||
|
public class JSAutoCompletePopupWindow extends AutoCompleteWithExtraRefreshPopupWindow { |
||||||
|
|
||||||
|
/** |
||||||
|
* Constructor. |
||||||
|
* |
||||||
|
* @param parent The parent window (hosting the text component). |
||||||
|
* @param ac The auto-completion instance. |
||||||
|
*/ |
||||||
|
public JSAutoCompletePopupWindow(Window parent, AutoCompletion ac) { |
||||||
|
super(parent, ac); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,31 @@ |
|||||||
|
package com.fr.design.gui.autocomplete; |
||||||
|
|
||||||
|
public class JSImplPaneAutoCompletion extends AutoCompletionWithExtraRefresh{ |
||||||
|
/** |
||||||
|
* Constructor. |
||||||
|
* |
||||||
|
* @param provider The completion provider. This cannot be |
||||||
|
* <code>null</code>. |
||||||
|
*/ |
||||||
|
public JSImplPaneAutoCompletion(CompletionProvider provider) { |
||||||
|
super(provider); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
protected AutoCompleteWithExtraRefreshPopupWindow createAutoCompletePopupWindow() { |
||||||
|
JSAutoCompletePopupWindow popupWindow = new JSAutoCompletePopupWindow(getParentWindow(),this); |
||||||
|
popupWindow.applyComponentOrientation( |
||||||
|
getTextComponentOrientation()); |
||||||
|
if (getCellRender() != null) { |
||||||
|
popupWindow.setListCellRenderer(getCellRender()); |
||||||
|
} |
||||||
|
if (getPreferredChoicesWindowSize() != null) { |
||||||
|
popupWindow.setSize(getPreferredChoicesWindowSize()); |
||||||
|
} |
||||||
|
if (getPreferredDescWindowSize() != null) { |
||||||
|
popupWindow.setDescriptionWindowSize( |
||||||
|
getPreferredDescWindowSize()); |
||||||
|
} |
||||||
|
return popupWindow; |
||||||
|
} |
||||||
|
} |
@ -1,77 +0,0 @@ |
|||||||
package com.fr.design.gui.icombobox; |
|
||||||
|
|
||||||
import com.fr.log.FineLoggerFactory; |
|
||||||
|
|
||||||
import javax.swing.JTree; |
|
||||||
import javax.swing.SwingWorker; |
|
||||||
import javax.swing.tree.TreeCellRenderer; |
|
||||||
import java.util.concurrent.FutureTask; |
|
||||||
|
|
||||||
/** |
|
||||||
* 模糊搜索前需执行完前置任务的TreeComboBox |
|
||||||
* @author Lucian.Chen |
|
||||||
* @version 10.0 |
|
||||||
* Created by Lucian.Chen on 2021/4/14 |
|
||||||
*/ |
|
||||||
public class SearchPreTaskTreeComboBox extends FRTreeComboBox { |
|
||||||
|
|
||||||
/** |
|
||||||
* 模糊搜索前任务 |
|
||||||
*/ |
|
||||||
private FutureTask<Void> preSearchTask; |
|
||||||
|
|
||||||
public SearchPreTaskTreeComboBox(JTree tree, TreeCellRenderer renderer, boolean editable) { |
|
||||||
super(tree, renderer, editable); |
|
||||||
} |
|
||||||
|
|
||||||
public FutureTask<Void> getPreSearchTask() { |
|
||||||
return preSearchTask; |
|
||||||
} |
|
||||||
|
|
||||||
public void setPreSearchTask(FutureTask<Void> preSearchTask) { |
|
||||||
this.preSearchTask = preSearchTask; |
|
||||||
} |
|
||||||
|
|
||||||
protected UIComboBoxEditor createEditor() { |
|
||||||
return new SearchPreTaskComboBoxEditor(this); |
|
||||||
} |
|
||||||
|
|
||||||
private class SearchPreTaskComboBoxEditor extends FrTreeSearchComboBoxEditor { |
|
||||||
|
|
||||||
public SearchPreTaskComboBoxEditor(FRTreeComboBox comboBox) { |
|
||||||
super(comboBox); |
|
||||||
} |
|
||||||
|
|
||||||
protected void changeHandler() { |
|
||||||
if (isSetting()) { |
|
||||||
return; |
|
||||||
} |
|
||||||
setPopupVisible(true); |
|
||||||
new SwingWorker<Void, Void>() { |
|
||||||
@Override |
|
||||||
protected Void doInBackground() { |
|
||||||
FutureTask<Void> task = getPreSearchTask(); |
|
||||||
try { |
|
||||||
// 确保模糊搜索前任务执行完成后,再进行模糊搜索
|
|
||||||
if (task != null) { |
|
||||||
task.get(); |
|
||||||
} |
|
||||||
} catch (Exception e) { |
|
||||||
FineLoggerFactory.getLogger().error(e.getMessage(), e); |
|
||||||
} |
|
||||||
if (task != null) { |
|
||||||
// 任务执行后置空,否则会被别的操作重复触发
|
|
||||||
setPreSearchTask(null); |
|
||||||
} |
|
||||||
return null; |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
protected void done() { |
|
||||||
// 模糊搜索
|
|
||||||
search(); |
|
||||||
} |
|
||||||
}.execute(); |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
@ -0,0 +1,228 @@ |
|||||||
|
package com.fr.design.gui.icombobox; |
||||||
|
|
||||||
|
import com.fr.data.core.DataCoreUtils; |
||||||
|
import com.fr.data.core.db.TableProcedure; |
||||||
|
import com.fr.data.impl.Connection; |
||||||
|
import com.fr.design.DesignerEnvManager; |
||||||
|
import com.fr.design.data.datapane.ChoosePane; |
||||||
|
import com.fr.design.dialog.FineJOptionPane; |
||||||
|
import com.fr.design.gui.itree.refreshabletree.ExpandMutableTreeNode; |
||||||
|
import com.fr.design.mainframe.DesignerContext; |
||||||
|
import com.fr.log.FineLoggerFactory; |
||||||
|
import com.fr.stable.ArrayUtils; |
||||||
|
import com.fr.stable.Filter; |
||||||
|
import com.fr.stable.StringUtils; |
||||||
|
|
||||||
|
import javax.swing.JOptionPane; |
||||||
|
import javax.swing.JTree; |
||||||
|
import javax.swing.SwingWorker; |
||||||
|
import javax.swing.event.PopupMenuEvent; |
||||||
|
import javax.swing.event.PopupMenuListener; |
||||||
|
import javax.swing.tree.DefaultMutableTreeNode; |
||||||
|
import javax.swing.tree.DefaultTreeModel; |
||||||
|
import javax.swing.tree.TreeCellRenderer; |
||||||
|
import javax.swing.tree.TreeNode; |
||||||
|
import javax.swing.tree.TreePath; |
||||||
|
import java.util.Enumeration; |
||||||
|
|
||||||
|
/** |
||||||
|
* 实现模糊搜索表名的FRTreeComboBox |
||||||
|
* FRTreeComboBox:搜索后滚动到首个匹配节点 |
||||||
|
* SearchFRTreeComboBox:显示所有匹配的节点 |
||||||
|
* |
||||||
|
* @author Lucian.Chen |
||||||
|
* @version 10.0 |
||||||
|
* Created by Lucian.Chen on 2021/4/14 |
||||||
|
*/ |
||||||
|
public class TableSearchTreeComboBox extends FRTreeComboBox { |
||||||
|
// 持有父容器,需要实时获取其他组件值
|
||||||
|
private final ChoosePane parent; |
||||||
|
|
||||||
|
public TableSearchTreeComboBox(ChoosePane parent, JTree tree, TreeCellRenderer renderer) { |
||||||
|
super(tree, renderer); |
||||||
|
this.parent = parent; |
||||||
|
initPopupListener(); |
||||||
|
} |
||||||
|
|
||||||
|
protected UIComboBoxEditor createEditor() { |
||||||
|
return new TableSearchComboBoxEditor(this); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
protected String pathToString(TreePath path) { |
||||||
|
Object obj = ((DefaultMutableTreeNode) path.getLastPathComponent()).getUserObject(); |
||||||
|
if (obj instanceof TableProcedure) { |
||||||
|
return ((TableProcedure) obj).getName(); |
||||||
|
} |
||||||
|
return super.pathToString(path); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void setSelectedItemString(String _name) { |
||||||
|
super.setSelectedItemString(_name); |
||||||
|
// 会因为连续两次选中的值一致,导致未触发编辑框联动
|
||||||
|
this.getEditor().setItem(_name); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 执行模糊搜索 |
||||||
|
*/ |
||||||
|
private void searchExecute() { |
||||||
|
UIComboBoxEditor searchEditor = (UIComboBoxEditor) this.getEditor(); |
||||||
|
new SwingWorker<Void, Void>() { |
||||||
|
@Override |
||||||
|
protected Void doInBackground() { |
||||||
|
processTableDataNames( |
||||||
|
parent.getDSName(), |
||||||
|
parent.getConnection(), |
||||||
|
parent.getSchema(), |
||||||
|
createFilter((String) searchEditor.getItem())); |
||||||
|
return null; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
protected void done() { |
||||||
|
expandTree(); |
||||||
|
// 输入框获取焦点
|
||||||
|
searchEditor.getEditorComponent().requestFocus(); |
||||||
|
} |
||||||
|
}.execute(); |
||||||
|
} |
||||||
|
|
||||||
|
private TableNameFilter createFilter(String text) { |
||||||
|
return StringUtils.isEmpty(text) ? EMPTY_FILTER : new TableNameFilter(text); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 查询数据库表,并构建节点目录 |
||||||
|
* |
||||||
|
* @param databaseName 数据库名 |
||||||
|
* @param connection 数据连接 |
||||||
|
* @param schema 模式 |
||||||
|
* @param filter 模糊搜索过滤器 |
||||||
|
*/ |
||||||
|
private void processTableDataNames(String databaseName, Connection connection, String schema, TableNameFilter filter) { |
||||||
|
if (tree == null) { |
||||||
|
return; |
||||||
|
} |
||||||
|
DefaultMutableTreeNode rootTreeNode = (DefaultMutableTreeNode) tree.getModel().getRoot(); |
||||||
|
rootTreeNode.removeAllChildren(); |
||||||
|
|
||||||
|
if (connection == null) { |
||||||
|
return; |
||||||
|
} |
||||||
|
try { |
||||||
|
schema = StringUtils.isEmpty(schema) ? null : schema; |
||||||
|
TableProcedure[] sqlTableArray = DataCoreUtils.getTables(connection, TableProcedure.TABLE, schema, DesignerEnvManager.getEnvManager().isOracleSystemSpace()); |
||||||
|
if (ArrayUtils.isNotEmpty(sqlTableArray)) { |
||||||
|
ExpandMutableTreeNode tableTreeNode = new ExpandMutableTreeNode(databaseName + "-" + com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_SQL_Table")); |
||||||
|
rootTreeNode.add(tableTreeNode); |
||||||
|
addArrayNode(tableTreeNode, sqlTableArray, filter); |
||||||
|
} |
||||||
|
TableProcedure[] sqlViewArray = DataCoreUtils.getTables(connection, TableProcedure.VIEW, schema, DesignerEnvManager.getEnvManager().isOracleSystemSpace()); |
||||||
|
if (ArrayUtils.isNotEmpty(sqlViewArray)) { |
||||||
|
ExpandMutableTreeNode viewTreeNode = new ExpandMutableTreeNode(databaseName + "-" + com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_SQL_View")); |
||||||
|
rootTreeNode.add(viewTreeNode); |
||||||
|
addArrayNode(viewTreeNode, sqlViewArray, filter); |
||||||
|
} |
||||||
|
} catch (Exception e) { |
||||||
|
FineLoggerFactory.getLogger().error(e.getMessage(), e); |
||||||
|
FineJOptionPane.showMessageDialog(DesignerContext.getDesignerFrame(), com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Database_Connection_Failed"), |
||||||
|
com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Failed"), JOptionPane.ERROR_MESSAGE); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private void addArrayNode(ExpandMutableTreeNode rootNode, TableProcedure[] sqlArray, TableNameFilter filter) { |
||||||
|
if (sqlArray != null) { |
||||||
|
for (TableProcedure procedure : sqlArray) { |
||||||
|
if (filter.accept(procedure)) { |
||||||
|
ExpandMutableTreeNode viewChildTreeNode = new ExpandMutableTreeNode(procedure); |
||||||
|
rootNode.add(viewChildTreeNode); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 展开节点 |
||||||
|
*/ |
||||||
|
private void expandTree() { |
||||||
|
((DefaultTreeModel) tree.getModel()).reload(); |
||||||
|
// daniel 展开所有tree
|
||||||
|
TreeNode root = (TreeNode) tree.getModel().getRoot(); |
||||||
|
TreePath parent = new TreePath(root); |
||||||
|
TreeNode node = (TreeNode) parent.getLastPathComponent(); |
||||||
|
for (Enumeration e = node.children(); e.hasMoreElements(); ) { |
||||||
|
TreeNode n = (TreeNode) e.nextElement(); |
||||||
|
TreePath path = parent.pathByAddingChild(n); |
||||||
|
tree.expandPath(path); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 表名模糊搜索实现 |
||||||
|
*/ |
||||||
|
private static class TableNameFilter implements Filter<TableProcedure> { |
||||||
|
private String searchFilter; |
||||||
|
|
||||||
|
public TableNameFilter() { |
||||||
|
} |
||||||
|
|
||||||
|
public TableNameFilter(String searchFilter) { |
||||||
|
this.searchFilter = searchFilter.toLowerCase().trim(); |
||||||
|
} |
||||||
|
|
||||||
|
// 表名匹配
|
||||||
|
@Override |
||||||
|
public boolean accept(TableProcedure procedure) { |
||||||
|
return procedure.getName().toLowerCase().contains(searchFilter); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private static final TableNameFilter EMPTY_FILTER = new TableNameFilter() { |
||||||
|
public boolean accept(TableProcedure procedure) { |
||||||
|
return true; |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
private void initPopupListener() { |
||||||
|
// 点击下拉时触发模糊搜索
|
||||||
|
this.addPopupMenuListener(new PopupMenuListener() { |
||||||
|
|
||||||
|
@Override |
||||||
|
public void popupMenuWillBecomeVisible(PopupMenuEvent e) { |
||||||
|
searchExecute(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void popupMenuWillBecomeInvisible(PopupMenuEvent e) { |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void popupMenuCanceled(PopupMenuEvent e) { |
||||||
|
|
||||||
|
} |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 重写输入框编辑器,实现输入框模糊搜索逻辑 |
||||||
|
*/ |
||||||
|
private class TableSearchComboBoxEditor extends FrTreeSearchComboBoxEditor { |
||||||
|
|
||||||
|
public TableSearchComboBoxEditor(FRTreeComboBox comboBox) { |
||||||
|
super(comboBox); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
protected void changeHandler() { |
||||||
|
if (isSetting()) { |
||||||
|
return; |
||||||
|
} |
||||||
|
setPopupVisible(true); |
||||||
|
this.item = textField.getText(); |
||||||
|
searchExecute(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,25 @@ |
|||||||
|
package com.fr.design.gui.ifilechooser; |
||||||
|
|
||||||
|
/** |
||||||
|
* @author hades |
||||||
|
* @version 10.0 |
||||||
|
* Created by hades on 2021/12/29 |
||||||
|
*/ |
||||||
|
public class ExtensionFilter { |
||||||
|
|
||||||
|
private final String des; |
||||||
|
private final String[] extensions; |
||||||
|
|
||||||
|
public ExtensionFilter(String des, String... extensions) { |
||||||
|
this.des = des; |
||||||
|
this.extensions = extensions; |
||||||
|
} |
||||||
|
|
||||||
|
public String getDes() { |
||||||
|
return des; |
||||||
|
} |
||||||
|
|
||||||
|
public String[] getExtensions() { |
||||||
|
return extensions; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,109 @@ |
|||||||
|
package com.fr.design.gui.ifilechooser; |
||||||
|
|
||||||
|
/** |
||||||
|
* 文件选择器可设置的参数集合 |
||||||
|
* |
||||||
|
* @author hades |
||||||
|
* @version 10.0 |
||||||
|
* Created by hades on 2021/12/21 |
||||||
|
*/ |
||||||
|
public class FileChooserArgs { |
||||||
|
|
||||||
|
private final FileSelectionMode fileSelectionMode; |
||||||
|
private final String filterDes; |
||||||
|
private final String[] extensions; |
||||||
|
private final String selectedPath; |
||||||
|
private final ExtensionFilter[] filters; |
||||||
|
private final String tipText; |
||||||
|
private final boolean multiSelectionEnabled; |
||||||
|
|
||||||
|
public static Builder newBuilder() { |
||||||
|
return new Builder(); |
||||||
|
} |
||||||
|
|
||||||
|
private FileChooserArgs(Builder builder) { |
||||||
|
this.fileSelectionMode = builder.fileSelectionMode; |
||||||
|
this.filterDes = builder.filterDes; |
||||||
|
this.extensions = builder.extensions; |
||||||
|
this.selectedPath = builder.selectedPath; |
||||||
|
this.filters = builder.filters; |
||||||
|
this.tipText = builder.tipText; |
||||||
|
this.multiSelectionEnabled = builder.multiSelectionEnabled; |
||||||
|
} |
||||||
|
|
||||||
|
public FileSelectionMode getFileSelectionMode() { |
||||||
|
return fileSelectionMode; |
||||||
|
} |
||||||
|
|
||||||
|
public String getFilterDes() { |
||||||
|
return filterDes; |
||||||
|
} |
||||||
|
|
||||||
|
public String[] getExtensions() { |
||||||
|
return extensions; |
||||||
|
} |
||||||
|
|
||||||
|
public String getSelectedPath() { |
||||||
|
return selectedPath; |
||||||
|
} |
||||||
|
|
||||||
|
public ExtensionFilter[] getFilters() { |
||||||
|
return filters; |
||||||
|
} |
||||||
|
|
||||||
|
public String getTipText() { |
||||||
|
return tipText; |
||||||
|
} |
||||||
|
|
||||||
|
public boolean isMultiSelectionEnabled() { |
||||||
|
return multiSelectionEnabled; |
||||||
|
} |
||||||
|
|
||||||
|
public static class Builder { |
||||||
|
|
||||||
|
private FileSelectionMode fileSelectionMode; |
||||||
|
private String filterDes; |
||||||
|
private String[] extensions; |
||||||
|
private String selectedPath; |
||||||
|
private ExtensionFilter[] filters = new ExtensionFilter[0]; |
||||||
|
private String tipText; |
||||||
|
private boolean multiSelectionEnabled; |
||||||
|
|
||||||
|
public Builder setFileSelectionMode(FileSelectionMode fileSelectionMode) { |
||||||
|
this.fileSelectionMode = fileSelectionMode; |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
public Builder setFilter(String filterDes, String... extensions) { |
||||||
|
this.filterDes = filterDes; |
||||||
|
this.extensions = extensions; |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
public Builder setSelectedPath(String selectedPath) { |
||||||
|
this.selectedPath = selectedPath; |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
public Builder setFilters(ExtensionFilter[] filters) { |
||||||
|
this.filters = filters; |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
public Builder setTipText(String tipText) { |
||||||
|
this.tipText = tipText; |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
public Builder setMultiSelectionEnabled(boolean multiSelectionEnabled) { |
||||||
|
this.multiSelectionEnabled = multiSelectionEnabled; |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
public FileChooserArgs build() { |
||||||
|
return new FileChooserArgs(this); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
} |
@ -0,0 +1,44 @@ |
|||||||
|
package com.fr.design.gui.ifilechooser; |
||||||
|
|
||||||
|
import com.fr.design.i18n.Toolkit; |
||||||
|
import com.fr.design.os.impl.SupportOSImpl; |
||||||
|
|
||||||
|
/** |
||||||
|
* @author hades |
||||||
|
* @version 10.0 |
||||||
|
* Created by hades on 2021/12/21 |
||||||
|
*/ |
||||||
|
public class FileChooserFactory { |
||||||
|
|
||||||
|
public static FileChooserProvider createFileChooser(FileChooserArgs fileChooserArgs) { |
||||||
|
if (SupportOSImpl.OLD_STYLE_CHOOSER.support()) { |
||||||
|
return new SwingFileChooser.Builder(). |
||||||
|
setFileSelectionMode(fileChooserArgs.getFileSelectionMode()). |
||||||
|
setFileFilter(fileChooserArgs.getFilterDes(), fileChooserArgs.getExtensions()). |
||||||
|
setSelectedFile(fileChooserArgs.getSelectedPath()). |
||||||
|
setMultiSelectionEnabled(fileChooserArgs.isMultiSelectionEnabled()). |
||||||
|
setTipText(fileChooserArgs.getTipText()). |
||||||
|
setFileFilter(fileChooserArgs.getFilters()).build(); |
||||||
|
} else { |
||||||
|
return new JavaFxNativeFileChooser.Builder(). |
||||||
|
fileSelectionMode(fileChooserArgs.getFileSelectionMode()). |
||||||
|
filter(fileChooserArgs.getFilterDes(), fileChooserArgs.getExtensions()). |
||||||
|
currentDirectory(fileChooserArgs.getSelectedPath()). |
||||||
|
title(fileChooserArgs.getTipText()). |
||||||
|
filters(fileChooserArgs.getFilters()).build(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public static FileChooserProvider createImageFileChooser() { |
||||||
|
if (SupportOSImpl.OLD_STYLE_CHOOSER.support()) { |
||||||
|
return new SwingImageFileChooser(); |
||||||
|
} else { |
||||||
|
return new JavaFxNativeFileChooser.Builder(). |
||||||
|
fileSelectionMode(FileSelectionMode.FILE). |
||||||
|
title(Toolkit.i18nText("Fine-Design_Basic_Open")). |
||||||
|
filter(Toolkit.i18nText("Fine-Design_Basic_Image_Image_Files"), "*.jpg", "*.gif", "*.png", "*.bmp"). |
||||||
|
build(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,130 @@ |
|||||||
|
package com.fr.design.gui.ifilechooser; |
||||||
|
|
||||||
|
import com.fr.common.annotations.Negative; |
||||||
|
import com.fr.design.upm.UpmUtils; |
||||||
|
import com.fr.stable.ArrayUtils; |
||||||
|
import com.fr.stable.StringUtils; |
||||||
|
import java.awt.Component; |
||||||
|
import java.io.File; |
||||||
|
import javax.swing.JFileChooser; |
||||||
|
import javax.swing.filechooser.FileFilter; |
||||||
|
import javax.swing.filechooser.FileNameExtensionFilter; |
||||||
|
|
||||||
|
/** |
||||||
|
* @author hades |
||||||
|
* @version 10.0 |
||||||
|
* Created by hades on 2021/12/21 |
||||||
|
*/ |
||||||
|
@Negative(until = "2022-6-1") |
||||||
|
class SwingFileChooser implements FileChooserProvider { |
||||||
|
|
||||||
|
private final JFileChooser fileChooser; |
||||||
|
private final Builder builder; |
||||||
|
|
||||||
|
private SwingFileChooser(Builder builder) { |
||||||
|
fileChooser = new JFileChooser(); |
||||||
|
fileChooser.setFileSelectionMode(builder.fileSelectionMode); |
||||||
|
fileChooser.setFileFilter(builder.fileFilter); |
||||||
|
fileChooser.setSelectedFile(builder.selectedFile); |
||||||
|
fileChooser.setMultiSelectionEnabled(builder.multiSelectionEnabled); |
||||||
|
this.builder = builder; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public File[] getSelectedFiles() { |
||||||
|
return fileChooser.getSelectedFiles(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public File getSelectedFile() { |
||||||
|
return fileChooser.getSelectedFile(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public int showDialog(Component parent) { |
||||||
|
if (StringUtils.isEmpty(builder.tipText)) { |
||||||
|
return fileChooser.showOpenDialog(parent); |
||||||
|
} else { |
||||||
|
return fileChooser.showDialog(parent, builder.tipText); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public int showOpenDialog(Component parent, String approveButtonText) { |
||||||
|
return fileChooser.showDialog(parent, approveButtonText); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void setCurrentDirectory(File file) { |
||||||
|
fileChooser.setCurrentDirectory(file); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void setMultiSelectionEnabled(boolean multiple) { |
||||||
|
fileChooser.setMultiSelectionEnabled(multiple); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 和一般builder不同的是 setXXX还做参数转换逻辑 |
||||||
|
*/ |
||||||
|
public static class Builder { |
||||||
|
|
||||||
|
private int fileSelectionMode = JFileChooser.FILES_ONLY; |
||||||
|
private FileFilter fileFilter; |
||||||
|
private File selectedFile; |
||||||
|
private boolean multiSelectionEnabled; |
||||||
|
private String tipText; |
||||||
|
|
||||||
|
public Builder setFileSelectionMode(FileSelectionMode fileSelectionMode) { |
||||||
|
if (FileSelectionMode.DIR.equals(fileSelectionMode)) { |
||||||
|
this.fileSelectionMode = JFileChooser.DIRECTORIES_ONLY; |
||||||
|
} else if (FileSelectionMode.FILE.equals(fileSelectionMode)) { |
||||||
|
this.fileSelectionMode = JFileChooser.FILES_ONLY; |
||||||
|
} else if (FileSelectionMode.MULTIPLE_FILE.equals(fileSelectionMode)) { |
||||||
|
this.fileSelectionMode = JFileChooser.FILES_AND_DIRECTORIES; |
||||||
|
} |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
public Builder setFileFilter(String des, String... extension) { |
||||||
|
if (StringUtils.isNotEmpty(des) && ArrayUtils.isNotEmpty(extension)) { |
||||||
|
this.fileFilter = new FileNameExtensionFilter(des, UpmUtils.findMatchedExtension(extension)); |
||||||
|
} |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
public Builder setFileFilter(ExtensionFilter[] extensionFilters) { |
||||||
|
StringBuilder desBuilder = new StringBuilder(); |
||||||
|
String[] extensions = new String[0]; |
||||||
|
for (ExtensionFilter extensionFilter : extensionFilters) { |
||||||
|
desBuilder.append(extensionFilter.getDes()).append(" "); |
||||||
|
extensions = ArrayUtils.addAll(extensions, UpmUtils.findMatchedExtension(extensionFilter.getExtensions())); |
||||||
|
} |
||||||
|
if (ArrayUtils.isNotEmpty(extensionFilters)) { |
||||||
|
this.fileFilter = new FileNameExtensionFilter(desBuilder.toString().trim(), extensions); |
||||||
|
} |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
public Builder setSelectedFile(String selectedPath) { |
||||||
|
if (StringUtils.isNotEmpty(selectedPath)) { |
||||||
|
this.selectedFile = new File(selectedPath); |
||||||
|
} |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
public Builder setMultiSelectionEnabled(boolean multiSelectionEnabled) { |
||||||
|
this.multiSelectionEnabled = multiSelectionEnabled; |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
public Builder setTipText(String tipText) { |
||||||
|
this.tipText = tipText; |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
public SwingFileChooser build() { |
||||||
|
return new SwingFileChooser(this); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,276 @@ |
|||||||
|
package com.fr.design.gui.ifilechooser; |
||||||
|
|
||||||
|
import com.fr.base.BaseUtils; |
||||||
|
import com.fr.common.annotations.Negative; |
||||||
|
import com.fr.design.DesignerEnvManager; |
||||||
|
import com.fr.design.style.ChooseFileView; |
||||||
|
import com.fr.design.style.background.image.ExpandFileChooser; |
||||||
|
import java.awt.Component; |
||||||
|
import java.awt.event.ActionEvent; |
||||||
|
import java.awt.event.ActionListener; |
||||||
|
import java.io.File; |
||||||
|
import java.util.Enumeration; |
||||||
|
import java.util.Hashtable; |
||||||
|
import javax.swing.filechooser.FileFilter; |
||||||
|
|
||||||
|
/** |
||||||
|
* @author hades |
||||||
|
* @version 10.0 |
||||||
|
* Created by hades on 2021/12/30 |
||||||
|
*/ |
||||||
|
@Negative(until = "2022-6-1") |
||||||
|
class SwingImageFileChooser extends ExpandFileChooser implements FileChooserProvider { |
||||||
|
|
||||||
|
public SwingImageFileChooser() { |
||||||
|
super(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Image_Compress"),com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Open")); |
||||||
|
ExampleFileFilter bothFilter = new ExampleFileFilter( |
||||||
|
new String[]{"jpg", "gif", "png", "bmp"}, |
||||||
|
com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Image_Image_Files")); |
||||||
|
bothFilter.setExtensionListInDescription(true); |
||||||
|
this.addChoosableFileFilter(bothFilter); |
||||||
|
this.setAcceptAllFileFilterUsed(false); |
||||||
|
|
||||||
|
// Create Custom FileView
|
||||||
|
ChooseFileView fileView = new ChooseFileView(); |
||||||
|
fileView.putIcon("jpg", BaseUtils.readIcon("/com/fr/base/images/dialog/file/jpgFile.gif")); |
||||||
|
fileView.putIcon("gif", BaseUtils.readIcon("/com/fr/base/images/dialog/file/gifFile.gif")); |
||||||
|
fileView.putIcon("png", BaseUtils.readIcon("/com/fr/base/images/dialog/file/pngFile.png")); |
||||||
|
fileView.putIcon("bmp", BaseUtils.readIcon("/com/fr/base/images/dialog/file/bmpFile.gif")); |
||||||
|
|
||||||
|
this.setFileView(fileView); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public int showDialog(Component parent, String approveButtonText) { |
||||||
|
return super.showDialog(parent, approveButtonText); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public ActionListener checkAction() { |
||||||
|
return new ActionListener() { |
||||||
|
@Override |
||||||
|
public void actionPerformed(ActionEvent e) { |
||||||
|
DesignerEnvManager.getEnvManager().setImageCompress(isCheckSelected()); |
||||||
|
DesignerEnvManager.getEnvManager().saveXMLFile(); |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public int showDialog(Component parent) { |
||||||
|
return showOpenDialog(parent); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* A convenience implementation of FileFilter that filters out |
||||||
|
* all files except for those type extensions that it knows about. |
||||||
|
* <p/>D:\finereport\develop\code\test\TestCase\WEB-INF\reportlets\TestCase\01903.cpt |
||||||
|
* <p> |
||||||
|
* Extensions are of the type ".foo", which is typically found on |
||||||
|
* Windows and Unix boxes, but not on Macinthosh. Case is ignored. |
||||||
|
* <p/> |
||||||
|
* Example - create a new filter that filerts out all files |
||||||
|
* but gif and jpg image files: |
||||||
|
* <p/> |
||||||
|
* JFileChooser chooser = new JFileChooser(); |
||||||
|
* ExampleFileFilter filter = new ExampleFileFilter( |
||||||
|
* new String{"gif", "jpg"}, "JPEG & GIF Images") |
||||||
|
* chooser.addChoosableFileFilter(filter); |
||||||
|
* chooser.showOpenDialog(this); |
||||||
|
* |
||||||
|
* @author Jeff Dinkins |
||||||
|
* @version 1.12 12/03/01 |
||||||
|
*/ |
||||||
|
class ExampleFileFilter extends FileFilter { |
||||||
|
private Hashtable filters = null; |
||||||
|
private String description = null; |
||||||
|
private String fullDescription = null; |
||||||
|
private boolean useExtensionsInDescription = true; |
||||||
|
|
||||||
|
/** |
||||||
|
* Creates a file filter. If no filters are added, then all |
||||||
|
* files are accepted. |
||||||
|
* |
||||||
|
* @see #addExtension |
||||||
|
*/ |
||||||
|
public ExampleFileFilter() { |
||||||
|
this.filters = new Hashtable(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Creates a file filter that accepts files with the given extension. |
||||||
|
* Example: new ExampleFileFilter("jpg"); |
||||||
|
* |
||||||
|
* @see #addExtension |
||||||
|
*/ |
||||||
|
public ExampleFileFilter(String extension) { |
||||||
|
this(extension, null); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Creates a file filter that accepts the given file type. |
||||||
|
* Example: new ExampleFileFilter("jpg", "JPEG Image Images"); |
||||||
|
* <p/> |
||||||
|
* Note that the "." before the extension is not needed. If |
||||||
|
* provided, it will be ignored. |
||||||
|
* |
||||||
|
* @see #addExtension |
||||||
|
*/ |
||||||
|
public ExampleFileFilter(String extension, String description) { |
||||||
|
this(); |
||||||
|
if (extension != null) addExtension(extension); |
||||||
|
if (description != null) setDescription(description); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Creates a file filter from the given string array. |
||||||
|
* Example: new ExampleFileFilter(String {"gif", "jpg"}); |
||||||
|
* <p/> |
||||||
|
* Note that the "." before the extension is not needed adn |
||||||
|
* will be ignored. |
||||||
|
* |
||||||
|
* @see #addExtension |
||||||
|
*/ |
||||||
|
public ExampleFileFilter(String[] filters) { |
||||||
|
this(filters, null); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Creates a file filter from the given string array and description. |
||||||
|
* Example: new ExampleFileFilter(String {"gif", "jpg"}, "Gif and JPG Images"); |
||||||
|
* <p/> |
||||||
|
* Note that the "." before the extension is not needed and will be ignored. |
||||||
|
* |
||||||
|
* @see #addExtension |
||||||
|
*/ |
||||||
|
public ExampleFileFilter(String[] filters, String description) { |
||||||
|
this(); |
||||||
|
for (int i = 0; i < filters.length; i++) { |
||||||
|
// add filters one by one
|
||||||
|
addExtension(filters[i]); |
||||||
|
} |
||||||
|
if (description != null) setDescription(description); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Return true if this file should be shown in the directory pane, |
||||||
|
* false if it shouldn't. |
||||||
|
* <p/> |
||||||
|
* Files that begin with "." are ignored. |
||||||
|
* |
||||||
|
* @see #getExtension |
||||||
|
*/ |
||||||
|
@Override |
||||||
|
public boolean accept(File f) { |
||||||
|
if (f != null) { |
||||||
|
if (f.isDirectory()) { |
||||||
|
return true; |
||||||
|
} |
||||||
|
String extension = getExtension(f); |
||||||
|
if (extension != null && filters.get(getExtension(f)) != null) { |
||||||
|
return true; |
||||||
|
} |
||||||
|
} |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Return the extension portion of the file's name . |
||||||
|
* |
||||||
|
* @see #getExtension |
||||||
|
* @see FileFilter#accept |
||||||
|
*/ |
||||||
|
public String getExtension(File f) { |
||||||
|
if (f != null) { |
||||||
|
String filename = f.getName(); |
||||||
|
int i = filename.lastIndexOf('.'); |
||||||
|
if (i > 0 && i < filename.length() - 1) { |
||||||
|
return filename.substring(i + 1).toLowerCase(); |
||||||
|
} |
||||||
|
} |
||||||
|
return null; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Adds a filetype "dot" extension to filter against. |
||||||
|
* <p/> |
||||||
|
* For example: the following code will create a filter that filters |
||||||
|
* out all files except those that end in ".jpg" and ".tif": |
||||||
|
* <p/> |
||||||
|
* ExampleFileFilter filter = new ExampleFileFilter(); |
||||||
|
* filter.addExtension("jpg"); |
||||||
|
* filter.addExtension("tif"); |
||||||
|
* <p/> |
||||||
|
* Note that the "." before the extension is not needed and will be ignored. |
||||||
|
*/ |
||||||
|
public void addExtension(String extension) { |
||||||
|
if (filters == null) { |
||||||
|
filters = new Hashtable(5); |
||||||
|
} |
||||||
|
filters.put(extension.toLowerCase(), this); |
||||||
|
fullDescription = null; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* Returns the human readable description of this filter. For |
||||||
|
* example: "JPEG and GIF Image Files (*.jpg, *.gif)" |
||||||
|
* |
||||||
|
* @see FileFilter#getDescription |
||||||
|
*/ |
||||||
|
@Override |
||||||
|
public String getDescription() { |
||||||
|
if (fullDescription == null) { |
||||||
|
if (description == null || isExtensionListInDescription()) { |
||||||
|
fullDescription = description == null ? "(" : description + " ("; |
||||||
|
// build the description from the extension list
|
||||||
|
Enumeration extensions = filters.keys(); |
||||||
|
if (extensions != null) { |
||||||
|
fullDescription += "." + extensions.nextElement(); |
||||||
|
while (extensions.hasMoreElements()) { |
||||||
|
fullDescription += ", ." + extensions.nextElement(); |
||||||
|
} |
||||||
|
} |
||||||
|
fullDescription += ")"; |
||||||
|
} else { |
||||||
|
fullDescription = description; |
||||||
|
} |
||||||
|
} |
||||||
|
return fullDescription; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Sets the human readable description of this filter. For |
||||||
|
* example: filter.setDescription("Gif and JPG Images"); |
||||||
|
*/ |
||||||
|
public void setDescription(String description) { |
||||||
|
this.description = description; |
||||||
|
fullDescription = null; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Determines whether the extension list (.jpg, .gif, etc) should |
||||||
|
* show up in the human readable description. |
||||||
|
* <p/> |
||||||
|
* Only relevent if a description was provided in the constructor |
||||||
|
* or using setDescription(); |
||||||
|
*/ |
||||||
|
public void setExtensionListInDescription(boolean b) { |
||||||
|
useExtensionsInDescription = b; |
||||||
|
fullDescription = null; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns whether the extension list (.jpg, .gif, etc) should |
||||||
|
* show up in the human readable description. |
||||||
|
* <p/> |
||||||
|
* Only relevent if a description was provided in the constructor |
||||||
|
* or using setDescription(); |
||||||
|
*/ |
||||||
|
public boolean isExtensionListInDescription() { |
||||||
|
return useExtensionsInDescription; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,78 @@ |
|||||||
|
package com.fr.design.gui.ilable; |
||||||
|
|
||||||
|
import javax.swing.JLabel; |
||||||
|
import java.awt.Dimension; |
||||||
|
import java.awt.FontMetrics; |
||||||
|
import java.util.ArrayList; |
||||||
|
import java.util.List; |
||||||
|
|
||||||
|
public class UIAutoChangeLineLabel extends JLabel { |
||||||
|
private final String text; |
||||||
|
private final int width; |
||||||
|
|
||||||
|
|
||||||
|
public UIAutoChangeLineLabel(String text, int width) { |
||||||
|
super(text); |
||||||
|
this.text = text; |
||||||
|
this.width = width; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
@Override |
||||||
|
public void doLayout() { |
||||||
|
super.doLayout(); |
||||||
|
this.setText(wrapperHtmlText()); |
||||||
|
} |
||||||
|
|
||||||
|
private String wrapperHtmlText() { |
||||||
|
List<String> stringList = autoChangeLine(this.getWidth()); |
||||||
|
StringBuilder builder = new StringBuilder("<html>"); |
||||||
|
for (String s : stringList) { |
||||||
|
//用THML标签进行拼接,以实现自动换行
|
||||||
|
builder.append(s).append("<br/>"); |
||||||
|
} |
||||||
|
builder.append("</html>"); |
||||||
|
return builder.toString(); |
||||||
|
} |
||||||
|
|
||||||
|
private List<String> autoChangeLine(int width) { |
||||||
|
List<String> result = new ArrayList<>(); |
||||||
|
if (width <= 0) { |
||||||
|
result.add(this.text); |
||||||
|
} else { |
||||||
|
|
||||||
|
char[] chars = this.text.toCharArray(); |
||||||
|
//获取字体计算大小
|
||||||
|
FontMetrics fontMetrics = this.getFontMetrics(this.getFont()); |
||||||
|
int start = 0; |
||||||
|
int len = 0; |
||||||
|
while (start + len < this.text.length()) { |
||||||
|
while (true) { |
||||||
|
len++; |
||||||
|
if (start + len > this.text.length()) |
||||||
|
break; |
||||||
|
if (fontMetrics.charsWidth(chars, start, len) |
||||||
|
> width) { |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
result.add(String.copyValueOf(chars, start, len - 1)); |
||||||
|
start = start + len - 1; |
||||||
|
len = 0; |
||||||
|
} |
||||||
|
if (this.text.length() - start > 0) { |
||||||
|
result.add(String.copyValueOf(chars, start, this.text.length() - start)); |
||||||
|
} |
||||||
|
} |
||||||
|
return result; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
@Override |
||||||
|
public Dimension getPreferredSize() { |
||||||
|
Dimension preferredSize = super.getPreferredSize(); |
||||||
|
List<String> stringList = autoChangeLine(width); |
||||||
|
FontMetrics fontMetrics = this.getFontMetrics(this.getFont()); |
||||||
|
return new Dimension(preferredSize.width, fontMetrics.getHeight() * stringList.size()); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,924 @@ |
|||||||
|
package com.fr.design.javascript; |
||||||
|
|
||||||
|
import com.fr.base.svg.IconUtils; |
||||||
|
import com.fr.design.border.UIRoundedBorder; |
||||||
|
import com.fr.design.constants.UIConstants; |
||||||
|
import com.fr.design.gui.autocomplete.AutoCompleteExtraRefreshComponent; |
||||||
|
import com.fr.design.gui.autocomplete.BasicCompletion; |
||||||
|
import com.fr.design.gui.autocomplete.CompletionCellRenderer; |
||||||
|
import com.fr.design.gui.autocomplete.CompletionProvider; |
||||||
|
import com.fr.design.gui.autocomplete.DefaultCompletionProvider; |
||||||
|
import com.fr.design.gui.autocomplete.JSImplPaneAutoCompletion; |
||||||
|
import com.fr.design.gui.ibutton.UIButton; |
||||||
|
import com.fr.design.gui.icontainer.UIScrollPane; |
||||||
|
import com.fr.design.gui.ilable.UILabel; |
||||||
|
import com.fr.design.gui.itextarea.UITextArea; |
||||||
|
import com.fr.design.gui.itextfield.PlaceholderTextField; |
||||||
|
import com.fr.design.i18n.Toolkit; |
||||||
|
import com.fr.design.javascript.jsapi.JSAPITreeHelper; |
||||||
|
import com.fr.design.javascript.jsapi.JSAPIUserObject; |
||||||
|
import com.fr.design.layout.FRGUIPaneFactory; |
||||||
|
import com.fr.general.CloudCenter; |
||||||
|
import com.fr.general.ComparatorUtils; |
||||||
|
import com.fr.general.http.HttpToolbox; |
||||||
|
import com.fr.json.JSONArray; |
||||||
|
import com.fr.json.JSONException; |
||||||
|
import com.fr.json.JSONObject; |
||||||
|
import com.fr.log.FineLoggerFactory; |
||||||
|
import com.fr.stable.StringUtils; |
||||||
|
import java.awt.BorderLayout; |
||||||
|
import java.awt.CardLayout; |
||||||
|
import java.awt.Color; |
||||||
|
import java.awt.Component; |
||||||
|
import java.awt.Cursor; |
||||||
|
import java.awt.Desktop; |
||||||
|
import java.awt.Dimension; |
||||||
|
import java.awt.event.ActionEvent; |
||||||
|
import java.awt.event.ActionListener; |
||||||
|
import java.awt.event.FocusAdapter; |
||||||
|
import java.awt.event.FocusEvent; |
||||||
|
import java.awt.event.KeyAdapter; |
||||||
|
import java.awt.event.KeyEvent; |
||||||
|
import java.awt.event.KeyListener; |
||||||
|
import java.awt.event.MouseAdapter; |
||||||
|
import java.awt.event.MouseEvent; |
||||||
|
import java.awt.event.MouseListener; |
||||||
|
import java.io.IOException; |
||||||
|
import java.net.URI; |
||||||
|
import java.net.URISyntaxException; |
||||||
|
import java.util.ArrayList; |
||||||
|
import java.util.Collections; |
||||||
|
import java.util.Comparator; |
||||||
|
import java.util.List; |
||||||
|
import javax.swing.BorderFactory; |
||||||
|
import javax.swing.DefaultListCellRenderer; |
||||||
|
import javax.swing.DefaultListModel; |
||||||
|
import javax.swing.JComponent; |
||||||
|
import javax.swing.JList; |
||||||
|
import javax.swing.JPanel; |
||||||
|
import javax.swing.JPopupMenu; |
||||||
|
import javax.swing.JScrollPane; |
||||||
|
import javax.swing.JTree; |
||||||
|
import javax.swing.event.ListSelectionEvent; |
||||||
|
import javax.swing.event.ListSelectionListener; |
||||||
|
import javax.swing.event.TreeSelectionEvent; |
||||||
|
import javax.swing.event.TreeSelectionListener; |
||||||
|
import javax.swing.tree.DefaultMutableTreeNode; |
||||||
|
import javax.swing.tree.DefaultTreeCellRenderer; |
||||||
|
import javax.swing.tree.DefaultTreeModel; |
||||||
|
import javax.swing.tree.TreeNode; |
||||||
|
import javax.swing.tree.TreePath; |
||||||
|
|
||||||
|
public class JSContentWithDescriptionPane extends JSContentPane implements KeyListener { |
||||||
|
|
||||||
|
//搜索关键词输入框
|
||||||
|
private PlaceholderTextField keyWordTextField = new PlaceholderTextField(16); |
||||||
|
//搜索出的提示列表
|
||||||
|
private JList tipsList; |
||||||
|
private DefaultListModel tipsListModel = new DefaultListModel(); |
||||||
|
|
||||||
|
private JList interfaceNameList; |
||||||
|
private DefaultListModel interfaceNameModel; |
||||||
|
|
||||||
|
private JTree moduleTree; |
||||||
|
|
||||||
|
private DefaultCompletionProvider completionProvider; |
||||||
|
private JSImplPaneAutoCompletion autoCompletion; |
||||||
|
|
||||||
|
//函数说明文本框
|
||||||
|
private UITextArea descriptionTextArea; |
||||||
|
|
||||||
|
private JPopupMenu popupMenu; |
||||||
|
|
||||||
|
private InterfaceAndDescriptionPanel interfaceAndDescriptionPanel; |
||||||
|
private JList helpDOCList; |
||||||
|
|
||||||
|
private int ifHasBeenWriten = 0; |
||||||
|
private int currentPosition = 0; |
||||||
|
private int beginPosition = 0; |
||||||
|
private int insertPosition = 0; |
||||||
|
private static final String SEPARATOR = "_"; |
||||||
|
|
||||||
|
private static final int KEY_10 = 10; |
||||||
|
//上下左右
|
||||||
|
private static final int KEY_37 = 37; |
||||||
|
private static final int KEY_38 = 38; |
||||||
|
private static final int KEY_39 = 39; |
||||||
|
private static final int KEY_40 = 40; |
||||||
|
|
||||||
|
private static final String URL_FOR_TEST_NETWORK = "https://www.baidu.com"; |
||||||
|
|
||||||
|
private static final String DOCUMENT_SEARCH_URL = "https://help.fanruan.com/finereport/api-helpdoc-title-"; |
||||||
|
|
||||||
|
private String currentValue; |
||||||
|
|
||||||
|
private static CardLayout card; |
||||||
|
|
||||||
|
private static final String RELOAD_CARD = "reloadCard"; |
||||||
|
private static final String DOC_LIST_CARD = "docListCard"; |
||||||
|
|
||||||
|
public JSContentWithDescriptionPane(String[] args) { |
||||||
|
this.setLayout(new BorderLayout()); |
||||||
|
//===============================
|
||||||
|
this.initFunctionTitle(args); |
||||||
|
JPanel jsParaAndSearchPane = new JPanel(new BorderLayout()); |
||||||
|
|
||||||
|
//js函数声明面板
|
||||||
|
JPanel jsParaPane = createJSParaPane(); |
||||||
|
|
||||||
|
jsParaPane.setPreferredSize(new Dimension(650, 80)); |
||||||
|
//右上角的搜索提示面板
|
||||||
|
JPanel tipsPane = createTipsPane(); |
||||||
|
|
||||||
|
jsParaAndSearchPane.add(jsParaPane, BorderLayout.CENTER); |
||||||
|
jsParaAndSearchPane.add(tipsPane, BorderLayout.EAST); |
||||||
|
|
||||||
|
initPopTips(); |
||||||
|
|
||||||
|
//js文本编辑面板
|
||||||
|
UIScrollPane contentTextAreaPanel = createContentTextAreaPanel(); |
||||||
|
initContextAreaListener(); |
||||||
|
|
||||||
|
contentTextAreaPanel.setPreferredSize(new Dimension(850, 250)); |
||||||
|
//js函数结束标签
|
||||||
|
UILabel endBracketsLabel = new UILabel(); |
||||||
|
endBracketsLabel.setText("}"); |
||||||
|
|
||||||
|
//结尾括号和复用函数按钮面板
|
||||||
|
JPanel endBracketsPanel = FRGUIPaneFactory.createBorderLayout_S_Pane(); |
||||||
|
endBracketsPanel.add(endBracketsLabel, BorderLayout.WEST); |
||||||
|
|
||||||
|
JPanel northPanel = FRGUIPaneFactory.createBorderLayout_S_Pane(); |
||||||
|
northPanel.add(jsParaAndSearchPane, BorderLayout.NORTH); |
||||||
|
|
||||||
|
northPanel.add(contentTextAreaPanel, BorderLayout.CENTER); |
||||||
|
northPanel.add(endBracketsPanel, BorderLayout.SOUTH); |
||||||
|
|
||||||
|
//主编辑框,也就是面板的正中间部分
|
||||||
|
this.add(northPanel, BorderLayout.CENTER); |
||||||
|
|
||||||
|
//函数分类和函数说明面板==================================
|
||||||
|
JPanel functionNameAndDescriptionPanel = createInterfaceAndDescriptionPanel(); |
||||||
|
functionNameAndDescriptionPanel.setPreferredSize(new Dimension(880, 220)); |
||||||
|
|
||||||
|
this.add(functionNameAndDescriptionPanel, BorderLayout.SOUTH); |
||||||
|
} |
||||||
|
|
||||||
|
public void populate(String js) { |
||||||
|
contentTextArea.setText(js); |
||||||
|
ifHasBeenWriten = 1; |
||||||
|
currentPosition = contentTextArea.getCaretPosition(); |
||||||
|
beginPosition = getBeginPosition(); |
||||||
|
insertPosition = beginPosition; |
||||||
|
} |
||||||
|
|
||||||
|
private void initContextAreaListener() { |
||||||
|
contentTextArea.addKeyListener(new KeyAdapter() { |
||||||
|
@Override |
||||||
|
public void keyTyped(KeyEvent e) { |
||||||
|
if ((e.getKeyChar() >= 'A' && e.getKeyChar() <= 'z') || e.getKeyChar() == '_') { |
||||||
|
if (autoCompletion != null) { |
||||||
|
autoCompletion.doCompletion(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void keyReleased(KeyEvent e) { |
||||||
|
contentTextArea.setForeground(Color.black); |
||||||
|
} |
||||||
|
}); |
||||||
|
contentTextArea.addKeyListener(this); |
||||||
|
contentTextArea.addFocusListener(new FocusAdapter() { |
||||||
|
@Override |
||||||
|
public void focusGained(FocusEvent e) { |
||||||
|
if (autoCompletion == null) { |
||||||
|
installAutoCompletion(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void focusLost(FocusEvent e) { |
||||||
|
uninstallAutoCompletion(); |
||||||
|
} |
||||||
|
}); |
||||||
|
contentTextArea.addMouseListener(new MouseAdapter() { |
||||||
|
@Override |
||||||
|
public void mousePressed(MouseEvent e) { |
||||||
|
insertPosition = contentTextArea.getCaretPosition(); |
||||||
|
if (ifHasBeenWriten == 0) { |
||||||
|
contentTextArea.setText(StringUtils.EMPTY); |
||||||
|
ifHasBeenWriten = 1; |
||||||
|
contentTextArea.setForeground(Color.black); |
||||||
|
insertPosition = 0; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void mouseReleased(MouseEvent e) { |
||||||
|
currentPosition = contentTextArea.getCaretPosition(); |
||||||
|
if (currentPosition == insertPosition) { |
||||||
|
beginPosition = getBeginPosition(); |
||||||
|
insertPosition = beginPosition; |
||||||
|
} |
||||||
|
} |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void keyTyped(KeyEvent e) { |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void keyPressed(KeyEvent e) { |
||||||
|
if (ifHasBeenWriten == 0) { |
||||||
|
this.contentTextArea.setText(StringUtils.EMPTY); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void keyReleased(KeyEvent e) { |
||||||
|
int key = e.getKeyCode(); |
||||||
|
if (key == KEY_38 || key == KEY_40 || key == KEY_37 || key == KEY_39 || key == KEY_10) //如果是删除符号 ,为了可读性 没有和其他按键的程序相融合
|
||||||
|
{ |
||||||
|
currentPosition = contentTextArea.getCaretPosition(); |
||||||
|
insertPosition = currentPosition; |
||||||
|
beginPosition = getBeginPosition(); |
||||||
|
} else { |
||||||
|
if (contentTextArea.getText().trim().length() == 0) { |
||||||
|
insertPosition = 0; |
||||||
|
} else { |
||||||
|
contentTextArea.setForeground(Color.black); |
||||||
|
currentPosition = contentTextArea.getCaretPosition(); |
||||||
|
beginPosition = getBeginPosition(); |
||||||
|
insertPosition = beginPosition; |
||||||
|
ifHasBeenWriten = 1; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private int getBeginPosition() { |
||||||
|
int i = currentPosition; |
||||||
|
String textArea = contentTextArea.getText(); |
||||||
|
for (; i > 0; i--) { |
||||||
|
String tested = textArea.substring(i - 1, i).toUpperCase(); |
||||||
|
char[] testedChar = tested.toCharArray(); |
||||||
|
if (isChar(testedChar[0]) || isNum(testedChar[0])) { |
||||||
|
continue; |
||||||
|
} else { |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
return i; |
||||||
|
} |
||||||
|
|
||||||
|
private static boolean isNum(char tested) { |
||||||
|
return tested >= '0' && tested <= '9'; |
||||||
|
} |
||||||
|
|
||||||
|
private boolean isChar(char tested) { |
||||||
|
return tested >= 'A' && tested <= 'Z' || tested >= 'a' && tested < 'z'; |
||||||
|
} |
||||||
|
|
||||||
|
public class InterfaceAndDescriptionPanel extends JPanel implements AutoCompleteExtraRefreshComponent { |
||||||
|
@Override |
||||||
|
public void refresh(String replacementText) { |
||||||
|
fixInterfaceNameList(replacementText); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private void fixInterfaceNameList(String interfaceName) { |
||||||
|
DefaultTreeModel defaultTreeModel = (DefaultTreeModel) moduleTree.getModel(); |
||||||
|
TreeNode root = (TreeNode) defaultTreeModel.getRoot(); |
||||||
|
String directCategory = JSAPITreeHelper.getDirectCategory(interfaceName); |
||||||
|
if (directCategory == null) { |
||||||
|
return; |
||||||
|
} |
||||||
|
setModuleTreeSelection(root, directCategory, defaultTreeModel); |
||||||
|
interfaceNameModel = (DefaultListModel) interfaceNameList.getModel(); |
||||||
|
interfaceNameModel.clear(); |
||||||
|
List<String> interfaceNames = JSAPITreeHelper.getNames(directCategory); |
||||||
|
int index = 0; |
||||||
|
for (int i = 0; i < interfaceNames.size(); i++) { |
||||||
|
interfaceNameModel.addElement(interfaceNames.get(i)); |
||||||
|
if (StringUtils.equals(interfaceNames.get(i), interfaceName)) { |
||||||
|
index = i; |
||||||
|
} |
||||||
|
} |
||||||
|
interfaceNameList.setSelectedIndex(index); |
||||||
|
interfaceNameList.ensureIndexIsVisible(index); |
||||||
|
} |
||||||
|
|
||||||
|
private boolean setModuleTreeSelection(TreeNode node, String directCategory, DefaultTreeModel treeModel) { |
||||||
|
|
||||||
|
DefaultMutableTreeNode defaultMutableTreeNode = (DefaultMutableTreeNode) node; |
||||||
|
Object userObject = defaultMutableTreeNode.getUserObject(); |
||||||
|
if (userObject instanceof JSAPIUserObject) { |
||||||
|
String value = ((JSAPIUserObject) userObject).getValue(); |
||||||
|
if (StringUtils.equals(value, directCategory)) { |
||||||
|
moduleTree.setSelectionPath(new TreePath(treeModel.getPathToRoot(node))); |
||||||
|
moduleTree.scrollPathToVisible(moduleTree.getSelectionPath()); |
||||||
|
return true; |
||||||
|
} |
||||||
|
} |
||||||
|
for (int i = 0; i < node.getChildCount(); i++) { |
||||||
|
if (setModuleTreeSelection(node.getChildAt(i), directCategory, treeModel)) { |
||||||
|
return true; |
||||||
|
} |
||||||
|
} |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
private JPanel createInterfaceAndDescriptionPanel() { |
||||||
|
interfaceAndDescriptionPanel = new InterfaceAndDescriptionPanel(); |
||||||
|
interfaceAndDescriptionPanel.setLayout(new BorderLayout(4, 4)); |
||||||
|
JPanel interfacePanel = new JPanel(new BorderLayout(4, 4)); |
||||||
|
interfaceAndDescriptionPanel.add(interfacePanel, BorderLayout.WEST); |
||||||
|
JPanel descriptionAndDocumentPanel = new JPanel(new BorderLayout(4, 4)); |
||||||
|
//函数说明和帮助文档框
|
||||||
|
initDescriptionArea(descriptionAndDocumentPanel); |
||||||
|
|
||||||
|
//模块和接口面板
|
||||||
|
initInterfaceModuleTree(interfacePanel); |
||||||
|
initInterfaceNameList(interfacePanel); |
||||||
|
|
||||||
|
initHelpDocumentPane(descriptionAndDocumentPanel); |
||||||
|
|
||||||
|
interfaceAndDescriptionPanel.add(descriptionAndDocumentPanel, BorderLayout.CENTER); |
||||||
|
return interfaceAndDescriptionPanel; |
||||||
|
} |
||||||
|
|
||||||
|
private void doHelpDocumentSearch() { |
||||||
|
Object value = interfaceNameList.getSelectedValue(); |
||||||
|
if (value != null) { |
||||||
|
String url = CloudCenter.getInstance().acquireUrlByKind("af.doc_search", DOCUMENT_SEARCH_URL) + value.toString(); |
||||||
|
try { |
||||||
|
String result = HttpToolbox.get(url); |
||||||
|
JSONObject jsonObject = new JSONObject(result); |
||||||
|
JSONArray jsonArray = jsonObject.optJSONArray("list"); |
||||||
|
if (jsonArray != null) { |
||||||
|
DefaultListModel helpDOCModel = (DefaultListModel) helpDOCList.getModel(); |
||||||
|
helpDOCModel.clear(); |
||||||
|
for (int i = 0; i < jsonArray.length(); i++) { |
||||||
|
JSONObject resultJSONObject = jsonArray.optJSONObject(i); |
||||||
|
String docURL = resultJSONObject.optString("url"); |
||||||
|
String name = resultJSONObject.optString("title").trim(); |
||||||
|
HelpDocument helpDocument = new HelpDocument(docURL, name); |
||||||
|
helpDOCModel.addElement(helpDocument); |
||||||
|
} |
||||||
|
} |
||||||
|
} catch (JSONException e) { |
||||||
|
FineLoggerFactory.getLogger().debug(e.getMessage(), e); |
||||||
|
} catch (Exception e) { |
||||||
|
FineLoggerFactory.getLogger().error(e.getMessage(), e); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private void initHelpDocumentPane(JPanel descriptionAndDocumentPanel) { |
||||||
|
card = new CardLayout(); |
||||||
|
JPanel mainPane = new JPanel(card); |
||||||
|
initHelpDocumentList(); |
||||||
|
UIScrollPane helpDOCScrollPane = new UIScrollPane(helpDOCList); |
||||||
|
helpDOCScrollPane.setPreferredSize(new Dimension(200, 200)); |
||||||
|
helpDOCScrollPane.setBorder(null); |
||||||
|
mainPane.add(helpDOCScrollPane, DOC_LIST_CARD); |
||||||
|
|
||||||
|
UILabel imageLabel = new UILabel(); |
||||||
|
imageLabel.setIcon(IconUtils.readIcon("com/fr/design/javascript/jsapi/images/connectFailed.svg")); |
||||||
|
imageLabel.setPreferredSize(new Dimension(180, 65)); |
||||||
|
JPanel imagePane = FRGUIPaneFactory.createBorderLayout_S_Pane(); |
||||||
|
imagePane.setBorder(BorderFactory.createEmptyBorder(0, 42, 0, 0)); |
||||||
|
imagePane.add(imageLabel); |
||||||
|
imagePane.setBackground(Color.WHITE); |
||||||
|
|
||||||
|
UILabel failedLabel = new UILabel(Toolkit.i18nText("Fine-Design_Net_Connect_Failed"), 0); |
||||||
|
failedLabel.setPreferredSize(new Dimension(180, 20)); |
||||||
|
UILabel reloadLabel = new UILabel(Toolkit.i18nText("Fine-Design_Basic_Reload"), 0); |
||||||
|
reloadLabel.setCursor(new Cursor(Cursor.HAND_CURSOR)); |
||||||
|
reloadLabel.setPreferredSize(new Dimension(180, 20)); |
||||||
|
reloadLabel.setForeground(Color.blue); |
||||||
|
JPanel labelPane = FRGUIPaneFactory.createVerticalFlowLayout_Pane(true, 0, 0, 0); |
||||||
|
|
||||||
|
labelPane.setBorder(BorderFactory.createEmptyBorder(35, 45, 0, 0)); |
||||||
|
labelPane.setBackground(Color.WHITE); |
||||||
|
labelPane.add(imagePane); |
||||||
|
labelPane.add(failedLabel); |
||||||
|
labelPane.add(reloadLabel); |
||||||
|
JPanel containerPanel = FRGUIPaneFactory.createBorderLayout_S_Pane(); |
||||||
|
containerPanel.add(labelPane, BorderLayout.CENTER); |
||||||
|
reloadLabel.addMouseListener(new MouseAdapter() { |
||||||
|
@Override |
||||||
|
public void mouseClicked(MouseEvent e) { |
||||||
|
if (isNetworkOk()) { |
||||||
|
doHelpDocumentSearch(); |
||||||
|
card.show(mainPane, DOC_LIST_CARD); |
||||||
|
} |
||||||
|
} |
||||||
|
}); |
||||||
|
mainPane.add(containerPanel, RELOAD_CARD); |
||||||
|
if (isNetworkOk()) { |
||||||
|
doHelpDocumentSearch(); |
||||||
|
card.show(mainPane, DOC_LIST_CARD); |
||||||
|
} else { |
||||||
|
card.show(mainPane, RELOAD_CARD); |
||||||
|
} |
||||||
|
descriptionAndDocumentPanel.add(this.createNamePane(Toolkit.i18nText("Fine-Design_Relevant_Cases"), mainPane), BorderLayout.EAST); |
||||||
|
} |
||||||
|
|
||||||
|
private void initHelpDocumentList() { |
||||||
|
helpDOCList = new JList(new DefaultListModel()); |
||||||
|
initHelpDOCListRender(); |
||||||
|
initHelpDOCListListener(); |
||||||
|
} |
||||||
|
|
||||||
|
private void initHelpDOCListListener() { |
||||||
|
helpDOCList.addMouseListener(new MouseAdapter() { |
||||||
|
@Override |
||||||
|
public void mouseClicked(MouseEvent e) { |
||||||
|
if (e.getClickCount() == 2) { |
||||||
|
Object value = helpDOCList.getSelectedValue(); |
||||||
|
if (value instanceof HelpDocument) { |
||||||
|
String url = ((HelpDocument) value).getDocumentUrl(); |
||||||
|
browse(url); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
private void browse(String url){ |
||||||
|
try { |
||||||
|
Desktop.getDesktop().browse(new URI(url)); |
||||||
|
} catch (IOException | URISyntaxException ex) { |
||||||
|
FineLoggerFactory.getLogger().error(ex.getMessage(), ex); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private void initHelpDOCListRender() { |
||||||
|
helpDOCList.setCellRenderer(new DefaultListCellRenderer() { |
||||||
|
@Override |
||||||
|
public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { |
||||||
|
super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus); |
||||||
|
if (value instanceof HelpDocument) { |
||||||
|
this.setText(((HelpDocument) value).getName()); |
||||||
|
this.setForeground(Color.BLUE); |
||||||
|
} |
||||||
|
return this; |
||||||
|
} |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
private static boolean isNetworkOk() { |
||||||
|
try { |
||||||
|
HttpToolbox.get(URL_FOR_TEST_NETWORK); |
||||||
|
return true; |
||||||
|
} catch (Exception ignore) { |
||||||
|
// 网络异常
|
||||||
|
return false; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private static class HelpDocument { |
||||||
|
private String documentUrl; |
||||||
|
|
||||||
|
|
||||||
|
private String name; |
||||||
|
|
||||||
|
public HelpDocument(String documentUrl, String name) { |
||||||
|
this.documentUrl = documentUrl; |
||||||
|
this.name = name; |
||||||
|
} |
||||||
|
|
||||||
|
public String getDocumentUrl() { |
||||||
|
return documentUrl; |
||||||
|
} |
||||||
|
|
||||||
|
public String getName() { |
||||||
|
return name; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private void initDescriptionArea(JPanel descriptionPanel) { |
||||||
|
descriptionTextArea = new UITextArea(); |
||||||
|
UIScrollPane descriptionScrollPane = new UIScrollPane(descriptionTextArea); |
||||||
|
descriptionScrollPane.setPreferredSize(new Dimension(300, 200)); |
||||||
|
descriptionPanel.add(this.createNamePane(Toolkit.i18nText("Fine-Design_Interface_Description"), descriptionScrollPane), BorderLayout.CENTER); |
||||||
|
descriptionTextArea.setBackground(Color.white); |
||||||
|
descriptionTextArea.setLineWrap(true); |
||||||
|
descriptionTextArea.setWrapStyleWord(true); |
||||||
|
descriptionTextArea.setEditable(false); |
||||||
|
} |
||||||
|
|
||||||
|
private void installAutoCompletion() { |
||||||
|
CompletionProvider provider = createCompletionProvider(); |
||||||
|
autoCompletion = new JSImplPaneAutoCompletion(provider); |
||||||
|
autoCompletion.setListCellRenderer(new CompletionCellRenderer()); |
||||||
|
autoCompletion.install(contentTextArea); |
||||||
|
autoCompletion.installExtraRefreshComponent(interfaceAndDescriptionPanel); |
||||||
|
} |
||||||
|
|
||||||
|
private void uninstallAutoCompletion() { |
||||||
|
if (autoCompletion != null) { |
||||||
|
autoCompletion.uninstall(); |
||||||
|
autoCompletion = null; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private CompletionProvider createCompletionProvider() { |
||||||
|
if (completionProvider == null) { |
||||||
|
completionProvider = new DefaultCompletionProvider(); |
||||||
|
for (String name : JSAPITreeHelper.getAllNames()) { |
||||||
|
completionProvider.addCompletion(new BasicCompletion(completionProvider, name)); |
||||||
|
} |
||||||
|
} |
||||||
|
return completionProvider; |
||||||
|
} |
||||||
|
|
||||||
|
private void initInterfaceModuleTree(JPanel interfacePanel) { |
||||||
|
moduleTree = new JTree(); |
||||||
|
UIScrollPane moduleTreePane = new UIScrollPane(moduleTree); |
||||||
|
moduleTreePane.setBorder(new UIRoundedBorder(UIConstants.LINE_COLOR, 1, UIConstants.ARC)); |
||||||
|
interfacePanel.add(this.createNamePane(Toolkit.i18nText("Fine-Design_Module"), moduleTreePane), BorderLayout.WEST); |
||||||
|
moduleTreePane.setPreferredSize(new Dimension(180, 200)); |
||||||
|
|
||||||
|
moduleTree.setRootVisible(false); |
||||||
|
moduleTree.setShowsRootHandles(true); |
||||||
|
moduleTree.setCellRenderer(moduleTreeCellRender); |
||||||
|
DefaultTreeModel moduleTreeModel = (DefaultTreeModel) moduleTree.getModel(); |
||||||
|
DefaultMutableTreeNode rootNode = (DefaultMutableTreeNode) moduleTreeModel.getRoot(); |
||||||
|
rootNode.removeAllChildren(); |
||||||
|
|
||||||
|
JSAPITreeHelper.createJSAPITree(rootNode); |
||||||
|
moduleTreeModel.reload(); |
||||||
|
|
||||||
|
initModuleTreeSelectionListener(); |
||||||
|
} |
||||||
|
|
||||||
|
private void initModuleTreeSelectionListener() { |
||||||
|
moduleTree.addTreeSelectionListener(new TreeSelectionListener() { |
||||||
|
@Override |
||||||
|
public void valueChanged(TreeSelectionEvent e) { |
||||||
|
DefaultMutableTreeNode selectedTreeNode = (DefaultMutableTreeNode) moduleTree.getLastSelectedPathComponent(); |
||||||
|
Object selectedValue = selectedTreeNode.getUserObject(); |
||||||
|
if (null == selectedValue) { |
||||||
|
return; |
||||||
|
} |
||||||
|
if (selectedValue instanceof JSAPIUserObject) { |
||||||
|
interfaceNameModel = (DefaultListModel) interfaceNameList.getModel(); |
||||||
|
interfaceNameModel.clear(); |
||||||
|
String text = ((JSAPIUserObject) selectedValue).getValue(); |
||||||
|
List<String> allInterfaceNames = JSAPITreeHelper.getNames(text); |
||||||
|
for (String interfaceName : allInterfaceNames) { |
||||||
|
interfaceNameModel.addElement(interfaceName); |
||||||
|
} |
||||||
|
if (interfaceNameModel.size() > 0) { |
||||||
|
interfaceNameList.setSelectedIndex(0); |
||||||
|
setDescription(interfaceNameList.getSelectedValue().toString()); |
||||||
|
interfaceNameList.ensureIndexIsVisible(0); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
private DefaultTreeCellRenderer moduleTreeCellRender = new DefaultTreeCellRenderer() { |
||||||
|
public Component getTreeCellRendererComponent(JTree tree, |
||||||
|
Object value, boolean selected, boolean expanded, |
||||||
|
boolean leaf, int row, boolean hasFocus) { |
||||||
|
super.getTreeCellRendererComponent(tree, value, selected, |
||||||
|
expanded, leaf, row, hasFocus); |
||||||
|
|
||||||
|
DefaultMutableTreeNode treeNode = (DefaultMutableTreeNode) value; |
||||||
|
Object userObj = treeNode.getUserObject(); |
||||||
|
if (userObj instanceof JSAPIUserObject) { |
||||||
|
this.setText(((JSAPIUserObject) userObj).getDisplayText()); |
||||||
|
this.setIcon(null); |
||||||
|
} |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
}; |
||||||
|
|
||||||
|
|
||||||
|
private void initInterfaceNameList(JPanel interfacePanel) { |
||||||
|
interfaceNameList = new JList(new DefaultListModel()); |
||||||
|
UIScrollPane interfaceNamePanelScrollPane = new UIScrollPane(interfaceNameList); |
||||||
|
interfaceNamePanelScrollPane.setPreferredSize(new Dimension(180, 200)); |
||||||
|
interfacePanel.add( |
||||||
|
this.createNamePane(Toolkit.i18nText("Fine-Design_Interface") + ":", interfaceNamePanelScrollPane), |
||||||
|
BorderLayout.CENTER); |
||||||
|
|
||||||
|
interfaceNamePanelScrollPane.setBorder(new UIRoundedBorder(UIConstants.LINE_COLOR, 1, UIConstants.ARC)); |
||||||
|
initInterfaceNameModule(); |
||||||
|
initInterfaceNameListSelectionListener(); |
||||||
|
initInterfaceNameListMouseListener(); |
||||||
|
} |
||||||
|
|
||||||
|
private void initInterfaceNameModule() { |
||||||
|
DefaultTreeModel defaultTreeModel = (DefaultTreeModel) moduleTree.getModel(); |
||||||
|
TreeNode root = (TreeNode) defaultTreeModel.getRoot(); |
||||||
|
while (root.getChildCount() > 0){ |
||||||
|
root = root.getChildAt(0); |
||||||
|
} |
||||||
|
moduleTree.setSelectionPath(new TreePath(defaultTreeModel.getPathToRoot(root))); |
||||||
|
} |
||||||
|
|
||||||
|
private void setDescription(String interfaceName) { |
||||||
|
StringBuilder il8Key = new StringBuilder(); |
||||||
|
moduleTree.getSelectionPath().getPath(); |
||||||
|
Object obj = moduleTree.getSelectionPath().getPath()[moduleTree.getSelectionPath().getPath().length - 1]; |
||||||
|
Object userObject = ((DefaultMutableTreeNode) obj).getUserObject(); |
||||||
|
if (userObject instanceof JSAPIUserObject) { |
||||||
|
il8Key.append(JSAPITreeHelper.getDirectCategory(interfaceName)); |
||||||
|
} |
||||||
|
interfaceName = interfaceName.toUpperCase(); |
||||||
|
if (!interfaceName.startsWith(SEPARATOR)) { |
||||||
|
interfaceName = SEPARATOR + interfaceName; |
||||||
|
} |
||||||
|
il8Key.append(interfaceName); |
||||||
|
descriptionTextArea.setText(Toolkit.i18nText(il8Key.toString())); |
||||||
|
descriptionTextArea.moveCaretPosition(0); |
||||||
|
} |
||||||
|
|
||||||
|
private void initInterfaceNameListSelectionListener() { |
||||||
|
interfaceNameList.addListSelectionListener(new ListSelectionListener() { |
||||||
|
|
||||||
|
public void valueChanged(ListSelectionEvent evt) { |
||||||
|
Object selectedValue = interfaceNameList.getSelectedValue(); |
||||||
|
if (selectedValue == null) { |
||||||
|
return; |
||||||
|
} |
||||||
|
String interfaceName = selectedValue.toString(); |
||||||
|
if (!StringUtils.equals(interfaceName, currentValue)) { |
||||||
|
setDescription(interfaceName); |
||||||
|
doHelpDocumentSearch(); |
||||||
|
currentValue = interfaceName; |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
private void initInterfaceNameListMouseListener() { |
||||||
|
interfaceNameList.addMouseListener(new MouseAdapter() { |
||||||
|
public void mouseClicked(MouseEvent evt) { |
||||||
|
if (evt.getClickCount() >= 2) { |
||||||
|
Object selectedValue = interfaceNameList.getSelectedValue(); |
||||||
|
String interfaceName = selectedValue.toString(); |
||||||
|
applyText(interfaceName); |
||||||
|
} |
||||||
|
} |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
private void applyText(String text) { |
||||||
|
if (text == null || text.length() <= 0) { |
||||||
|
return; |
||||||
|
} |
||||||
|
if (ifHasBeenWriten == 0) { |
||||||
|
contentTextArea.setForeground(Color.black); |
||||||
|
contentTextArea.setText(StringUtils.EMPTY); |
||||||
|
ifHasBeenWriten = 1; |
||||||
|
insertPosition = 0; |
||||||
|
} |
||||||
|
String textAll = contentTextArea.getText(); |
||||||
|
currentPosition = contentTextArea.getCaretPosition(); |
||||||
|
int insert = 0; |
||||||
|
int current = 0; |
||||||
|
if (insertPosition <= currentPosition) { |
||||||
|
insert = insertPosition; |
||||||
|
current = currentPosition; |
||||||
|
} else { |
||||||
|
insert = currentPosition; |
||||||
|
current = insertPosition; |
||||||
|
} |
||||||
|
String beforeIndexOfInsertString = textAll.substring(0, insert); |
||||||
|
String afterIndexofInsertString = textAll.substring(current); |
||||||
|
contentTextArea.setText(beforeIndexOfInsertString + text + afterIndexofInsertString); |
||||||
|
contentTextArea.getText(); |
||||||
|
contentTextArea.requestFocus(); |
||||||
|
insertPosition = contentTextArea.getCaretPosition(); |
||||||
|
} |
||||||
|
|
||||||
|
private JPanel createNamePane(String name, JComponent comp) { |
||||||
|
JPanel namePane = new JPanel(new BorderLayout(4, 4)); |
||||||
|
namePane.add(new UILabel(name), BorderLayout.NORTH); |
||||||
|
namePane.add(comp, BorderLayout.CENTER); |
||||||
|
return namePane; |
||||||
|
} |
||||||
|
|
||||||
|
private JPanel createTipsPane() { |
||||||
|
JPanel tipsPane = FRGUIPaneFactory.createBorderLayout_S_Pane(); |
||||||
|
tipsPane.setLayout(new BorderLayout(4, 4)); |
||||||
|
tipsPane.setBorder(BorderFactory.createEmptyBorder(30, 2, 0, 0)); |
||||||
|
JPanel searchPane = FRGUIPaneFactory.createBorderLayout_S_Pane(); |
||||||
|
searchPane.setLayout(new BorderLayout(4, 4)); |
||||||
|
keyWordTextField.setPlaceholder(Toolkit.i18nText("Fine-Design_Search_Interface")); |
||||||
|
searchPane.add(keyWordTextField, BorderLayout.CENTER); |
||||||
|
|
||||||
|
//搜索按钮
|
||||||
|
UIButton searchButton = new UIButton(Toolkit.i18nText("Fine-Design_Basic_Search")); |
||||||
|
searchButton.addActionListener(new ActionListener() { |
||||||
|
@Override |
||||||
|
public void actionPerformed(ActionEvent e) { |
||||||
|
String toFind = keyWordTextField.getText(); |
||||||
|
search(toFind); |
||||||
|
popTips(); |
||||||
|
tipsList.requestFocusInWindow(); |
||||||
|
} |
||||||
|
}); |
||||||
|
|
||||||
|
keyWordTextField.addKeyListener(new KeyAdapter() { |
||||||
|
@Override |
||||||
|
public void keyPressed(KeyEvent e) { |
||||||
|
if (e.getKeyChar() == KeyEvent.VK_ENTER) { |
||||||
|
e.consume(); |
||||||
|
String toFind = keyWordTextField.getText(); |
||||||
|
search(toFind); |
||||||
|
popTips(); |
||||||
|
tipsList.requestFocusInWindow(); |
||||||
|
} |
||||||
|
} |
||||||
|
}); |
||||||
|
|
||||||
|
searchPane.add(searchButton, BorderLayout.EAST); |
||||||
|
tipsPane.add(searchPane, BorderLayout.NORTH); |
||||||
|
|
||||||
|
tipsList = new JList(tipsListModel); |
||||||
|
tipsList.addMouseListener(tipsListMouseListener); |
||||||
|
tipsList.addListSelectionListener(tipsListSelectionListener); |
||||||
|
tipsList.addKeyListener(tipListKeyListener); |
||||||
|
|
||||||
|
return tipsPane; |
||||||
|
} |
||||||
|
|
||||||
|
private void search(String key) { |
||||||
|
tipsListModel.removeAllElements(); |
||||||
|
tipsListModel.clear(); |
||||||
|
key = key.replaceAll(StringUtils.BLANK, StringUtils.EMPTY); |
||||||
|
ArrayList<String> list = new ArrayList<>(); |
||||||
|
if (!StringUtils.isEmpty(key)) { |
||||||
|
List<String> allNames = JSAPITreeHelper.getAllNames(); |
||||||
|
for (String name : allNames) { |
||||||
|
if (searchResult(key, name)) { |
||||||
|
list.add(name); |
||||||
|
} |
||||||
|
} |
||||||
|
String finalKey = key; |
||||||
|
Collections.sort(list, new Comparator<String>() { |
||||||
|
@Override |
||||||
|
public int compare(String o1, String o2) { |
||||||
|
int result; |
||||||
|
boolean o1StartWidth = o1.toLowerCase().startsWith(finalKey.toLowerCase()); |
||||||
|
boolean o2StartWidth = o2.toLowerCase().startsWith(finalKey.toLowerCase()); |
||||||
|
if (o1StartWidth) { |
||||||
|
result = o2StartWidth ? o1.compareTo(o2) : -1; |
||||||
|
} else { |
||||||
|
result = o2StartWidth ? 1 : o1.compareTo(o2); |
||||||
|
} |
||||||
|
return result; |
||||||
|
} |
||||||
|
}); |
||||||
|
for (String name : list) { |
||||||
|
tipsListModel.addElement(name); |
||||||
|
} |
||||||
|
if (!tipsListModel.isEmpty()) { |
||||||
|
tipsList.setSelectedIndex(0); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private boolean searchResult(String key, String interfaceName) { |
||||||
|
if (StringUtils.isBlank(key) || StringUtils.isBlank(interfaceName)) { |
||||||
|
return false; |
||||||
|
} |
||||||
|
int length = key.length(); |
||||||
|
String temp = interfaceName.toUpperCase(); |
||||||
|
for (int i = 0; i < length; i++) { |
||||||
|
String check = key.substring(i, i + 1); |
||||||
|
int index = temp.indexOf(check.toUpperCase()); |
||||||
|
if (index == -1) { |
||||||
|
return false; |
||||||
|
} else { |
||||||
|
temp = temp.substring(index + 1); |
||||||
|
} |
||||||
|
} |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
private void initPopTips() { |
||||||
|
popupMenu = new JPopupMenu(); |
||||||
|
JScrollPane tipsScrollPane = new JScrollPane(tipsList); |
||||||
|
popupMenu.add(tipsScrollPane); |
||||||
|
tipsScrollPane.setPreferredSize(new Dimension(220, 146)); |
||||||
|
tipsScrollPane.setBorder(new UIRoundedBorder(UIConstants.LINE_COLOR, 1, UIConstants.ARC)); |
||||||
|
} |
||||||
|
|
||||||
|
private void popTips() { |
||||||
|
popupMenu.show(keyWordTextField, 0, 23); |
||||||
|
} |
||||||
|
|
||||||
|
private ListSelectionListener tipsListSelectionListener = new ListSelectionListener() { |
||||||
|
@Override |
||||||
|
public void valueChanged(ListSelectionEvent e) { |
||||||
|
Object selectValue = tipsList.getSelectedValue(); |
||||||
|
if (selectValue == null) { |
||||||
|
return; |
||||||
|
} |
||||||
|
String interfaceName = selectValue.toString(); |
||||||
|
fixInterfaceNameList(interfaceName); |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
private KeyListener tipListKeyListener = new KeyAdapter() { |
||||||
|
@Override |
||||||
|
public void keyPressed(KeyEvent e) { |
||||||
|
if (e.getKeyChar() == KeyEvent.VK_ENTER) { |
||||||
|
Object selectValue = tipsList.getSelectedValue(); |
||||||
|
if (selectValue == null) { |
||||||
|
return; |
||||||
|
} |
||||||
|
tipListValueSelectAction(selectValue.toString()); |
||||||
|
if (popupMenu != null) { |
||||||
|
popupMenu.setVisible(false); |
||||||
|
} |
||||||
|
contentTextArea.requestFocusInWindow(); |
||||||
|
} |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
private void tipListValueSelectAction(String value) { |
||||||
|
if (ifHasBeenWriten == 0) { |
||||||
|
contentTextArea.setForeground(Color.black); |
||||||
|
contentTextArea.setText(StringUtils.EMPTY); |
||||||
|
} |
||||||
|
contentTextArea.setForeground(Color.black); |
||||||
|
currentPosition = contentTextArea.getCaretPosition(); |
||||||
|
String output = value; |
||||||
|
String textAll = contentTextArea.getText(); |
||||||
|
String textReplaced; |
||||||
|
int position = 0; |
||||||
|
if (insertPosition <= currentPosition) { |
||||||
|
textReplaced = textAll.substring(0, insertPosition) + output + textAll.substring(currentPosition); |
||||||
|
position = insertPosition + output.length(); |
||||||
|
} else { |
||||||
|
textReplaced = textAll.substring(0, currentPosition) + output + textAll.substring(insertPosition); |
||||||
|
position = currentPosition + output.length(); |
||||||
|
} |
||||||
|
contentTextArea.setText(textReplaced); |
||||||
|
contentTextArea.setCaretPosition(position); |
||||||
|
insertPosition = position; |
||||||
|
ifHasBeenWriten = 1; |
||||||
|
tipsListModel.removeAllElements(); |
||||||
|
} |
||||||
|
|
||||||
|
private MouseListener tipsListMouseListener = new MouseAdapter() { |
||||||
|
String singlePressContent; |
||||||
|
|
||||||
|
String doublePressContent; |
||||||
|
|
||||||
|
@Override |
||||||
|
public void mousePressed(MouseEvent e) { |
||||||
|
int index = tipsList.getSelectedIndex(); |
||||||
|
if (index != -1) { |
||||||
|
if (e.getClickCount() == 1) { |
||||||
|
singlePressContent = (String) tipsListModel.getElementAt(index); |
||||||
|
} else if (e.getClickCount() == 2) { |
||||||
|
doublePressContent = (String) tipsListModel.getElementAt(index); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void mouseReleased(MouseEvent e) { |
||||||
|
int index = tipsList.getSelectedIndex(); |
||||||
|
if (index != -1) { |
||||||
|
if (e.getClickCount() == 1) { |
||||||
|
if (ComparatorUtils.equals((String) tipsListModel.getElementAt(index), singlePressContent)) { |
||||||
|
singleClickActuator(singlePressContent); |
||||||
|
} |
||||||
|
} else if (e.getClickCount() == 2) { |
||||||
|
if (ComparatorUtils.equals((String) tipsListModel.getElementAt(index), doublePressContent)) { |
||||||
|
doubleClickActuator(doublePressContent); |
||||||
|
} |
||||||
|
if (popupMenu != null) { |
||||||
|
popupMenu.setVisible(false); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private void singleClickActuator(String currentLineContent) { |
||||||
|
setDescription(currentLineContent); |
||||||
|
fixInterfaceNameList(currentLineContent); |
||||||
|
} |
||||||
|
|
||||||
|
private void doubleClickActuator(String currentLineContent) { |
||||||
|
tipListValueSelectAction(currentLineContent); |
||||||
|
} |
||||||
|
}; |
||||||
|
} |
@ -0,0 +1,23 @@ |
|||||||
|
package com.fr.design.javascript; |
||||||
|
|
||||||
|
|
||||||
|
import com.fr.js.JavaScriptImpl; |
||||||
|
|
||||||
|
|
||||||
|
public class NewJavaScriptImplPane extends JavaScriptImplPane { |
||||||
|
public NewJavaScriptImplPane(String[] args) { |
||||||
|
super(args); |
||||||
|
} |
||||||
|
|
||||||
|
protected JSContentPane createJSContentPane(String[] defaultArgs){ |
||||||
|
return new JSContentWithDescriptionPane(defaultArgs); |
||||||
|
} |
||||||
|
|
||||||
|
public void populate(JavaScriptImpl javaScript) { |
||||||
|
if (javaScript != null) { |
||||||
|
populateBean(javaScript); |
||||||
|
} else { |
||||||
|
jsPane.reset(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,21 @@ |
|||||||
|
package com.fr.design.javascript.jsapi; |
||||||
|
|
||||||
|
import com.fr.design.i18n.Toolkit; |
||||||
|
|
||||||
|
public class CategoryTreeNodesUserObject implements JSAPIUserObject { |
||||||
|
private String value; |
||||||
|
|
||||||
|
public CategoryTreeNodesUserObject(String value) { |
||||||
|
this.value = value; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public String getValue() { |
||||||
|
return value; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public String getDisplayText() { |
||||||
|
return Toolkit.i18nText(value); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,155 @@ |
|||||||
|
package com.fr.design.javascript.jsapi; |
||||||
|
|
||||||
|
import com.fr.general.IOUtils; |
||||||
|
import com.fr.json.JSONArray; |
||||||
|
import com.fr.json.JSONObject; |
||||||
|
import com.fr.log.FineLoggerFactory; |
||||||
|
import com.fr.stable.StringUtils; |
||||||
|
import java.io.BufferedReader; |
||||||
|
import java.io.InputStreamReader; |
||||||
|
import java.util.ArrayList; |
||||||
|
import java.util.Iterator; |
||||||
|
import java.util.List; |
||||||
|
import javax.swing.tree.DefaultMutableTreeNode; |
||||||
|
|
||||||
|
public class JSAPITreeHelper { |
||||||
|
private static final String JSAPI_PATH = "com/fr/design/javascript/jsapi/jsapi.json"; |
||||||
|
private static final String CATEGORY_PATH = "com/fr/design/javascript/jsapi/category.json"; |
||||||
|
private static JSONObject categoryJSON ; |
||||||
|
private static JSONObject jsapiJSON ; |
||||||
|
|
||||||
|
static { |
||||||
|
jsapiJSON = createJSON(JSAPI_PATH); |
||||||
|
categoryJSON = createJSON(CATEGORY_PATH); |
||||||
|
} |
||||||
|
|
||||||
|
private static JSONObject createJSON(String path) { |
||||||
|
StringBuilder jsonString = new StringBuilder(StringUtils.EMPTY); |
||||||
|
try (BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(IOUtils.readResource(path)))) { |
||||||
|
String s; |
||||||
|
while ((s = bufferedReader.readLine()) != null) { |
||||||
|
jsonString.append(s); |
||||||
|
} |
||||||
|
} catch (Exception e) { |
||||||
|
FineLoggerFactory.getLogger().error(e.getMessage(), e); |
||||||
|
} |
||||||
|
return new JSONObject(jsonString.toString()); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
public static void createJSAPITree(DefaultMutableTreeNode rootNode) { |
||||||
|
createJSAPITree(categoryJSON, rootNode); |
||||||
|
} |
||||||
|
|
||||||
|
public static String getDirectCategory(String name) { |
||||||
|
if (jsapiJSON != null) { |
||||||
|
Iterator<String> it = jsapiJSON.keys(); |
||||||
|
while (it.hasNext()) { |
||||||
|
String key = it.next(); |
||||||
|
JSONArray nameArray = jsapiJSON.optJSONArray(key); |
||||||
|
for (int i = 0; i < nameArray.length(); i++) { |
||||||
|
if (StringUtils.equals(nameArray.getString(i), name)) { |
||||||
|
return key; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
return null; |
||||||
|
} |
||||||
|
|
||||||
|
private static void createJSAPITree(JSONObject jsonObject, DefaultMutableTreeNode rootNode) { |
||||||
|
if (jsonObject != null && rootNode != null) { |
||||||
|
Iterator<String> it = jsonObject.keys(); |
||||||
|
while (it.hasNext()) { |
||||||
|
String key = it.next(); |
||||||
|
JSONObject subNode = jsonObject.optJSONObject(key); |
||||||
|
if (subNode.size() == 0) { |
||||||
|
rootNode.add(new DefaultMutableTreeNode(new CategoryTreeNodesUserObject(key))); |
||||||
|
} else { |
||||||
|
DefaultMutableTreeNode treeNode = new DefaultMutableTreeNode(new CategoryTreeNodesUserObject(key)); |
||||||
|
rootNode.add(treeNode); |
||||||
|
createJSAPITree(subNode, treeNode); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private static List<String> getAllSubNodes(String name) { |
||||||
|
return getAllSubNodes(name, categoryJSON); |
||||||
|
} |
||||||
|
|
||||||
|
public static List<String> getAllNames() { |
||||||
|
ArrayList<String> result = new ArrayList<>(); |
||||||
|
if (jsapiJSON != null) { |
||||||
|
Iterator<String> it = jsapiJSON.keys(); |
||||||
|
while (it.hasNext()) { |
||||||
|
String key = it.next(); |
||||||
|
JSONArray nameArray = jsapiJSON.optJSONArray(key); |
||||||
|
for (int i = 0; i < nameArray.length(); i++) { |
||||||
|
result.add(nameArray.getString(i)); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
return result; |
||||||
|
} |
||||||
|
|
||||||
|
public static List<String> getNames(String category) { |
||||||
|
ArrayList<String> result = new ArrayList<>(); |
||||||
|
List<String> subCategories = getAllSubNodes(category); |
||||||
|
if (jsapiJSON != null) { |
||||||
|
for (String subCategory : subCategories) { |
||||||
|
if (jsapiJSON.containsKey(subCategory)) { |
||||||
|
JSONArray nameArray = jsapiJSON.optJSONArray(subCategory); |
||||||
|
for (int i = 0; i < nameArray.length(); i++) { |
||||||
|
result.add(nameArray.getString(i)); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
return result; |
||||||
|
} |
||||||
|
|
||||||
|
private static List<String> getAllSubNodes(String name, JSONObject jsonObject) { |
||||||
|
ArrayList<String> result = new ArrayList<>(); |
||||||
|
if (jsonObject != null) { |
||||||
|
Iterator<String> it = jsonObject.keys(); |
||||||
|
while (it.hasNext()) { |
||||||
|
String key = it.next(); |
||||||
|
JSONObject subNode = jsonObject.optJSONObject(key); |
||||||
|
if (subNode.size() == 0) { |
||||||
|
if (StringUtils.equals(key, name)) { |
||||||
|
result.add(key); |
||||||
|
return result; |
||||||
|
} |
||||||
|
} else { |
||||||
|
if (StringUtils.equals(key, name)) { |
||||||
|
result.add(key); |
||||||
|
result.addAll(getAllSubNodes(subNode)); |
||||||
|
return result; |
||||||
|
} else { |
||||||
|
result.addAll(getAllSubNodes(name, subNode)); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
return result; |
||||||
|
} |
||||||
|
|
||||||
|
private static List<String> getAllSubNodes(JSONObject jsonObject) { |
||||||
|
ArrayList<String> result = new ArrayList<>(); |
||||||
|
if (jsonObject != null) { |
||||||
|
Iterator<String> it = jsonObject.keys(); |
||||||
|
while (it.hasNext()) { |
||||||
|
String key = it.next(); |
||||||
|
JSONObject subNode = jsonObject.optJSONObject(key); |
||||||
|
if (subNode.size() == 0) { |
||||||
|
result.add(key); |
||||||
|
} else { |
||||||
|
result.add(key); |
||||||
|
result.addAll(getAllSubNodes(subNode)); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
return result; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,9 @@ |
|||||||
|
package com.fr.design.javascript.jsapi; |
||||||
|
|
||||||
|
|
||||||
|
public interface JSAPIUserObject { |
||||||
|
|
||||||
|
String getValue(); |
||||||
|
|
||||||
|
String getDisplayText(); |
||||||
|
} |
@ -0,0 +1,7 @@ |
|||||||
|
package com.fr.design.javascript.jsapi; |
||||||
|
|
||||||
|
import com.fr.js.JavaScriptImpl; |
||||||
|
|
||||||
|
public interface JSImplPopulateAction { |
||||||
|
void populate(JavaScriptImpl javaScript); |
||||||
|
} |
@ -0,0 +1,7 @@ |
|||||||
|
package com.fr.design.javascript.jsapi; |
||||||
|
|
||||||
|
import com.fr.js.JavaScriptImpl; |
||||||
|
|
||||||
|
public interface JSImplUpdateAction { |
||||||
|
void update(JavaScriptImpl javaScript); |
||||||
|
} |
@ -0,0 +1,122 @@ |
|||||||
|
package com.fr.design.lock; |
||||||
|
|
||||||
|
import com.fr.design.file.TemplateTreePane; |
||||||
|
import com.fr.design.gui.ibutton.UIButton; |
||||||
|
import com.fr.design.gui.ilable.UILabel; |
||||||
|
import com.fr.design.i18n.DesignSizeI18nManager; |
||||||
|
import com.fr.design.i18n.Toolkit; |
||||||
|
import com.fr.design.layout.FRGUIPaneFactory; |
||||||
|
import com.fr.design.mainframe.DesignerContext; |
||||||
|
import com.fr.design.mainframe.DesignerFrameFileDealerPane; |
||||||
|
import com.fr.design.utils.TemplateUtils; |
||||||
|
import com.fr.design.utils.gui.GUICoreUtils; |
||||||
|
import com.fr.file.FileNodeFILE; |
||||||
|
import com.fr.file.filetree.FileNode; |
||||||
|
import com.fr.general.IOUtils; |
||||||
|
import com.fr.stable.StableUtils; |
||||||
|
import com.fr.stable.StringUtils; |
||||||
|
import com.fr.stable.project.ProjectConstants; |
||||||
|
import com.fr.workspace.base.UserInfo; |
||||||
|
import java.awt.BorderLayout; |
||||||
|
import java.awt.Color; |
||||||
|
import java.awt.FlowLayout; |
||||||
|
import java.awt.event.ActionEvent; |
||||||
|
import java.awt.event.ActionListener; |
||||||
|
import java.time.LocalDateTime; |
||||||
|
import java.time.format.DateTimeFormatter; |
||||||
|
import javax.swing.BorderFactory; |
||||||
|
import javax.swing.JDialog; |
||||||
|
import javax.swing.JPanel; |
||||||
|
|
||||||
|
/** |
||||||
|
* @author hades |
||||||
|
* @version 11.0 |
||||||
|
* Created by hades on 2021/12/2 |
||||||
|
*/ |
||||||
|
public class LockInfoDialog extends JDialog { |
||||||
|
|
||||||
|
private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm"); |
||||||
|
|
||||||
|
public LockInfoDialog(UserInfo userInfo) { |
||||||
|
super(DesignerContext.getDesignerFrame()); |
||||||
|
JPanel panel = new JPanel(new BorderLayout()); |
||||||
|
panel.setBorder(BorderFactory.createEmptyBorder(0, 10, 0, 0)); |
||||||
|
panel.add(createContentPane(userInfo), BorderLayout.CENTER); |
||||||
|
panel.add(createControlPane(), BorderLayout.SOUTH); |
||||||
|
this.getContentPane().add(panel); |
||||||
|
this.setTitle(Toolkit.i18nText("Fine-Design_Basic_Remote_Design_Title_Hint")); |
||||||
|
this.setSize(DesignSizeI18nManager.getInstance().i18nDimension("com.fr.design.lock.LockInfoDialog")); |
||||||
|
this.setResizable(false); |
||||||
|
this.setModal(true); |
||||||
|
GUICoreUtils.centerWindow(this); |
||||||
|
this.setVisible(true); |
||||||
|
} |
||||||
|
|
||||||
|
private JPanel createContentPane(UserInfo userInfo) { |
||||||
|
JPanel contentPanel = new JPanel(new BorderLayout()); |
||||||
|
contentPanel.setBorder(BorderFactory.createEmptyBorder(5, 0, 0, 0)); |
||||||
|
JPanel messagePane = new JPanel(new BorderLayout(13, 0)); |
||||||
|
UILabel iconLabel = new UILabel(IOUtils.readIcon("/com/fr/design/images/warnings/warning32.png")); |
||||||
|
iconLabel.setBorder(BorderFactory.createEmptyBorder(5, 0, 0, 0)); |
||||||
|
messagePane.add(iconLabel, BorderLayout.WEST); |
||||||
|
UILabel tipLabel = new UILabel(Toolkit.i18nText("Fine-Design_Template_Lock_And_SaveAs_Tip")); |
||||||
|
tipLabel.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0)); |
||||||
|
messagePane.add(tipLabel, BorderLayout.CENTER); |
||||||
|
contentPanel.add(messagePane, BorderLayout.NORTH); |
||||||
|
JPanel detailInfoPane = FRGUIPaneFactory.createY_AXISBoxInnerContainer_S_Pane(); |
||||||
|
detailInfoPane.setBorder(BorderFactory.createEmptyBorder(0, 45, 0,0)); |
||||||
|
if (userInfo != null && StringUtils.isNotEmpty(userInfo.getUserName())) { |
||||||
|
UILabel label = createLabel(Toolkit.i18nText("Fine-Design_Template_Lock_Holder", userInfo.getUserName())); |
||||||
|
label .setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0)); |
||||||
|
detailInfoPane.add(label); |
||||||
|
} |
||||||
|
if (userInfo != null && StringUtils.isNotEmpty(userInfo.getIp())) { |
||||||
|
detailInfoPane.add(createLabel(Toolkit.i18nText("Fine-Design_Template_Lock_Holder_Ip", userInfo.getIp()) )); |
||||||
|
} |
||||||
|
detailInfoPane.add(createLabel(Toolkit.i18nText("Fine-Design_Template_Lock_Get_Time", FORMATTER.format(LocalDateTime.now())))); |
||||||
|
contentPanel.add(detailInfoPane, BorderLayout.CENTER); |
||||||
|
return contentPanel; |
||||||
|
} |
||||||
|
|
||||||
|
private UILabel createLabel(String text) { |
||||||
|
UILabel label = new UILabel(text); |
||||||
|
label.setForeground(Color.GRAY); |
||||||
|
label.setBorder(BorderFactory.createEmptyBorder(8, 0, 0, 0)); |
||||||
|
return label; |
||||||
|
} |
||||||
|
|
||||||
|
private JPanel createControlPane() { |
||||||
|
JPanel controlPane = new JPanel(new FlowLayout(FlowLayout.RIGHT, 10, 5)); |
||||||
|
controlPane.setBorder(BorderFactory.createEmptyBorder(0, 0, 0,5)); |
||||||
|
UIButton saveAsButton = new UIButton(Toolkit.i18nText("Fine_Design_Template_Lock_Save_As")); |
||||||
|
UIButton cancelButton = new UIButton(Toolkit.i18nText("Fine-Design_Basic_Button_Cancel")); |
||||||
|
saveAsButton.addActionListener(new ActionListener() { |
||||||
|
@Override |
||||||
|
public void actionPerformed(ActionEvent e) { |
||||||
|
dispose(); |
||||||
|
FileNode node = TemplateTreePane.getInstance().getFileNode(); |
||||||
|
if (node == null) { |
||||||
|
return; |
||||||
|
} |
||||||
|
final String selectedFilePath = StableUtils.pathJoin(ProjectConstants.REPORTLETS_NAME, TemplateTreePane.getInstance().getFilePath()); |
||||||
|
TemplateUtils.createAndOpenTemplate(Toolkit.i18nText("Fine_Design_Template_Lock_Copy"), new FileNodeFILE(new FileNode(selectedFilePath, false)), true); |
||||||
|
} |
||||||
|
}); |
||||||
|
cancelButton.addActionListener(new ActionListener() { |
||||||
|
@Override |
||||||
|
public void actionPerformed(ActionEvent e) { |
||||||
|
dispose(); |
||||||
|
} |
||||||
|
}); |
||||||
|
controlPane.add(saveAsButton); |
||||||
|
controlPane.add(cancelButton); |
||||||
|
return controlPane; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
public static void show(UserInfo userInfo) { |
||||||
|
DesignerFrameFileDealerPane.getInstance().refreshRightToolBarBy(TemplateTreePane.getInstance().getFileNode()); |
||||||
|
new LockInfoDialog(userInfo); |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,23 @@ |
|||||||
|
package com.fr.design.lock; |
||||||
|
|
||||||
|
import com.fr.report.lock.DefaultLockInfoOperator; |
||||||
|
import com.fr.report.lock.LockInfoOperator; |
||||||
|
import com.fr.start.server.FineEmbedServer; |
||||||
|
import com.fr.workspace.WorkContext; |
||||||
|
|
||||||
|
/** |
||||||
|
* @author hades |
||||||
|
* @version 11.0 |
||||||
|
* Created by hades on 2021/12/8 |
||||||
|
*/ |
||||||
|
public class LockInfoUtils { |
||||||
|
|
||||||
|
public static boolean isCompatibleOperator() { |
||||||
|
LockInfoOperator lockInfoOperator = WorkContext.getCurrent().get(LockInfoOperator.class); |
||||||
|
return lockInfoOperator instanceof DefaultLockInfoOperator; |
||||||
|
} |
||||||
|
|
||||||
|
public static boolean unableGetLockInfo() { |
||||||
|
return WorkContext.getCurrent().isLocal() && !FineEmbedServer.isRunning(); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,190 @@ |
|||||||
|
package com.fr.design.mainframe; |
||||||
|
|
||||||
|
import com.fr.design.file.HistoryTemplateListCache; |
||||||
|
import com.fr.design.gui.ilable.UILabel; |
||||||
|
import com.fr.design.i18n.DesignSizeI18nManager; |
||||||
|
import com.fr.design.i18n.Toolkit; |
||||||
|
import com.fr.design.mainframe.guide.base.GuideView; |
||||||
|
import com.fr.design.utils.gui.GUICoreUtils; |
||||||
|
import com.fr.general.IOUtils; |
||||||
|
import com.fr.log.FineLoggerFactory; |
||||||
|
import com.fr.workspace.WorkContext; |
||||||
|
import com.fr.report.lock.LockInfoOperator; |
||||||
|
import java.awt.BorderLayout; |
||||||
|
import java.awt.Color; |
||||||
|
import java.awt.Component; |
||||||
|
import java.awt.Container; |
||||||
|
import java.awt.Dimension; |
||||||
|
import java.awt.Font; |
||||||
|
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 javax.swing.ImageIcon; |
||||||
|
import javax.swing.JButton; |
||||||
|
import javax.swing.JDialog; |
||||||
|
import javax.swing.JFrame; |
||||||
|
import javax.swing.JPanel; |
||||||
|
import javax.swing.SwingWorker; |
||||||
|
|
||||||
|
/** |
||||||
|
* @author hades |
||||||
|
* @version 11.0 |
||||||
|
* Created by hades on 2021/12/6 |
||||||
|
*/ |
||||||
|
public class ForbiddenPane extends JPanel { |
||||||
|
|
||||||
|
private static final ImageIcon LOCK_ICON = new ImageIcon(IOUtils.readImage("/com/fr/design/images/mainframe/lock_template.png")); |
||||||
|
private static final Color TIP_COLOR = new Color(108, 174, 235); |
||||||
|
private static final Color BUTTON_COLOR = new Color(63, 155, 249); |
||||||
|
private static final int Y_GAP = 10; |
||||||
|
private static final int X_GAP = 10; |
||||||
|
private static final int ARC = 4; |
||||||
|
|
||||||
|
private final UILabel lockLabel; |
||||||
|
private final UILabel tipLabel; |
||||||
|
private final JButton refreshButton; |
||||||
|
|
||||||
|
public ForbiddenPane() { |
||||||
|
|
||||||
|
setLayout(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) { |
||||||
|
int width = parent.getParent().getWidth(); |
||||||
|
int height = parent.getParent().getHeight(); |
||||||
|
int lockLabelWidth = lockLabel.getPreferredSize().width; |
||||||
|
int lockLabelHeight = lockLabel.getPreferredSize().height; |
||||||
|
int lockLabelX = (width - lockLabelWidth) / 2; |
||||||
|
int lockLabelY = (height - lockLabelHeight) / 2; |
||||||
|
int tipLabelWidth = tipLabel.getPreferredSize().width; |
||||||
|
int tipLabelHeight = tipLabel.getPreferredSize().height; |
||||||
|
int tipLabelX = (width - tipLabelWidth) / 2 + X_GAP; |
||||||
|
int tipLabelY = lockLabelY + lockLabelHeight; |
||||||
|
int refreshButtonWidth = refreshButton.getPreferredSize().width; |
||||||
|
int refreshButtonHeight = refreshButton.getPreferredSize().height; |
||||||
|
int refreshButtonX = (width - refreshButtonWidth) / 2 + X_GAP; |
||||||
|
int refreshButtonY = tipLabelY + refreshButtonHeight + Y_GAP; |
||||||
|
lockLabel.setBounds(lockLabelX, lockLabelY, lockLabelWidth, lockLabelHeight); |
||||||
|
tipLabel.setBounds(tipLabelX, tipLabelY, tipLabelWidth, tipLabelHeight); |
||||||
|
refreshButton.setBounds(refreshButtonX, refreshButtonY, refreshButtonWidth, refreshButtonHeight); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void addLayoutComponent(String name, Component comp) { |
||||||
|
} |
||||||
|
}); |
||||||
|
setBackground(Color.WHITE); |
||||||
|
lockLabel = new UILabel(LOCK_ICON); |
||||||
|
tipLabel = new UILabel(Toolkit.i18nText("Fine_Design_Template_Has_Been_Locked_Tip")); |
||||||
|
Font labelFont = tipLabel.getFont(); |
||||||
|
tipLabel.setFont(new Font(labelFont.getName(), labelFont.getStyle(), 14)); |
||||||
|
tipLabel.setForeground(TIP_COLOR); |
||||||
|
refreshButton = new JButton(Toolkit.i18nText("Fine-Design_Basic_Refresh")) { |
||||||
|
@Override |
||||||
|
public void paintComponent(Graphics g) { |
||||||
|
Graphics2D g2d = (Graphics2D) g; |
||||||
|
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); |
||||||
|
g2d.setColor(BUTTON_COLOR); |
||||||
|
g2d.fillRoundRect(0, 0, getWidth(), getHeight(), ARC, ARC); |
||||||
|
super.paintComponent(g2d); |
||||||
|
} |
||||||
|
}; |
||||||
|
refreshButton.setPreferredSize(DesignSizeI18nManager.getInstance().i18nDimension("com.fr.design.mainframe.ForbiddenPane.refreshButton")); |
||||||
|
refreshButton.setForeground(Color.WHITE); |
||||||
|
refreshButton.setBorderPainted(false); |
||||||
|
refreshButton.setContentAreaFilled(false); |
||||||
|
refreshButton.addActionListener(new ActionListener() { |
||||||
|
@Override |
||||||
|
public void actionPerformed(ActionEvent e) { |
||||||
|
final JTemplate<?, ?> template = HistoryTemplateListCache.getInstance().getCurrentEditingTemplate(); |
||||||
|
if (template == null) { |
||||||
|
return; |
||||||
|
} |
||||||
|
// 展示下画面
|
||||||
|
LoadingDialog loadingDialog = new LoadingDialog(); |
||||||
|
loadingDialog.showDialog(); |
||||||
|
new SwingWorker<Boolean, Void>(){ |
||||||
|
|
||||||
|
@Override |
||||||
|
protected Boolean doInBackground() throws Exception { |
||||||
|
return WorkContext.getCurrent().get(LockInfoOperator.class).isTplLocked(template.getEditingFILE().getPath()); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
protected void done() { |
||||||
|
boolean unLocked; |
||||||
|
loadingDialog.hideDialog(); |
||||||
|
try { |
||||||
|
unLocked = !get(); |
||||||
|
if (unLocked) { |
||||||
|
template.whenClose(); |
||||||
|
JTemplate<?, ?> newTemplate = JTemplateFactory.createJTemplate(template.getEditingFILE()); |
||||||
|
HistoryTemplateListCache.getInstance().replaceCurrentEditingTemplate(newTemplate); |
||||||
|
DesignerContext.getDesignerFrame().addAndActivateJTemplate(newTemplate); |
||||||
|
} |
||||||
|
} catch (Exception e) { |
||||||
|
loadingDialog.hideDialog(); |
||||||
|
FineLoggerFactory.getLogger().error(e.getMessage(), e); |
||||||
|
} |
||||||
|
} |
||||||
|
}.execute(); |
||||||
|
} |
||||||
|
}); |
||||||
|
add(lockLabel); |
||||||
|
add(tipLabel); |
||||||
|
add(refreshButton); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
class LoadingDialog extends JDialog { |
||||||
|
|
||||||
|
private static final ImageIcon LOADING_ICON = new ImageIcon(IOUtils.readImage("/com/fr/design/images/mainframe/refreh_icon.png")); |
||||||
|
|
||||||
|
private GuideView guideView; |
||||||
|
|
||||||
|
public LoadingDialog() { |
||||||
|
super(DesignerContext.getDesignerFrame()); |
||||||
|
setLayout(new BorderLayout()); |
||||||
|
this.getContentPane().setBackground(Color.WHITE); |
||||||
|
this.setResizable(false); |
||||||
|
this.setUndecorated(true); |
||||||
|
this.setAlwaysOnTop(true); |
||||||
|
this.setModal(false); |
||||||
|
this.setSize(new Dimension(400, 100)); |
||||||
|
this.add(new UILabel(LOADING_ICON, UILabel.CENTER), BorderLayout.NORTH); |
||||||
|
this.add(new UILabel(Toolkit.i18nText(Toolkit.i18nText("Fine_Design_Template_Refresh")), UILabel.CENTER), BorderLayout.CENTER); |
||||||
|
GUICoreUtils.centerWindow(this); |
||||||
|
} |
||||||
|
|
||||||
|
public void showDialog() { |
||||||
|
DesignerContext.getDesignerFrame().setExtendedState(JFrame.MAXIMIZED_BOTH); |
||||||
|
guideView = new GuideView(DesignerContext.getDesignerFrame()); |
||||||
|
GUICoreUtils.centerWindow(DesignerContext.getDesignerFrame(), guideView); |
||||||
|
guideView.setBounds(DesignerContext.getDesignerFrame().getBounds()); |
||||||
|
guideView.setVisible(true); |
||||||
|
this.setVisible(true); |
||||||
|
} |
||||||
|
|
||||||
|
public void hideDialog() { |
||||||
|
this.dispose(); |
||||||
|
guideView.dismissGuide(); |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,10 @@ |
|||||||
|
package com.fr.design.mainframe; |
||||||
|
|
||||||
|
/** |
||||||
|
* @author Starryi |
||||||
|
* @version 1.0 |
||||||
|
* Created by Starryi on 2022/3/1 |
||||||
|
*/ |
||||||
|
public interface JDashboard { |
||||||
|
void switchToDashBoardEditor(); |
||||||
|
} |
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue