From 4479a3fbd598c6834815fe8d0e111c4b0862f699 Mon Sep 17 00:00:00 2001 From: Skylot Date: Fri, 29 Oct 2021 15:23:22 +0100 Subject: [PATCH] fix(gui): restore resource tabs on project open --- .../java/jadx/gui/jobs/DecompileTask.java | 11 +++--- .../java/jadx/gui/settings/JadxProject.java | 15 ++++++-- .../gui/settings/TabStateViewAdapter.java | 36 ++++++++++++++----- .../main/java/jadx/gui/treemodel/JRoot.java | 20 +++++------ .../src/main/java/jadx/gui/ui/MainWindow.java | 36 ++++++++----------- .../src/main/java/jadx/gui/ui/TabbedPane.java | 12 ++++--- .../ui/codearea/ClassCodeContentPanel.java | 3 +- .../gui/ui/codearea/CodeContentPanel.java | 17 ++++++++- .../jadx/gui/ui/codearea/EditorViewState.java | 26 ++++---------- .../java/jadx/gui/ui/panel/ContentPanel.java | 9 ----- .../jadx/gui/ui/panel/IViewStateSupport.java | 10 ++++++ .../main/java/jadx/gui/utils/JNodeCache.java | 12 ------- 12 files changed, 111 insertions(+), 96 deletions(-) create mode 100644 jadx-gui/src/main/java/jadx/gui/ui/panel/IViewStateSupport.java diff --git a/jadx-gui/src/main/java/jadx/gui/jobs/DecompileTask.java b/jadx-gui/src/main/java/jadx/gui/jobs/DecompileTask.java index ee8d4ab34..b691bc39e 100644 --- a/jadx-gui/src/main/java/jadx/gui/jobs/DecompileTask.java +++ b/jadx-gui/src/main/java/jadx/gui/jobs/DecompileTask.java @@ -69,10 +69,13 @@ public class DecompileTask implements IBackgroundTask { @Override public void onFinish(TaskStatus status, long skippedJobs) { long taskTime = System.currentTimeMillis() - startTime; - long avgPerCls = taskTime / expectedCompleteCount; - LOG.info("Decompile task complete in {} ms (avg {} ms per class), classes: {}," - + " time limit:{ total: {}ms, per cls: {}ms }, status: {}", - taskTime, avgPerCls, expectedCompleteCount, timeLimit(), CLS_LIMIT, status); + long avgPerCls = taskTime / Math.max(expectedCompleteCount, 1); + if (LOG.isInfoEnabled()) { + LOG.info("Decompile task complete in " + taskTime + " ms (avg " + avgPerCls + " ms per class)" + + ", classes: " + expectedCompleteCount + + ", time limit:{ total: " + timeLimit() + "ms, per cls: " + CLS_LIMIT + "ms }" + + ", status: " + status); + } IndexService indexService = mainWindow.getCacheObject().getIndexService(); indexService.setComplete(true); diff --git a/jadx-gui/src/main/java/jadx/gui/settings/JadxProject.java b/jadx-gui/src/main/java/jadx/gui/settings/JadxProject.java index 026af8a86..224c3bbe9 100644 --- a/jadx-gui/src/main/java/jadx/gui/settings/JadxProject.java +++ b/jadx-gui/src/main/java/jadx/gui/settings/JadxProject.java @@ -7,6 +7,8 @@ import java.nio.file.Files; import java.nio.file.Path; import java.util.Arrays; import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -24,9 +26,9 @@ 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.settings.data.TabViewState; import jadx.gui.ui.MainWindow; import jadx.gui.ui.codearea.EditorViewState; import jadx.gui.utils.PathTypeAdapter; @@ -127,13 +129,20 @@ public class JadxProject { } public void saveOpenTabs(List tabs, int activeTab) { - data.setOpenTabs(Utils.collectionMap(tabs, TabStateViewAdapter::build)); + List tabStateList = tabs.stream() + .map(TabStateViewAdapter::build) + .filter(Objects::nonNull) + .collect(Collectors.toList()); + data.setOpenTabs(tabStateList); data.setActiveTab(activeTab); changed(); } public List getOpenTabs(MainWindow mw) { - return Utils.collectionMap(data.getOpenTabs(), s -> TabStateViewAdapter.load(mw, s)); + return data.getOpenTabs().stream() + .map(s -> TabStateViewAdapter.load(mw, s)) + .filter(Objects::nonNull) + .collect(Collectors.toList()); } public int getActiveTab() { diff --git a/jadx-gui/src/main/java/jadx/gui/settings/TabStateViewAdapter.java b/jadx-gui/src/main/java/jadx/gui/settings/TabStateViewAdapter.java index c04b0a2f1..35fb68d6c 100644 --- a/jadx-gui/src/main/java/jadx/gui/settings/TabStateViewAdapter.java +++ b/jadx-gui/src/main/java/jadx/gui/settings/TabStateViewAdapter.java @@ -1,21 +1,26 @@ package jadx.gui.settings; import org.jetbrains.annotations.Nullable; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; 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.treemodel.JResource; import jadx.gui.ui.MainWindow; import jadx.gui.ui.codearea.EditorViewState; public class TabStateViewAdapter { + private static final Logger LOG = LoggerFactory.getLogger(TabStateViewAdapter.class); @Nullable public static TabViewState build(EditorViewState viewState) { TabViewState tvs = new TabViewState(); if (!saveJNode(tvs, viewState.getNode())) { + LOG.debug("Can't save view state: " + viewState); return null; } tvs.setSubPath(viewState.getSubPath()); @@ -26,20 +31,30 @@ public class TabStateViewAdapter { @Nullable public static EditorViewState load(MainWindow mw, TabViewState tvs) { - JNode node = loadJNode(mw, tvs); - if (node == null) { + try { + JNode node = loadJNode(mw, tvs); + if (node == null) { + return null; + } + return new EditorViewState(node, tvs.getSubPath(), tvs.getCaret(), tvs.getView().toPoint()); + } catch (Exception e) { + LOG.error("Failed to load tab state: " + tvs, e); 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); - } + switch (tvs.getType()) { + case "class": + JavaClass javaClass = mw.getWrapper().searchJavaClassByRawName(tvs.getTabPath()); + if (javaClass != null) { + return mw.getCacheObject().getNodeCache().makeFrom(javaClass); + } + break; + case "resource": + JResource tmpNode = new JResource(null, tvs.getTabPath(), JResource.JResType.FILE); + return mw.getTreeRoot().searchNode(tmpNode); // equals method in JResource check only name } return null; } @@ -50,6 +65,11 @@ public class TabStateViewAdapter { tvs.setTabPath(((JClass) node).getCls().getRawName()); return true; } + if (node instanceof JResource) { + tvs.setType("resource"); + tvs.setTabPath(node.getName()); + return true; + } return false; } } diff --git a/jadx-gui/src/main/java/jadx/gui/treemodel/JRoot.java b/jadx-gui/src/main/java/jadx/gui/treemodel/JRoot.java index 3227fe776..106f26dff 100644 --- a/jadx-gui/src/main/java/jadx/gui/treemodel/JRoot.java +++ b/jadx-gui/src/main/java/jadx/gui/treemodel/JRoot.java @@ -3,7 +3,6 @@ package jadx.gui.treemodel; import java.io.File; import java.nio.file.Path; import java.util.ArrayList; -import java.util.Collections; import java.util.Enumeration; import java.util.List; import java.util.regex.Pattern; @@ -38,20 +37,16 @@ public class JRoot extends JNode { removeAllChildren(); add(new JSources(this, wrapper)); - List resList = getHierarchyResources(wrapper.getResources()); - for (JResource jRes : resList) { - jRes.update(); - add(jRes); + List resources = wrapper.getResources(); + if (!resources.isEmpty()) { + add(getHierarchyResources(resources)); } for (JNode customNode : customNodes) { add(customNode); } } - private List getHierarchyResources(List resources) { - if (resources.isEmpty()) { - return Collections.emptyList(); - } + private JResource getHierarchyResources(List resources) { JResource root = new JResource(null, NLS.str("tree.resources_title"), JResType.ROOT); String splitPathStr = Pattern.quote(File.separator); for (ResourceFile rf : resources) { @@ -71,14 +66,15 @@ public class JRoot extends JNode { if (i != count - 1) { subRF = new JResource(null, name, JResType.DIR); } else { - subRF = new JResource(rf, name, JResType.FILE); + subRF = new JResource(rf, rf.getOriginalName(), name, JResType.FILE); } curRf.getFiles().add(subRF); } curRf = subRF; } } - return Collections.singletonList(root); + root.update(); + return root; } private JResource getResourceByName(JResource rf, String name) { @@ -90,7 +86,7 @@ public class JRoot extends JNode { return null; } - public JNode searchClassInTree(JNode node) { + public JNode searchNode(JNode node) { Enumeration en = this.breadthFirstEnumeration(); while (en.hasMoreElements()) { Object obj = en.nextElement(); diff --git a/jadx-gui/src/main/java/jadx/gui/ui/MainWindow.java b/jadx-gui/src/main/java/jadx/gui/ui/MainWindow.java index 9edc7f20d..d90dd3e56 100644 --- a/jadx-gui/src/main/java/jadx/gui/ui/MainWindow.java +++ b/jadx-gui/src/main/java/jadx/gui/ui/MainWindow.java @@ -124,7 +124,6 @@ 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; @@ -446,7 +445,7 @@ public class MainWindow extends JFrame { deobfToggleBtn.setSelected(settings.isDeobfuscationOn()); initTree(); update(); - restoreOpenTabs(project.getOpenTabs(this), project.getActiveTab()); + restoreOpenTabs(); runInitialBackgroundJobs(); BreakpointManager.init(paths.get(0).getParent()); } @@ -567,27 +566,11 @@ public class MainWindow extends JFrame { public void reOpenFile() { List openedFile = wrapper.getOpenPaths(); if (openedFile != null) { - int activeTab = tabbedPane.getSelectedIndex(); - List viewStates = tabbedPane.getEditorViewStates(); - open(openedFile, () -> restoreOpenTabs(viewStates, activeTab)); + saveOpenTabs(); + open(openedFile); } } - private void restoreOpenTabs(List openTabs, int activeTab) { - if (openTabs.isEmpty()) { - return; - } - JNodeCache nodeCache = getCacheObject().getNodeCache(); - for (EditorViewState viewState : openTabs) { - JNode node = nodeCache.renew(wrapper, viewState.getNode()); - if (node != null) { - viewState.setNode(node); - tabbedPane.restoreEditorViewState(viewState); - } - } - tabbedPane.setSelectedIndex(activeTab); - } - private void saveAll(boolean export) { JFileChooser fileChooser = new JFileChooser(); fileChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); @@ -755,7 +738,7 @@ public class MainWindow extends JFrame { JNode node = selectedContentPanel.getNode(); if (node.getParent() == null && treeRoot != null) { // node not register in tree - node = treeRoot.searchClassInTree(node); + node = treeRoot.searchNode(node); if (node == null) { LOG.error("Class not found in tree"); return; @@ -1328,6 +1311,17 @@ public class MainWindow extends JFrame { project.saveOpenTabs(tabbedPane.getEditorViewStates(), tabbedPane.getSelectedIndex()); } + private void restoreOpenTabs() { + List openTabs = project.getOpenTabs(this); + if (openTabs.isEmpty()) { + return; + } + for (EditorViewState viewState : openTabs) { + tabbedPane.restoreEditorViewState(viewState); + } + tabbedPane.setSelectedIndex(project.getActiveTab()); + } + private void saveSplittersInfo() { settings.setMainWindowVerticalSplitterLoc(verticalSplitter.getDividerLocation()); settings.setDebuggerStackFrameSplitterLoc(debuggerPanel.getLeftSplitterLocation()); diff --git a/jadx-gui/src/main/java/jadx/gui/ui/TabbedPane.java b/jadx-gui/src/main/java/jadx/gui/ui/TabbedPane.java index 468722fd2..4bfe54fe6 100644 --- a/jadx-gui/src/main/java/jadx/gui/ui/TabbedPane.java +++ b/jadx-gui/src/main/java/jadx/gui/ui/TabbedPane.java @@ -31,6 +31,7 @@ import jadx.gui.ui.codearea.EditorViewState; import jadx.gui.ui.codearea.SmaliArea; import jadx.gui.ui.panel.ContentPanel; import jadx.gui.ui.panel.HtmlPanel; +import jadx.gui.ui.panel.IViewStateSupport; import jadx.gui.ui.panel.ImagePanel; import jadx.gui.utils.JumpManager; import jadx.gui.utils.JumpPosition; @@ -263,9 +264,10 @@ public class TabbedPane extends JTabbedPane { public List getEditorViewStates() { List states = new ArrayList<>(); for (ContentPanel panel : openTabs.values()) { - EditorViewState viewState = panel.getEditorViewState(); - if (viewState != null) { - states.add(viewState); + if (panel instanceof IViewStateSupport) { + states.add(((IViewStateSupport) panel).getEditorViewState()); + } else { + states.add(new EditorViewState(panel.getNode(), "", 0, EditorViewState.ZERO)); } } return states; @@ -273,8 +275,8 @@ public class TabbedPane extends JTabbedPane { public void restoreEditorViewState(EditorViewState viewState) { ContentPanel contentPanel = getContentPanel(viewState.getNode()); - if (contentPanel != null) { - contentPanel.restoreEditorViewState(viewState); + if (contentPanel instanceof IViewStateSupport) { + ((IViewStateSupport) contentPanel).restoreEditorViewState(viewState); } } diff --git a/jadx-gui/src/main/java/jadx/gui/ui/codearea/ClassCodeContentPanel.java b/jadx-gui/src/main/java/jadx/gui/ui/codearea/ClassCodeContentPanel.java index f568490fa..151bc3e50 100644 --- a/jadx-gui/src/main/java/jadx/gui/ui/codearea/ClassCodeContentPanel.java +++ b/jadx-gui/src/main/java/jadx/gui/ui/codearea/ClassCodeContentPanel.java @@ -8,6 +8,7 @@ import javax.swing.border.EmptyBorder; import jadx.gui.treemodel.JNode; import jadx.gui.ui.TabbedPane; +import jadx.gui.ui.panel.IViewStateSupport; import jadx.gui.utils.NLS; /** @@ -18,7 +19,7 @@ import jadx.gui.utils.NLS; *
  • Smali source code of the selected class
  • * */ -public final class ClassCodeContentPanel extends AbstractCodeContentPanel { +public final class ClassCodeContentPanel extends AbstractCodeContentPanel implements IViewStateSupport { private static final long serialVersionUID = -7229931102504634591L; private final transient CodePanel javaCodePanel; diff --git a/jadx-gui/src/main/java/jadx/gui/ui/codearea/CodeContentPanel.java b/jadx-gui/src/main/java/jadx/gui/ui/codearea/CodeContentPanel.java index d01e5c014..9a507c066 100644 --- a/jadx-gui/src/main/java/jadx/gui/ui/codearea/CodeContentPanel.java +++ b/jadx-gui/src/main/java/jadx/gui/ui/codearea/CodeContentPanel.java @@ -1,11 +1,13 @@ package jadx.gui.ui.codearea; import java.awt.BorderLayout; +import java.awt.Point; import jadx.gui.treemodel.JNode; import jadx.gui.ui.TabbedPane; +import jadx.gui.ui.panel.IViewStateSupport; -public final class CodeContentPanel extends AbstractCodeContentPanel { +public final class CodeContentPanel extends AbstractCodeContentPanel implements IViewStateSupport { private static final long serialVersionUID = 5310536092010045565L; private final CodePanel codePanel; @@ -47,4 +49,17 @@ public final class CodeContentPanel extends AbstractCodeContentPanel { } return '/' + s; } + + @Override + public EditorViewState getEditorViewState() { + int caretPos = codePanel.getCodeArea().getCaretPosition(); + Point viewPoint = codePanel.getCodeScrollPane().getViewport().getViewPosition(); + return new EditorViewState(getNode(), "", caretPos, viewPoint); + } + + @Override + public void restoreEditorViewState(EditorViewState viewState) { + codePanel.getCodeScrollPane().getViewport().setViewPosition(viewState.getViewPoint()); + codePanel.getCodeArea().setCaretPosition(viewState.getCaretPos()); + } } diff --git a/jadx-gui/src/main/java/jadx/gui/ui/codearea/EditorViewState.java b/jadx-gui/src/main/java/jadx/gui/ui/codearea/EditorViewState.java index b57cc3b70..2b3f70a0e 100644 --- a/jadx-gui/src/main/java/jadx/gui/ui/codearea/EditorViewState.java +++ b/jadx-gui/src/main/java/jadx/gui/ui/codearea/EditorViewState.java @@ -5,10 +5,12 @@ 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 static final Point ZERO = new Point(0, 0); + + private final JNode node; + private final int caretPos; + private final Point viewPoint; + private final String subPath; public EditorViewState(JNode node, String subPath, int caretPos, Point viewPoint) { this.node = node; @@ -21,34 +23,18 @@ public class EditorViewState { 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 diff --git a/jadx-gui/src/main/java/jadx/gui/ui/panel/ContentPanel.java b/jadx-gui/src/main/java/jadx/gui/ui/panel/ContentPanel.java index cd67dc6eb..1056f217f 100644 --- a/jadx-gui/src/main/java/jadx/gui/ui/panel/ContentPanel.java +++ b/jadx-gui/src/main/java/jadx/gui/ui/panel/ContentPanel.java @@ -7,7 +7,6 @@ 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 { @@ -23,14 +22,6 @@ 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; } diff --git a/jadx-gui/src/main/java/jadx/gui/ui/panel/IViewStateSupport.java b/jadx-gui/src/main/java/jadx/gui/ui/panel/IViewStateSupport.java new file mode 100644 index 000000000..72365b3a5 --- /dev/null +++ b/jadx-gui/src/main/java/jadx/gui/ui/panel/IViewStateSupport.java @@ -0,0 +1,10 @@ +package jadx.gui.ui.panel; + +import jadx.gui.ui.codearea.EditorViewState; + +public interface IViewStateSupport { + + EditorViewState getEditorViewState(); + + void restoreEditorViewState(EditorViewState viewState); +} diff --git a/jadx-gui/src/main/java/jadx/gui/utils/JNodeCache.java b/jadx-gui/src/main/java/jadx/gui/utils/JNodeCache.java index ec39be2db..8d4bc1752 100644 --- a/jadx-gui/src/main/java/jadx/gui/utils/JNodeCache.java +++ b/jadx-gui/src/main/java/jadx/gui/utils/JNodeCache.java @@ -3,15 +3,12 @@ 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; @@ -42,15 +39,6 @@ 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;