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

feat(gui): show start page on jadx open

上级 5193c6a5
......@@ -135,6 +135,9 @@ public class JadxProject {
.map(TabStateViewAdapter::build)
.filter(Objects::nonNull)
.collect(Collectors.toList());
if (tabStateList.isEmpty()) {
return;
}
boolean dataChanged;
dataChanged = data.setOpenTabs(tabStateList);
dataChanged |= data.setActiveTab(activeTab);
......
......@@ -210,6 +210,11 @@ public class JadxSettings extends JadxCLIArgs {
partialSync(settings -> settings.recentProjects = recentProjects);
}
public void removeRecentProject(Path projectPath) {
recentProjects.remove(projectPath);
partialSync(settings -> settings.recentProjects = recentProjects);
}
public void saveWindowPos(Window window) {
WindowLocation pos = new WindowLocation(window.getClass().getSimpleName(), window.getBounds());
windowPos.put(pos.getWindowId(), pos);
......
......@@ -14,6 +14,7 @@ import jadx.api.JavaNode;
import jadx.core.dex.attributes.AFlag;
import jadx.core.dex.info.AccessInfo;
import jadx.core.dex.instructions.args.ArgType;
import jadx.gui.utils.Icons;
import jadx.gui.utils.OverlayIcon;
import jadx.gui.utils.UiUtils;
......@@ -84,10 +85,10 @@ public class JMethod extends JNode {
OverlayIcon overIcon = new OverlayIcon(icon);
if (accessFlags.isFinal()) {
overIcon.add(UiUtils.ICON_FINAL);
overIcon.add(Icons.FINAL);
}
if (accessFlags.isStatic()) {
overIcon.add(UiUtils.ICON_STATIC);
overIcon.add(Icons.STATIC);
}
return overIcon;
......
......@@ -125,12 +125,14 @@ import jadx.gui.ui.panel.IssuesPanel;
import jadx.gui.ui.panel.JDebuggerPanel;
import jadx.gui.ui.panel.ProgressPanel;
import jadx.gui.ui.popupmenu.JPackagePopupMenu;
import jadx.gui.ui.treenodes.StartPageNode;
import jadx.gui.ui.treenodes.SummaryNode;
import jadx.gui.update.JadxUpdate;
import jadx.gui.update.JadxUpdate.IUpdateCallback;
import jadx.gui.update.data.Release;
import jadx.gui.utils.CacheObject;
import jadx.gui.utils.FontUtils;
import jadx.gui.utils.Icons;
import jadx.gui.utils.LafManager;
import jadx.gui.utils.Link;
import jadx.gui.utils.NLS;
......@@ -152,7 +154,6 @@ public class MainWindow extends JFrame {
private static final double WINDOW_RATIO = 1 - BORDER_RATIO * 2;
public static final double SPLIT_PANE_RESIZE_WEIGHT = 0.15;
private static final ImageIcon ICON_OPEN = UiUtils.openSvgIcon("ui/openDisk");
private static final ImageIcon ICON_ADD_FILES = UiUtils.openSvgIcon("ui/addFile");
private static final ImageIcon ICON_SAVE_ALL = UiUtils.openSvgIcon("ui/menu-saveall");
private static final ImageIcon ICON_RELOAD = UiUtils.openSvgIcon("ui/refresh");
......@@ -249,7 +250,7 @@ public class MainWindow extends JFrame {
private void processCommandLineArgs() {
if (settings.getFiles().isEmpty()) {
openFileOrProject();
tabbedPane.showNode(new StartPageNode());
} else {
open(FileUtils.fileNamesToPaths(settings.getFiles()), this::handleSelectClassOption);
}
......@@ -286,12 +287,20 @@ public class MainWindow extends JFrame {
});
}
public void openFileOrProject() {
public void openFileDialog() {
showOpenDialog(FileDialog.OpenMode.OPEN);
}
public void openProjectDialog() {
showOpenDialog(FileDialog.OpenMode.OPEN_PROJECT);
}
private void showOpenDialog(FileDialog.OpenMode mode) {
saveAll();
if (!ensureProjectIsSaved()) {
return;
}
FileDialog fileDialog = new FileDialog(this, FileDialog.OpenMode.OPEN);
FileDialog fileDialog = new FileDialog(this, mode);
List<Path> openPaths = fileDialog.show();
if (!openPaths.isEmpty()) {
settings.setLastOpenFilePath(fileDialog.getCurrentDir());
......@@ -313,6 +322,7 @@ public class MainWindow extends JFrame {
}
private void newProject() {
saveAll();
if (!ensureProjectIsSaved()) {
return;
}
......@@ -386,7 +396,11 @@ public class MainWindow extends JFrame {
s -> update());
}
void open(List<Path> paths) {
public void open(Path path) {
open(Collections.singletonList(path), EMPTY_RUNNABLE);
}
public void open(List<Path> paths) {
open(paths, EMPTY_RUNNABLE);
}
......@@ -825,14 +839,15 @@ public class MainWindow extends JFrame {
}
private void initMenuAndToolbar() {
Action openAction = new AbstractAction(NLS.str("file.open_action"), ICON_OPEN) {
@Override
public void actionPerformed(ActionEvent e) {
openFileOrProject();
}
};
openAction.putValue(Action.SHORT_DESCRIPTION, NLS.str("file.open_action"));
openAction.putValue(Action.ACCELERATOR_KEY, getKeyStroke(KeyEvent.VK_O, UiUtils.ctrlButton()));
ActionHandler openAction = new ActionHandler(this::openFileDialog);
openAction.setNameAndDesc(NLS.str("file.open_action"));
openAction.setIcon(Icons.OPEN);
openAction.setKeyBinding(getKeyStroke(KeyEvent.VK_O, UiUtils.ctrlButton()));
ActionHandler openProject = new ActionHandler(this::openProjectDialog);
openProject.setNameAndDesc(NLS.str("file.open_project"));
openProject.setIcon(Icons.OPEN_PROJECT);
openProject.setKeyBinding(getKeyStroke(KeyEvent.VK_O, InputEvent.SHIFT_DOWN_MASK | UiUtils.ctrlButton()));
Action addFilesAction = new AbstractAction(NLS.str("file.add_files_action"), ICON_ADD_FILES) {
@Override
......@@ -842,7 +857,7 @@ public class MainWindow extends JFrame {
};
addFilesAction.putValue(Action.SHORT_DESCRIPTION, NLS.str("file.add_files_action"));
newProjectAction = new AbstractAction(NLS.str("file.new_project")) {
newProjectAction = new AbstractAction(NLS.str("file.new_project"), Icons.NEW_PROJECT) {
@Override
public void actionPerformed(ActionEvent e) {
newProject();
......@@ -1089,6 +1104,7 @@ public class MainWindow extends JFrame {
JMenu file = new JMenu(NLS.str("menu.file"));
file.setMnemonic(KeyEvent.VK_F);
file.add(openAction);
file.add(openProject);
file.add(addFilesAction);
file.addSeparator();
file.add(newProjectAction);
......
......@@ -9,7 +9,6 @@ import java.util.List;
import java.util.Map;
import javax.swing.BorderFactory;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JMenuItem;
......@@ -21,15 +20,13 @@ import javax.swing.plaf.basic.BasicButtonUI;
import jadx.gui.treemodel.JClass;
import jadx.gui.treemodel.JNode;
import jadx.gui.ui.panel.ContentPanel;
import jadx.gui.utils.Icons;
import jadx.gui.utils.NLS;
import jadx.gui.utils.UiUtils;
public class TabComponent extends JPanel {
private static final long serialVersionUID = -8147035487543610321L;
private static final ImageIcon ICON_CLOSE = UiUtils.openSvgIcon("ui/closeHovered");
private static final ImageIcon ICON_CLOSE_INACTIVE = UiUtils.openSvgIcon("ui/close");
private final TabbedPane tabbedPane;
private final ContentPanel contentPanel;
......@@ -71,8 +68,8 @@ public class TabComponent extends JPanel {
label.setIcon(node.getIcon());
final JButton closeBtn = new JButton();
closeBtn.setIcon(ICON_CLOSE_INACTIVE);
closeBtn.setRolloverIcon(ICON_CLOSE);
closeBtn.setIcon(Icons.CLOSE_INACTIVE);
closeBtn.setRolloverIcon(Icons.CLOSE);
closeBtn.setRolloverEnabled(true);
closeBtn.setOpaque(false);
closeBtn.setUI(new BasicButtonUI());
......
......@@ -29,6 +29,7 @@ public class FileDialog {
public enum OpenMode {
OPEN,
OPEN_PROJECT,
ADD,
SAVE_PROJECT,
EXPORT,
......@@ -95,13 +96,19 @@ public class FileDialog {
private void initForMode(OpenMode mode) {
switch (mode) {
case OPEN:
case OPEN_PROJECT:
case ADD:
fileExtList = new ArrayList<>(Arrays.asList("apk", "dex", "jar", "class", "smali", "zip", "aar", "arsc"));
if (mode == OpenMode.OPEN) {
fileExtList.addAll(Arrays.asList(JadxProject.PROJECT_EXTENSION, "aab"));
if (mode == OpenMode.OPEN_PROJECT) {
fileExtList = Collections.singletonList(JadxProject.PROJECT_EXTENSION);
title = NLS.str("file.open_title");
} else {
title = NLS.str("file.add_files_action");
fileExtList = new ArrayList<>(Arrays.asList("apk", "dex", "jar", "class", "smali", "zip", "xapk", "aar", "arsc"));
if (mode == OpenMode.OPEN) {
fileExtList.addAll(Arrays.asList(JadxProject.PROJECT_EXTENSION, "aab"));
title = NLS.str("file.open_title");
} else {
title = NLS.str("file.add_files_action");
}
}
selectionMode = JFileChooser.FILES_AND_DIRECTORIES;
currentDir = mainWindow.getSettings().getLastOpenFilePath();
......
package jadx.gui.ui.panel;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Font;
import java.nio.file.Path;
import java.util.List;
import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.ScrollPaneConstants;
import javax.swing.border.Border;
import javax.swing.border.TitledBorder;
import jadx.gui.settings.JadxSettings;
import jadx.gui.ui.MainWindow;
import jadx.gui.ui.TabbedPane;
import jadx.gui.ui.treenodes.StartPageNode;
import jadx.gui.utils.Icons;
import jadx.gui.utils.NLS;
public class StartPagePanel extends ContentPanel {
public StartPagePanel(TabbedPane tabbedPane, StartPageNode node) {
super(tabbedPane, node);
MainWindow mainWindow = tabbedPane.getMainWindow();
Font baseFont = mainWindow.getSettings().getFont();
JButton openFile = new JButton(NLS.str("file.open_title"), Icons.OPEN);
openFile.addActionListener(ev -> mainWindow.openFileDialog());
JButton openProject = new JButton(NLS.str("file.open_project"), Icons.OPEN_PROJECT);
openProject.addActionListener(ev -> mainWindow.openProjectDialog());
JPanel start = new JPanel();
start.setBorder(sectionFrame(NLS.str("start_page.start"), baseFont));
start.setLayout(new BoxLayout(start, BoxLayout.LINE_AXIS));
start.add(openFile);
start.add(Box.createRigidArea(new Dimension(10, 0)));
start.add(openProject);
start.add(Box.createHorizontalGlue());
JPanel recentPanel = new JPanel();
JScrollPane scrollPane = new JScrollPane(recentPanel);
scrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED);
scrollPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED);
scrollPane.setPreferredSize(new Dimension(400, 200));
scrollPane.setBorder(BorderFactory.createEmptyBorder());
fillRecentPanel(recentPanel, scrollPane, mainWindow);
JPanel recent = new JPanel();
recent.setBorder(sectionFrame(NLS.str("start_page.recent"), baseFont));
recent.setLayout(new BoxLayout(recent, BoxLayout.PAGE_AXIS));
recent.add(scrollPane);
JPanel center = new JPanel();
center.setLayout(new BorderLayout(10, 10));
center.add(start, BorderLayout.PAGE_START);
center.add(recent, BorderLayout.CENTER);
center.setMaximumSize(new Dimension(700, 600));
center.setAlignmentX(CENTER_ALIGNMENT);
setLayout(new BoxLayout(this, BoxLayout.PAGE_AXIS));
setBorder(BorderFactory.createEmptyBorder(50, 50, 50, 50));
add(Box.createVerticalGlue());
add(center);
add(Box.createVerticalGlue());
}
private void fillRecentPanel(JPanel panel, JScrollPane scrollPane, MainWindow mainWindow) {
JadxSettings settings = mainWindow.getSettings();
List<Path> recentProjects = settings.getRecentProjects();
panel.removeAll();
panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
Font baseFont = settings.getFont();
Font font = baseFont.deriveFont(baseFont.getSize() - 1f);
for (Path path : recentProjects) {
JButton openBtn = new JButton(path.getFileName().toString());
openBtn.setToolTipText(path.toAbsolutePath().toString());
openBtn.setFont(font);
openBtn.setBorderPainted(false);
openBtn.addActionListener(ev -> mainWindow.open(path));
JButton removeBtn = new JButton();
removeBtn.setIcon(Icons.CLOSE_INACTIVE);
removeBtn.setRolloverIcon(Icons.CLOSE);
removeBtn.setRolloverEnabled(true);
removeBtn.setFocusable(false);
removeBtn.setBorder(null);
removeBtn.setBorderPainted(false);
removeBtn.setContentAreaFilled(false);
removeBtn.setOpaque(true);
removeBtn.addActionListener(e -> {
mainWindow.getSettings().removeRecentProject(path);
fillRecentPanel(panel, scrollPane, mainWindow);
panel.revalidate();
scrollPane.repaint();
});
JPanel linePanel = new JPanel();
linePanel.setLayout(new BoxLayout(linePanel, BoxLayout.LINE_AXIS));
linePanel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
linePanel.add(openBtn);
linePanel.add(Box.createHorizontalGlue());
linePanel.add(removeBtn);
panel.add(linePanel);
}
panel.add(Box.createVerticalGlue());
}
private static Border sectionFrame(String title, Font font) {
TitledBorder titledBorder = BorderFactory.createTitledBorder(title);
titledBorder.setTitleFont(font.deriveFont(Font.BOLD, font.getSize() + 1));
Border spacing = BorderFactory.createEmptyBorder(10, 10, 10, 10);
return BorderFactory.createCompoundBorder(titledBorder, spacing);
}
@Override
public void loadSettings() {
}
}
package jadx.gui.ui.treenodes;
import javax.swing.Icon;
import jadx.gui.treemodel.JClass;
import jadx.gui.treemodel.JNode;
import jadx.gui.ui.TabbedPane;
import jadx.gui.ui.panel.ContentPanel;
import jadx.gui.ui.panel.StartPagePanel;
import jadx.gui.utils.Icons;
import jadx.gui.utils.NLS;
public class StartPageNode extends JNode {
private static final long serialVersionUID = 8983134608645736174L;
@Override
public ContentPanel getContentPanel(TabbedPane tabbedPane) {
return new StartPagePanel(tabbedPane, this);
}
@Override
public String makeString() {
return NLS.str("start_page.title");
}
@Override
public Icon getIcon() {
return Icons.START_PAGE;
}
@Override
public JClass getJParent() {
return null;
}
}
package jadx.gui.utils;
import javax.swing.ImageIcon;
import static jadx.gui.utils.UiUtils.openSvgIcon;
public class Icons {
public static final ImageIcon OPEN = openSvgIcon("ui/openDisk");
public static final ImageIcon OPEN_PROJECT = openSvgIcon("ui/projectDirectory");
public static final ImageIcon NEW_PROJECT = openSvgIcon("ui/newFolder");
public static final ImageIcon CLOSE = openSvgIcon("ui/closeHovered");
public static final ImageIcon CLOSE_INACTIVE = openSvgIcon("ui/close");
public static final ImageIcon STATIC = openSvgIcon("nodes/staticMark");
public static final ImageIcon FINAL = openSvgIcon("nodes/finalMark");
public static final ImageIcon START_PAGE = openSvgIcon("nodes/newWindow");
}
......@@ -46,9 +46,6 @@ import jadx.gui.ui.codearea.AbstractCodeArea;
public class UiUtils {
private static final Logger LOG = LoggerFactory.getLogger(UiUtils.class);
public static final ImageIcon ICON_STATIC = openSvgIcon("nodes/staticMark");
public static final ImageIcon ICON_FINAL = openSvgIcon("nodes/finalMark");
/**
* The minimum about of memory in bytes we are trying to keep free, otherwise the application may
* run out of heap
......@@ -190,10 +187,10 @@ public class UiUtils {
}
OverlayIcon overIcon = new OverlayIcon(icon);
if (af.isFinal()) {
overIcon.add(ICON_FINAL);
overIcon.add(Icons.FINAL);
}
if (af.isStatic()) {
overIcon.add(ICON_STATIC);
overIcon.add(Icons.STATIC);
}
return overIcon;
}
......
......@@ -11,6 +11,10 @@ public class ActionHandler extends AbstractAction {
private final Consumer<ActionEvent> consumer;
public ActionHandler(Runnable action) {
this.consumer = ev -> action.run();
}
public ActionHandler(Consumer<ActionEvent> consumer) {
this.consumer = consumer;
}
......
......@@ -23,6 +23,7 @@ menu.update_label=Neue Version %s verfügbar!
file.open_action=Datei öffnen…
file.add_files_action=Dateien hinzufügen…
file.open_title=Datei öffnen
#file.open_project=Open project
file.new_project=Neues Projekt
file.save_project=Projekt speichern
file.save_project_as=Projekt speichern als…
......@@ -35,6 +36,10 @@ file.export_gradle=Als Gradle-Projekt speichern
file.save_all_msg=Verzeichnis für das Speichern dekompilierter Ressourcen auswählen
file.exit=Beenden
#start_page.title=Start page
#start_page.start=Start
#start_page.recent=Recent projects
tree.sources_title=Quelltexte
tree.resources_title=Ressourcen
tree.loading=Laden…
......
......@@ -23,6 +23,7 @@ menu.update_label=New version %s available!
file.open_action=Open files ...
file.add_files_action=Add files
file.open_title=Open file
file.open_project=Open project
file.new_project=New project
file.save_project=Save project
file.save_project_as=Save project as...
......@@ -35,6 +36,10 @@ file.export_gradle=Save as gradle project
file.save_all_msg=Select directory for save decompiled sources
file.exit=Exit
start_page.title=Start page
start_page.start=Start
start_page.recent=Recent projects
tree.sources_title=Source code
tree.resources_title=Resources
tree.loading=Loading...
......
......@@ -23,6 +23,7 @@ menu.update_label=¡Nueva versión %s disponible!
file.open_action=Abrir archivo...
#file.add_files_action=Add files ...
file.open_title=Abrir archivo
#file.open_project=Open project
#file.new_project=
#file.save_project=
#file.save_project_as=
......@@ -35,6 +36,10 @@ file.export_gradle=Guardar como proyecto Gradle
file.save_all_msg=Seleccionar carpeta para guardar fuentes descompiladas
file.exit=Salir
#start_page.title=Start page
#start_page.start=Start
#start_page.recent=Recent projects
tree.sources_title=Código fuente
tree.resources_title=Recursos
tree.loading=Cargando...
......
......@@ -23,6 +23,7 @@ menu.update_label=새 버전 %s 이(가) 존재합니다!
file.open_action=파일 열기 ...
file.add_files_action=파일 추가
file.open_title=파일 열기
#file.open_project=Open project
file.new_project=새 프로젝트
file.save_project=프로젝트 저장
file.save_project_as=다른 이름으로 프로젝트 저장...
......@@ -35,6 +36,10 @@ file.export_gradle=Gradle 프로젝트로 저장
file.save_all_msg=디컴파일된 소스를 저장할 디렉토리 선택
file.exit=나가기
#start_page.title=Start page
#start_page.start=Start
#start_page.recent=Recent projects
tree.sources_title=소스코드
tree.resources_title=리소스
tree.loading=로딩중...
......
......@@ -23,6 +23,7 @@ menu.update_label=Nova versão %s disponível!
file.open_action=Abrir arquivos...
file.add_files_action=Adicionar arquivos
file.open_title=Abrir arquivo
#file.open_project=Open project
file.new_project=Novo projeto
file.save_project=Salvar projeto
file.save_project_as=Salvar projeto como...
......@@ -35,6 +36,10 @@ file.export_gradle=Salvar como um projeto gradle
file.save_all_msg=Selecionar diretório para salvar arquivos descompilados
file.exit=Sair
#start_page.title=Start page
#start_page.start=Start
#start_page.recent=Recent projects
tree.sources_title=Código fonte
tree.resources_title=Recursos
tree.loading=Carregando...
......
......@@ -23,6 +23,7 @@ menu.update_label=发现新版本 %s!
file.open_action=打开文件...
file.add_files_action=添加文件
file.open_title=打开文件
#file.open_project=Open project
file.new_project=新建项目
file.save_project=保存项目
file.save_project_as=另存项目为...
......@@ -35,6 +36,10 @@ file.export_gradle=另存为 Gradle 项目
file.save_all_msg=请选择保存反编译资源的目录
file.exit=退出
#start_page.title=Start page
#start_page.start=Start
#start_page.recent=Recent projects
tree.sources_title=源代码
tree.resources_title=资源文件
tree.loading=加载中...
......
......@@ -23,6 +23,7 @@ menu.update_label=新版本 %s 可供下載!
file.open_action=開啟檔案...
file.add_files_action=新增檔案
file.open_title=開啟檔案
#file.open_project=Open project
file.new_project=新建專案
file.save_project=儲存專案
file.save_project_as=另存專案...
......@@ -35,6 +36,10 @@ file.export_gradle=另存為 gradle 專案
file.save_all_msg=選擇儲存反編譯原始碼的路徑
file.exit=離開
#start_page.title=Start page
#start_page.start=Start
#start_page.recent=Recent projects
tree.sources_title=原始碼
tree.resources_title=資源
tree.loading=載入中...
......
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M2 2H13V4V5H11V4H4V11H5V13H2V12V11V4V2Z" fill="#6E6E6E"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M14 6H6V14H14V6ZM12 8H8V12H12V8Z" fill="#6E6E6E"/>
</svg>
<!-- Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. -->
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
<g fill="none" fill-rule="evenodd">
<path fill="#6E6E6E" d="M9,13 L2,13 L2,5 L2,3 L6.60006714,3 L7.75640322,5 L14,5 L14,8 L11,8 L11,10 L9,10 L9,13 Z"/>
<rect width="2" height="6" x="12" y="9" fill="#6E6E6E"/>
<rect width="6" height="2" x="10" y="11" fill="#6E6E6E"/>
</g>
</svg>
<!-- Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. -->
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
<g fill="none" fill-rule="evenodd">
<path fill="#6E6E6E" d="M10,15 L10,10 L15,10 L15,15 L10,15 Z M11,14 L13,14 L13,13 L11,13 L11,14 Z"/>
<path fill="#6E6E6E" d="M14,9 L9,9 L9,13 L2,13 L2,5 L2,3 L6.60006714,3 L7.75640322,5 L14,5 L14,9 Z"/>
</g>
</svg>
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册