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

feat(gui): save open tabs in project file

上级 f3700595
......@@ -5,7 +5,6 @@ import java.io.Writer;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
......@@ -25,8 +24,11 @@ import jadx.api.data.impl.JadxCodeRef;
import jadx.api.data.impl.JadxCodeRename;
import jadx.api.data.impl.JadxNodeRef;
import jadx.core.utils.GsonUtils;
import jadx.core.utils.Utils;
import jadx.core.utils.exceptions.JadxRuntimeException;
import jadx.gui.settings.data.ProjectData;
import jadx.gui.ui.MainWindow;
import jadx.gui.ui.codearea.EditorViewState;
import jadx.gui.utils.PathTypeAdapter;
public class JadxProject {
......@@ -53,14 +55,7 @@ public class JadxProject {
private transient boolean initial = true;
private transient boolean saved;
private List<Path> files;
private List<String[]> treeExpansions = new ArrayList<>();
private JadxCodeData codeData = new JadxCodeData();
private int projectVersion;
public JadxProject() {
}
private ProjectData data = new ProjectData();
public void setSettings(JadxSettings settings) {
this.settings = settings;
......@@ -76,7 +71,7 @@ public class JadxProject {
private void setProjectPath(Path projectPath) {
this.projectPath = projectPath;
name = projectPath.getFileName().toString();
this.name = projectPath.getFileName().toString();
int dotPos = name.lastIndexOf('.');
if (dotPos != -1) {
name = name.substring(0, dotPos);
......@@ -85,27 +80,27 @@ public class JadxProject {
}
public List<Path> getFilePaths() {
return files;
return data.getFiles();
}
public void setFilePath(List<Path> files) {
if (!files.equals(getFilePaths())) {
this.files = files;
data.setFiles(files);
changed();
}
}
public List<String[]> getTreeExpansions() {
return treeExpansions;
return data.getTreeExpansions();
}
public void addTreeExpansion(String[] expansion) {
treeExpansions.add(expansion);
data.getTreeExpansions().add(expansion);
changed();
}
public void removeTreeExpansion(String[] expansion) {
treeExpansions.removeIf(strings -> isParentOfExpansion(expansion, strings));
data.getTreeExpansions().removeIf(strings -> isParentOfExpansion(expansion, strings));
changed();
}
......@@ -123,14 +118,28 @@ public class JadxProject {
}
public JadxCodeData getCodeData() {
return codeData;
return data.getCodeData();
}
public void setCodeData(JadxCodeData codeData) {
this.codeData = codeData;
data.setCodeData(codeData);
changed();
}
public void saveOpenTabs(List<EditorViewState> tabs, int activeTab) {
data.setOpenTabs(Utils.collectionMap(tabs, TabStateViewAdapter::build));
data.setActiveTab(activeTab);
changed();
}
public List<EditorViewState> getOpenTabs(MainWindow mw) {
return Utils.collectionMap(data.getOpenTabs(), s -> TabStateViewAdapter.load(mw, s));
}
public int getActiveTab() {
return data.getActiveTab();
}
private void changed() {
if (settings != null && settings.isAutoSaveProject()) {
save();
......@@ -163,7 +172,7 @@ public class JadxProject {
public void save() {
if (getProjectPath() != null) {
try (Writer writer = Files.newBufferedWriter(getProjectPath(), StandardCharsets.UTF_8)) {
GSON.toJson(this, writer);
GSON.toJson(data, writer);
saved = true;
} catch (Exception e) {
LOG.error("Error saving project", e);
......@@ -173,7 +182,8 @@ public class JadxProject {
public static JadxProject from(Path path) {
try (Reader reader = Files.newBufferedReader(path, StandardCharsets.UTF_8)) {
JadxProject project = GSON.fromJson(reader, JadxProject.class);
JadxProject project = new JadxProject();
project.data = GSON.fromJson(reader, ProjectData.class);
project.saved = true;
project.setProjectPath(path);
project.upgrade();
......@@ -185,7 +195,7 @@ public class JadxProject {
}
private void upgrade() {
int fromVersion = projectVersion;
int fromVersion = data.getProjectVersion();
LOG.debug("upgrade settings from version: {} to {}", fromVersion, CURRENT_PROJECT_VERSION);
if (fromVersion == 0) {
fromVersion++;
......@@ -193,7 +203,7 @@ public class JadxProject {
if (fromVersion != CURRENT_PROJECT_VERSION) {
throw new JadxRuntimeException("Project update failed");
}
projectVersion = CURRENT_PROJECT_VERSION;
data.setProjectVersion(CURRENT_PROJECT_VERSION);
save();
}
}
package jadx.gui.settings;
import org.jetbrains.annotations.Nullable;
import jadx.api.JavaClass;
import jadx.gui.settings.data.TabViewState;
import jadx.gui.settings.data.ViewPoint;
import jadx.gui.treemodel.JClass;
import jadx.gui.treemodel.JNode;
import jadx.gui.ui.MainWindow;
import jadx.gui.ui.codearea.EditorViewState;
public class TabStateViewAdapter {
@Nullable
public static TabViewState build(EditorViewState viewState) {
TabViewState tvs = new TabViewState();
if (!saveJNode(tvs, viewState.getNode())) {
return null;
}
tvs.setSubPath(viewState.getSubPath());
tvs.setCaret(viewState.getCaretPos());
tvs.setView(new ViewPoint(viewState.getViewPoint()));
return tvs;
}
@Nullable
public static EditorViewState load(MainWindow mw, TabViewState tvs) {
JNode node = loadJNode(mw, tvs);
if (node == null) {
return null;
}
return new EditorViewState(node, tvs.getSubPath(), tvs.getCaret(), tvs.getView().toPoint());
}
@Nullable
private static JNode loadJNode(MainWindow mw, TabViewState tvs) {
if ("class".equals(tvs.getType())) {
JavaClass javaClass = mw.getWrapper().searchJavaClassByRawName(tvs.getTabPath());
if (javaClass != null) {
return mw.getCacheObject().getNodeCache().makeFrom(javaClass);
}
}
return null;
}
private static boolean saveJNode(TabViewState tvs, JNode node) {
if (node instanceof JClass) {
tvs.setType("class");
tvs.setTabPath(((JClass) node).getCls().getRawName());
return true;
}
return false;
}
}
package jadx.gui.settings.data;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import jadx.api.data.impl.JadxCodeData;
public class ProjectData {
private int projectVersion;
private List<Path> files;
private List<String[]> treeExpansions = new ArrayList<>();
private JadxCodeData codeData = new JadxCodeData();
private List<TabViewState> openTabs = Collections.emptyList();
private int activeTab;
public List<Path> getFiles() {
return files;
}
public void setFiles(List<Path> files) {
this.files = files;
}
public List<String[]> getTreeExpansions() {
return treeExpansions;
}
public void setTreeExpansions(List<String[]> treeExpansions) {
this.treeExpansions = treeExpansions;
}
public JadxCodeData getCodeData() {
return codeData;
}
public void setCodeData(JadxCodeData codeData) {
this.codeData = codeData;
}
public int getProjectVersion() {
return projectVersion;
}
public void setProjectVersion(int projectVersion) {
this.projectVersion = projectVersion;
}
public List<TabViewState> getOpenTabs() {
return openTabs;
}
public void setOpenTabs(List<TabViewState> openTabs) {
this.openTabs = openTabs;
}
public int getActiveTab() {
return activeTab;
}
public void setActiveTab(int activeTab) {
this.activeTab = activeTab;
}
}
package jadx.gui.settings.data;
public class TabViewState {
private String type;
private String tabPath;
private String subPath;
private int caret;
private ViewPoint view;
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getTabPath() {
return tabPath;
}
public void setTabPath(String tabPath) {
this.tabPath = tabPath;
}
public String getSubPath() {
return subPath;
}
public void setSubPath(String subPath) {
this.subPath = subPath;
}
public int getCaret() {
return caret;
}
public void setCaret(int caret) {
this.caret = caret;
}
public ViewPoint getView() {
return view;
}
public void setView(ViewPoint view) {
this.view = view;
}
}
package jadx.gui.settings.data;
import java.awt.Point;
public class ViewPoint {
private int x;
private int y;
public ViewPoint() {
this(0, 0);
}
public ViewPoint(Point p) {
this(p.x, p.y);
}
public ViewPoint(int x, int y) {
this.x = x;
this.y = y;
}
public Point toPoint() {
return new Point(x, y);
}
public int getX() {
return x;
}
public void setX(int x) {
this.x = x;
}
public int getY() {
return y;
}
public void setY(int y) {
this.y = y;
}
@Override
public String toString() {
return "ViewPoint{" + x + ", " + y + '}';
}
}
......@@ -28,10 +28,8 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.Timer;
import java.util.TimerTask;
......@@ -75,7 +73,6 @@ import javax.swing.tree.TreePath;
import javax.swing.tree.TreeSelectionModel;
import org.fife.ui.rsyntaxtextarea.Theme;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
......@@ -83,7 +80,6 @@ import org.slf4j.LoggerFactory;
import ch.qos.logback.classic.Level;
import jadx.api.JadxArgs;
import jadx.api.JavaClass;
import jadx.api.JavaNode;
import jadx.api.ResourceFile;
import jadx.core.utils.StringUtils;
......@@ -110,6 +106,7 @@ import jadx.gui.treemodel.JPackage;
import jadx.gui.treemodel.JResource;
import jadx.gui.treemodel.JRoot;
import jadx.gui.ui.codearea.AbstractCodeContentPanel;
import jadx.gui.ui.codearea.EditorViewState;
import jadx.gui.ui.dialog.ADBDialog;
import jadx.gui.ui.dialog.AboutDialog;
import jadx.gui.ui.dialog.LogViewerDialog;
......@@ -127,6 +124,7 @@ import jadx.gui.update.data.Release;
import jadx.gui.utils.CacheObject;
import jadx.gui.utils.CodeUsageInfo;
import jadx.gui.utils.FontUtils;
import jadx.gui.utils.JNodeCache;
import jadx.gui.utils.JumpPosition;
import jadx.gui.utils.LafManager;
import jadx.gui.utils.Link;
......@@ -448,6 +446,7 @@ public class MainWindow extends JFrame {
deobfToggleBtn.setSelected(settings.isDeobfuscationOn());
initTree();
update();
restoreOpenTabs(project.getOpenTabs(this), project.getActiveTab());
runInitialBackgroundJobs();
BreakpointManager.init(paths.get(0).getParent());
}
......@@ -567,45 +566,26 @@ public class MainWindow extends JFrame {
public void reOpenFile() {
List<Path> openedFile = wrapper.getOpenPaths();
Map<String, Integer> openTabs = storeOpenTabs();
if (openedFile != null) {
open(openedFile, () -> restoreOpenTabs(openTabs));
int activeTab = tabbedPane.getSelectedIndex();
List<EditorViewState> viewStates = tabbedPane.getEditorViewStates();
open(openedFile, () -> restoreOpenTabs(viewStates, activeTab));
}
}
@NotNull
private Map<String, Integer> storeOpenTabs() {
Map<String, Integer> openTabs = new LinkedHashMap<>();
for (Map.Entry<JNode, ContentPanel> entry : tabbedPane.getOpenTabs().entrySet()) {
JavaNode javaNode = entry.getKey().getJavaNode();
String classRealName = "";
if (javaNode instanceof JavaClass) {
JavaClass javaClass = (JavaClass) javaNode;
classRealName = javaClass.getRawName();
}
@Nullable
JumpPosition position = entry.getValue().getTabbedPane().getCurrentPosition();
int line = 0;
if (position != null) {
line = position.getLine();
}
openTabs.put(classRealName, line);
private void restoreOpenTabs(List<EditorViewState> openTabs, int activeTab) {
if (openTabs.isEmpty()) {
return;
}
return openTabs;
}
private void restoreOpenTabs(Map<String, Integer> openTabs) {
for (Map.Entry<String, Integer> entry : openTabs.entrySet()) {
String classRealName = entry.getKey();
int position = entry.getValue();
@Nullable
JavaClass newClass = wrapper.searchJavaClassByRawName(classRealName);
if (newClass == null) {
continue;
JNodeCache nodeCache = getCacheObject().getNodeCache();
for (EditorViewState viewState : openTabs) {
JNode node = nodeCache.renew(wrapper, viewState.getNode());
if (node != null) {
viewState.setNode(node);
tabbedPane.restoreEditorViewState(viewState);
}
JNode newNode = cacheObject.getNodeCache().makeFrom(newClass);
tabbedPane.codeJump(new JumpPosition(newNode, position, JumpPosition.getDefPos(newNode)));
}
tabbedPane.setSelectedIndex(activeTab);
}
private void saveAll(boolean export) {
......@@ -1328,6 +1308,7 @@ public class MainWindow extends JFrame {
}
private void closeWindow() {
saveOpenTabs();
if (!ensureProjectIsSaved()) {
return;
}
......@@ -1347,6 +1328,10 @@ public class MainWindow extends JFrame {
System.exit(0);
}
private void saveOpenTabs() {
project.saveOpenTabs(tabbedPane.getEditorViewStates(), tabbedPane.getSelectedIndex());
}
private void saveSplittersInfo() {
settings.setMainWindowVerticalSplitterLoc(verticalSplitter.getDividerLocation());
settings.setDebuggerStackFrameSplitterLoc(debuggerPanel.getLeftSplitterLocation());
......
......@@ -27,6 +27,7 @@ import jadx.gui.treemodel.JNode;
import jadx.gui.ui.codearea.AbstractCodeArea;
import jadx.gui.ui.codearea.AbstractCodeContentPanel;
import jadx.gui.ui.codearea.ClassCodeContentPanel;
import jadx.gui.ui.codearea.EditorViewState;
import jadx.gui.ui.codearea.SmaliArea;
import jadx.gui.ui.panel.ContentPanel;
import jadx.gui.ui.panel.HtmlPanel;
......@@ -259,6 +260,24 @@ public class TabbedPane extends JTabbedPane {
return null;
}
public List<EditorViewState> getEditorViewStates() {
List<EditorViewState> states = new ArrayList<>();
for (ContentPanel panel : openTabs.values()) {
EditorViewState viewState = panel.getEditorViewState();
if (viewState != null) {
states.add(viewState);
}
}
return states;
}
public void restoreEditorViewState(EditorViewState viewState) {
ContentPanel contentPanel = getContentPanel(viewState.getNode());
if (contentPanel != null) {
contentPanel.restoreEditorViewState(viewState);
}
}
public void navBack() {
if (jumps.size() > 1) {
jumps.updateCurPosition(getCurrentPosition());
......
package jadx.gui.ui.codearea;
import java.awt.BorderLayout;
import java.awt.Point;
import javax.swing.JTabbedPane;
import javax.swing.border.EmptyBorder;
......@@ -55,16 +56,6 @@ public final class ClassCodeContentPanel extends AbstractCodeContentPanel {
updateUI();
}
@Override
public TabbedPane getTabbedPane() {
return tabbedPane;
}
@Override
public JNode getNode() {
return node;
}
@Override
public AbstractCodeArea getCodeArea() {
return javaCodePanel.getCodeArea();
......@@ -90,4 +81,22 @@ public final class ClassCodeContentPanel extends AbstractCodeContentPanel {
public void showSmaliPane() {
areaTabbedPane.setSelectedComponent(smaliCodePanel);
}
@Override
public EditorViewState getEditorViewState() {
CodePanel codePanel = (CodePanel) areaTabbedPane.getSelectedComponent();
int caretPos = codePanel.getCodeArea().getCaretPosition();
Point viewPoint = codePanel.getCodeScrollPane().getViewport().getViewPosition();
String subPath = codePanel == javaCodePanel ? "java" : "smali";
return new EditorViewState(getNode(), subPath, caretPos, viewPoint);
}
@Override
public void restoreEditorViewState(EditorViewState viewState) {
boolean isJava = viewState.getSubPath().equals("java");
CodePanel activePanel = isJava ? javaCodePanel : smaliCodePanel;
areaTabbedPane.setSelectedComponent(activePanel);
activePanel.getCodeScrollPane().getViewport().setViewPosition(viewState.getViewPoint());
activePanel.getCodeArea().setCaretPosition(viewState.getCaretPos());
}
}
......@@ -24,16 +24,6 @@ public final class CodeContentPanel extends AbstractCodeContentPanel {
updateUI();
}
@Override
public TabbedPane getTabbedPane() {
return tabbedPane;
}
@Override
public JNode getNode() {
return node;
}
SearchBar getSearchBar() {
return codePanel.getSearchBar();
}
......
package jadx.gui.ui.codearea;
import java.awt.Point;
import jadx.gui.treemodel.JNode;
public class EditorViewState {
private JNode node;
private int caretPos;
private Point viewPoint;
private String subPath;
public EditorViewState(JNode node, String subPath, int caretPos, Point viewPoint) {
this.node = node;
this.subPath = subPath;
this.caretPos = caretPos;
this.viewPoint = viewPoint;
}
public JNode getNode() {
return node;
}
public void setNode(JNode node) {
this.node = node;
}
public int getCaretPos() {
return caretPos;
}
public void setCaretPos(int caretPos) {
this.caretPos = caretPos;
}
public Point getViewPoint() {
return viewPoint;
}
public void setViewPoint(Point viewPoint) {
this.viewPoint = viewPoint;
}
public String getSubPath() {
return subPath;
}
public void setSubPath(String subPath) {
this.subPath = subPath;
}
@Override
public String toString() {
return "EditorViewState{node=" + node
+ ", caretPos=" + caretPos
+ ", viewPoint=" + viewPoint
+ ", subPath='" + subPath + '\''
+ '}';
}
}
......@@ -7,6 +7,7 @@ import org.jetbrains.annotations.Nullable;
import jadx.gui.treemodel.JClass;
import jadx.gui.treemodel.JNode;
import jadx.gui.ui.TabbedPane;
import jadx.gui.ui.codearea.EditorViewState;
public abstract class ContentPanel extends JPanel {
......@@ -22,6 +23,14 @@ public abstract class ContentPanel extends JPanel {
public abstract void loadSettings();
public EditorViewState getEditorViewState() {
return null;
}
public void restoreEditorViewState(EditorViewState viewState) {
}
public TabbedPane getTabbedPane() {
return tabbedPane;
}
......
......@@ -3,12 +3,15 @@ package jadx.gui.utils;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.jetbrains.annotations.Nullable;
import jadx.api.JavaClass;
import jadx.api.JavaField;
import jadx.api.JavaMethod;
import jadx.api.JavaNode;
import jadx.api.JavaVariable;
import jadx.core.utils.exceptions.JadxRuntimeException;
import jadx.gui.JadxWrapper;
import jadx.gui.treemodel.JClass;
import jadx.gui.treemodel.JField;
import jadx.gui.treemodel.JMethod;
......@@ -39,6 +42,15 @@ public class JNodeCache {
jn -> new JClass(javaCls, makeFrom(javaCls.getDeclaringClass())));
}
@Nullable
public JNode renew(JadxWrapper wrapper, JNode node) {
if (node instanceof JClass) {
String rawName = ((JClass) node).getCls().getRawName();
return makeFrom(wrapper.searchJavaClassByRawName(rawName));
}
return null;
}
private JNode convert(JavaNode node) {
if (node == null) {
return null;
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册