feat(box): 现代化用户界面并添加 jar 文件预览功能
- 使用 FlatDarculaLaf 样式库替换默认样式 - 添加 jar 文件预览功能,使用 CFR 进行反编译 - 更新 build.gradle 文件,添加新依赖项 - 新增 CFROutputSinkFactory 和 JarClassFileSource 类 - 修改主程序启动逻辑,支持 jar 文件预览
This commit is contained in:
25
build.gradle
25
build.gradle
@@ -2,6 +2,7 @@ plugins {
|
|||||||
id 'java'
|
id 'java'
|
||||||
id 'application'
|
id 'application'
|
||||||
id 'edu.sc.seis.launch4j' version '2.5.4'
|
id 'edu.sc.seis.launch4j' version '2.5.4'
|
||||||
|
id 'org.openjfx.javafxplugin' version '0.1.0'
|
||||||
}
|
}
|
||||||
|
|
||||||
// JDK 版本检查
|
// JDK 版本检查
|
||||||
@@ -58,7 +59,23 @@ dependencies {
|
|||||||
|
|
||||||
implementation 'org.python:jython-standalone:2.7.3'
|
implementation 'org.python:jython-standalone:2.7.3'
|
||||||
|
|
||||||
|
implementation 'org.fxmisc.richtext:richtextfx:0.11.0' // 更新后的richtextfx
|
||||||
|
implementation 'org.bitbucket.mstrobel:procyon-core:0.5.36' // 使用JitPack版本
|
||||||
|
implementation 'org.bitbucket.mstrobel:procyon-compilertools:0.5.36'
|
||||||
|
|
||||||
|
implementation 'com.fifesoft:rsyntaxtextarea:3.3.0'
|
||||||
|
implementation 'org.apache.commons:commons-compress:1.23.0'
|
||||||
|
implementation 'com.fasterxml.jackson.core:jackson-databind:2.15.2'
|
||||||
|
implementation 'org.controlsfx:controlsfx:11.1.2' // 现代化UI组件
|
||||||
|
implementation 'com.dlsc.formsfx:formsfx-core:11.6.0' // 表单组件
|
||||||
|
implementation 'net.sourceforge.plantuml:plantuml:8059' // UML支持(可选)
|
||||||
|
|
||||||
implementation 'com.google.code.gson:gson:2.10.1'
|
implementation 'com.google.code.gson:gson:2.10.1'
|
||||||
|
|
||||||
|
implementation 'org.openjfx:javafx-controls:21'
|
||||||
|
implementation 'org.benf:cfr:0.152'
|
||||||
|
|
||||||
|
implementation 'com.github.javaparser:javaparser-core:3.25.1'
|
||||||
}
|
}
|
||||||
|
|
||||||
// 分离依赖项到 libs 目录
|
// 分离依赖项到 libs 目录
|
||||||
@@ -108,6 +125,11 @@ tasks.withType(JavaExec).configureEach {
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
javafx {
|
||||||
|
version = "21"
|
||||||
|
modules = [ 'javafx.controls', 'javafx.fxml' ]
|
||||||
|
}
|
||||||
|
|
||||||
// 单独打包文档
|
// 单独打包文档
|
||||||
task packageOpenSourceDocs(type: Jar) {
|
task packageOpenSourceDocs(type: Jar) {
|
||||||
archiveClassifier = 'docs'
|
archiveClassifier = 'docs'
|
||||||
@@ -122,7 +144,8 @@ application {
|
|||||||
// 确保运行时参数生效
|
// 确保运行时参数生效
|
||||||
applicationDefaultJvmArgs = [
|
applicationDefaultJvmArgs = [
|
||||||
"-Djava.system.class.loader=com.axis.innovators.box.plugins.BoxClassLoader",
|
"-Djava.system.class.loader=com.axis.innovators.box.plugins.BoxClassLoader",
|
||||||
"-Dloader.library.path=$buildDir/libs/libs"
|
"-Dloader.library.path=$buildDir/libs/libs",
|
||||||
|
'-Dfile.encoding=UTF-8'
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
BIN
plug-in/Template-1.0-SNAPSHOT.jar
Normal file
BIN
plug-in/Template-1.0-SNAPSHOT.jar
Normal file
Binary file not shown.
@@ -5,13 +5,41 @@
|
|||||||
版本:1.1
|
版本:1.1
|
||||||
"""
|
"""
|
||||||
from com.axis.innovators.box.python import PyLocalSide
|
from com.axis.innovators.box.python import PyLocalSide
|
||||||
from javax.swing import AbstractAction
|
|
||||||
|
from javax.swing import AbstractAction, JFrame,JDialog, JLabel, JButton, JPanel, JOptionPane
|
||||||
|
from java.awt import BorderLayout, Dimension
|
||||||
|
from java.awt.event import ActionListener
|
||||||
|
|
||||||
class MyAction(AbstractAction):
|
class MyAction(AbstractAction):
|
||||||
def actionPerformed(self, event):
|
def actionPerformed(self, event):
|
||||||
"""工具项点击事件处理"""
|
"""工具项点击事件处理"""
|
||||||
print("[DEBUG] Tool item clicked! Event source:", event.getSource())
|
print("[DEBUG] Tool item clicked! Event source:", event.getSource())
|
||||||
|
|
||||||
|
# 创建 JDialog 作为子窗口
|
||||||
|
parent_frame = event.getSource().getTopLevelAncestor() # 获取父窗口
|
||||||
|
dialog = JDialog(parent_frame, u"工具面板", True) # True 表示模态对话框
|
||||||
|
dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE) # 关闭时释放资源
|
||||||
|
dialog.setSize(300, 200)
|
||||||
|
dialog.setLocationRelativeTo(parent_frame) # 居中于父窗口
|
||||||
|
|
||||||
|
# 创建面板并设置布局
|
||||||
|
panel = JPanel()
|
||||||
|
panel.layout = BorderLayout() # 使用边界布局
|
||||||
|
|
||||||
|
# 添加标签
|
||||||
|
label = JLabel(u"这是工具项的子面板")
|
||||||
|
panel.add(label, BorderLayout.CENTER)
|
||||||
|
|
||||||
|
# 添加关闭按钮
|
||||||
|
close_btn = JButton(u"关闭")
|
||||||
|
close_btn.addActionListener(lambda e: dialog.dispose()) # 点击关闭窗口
|
||||||
|
panel.add(close_btn, BorderLayout.SOUTH)
|
||||||
|
|
||||||
|
# 将面板添加到窗口
|
||||||
|
dialog.add(panel)
|
||||||
|
dialog.visible = True # 显示窗口
|
||||||
|
|
||||||
|
|
||||||
def onStartup():
|
def onStartup():
|
||||||
"""
|
"""
|
||||||
系统启动时自动执行的初始化逻辑
|
系统启动时自动执行的初始化逻辑
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package com.axis.innovators.box;
|
package com.axis.innovators.box;
|
||||||
|
|
||||||
|
import com.axis.innovators.box.decompilation.gui.ModernJarViewer;
|
||||||
import com.axis.innovators.box.events.GlobalEventBus;
|
import com.axis.innovators.box.events.GlobalEventBus;
|
||||||
import com.axis.innovators.box.events.OpenFileEvents;
|
import com.axis.innovators.box.events.OpenFileEvents;
|
||||||
import com.axis.innovators.box.events.StartupEvent;
|
import com.axis.innovators.box.events.StartupEvent;
|
||||||
@@ -26,6 +27,7 @@ import java.awt.*;
|
|||||||
import java.awt.event.ActionEvent;
|
import java.awt.event.ActionEvent;
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
@@ -334,11 +336,24 @@ public class AxisInnovatorsBox {
|
|||||||
try {
|
try {
|
||||||
main.initLog4j2();
|
main.initLog4j2();
|
||||||
main.setTopic();
|
main.setTopic();
|
||||||
|
|
||||||
List<Map<String, String>> validFiles = ArgsParser.parseArgs(args);
|
List<Map<String, String>> validFiles = ArgsParser.parseArgs(args);
|
||||||
|
|
||||||
for (Map<String, String> fileInfo : validFiles) {
|
for (Map<String, String> fileInfo : validFiles) {
|
||||||
OpenFileEvents openFileEvents = new OpenFileEvents(fileInfo.get("path"), fileInfo.get("extension"));
|
String extension = fileInfo.get("extension");
|
||||||
|
String path = fileInfo.get("path");
|
||||||
|
if (".jar".equals(extension)){
|
||||||
|
SwingUtilities.invokeLater(() -> {
|
||||||
|
try {
|
||||||
|
UIManager.setLookAndFeel(new com.formdev.flatlaf.FlatDarculaLaf());
|
||||||
|
} catch (Exception ex) {
|
||||||
|
ex.printStackTrace();
|
||||||
|
}
|
||||||
|
ModernJarViewer viewer = new ModernJarViewer(null, path);
|
||||||
|
main.popupWindow(viewer);
|
||||||
|
});
|
||||||
|
main.progressBarManager.close();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
OpenFileEvents openFileEvents = new OpenFileEvents(path, extension);
|
||||||
GlobalEventBus.EVENT_BUS.post(openFileEvents);
|
GlobalEventBus.EVENT_BUS.post(openFileEvents);
|
||||||
if (!openFileEvents.isContinue()) {
|
if (!openFileEvents.isContinue()) {
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ import com.axis.innovators.box.tools.FolderCleaner;
|
|||||||
import com.axis.innovators.box.tools.FolderCreator;
|
import com.axis.innovators.box.tools.FolderCreator;
|
||||||
import com.axis.innovators.box.register.LanguageManager;
|
import com.axis.innovators.box.register.LanguageManager;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author tzdwindows 7
|
* @author tzdwindows 7
|
||||||
*/
|
*/
|
||||||
@@ -16,6 +18,7 @@ public class Main {
|
|||||||
LanguageManager.loadLanguage("system:zh_CN");
|
LanguageManager.loadLanguage("system:zh_CN");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
System.out.println(Arrays.toString(args));
|
||||||
AxisInnovatorsBox.run(args);
|
AxisInnovatorsBox.run(args);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,51 @@
|
|||||||
|
package com.axis.innovators.box.decompilation.gui;
|
||||||
|
|
||||||
|
import org.benf.cfr.reader.api.OutputSinkFactory;
|
||||||
|
import org.benf.cfr.reader.api.SinkReturns.Decompiled;
|
||||||
|
import org.benf.cfr.reader.api.SinkReturns.ExceptionMessage;
|
||||||
|
import org.benf.cfr.reader.api.SinkReturns;
|
||||||
|
|
||||||
|
import javax.swing.*;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class CFROutputSinkFactory implements OutputSinkFactory {
|
||||||
|
private JTextArea codeArea;
|
||||||
|
private StringBuilder output = new StringBuilder();
|
||||||
|
|
||||||
|
public CFROutputSinkFactory(JTextArea codeArea) {
|
||||||
|
this.codeArea = codeArea;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CFROutputSinkFactory() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<SinkClass> getSupportedSinks(SinkType sgetOutputinkType, Collection<SinkClass> collection) {
|
||||||
|
return Collections.singletonList(SinkClass.DECOMPILED);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public String getOutput() {
|
||||||
|
return output.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <T> Sink<T> getSink(SinkType sinkType, SinkClass sinkClass) {
|
||||||
|
if (sinkClass == SinkClass.DECOMPILED) {
|
||||||
|
return (Sink<T>) new Sink<SinkReturns.Decompiled>() {
|
||||||
|
@Override
|
||||||
|
public void write(Decompiled decompiled) {
|
||||||
|
if (codeArea != null) {
|
||||||
|
SwingUtilities.invokeLater(() ->
|
||||||
|
codeArea.setText(decompiled.getJava())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
output.append(decompiled.getJava()).append("\n");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
package com.axis.innovators.box.decompilation.gui;
|
||||||
|
|
||||||
|
import org.benf.cfr.reader.api.ClassFileSource;
|
||||||
|
import org.benf.cfr.reader.bytecode.analysis.parse.utils.Pair;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.jar.JarEntry;
|
||||||
|
import java.util.jar.JarFile;
|
||||||
|
|
||||||
|
public class JarClassFileSource implements ClassFileSource {
|
||||||
|
private final JarFile jarFile;
|
||||||
|
|
||||||
|
public JarClassFileSource(JarFile jarFile) {
|
||||||
|
this.jarFile = jarFile;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void informAnalysisRelativePathDetail(String usePath, String classFilePath) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<String> addJar(String jarPath) {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getPossiblyRenamedPath(String path) {
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Pair<byte[], String> getClassFileContent(String path) throws IOException {
|
||||||
|
JarEntry entry = jarFile.getJarEntry(path);
|
||||||
|
if (entry == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
try (InputStream is = jarFile.getInputStream(entry)) {
|
||||||
|
byte[] bytes = new byte[(int) entry.getSize()];
|
||||||
|
is.read(bytes);
|
||||||
|
return Pair.make(bytes, path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -25,7 +25,7 @@ public class LoadIcon {
|
|||||||
* @param size 图片大小
|
* @param size 图片大小
|
||||||
* @return ImageIcon对象
|
* @return ImageIcon对象
|
||||||
*/
|
*/
|
||||||
static ImageIcon loadIcon(String filename, int size) {
|
public static ImageIcon loadIcon(String filename, int size) {
|
||||||
return loadIcon(LoadIcon.class, filename, size);
|
return loadIcon(LoadIcon.class, filename, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ public class BoxClassLoader extends URLClassLoader {
|
|||||||
"java.", "javax.", "sun.", "com.sun.", "jdk.",
|
"java.", "javax.", "sun.", "com.sun.", "jdk.",
|
||||||
"org.xml.", "org.w3c.", "org.apache.",
|
"org.xml.", "org.w3c.", "org.apache.",
|
||||||
"javax.management.", "javax.swing."
|
"javax.management.", "javax.swing."
|
||||||
|
, "javafx."
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -64,11 +64,12 @@ public class ArgsParser {
|
|||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
// 模拟 main 方法的 args 参数
|
// 模拟 main 方法的 args 参数
|
||||||
String[] testArgs = {
|
String[] testArgs = {
|
||||||
"C:\\Program Files\\test.txt", // 合法文件
|
"C:\\Program Files\\test.txt",
|
||||||
"C:\\invalid_file.exe", // 不存在的文件
|
"C:\\invalid_file.exe",
|
||||||
"D:\\Documents\\report.pdf", // 合法文件
|
"D:\\Documents\\report.pdf",
|
||||||
"not_a_path", // 无效路径
|
"not_a_path",
|
||||||
"C:\\Windows\\system32" // 目录,不是文件
|
"C:\\Windows\\system32" ,
|
||||||
|
"C:\\Users\\Administrator\\MCreatorWorkspaces\\AxisInnovatorsBox\\build.gradle"
|
||||||
};
|
};
|
||||||
|
|
||||||
// 解析 args 参数
|
// 解析 args 参数
|
||||||
|
|||||||
45
src/main/resources/modern-dark.css
Normal file
45
src/main/resources/modern-dark.css
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
.root {
|
||||||
|
-fx-base: #2D2D2D;
|
||||||
|
-fx-background: #1E1E1E;
|
||||||
|
-fx-control-inner-background: derive(-fx-base, 20%);
|
||||||
|
-fx-accent: #45A1FF;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu-bar {
|
||||||
|
-fx-background-color: linear-gradient(to bottom, #3C3C3C, #2D2D2D);
|
||||||
|
}
|
||||||
|
|
||||||
|
.tree-view {
|
||||||
|
-fx-background-color: -fx-base;
|
||||||
|
-fx-border-color: derive(-fx-base, -10%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.tree-cell {
|
||||||
|
-fx-text-fill: #DCDCDC;
|
||||||
|
-fx-font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tree-cell:selected {
|
||||||
|
-fx-background-color: #45A1FF;
|
||||||
|
-fx-text-fill: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.code-area {
|
||||||
|
-fx-font-family: "JetBrains Mono";
|
||||||
|
-fx-font-size: 14px;
|
||||||
|
-fx-highlight-fill: #264F78;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-pane {
|
||||||
|
-fx-background-color: derive(-fx-base, 10%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab {
|
||||||
|
-fx-background-color: linear-gradient(to bottom, #3C3C3C, #2D2D2D);
|
||||||
|
-fx-text-fill: #DCDCDC;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab:selected {
|
||||||
|
-fx-background-color: #45A1FF;
|
||||||
|
-fx-text-fill: white;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user