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 'application'
|
||||
id 'edu.sc.seis.launch4j' version '2.5.4'
|
||||
id 'org.openjfx.javafxplugin' version '0.1.0'
|
||||
}
|
||||
|
||||
// JDK 版本检查
|
||||
@@ -58,7 +59,23 @@ dependencies {
|
||||
|
||||
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 'org.openjfx:javafx-controls:21'
|
||||
implementation 'org.benf:cfr:0.152'
|
||||
|
||||
implementation 'com.github.javaparser:javaparser-core:3.25.1'
|
||||
}
|
||||
|
||||
// 分离依赖项到 libs 目录
|
||||
@@ -108,6 +125,11 @@ tasks.withType(JavaExec).configureEach {
|
||||
]
|
||||
}
|
||||
|
||||
javafx {
|
||||
version = "21"
|
||||
modules = [ 'javafx.controls', 'javafx.fxml' ]
|
||||
}
|
||||
|
||||
// 单独打包文档
|
||||
task packageOpenSourceDocs(type: Jar) {
|
||||
archiveClassifier = 'docs'
|
||||
@@ -122,7 +144,8 @@ application {
|
||||
// 确保运行时参数生效
|
||||
applicationDefaultJvmArgs = [
|
||||
"-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
|
||||
"""
|
||||
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):
|
||||
def actionPerformed(self, event):
|
||||
"""工具项点击事件处理"""
|
||||
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():
|
||||
"""
|
||||
系统启动时自动执行的初始化逻辑
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
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.OpenFileEvents;
|
||||
import com.axis.innovators.box.events.StartupEvent;
|
||||
@@ -26,6 +27,7 @@ import java.awt.*;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.io.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@@ -334,11 +336,24 @@ public class AxisInnovatorsBox {
|
||||
try {
|
||||
main.initLog4j2();
|
||||
main.setTopic();
|
||||
|
||||
List<Map<String, String>> validFiles = ArgsParser.parseArgs(args);
|
||||
|
||||
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);
|
||||
if (!openFileEvents.isContinue()) {
|
||||
return;
|
||||
|
||||
@@ -4,6 +4,8 @@ import com.axis.innovators.box.tools.FolderCleaner;
|
||||
import com.axis.innovators.box.tools.FolderCreator;
|
||||
import com.axis.innovators.box.register.LanguageManager;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* @author tzdwindows 7
|
||||
*/
|
||||
@@ -16,6 +18,7 @@ public class Main {
|
||||
LanguageManager.loadLanguage("system:zh_CN");
|
||||
}
|
||||
|
||||
System.out.println(Arrays.toString(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 图片大小
|
||||
* @return ImageIcon对象
|
||||
*/
|
||||
static ImageIcon loadIcon(String filename, int size) {
|
||||
public static ImageIcon loadIcon(String filename, int size) {
|
||||
return loadIcon(LoadIcon.class, filename, size);
|
||||
}
|
||||
|
||||
|
||||
@@ -26,6 +26,7 @@ public class BoxClassLoader extends URLClassLoader {
|
||||
"java.", "javax.", "sun.", "com.sun.", "jdk.",
|
||||
"org.xml.", "org.w3c.", "org.apache.",
|
||||
"javax.management.", "javax.swing."
|
||||
, "javafx."
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -64,11 +64,12 @@ public class ArgsParser {
|
||||
public static void main(String[] args) {
|
||||
// 模拟 main 方法的 args 参数
|
||||
String[] testArgs = {
|
||||
"C:\\Program Files\\test.txt", // 合法文件
|
||||
"C:\\invalid_file.exe", // 不存在的文件
|
||||
"D:\\Documents\\report.pdf", // 合法文件
|
||||
"not_a_path", // 无效路径
|
||||
"C:\\Windows\\system32" // 目录,不是文件
|
||||
"C:\\Program Files\\test.txt",
|
||||
"C:\\invalid_file.exe",
|
||||
"D:\\Documents\\report.pdf",
|
||||
"not_a_path",
|
||||
"C:\\Windows\\system32" ,
|
||||
"C:\\Users\\Administrator\\MCreatorWorkspaces\\AxisInnovatorsBox\\build.gradle"
|
||||
};
|
||||
|
||||
// 解析 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