未验证 提交 1a9cb832 编写于 作者: S Skylot

feat(gui): add alternative file open dialog (#1709)

上级 6844a46c
......@@ -73,6 +73,7 @@ public class JadxSettings extends JadxCLIArgs {
private boolean showHeapUsageBar = false;
private boolean alwaysSelectOpened = false;
private boolean useAlternativeFileDialog = false;
private Map<String, WindowLocation> windowPos = new HashMap<>();
private int mainWindowExtendedState = JFrame.NORMAL;
......@@ -268,6 +269,14 @@ public class JadxSettings extends JadxCLIArgs {
partialSync(settings -> settings.alwaysSelectOpened = alwaysSelectOpened);
}
public boolean isUseAlternativeFileDialog() {
return useAlternativeFileDialog;
}
public void setUseAlternativeFileDialog(boolean useAlternativeFileDialog) {
this.useAlternativeFileDialog = useAlternativeFileDialog;
}
public String getExcludedPackages() {
return excludedPackages;
}
......
......@@ -643,6 +643,10 @@ public class JadxSettingsWindow extends JDialog {
jumpOnDoubleClick.setSelected(settings.isJumpOnDoubleClick());
jumpOnDoubleClick.addItemListener(e -> settings.setJumpOnDoubleClick(e.getStateChange() == ItemEvent.SELECTED));
JCheckBox useAltFileDialog = new JCheckBox();
useAltFileDialog.setSelected(settings.isUseAlternativeFileDialog());
useAltFileDialog.addItemListener(e -> settings.setUseAlternativeFileDialog(e.getStateChange() == ItemEvent.SELECTED));
JCheckBox update = new JCheckBox();
update.setSelected(settings.isCheckForUpdates());
update.addItemListener(e -> settings.setCheckForUpdates(e.getStateChange() == ItemEvent.SELECTED));
......@@ -665,6 +669,7 @@ public class JadxSettingsWindow extends JDialog {
group.addRow(NLS.str("preferences.language"), languageCbx);
group.addRow(NLS.str("preferences.lineNumbersMode"), lineNumbersMode);
group.addRow(NLS.str("preferences.jumpOnDoubleClick"), jumpOnDoubleClick);
group.addRow(NLS.str("preferences.useAlternativeFileDialog"), useAltFileDialog);
group.addRow(NLS.str("preferences.check_for_updates"), update);
group.addRow(NLS.str("preferences.cfg"), cfg);
group.addRow(NLS.str("preferences.raw_cfg"), rawCfg);
......
......@@ -116,10 +116,11 @@ import jadx.gui.ui.codearea.EditorTheme;
import jadx.gui.ui.codearea.EditorViewState;
import jadx.gui.ui.dialog.ADBDialog;
import jadx.gui.ui.dialog.AboutDialog;
import jadx.gui.ui.dialog.FileDialog;
import jadx.gui.ui.dialog.LogViewerDialog;
import jadx.gui.ui.dialog.RenameDialog;
import jadx.gui.ui.dialog.SearchDialog;
import jadx.gui.ui.filedialog.FileDialogWrapper;
import jadx.gui.ui.filedialog.FileOpenMode;
import jadx.gui.ui.panel.ContentPanel;
import jadx.gui.ui.panel.IssuesPanel;
import jadx.gui.ui.panel.JDebuggerPanel;
......@@ -293,19 +294,19 @@ public class MainWindow extends JFrame {
}
public void openFileDialog() {
showOpenDialog(FileDialog.OpenMode.OPEN);
showOpenDialog(FileOpenMode.OPEN);
}
public void openProjectDialog() {
showOpenDialog(FileDialog.OpenMode.OPEN_PROJECT);
showOpenDialog(FileOpenMode.OPEN_PROJECT);
}
private void showOpenDialog(FileDialog.OpenMode mode) {
private void showOpenDialog(FileOpenMode mode) {
saveAll();
if (!ensureProjectIsSaved()) {
return;
}
FileDialog fileDialog = new FileDialog(this, mode);
FileDialogWrapper fileDialog = new FileDialogWrapper(this, mode);
List<Path> openPaths = fileDialog.show();
if (!openPaths.isEmpty()) {
settings.setLastOpenFilePath(fileDialog.getCurrentDir());
......@@ -314,7 +315,7 @@ public class MainWindow extends JFrame {
}
public void addFiles() {
FileDialog fileDialog = new FileDialog(this, FileDialog.OpenMode.ADD);
FileDialogWrapper fileDialog = new FileDialogWrapper(this, FileOpenMode.ADD);
List<Path> addPaths = fileDialog.show();
if (!addPaths.isEmpty()) {
addFiles(addPaths);
......@@ -346,7 +347,7 @@ public class MainWindow extends JFrame {
}
private void saveProjectAs() {
FileDialog fileDialog = new FileDialog(this, FileDialog.OpenMode.SAVE_PROJECT);
FileDialogWrapper fileDialog = new FileDialogWrapper(this, FileOpenMode.SAVE_PROJECT);
if (project.getFilePaths().size() == 1) {
// If there is only one file loaded we suggest saving the jadx project file next to the loaded file
Path projectPath = getProjectPathForFile(this.project.getFilePaths().get(0));
......@@ -377,7 +378,7 @@ public class MainWindow extends JFrame {
}
private void exportMappings(MappingFormat mappingFormat) {
FileDialog fileDialog = new FileDialog(this, FileDialog.OpenMode.CUSTOM_SAVE);
FileDialogWrapper fileDialog = new FileDialogWrapper(this, FileOpenMode.CUSTOM_SAVE);
fileDialog.setTitle(NLS.str("file.export_mappings_as"));
Path workingDir = project.getWorkingDir();
Path baseDir = workingDir != null ? workingDir : settings.getLastSaveFilePath();
......@@ -659,7 +660,7 @@ public class MainWindow extends JFrame {
}
private void saveAll(boolean export) {
FileDialog fileDialog = new FileDialog(this, FileDialog.OpenMode.EXPORT);
FileDialogWrapper fileDialog = new FileDialogWrapper(this, FileOpenMode.EXPORT);
List<Path> saveDirs = fileDialog.show();
if (saveDirs.isEmpty()) {
return;
......
package jadx.gui.ui.filedialog;
import java.awt.Component;
import java.awt.HeadlessException;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.File;
import java.nio.file.Path;
import java.util.Collections;
import java.util.List;
import javax.swing.JDialog;
import javax.swing.JFileChooser;
import javax.swing.JOptionPane;
import javax.swing.filechooser.FileNameExtensionFilter;
import jadx.api.plugins.utils.CommonFileUtils;
import jadx.core.utils.Utils;
import jadx.core.utils.files.FileUtils;
import jadx.gui.ui.MainWindow;
import jadx.gui.utils.NLS;
class CustomFileChooser extends JFileChooser {
private final FileDialogWrapper data;
public CustomFileChooser(FileDialogWrapper data) {
super(data.getCurrentDir() == null ? CommonFileUtils.CWD : data.getCurrentDir().toFile());
this.data = data;
}
public List<Path> showDialog() {
setToolTipText(data.getTitle());
setFileSelectionMode(data.getSelectionMode());
setMultiSelectionEnabled(data.isOpen());
setAcceptAllFileFilterUsed(true);
List<String> fileExtList = data.getFileExtList();
if (Utils.notEmpty(fileExtList)) {
String description = NLS.str("file_dialog.supported_files") + ": (" + Utils.listToString(fileExtList) + ')';
setFileFilter(new FileNameExtensionFilter(description, fileExtList.toArray(new String[0])));
}
if (data.getSelectedFile() != null) {
setSelectedFile(data.getSelectedFile().toFile());
}
MainWindow mainWindow = data.getMainWindow();
int ret = data.isOpen() ? showOpenDialog(mainWindow) : showSaveDialog(mainWindow);
if (ret != JFileChooser.APPROVE_OPTION) {
return Collections.emptyList();
}
data.setCurrentDir(getCurrentDirectory().toPath());
File[] selectedFiles = getSelectedFiles();
if (selectedFiles.length != 0) {
return FileUtils.toPaths(selectedFiles);
}
File chosenFile = getSelectedFile();
if (chosenFile != null) {
return Collections.singletonList(chosenFile.toPath());
}
return Collections.emptyList();
}
@Override
protected JDialog createDialog(Component parent) throws HeadlessException {
JDialog dialog = super.createDialog(parent);
dialog.setTitle(data.getTitle());
dialog.setLocationRelativeTo(null);
data.getMainWindow().getSettings().loadWindowPos(dialog);
dialog.addWindowListener(new WindowAdapter() {
@Override
public void windowClosed(WindowEvent e) {
data.getMainWindow().getSettings().saveWindowPos(dialog);
super.windowClosed(e);
}
});
return dialog;
}
@Override
public void approveSelection() {
if (data.getSelectionMode() == FILES_AND_DIRECTORIES) {
File currentFile = getSelectedFile();
if (currentFile.isDirectory()) {
int option = JOptionPane.showConfirmDialog(
data.getMainWindow(),
NLS.str("file_dialog.load_dir_confirm") + "\n " + currentFile,
NLS.str("file_dialog.load_dir_title"),
JOptionPane.YES_NO_OPTION);
if (option != JOptionPane.YES_OPTION) {
this.setCurrentDirectory(currentFile);
this.updateUI();
return;
}
}
}
super.approveSelection();
}
}
package jadx.gui.ui.filedialog;
import java.awt.FileDialog;
import java.io.File;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Collections;
import java.util.List;
import jadx.core.utils.ListUtils;
import jadx.core.utils.Utils;
import jadx.core.utils.files.FileUtils;
class CustomFileDialog {
private final FileDialogWrapper data;
public CustomFileDialog(FileDialogWrapper data) {
this.data = data;
}
public List<Path> showDialog() {
FileDialog fileDialog = new FileDialog(data.getMainWindow(), data.getTitle());
fileDialog.setMode(data.isOpen() ? FileDialog.LOAD : FileDialog.SAVE);
fileDialog.setMultipleMode(true);
List<String> fileExtList = data.getFileExtList();
if (Utils.notEmpty(fileExtList)) {
fileDialog.setFilenameFilter((dir, name) -> ListUtils.anyMatch(fileExtList, name::endsWith));
}
if (data.getSelectedFile() != null) {
fileDialog.setFile(data.getSelectedFile().toAbsolutePath().toString());
}
if (data.getCurrentDir() != null) {
fileDialog.setDirectory(data.getCurrentDir().toAbsolutePath().toString());
}
fileDialog.setVisible(true);
File[] selectedFiles = fileDialog.getFiles();
if (!Utils.isEmpty(selectedFiles)) {
data.setCurrentDir(Paths.get(fileDialog.getDirectory()));
return FileUtils.toPaths(selectedFiles);
}
if (fileDialog.getFile() != null) {
return Collections.singletonList(Paths.get(fileDialog.getFile()));
}
return Collections.emptyList();
}
}
package jadx.gui.ui.dialog;
package jadx.gui.ui.filedialog;
import java.awt.Component;
import java.awt.HeadlessException;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.File;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import javax.swing.JDialog;
import javax.swing.JFileChooser;
import javax.swing.JOptionPane;
import javax.swing.filechooser.FileNameExtensionFilter;
import org.jetbrains.annotations.Nullable;
import jadx.api.plugins.utils.CommonFileUtils;
import jadx.core.utils.Utils;
import jadx.core.utils.files.FileUtils;
import jadx.gui.settings.JadxProject;
import jadx.gui.ui.MainWindow;
import jadx.gui.utils.NLS;
public class FileDialog {
public enum OpenMode {
OPEN,
OPEN_PROJECT,
ADD,
SAVE_PROJECT,
EXPORT,
CUSTOM_SAVE,
CUSTOM_OPEN
}
public class FileDialogWrapper {
private final MainWindow mainWindow;
......@@ -46,7 +25,7 @@ public class FileDialog {
private @Nullable Path currentDir;
private @Nullable Path selectedFile;
public FileDialog(MainWindow mainWindow, OpenMode mode) {
public FileDialogWrapper(MainWindow mainWindow, FileOpenMode mode) {
this.mainWindow = mainWindow;
initForMode(mode);
}
......@@ -72,38 +51,24 @@ public class FileDialog {
}
public List<Path> show() {
FileChooser fileChooser = buildFileChooser();
int ret = isOpen ? fileChooser.showOpenDialog(mainWindow) : fileChooser.showSaveDialog(mainWindow);
if (ret != JFileChooser.APPROVE_OPTION) {
return Collections.emptyList();
}
currentDir = fileChooser.getCurrentDirectory().toPath();
File[] selectedFiles = fileChooser.getSelectedFiles();
if (selectedFiles.length != 0) {
return FileUtils.toPaths(selectedFiles);
}
File chosenFile = fileChooser.getSelectedFile();
if (chosenFile != null) {
return Collections.singletonList(chosenFile.toPath());
if (mainWindow.getSettings().isUseAlternativeFileDialog()) {
return new CustomFileDialog(this).showDialog();
} else {
return new CustomFileChooser(this).showDialog();
}
return Collections.emptyList();
}
public Path getCurrentDir() {
return currentDir;
}
private void initForMode(OpenMode mode) {
private void initForMode(FileOpenMode mode) {
switch (mode) {
case OPEN:
case OPEN_PROJECT:
case ADD:
if (mode == OpenMode.OPEN_PROJECT) {
if (mode == FileOpenMode.OPEN_PROJECT) {
fileExtList = Collections.singletonList(JadxProject.PROJECT_EXTENSION);
title = NLS.str("file.open_title");
} else {
fileExtList = new ArrayList<>(Arrays.asList("apk", "dex", "jar", "class", "smali", "zip", "xapk", "aar", "arsc"));
if (mode == OpenMode.OPEN) {
if (mode == FileOpenMode.OPEN) {
fileExtList.addAll(Arrays.asList(JadxProject.PROJECT_EXTENSION, "aab"));
title = NLS.str("file.open_title");
} else {
......@@ -133,70 +98,41 @@ public class FileDialog {
case CUSTOM_SAVE:
isOpen = false;
currentDir = mainWindow.getSettings().getLastSaveFilePath();
break;
case CUSTOM_OPEN:
isOpen = true;
currentDir = mainWindow.getSettings().getLastOpenFilePath();
break;
}
}
private FileChooser buildFileChooser() {
FileChooser fileChooser = new FileChooser(currentDir);
fileChooser.setToolTipText(title);
fileChooser.setFileSelectionMode(selectionMode);
fileChooser.setMultiSelectionEnabled(isOpen);
fileChooser.setAcceptAllFileFilterUsed(true);
if (Utils.notEmpty(fileExtList)) {
String description = NLS.str("file_dialog.supported_files") + ": (" + Utils.listToString(fileExtList) + ')';
fileChooser.setFileFilter(new FileNameExtensionFilter(description, fileExtList.toArray(new String[0])));
}
if (selectedFile != null) {
fileChooser.setSelectedFile(selectedFile.toFile());
}
return fileChooser;
public Path getCurrentDir() {
return currentDir;
}
private class FileChooser extends JFileChooser {
public MainWindow getMainWindow() {
return mainWindow;
}
public FileChooser(@Nullable Path currentDirectory) {
super(currentDirectory == null ? CommonFileUtils.CWD : currentDirectory.toFile());
}
public boolean isOpen() {
return isOpen;
}
@Override
protected JDialog createDialog(Component parent) throws HeadlessException {
JDialog dialog = super.createDialog(parent);
dialog.setTitle(title);
dialog.setLocationRelativeTo(null);
mainWindow.getSettings().loadWindowPos(dialog);
dialog.addWindowListener(new WindowAdapter() {
@Override
public void windowClosed(WindowEvent e) {
mainWindow.getSettings().saveWindowPos(dialog);
super.windowClosed(e);
}
});
return dialog;
}
public String getTitle() {
return title;
}
@Override
public void approveSelection() {
if (selectionMode == FILES_AND_DIRECTORIES) {
File currentFile = getSelectedFile();
if (currentFile.isDirectory()) {
int option = JOptionPane.showConfirmDialog(
mainWindow,
NLS.str("file_dialog.load_dir_confirm") + "\n " + currentFile,
NLS.str("file_dialog.load_dir_title"),
JOptionPane.YES_NO_OPTION);
if (option != JOptionPane.YES_OPTION) {
this.setCurrentDirectory(currentFile);
this.updateUI();
return;
}
}
}
super.approveSelection();
}
public List<String> getFileExtList() {
return fileExtList;
}
public int getSelectionMode() {
return selectionMode;
}
public Path getSelectedFile() {
return selectedFile;
}
}
package jadx.gui.ui.filedialog;
public enum FileOpenMode {
OPEN,
OPEN_PROJECT,
ADD,
SAVE_PROJECT,
EXPORT,
CUSTOM_SAVE,
CUSTOM_OPEN
}
......@@ -144,6 +144,7 @@ preferences.other=Andere
preferences.language=Sprache
preferences.lineNumbersMode=Editor Zeilennummern-Modus
preferences.jumpOnDoubleClick=Sprung bei Doppelklick aktivieren
#preferences.useAlternativeFileDialog=Use alternative file dialog
preferences.check_for_updates=Nach Updates beim Start suchen
preferences.useDx=dx/d8 zur Konvertierung von Java Bytecode verwenden
preferences.decompilationMode=Dekompilierungsmodus
......
......@@ -144,6 +144,7 @@ preferences.other=Other
preferences.language=Language
preferences.lineNumbersMode=Editor line numbers mode
preferences.jumpOnDoubleClick=Enable jump on double click
preferences.useAlternativeFileDialog=Use alternative file dialog
preferences.check_for_updates=Check for updates on startup
preferences.useDx=Use dx/d8 to convert java bytecode
preferences.decompilationMode=Decompilation mode
......
......@@ -144,6 +144,7 @@ preferences.other=Otros
preferences.language=Idioma
#preferences.lineNumbersMode=Editor line numbers mode
#preferences.jumpOnDoubleClick=Enable jump on double click
#preferences.useAlternativeFileDialog=Use alternative file dialog
preferences.check_for_updates=Buscar actualizaciones al iniciar
#preferences.useDx=Use dx/d8 to convert java bytecode
#preferences.decompilationMode=Decompilation mode
......
......@@ -144,6 +144,7 @@ preferences.other=기타
preferences.language=언어
preferences.lineNumbersMode=편집기 줄 번호 모드
preferences.jumpOnDoubleClick=더블 클릭 시 점프 활성화
#preferences.useAlternativeFileDialog=Use alternative file dialog
preferences.check_for_updates=시작시 업데이트 확인
preferences.useDx=dx/d8을 사용하여 Java 바이트 코드 변환
preferences.decompilationMode=디컴파일 모드
......
......@@ -144,6 +144,7 @@ preferences.other=Outro
preferences.language=Idioma
preferences.lineNumbersMode=Modo do contador de linhas do editor
preferences.jumpOnDoubleClick=Ativar salto no duplo clique
#preferences.useAlternativeFileDialog=Use alternative file dialog
preferences.check_for_updates=Verificar por atualizações ao inicializar
preferences.useDx=Usar dx/d8 para converter bytecode Java
preferences.decompilationMode=Modo de descompilação
......
......@@ -144,6 +144,7 @@ preferences.other=其他
preferences.language=语言
preferences.lineNumbersMode=编辑器行号模式
preferences.jumpOnDoubleClick=启用双击跳转
#preferences.useAlternativeFileDialog=Use alternative file dialog
preferences.check_for_updates=启动时检查更新
preferences.useDx=使用 dx/d8 来转换java字节码
preferences.decompilationMode=反编译模式
......
......@@ -144,6 +144,7 @@ preferences.other=其他
preferences.language=語言
preferences.lineNumbersMode=編輯器行號模式
preferences.jumpOnDoubleClick=啟用點擊兩下時跳躍
#preferences.useAlternativeFileDialog=Use alternative file dialog
preferences.check_for_updates=啟動時檢查更新
preferences.useDx=使用 dx/d8 來轉換 Java 位元組碼
preferences.decompilationMode=反編譯模式
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册