mirror of
https://github.com/skylot/jadx.git
synced 2025-02-20 04:51:49 +00:00
fix(gui): restore resource tabs on project open
This commit is contained in:
parent
f5216b77f8
commit
4479a3fbd5
@ -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);
|
||||
|
@ -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<EditorViewState> tabs, int activeTab) {
|
||||
data.setOpenTabs(Utils.collectionMap(tabs, TabStateViewAdapter::build));
|
||||
List<TabViewState> tabStateList = tabs.stream()
|
||||
.map(TabStateViewAdapter::build)
|
||||
.filter(Objects::nonNull)
|
||||
.collect(Collectors.toList());
|
||||
data.setOpenTabs(tabStateList);
|
||||
data.setActiveTab(activeTab);
|
||||
changed();
|
||||
}
|
||||
|
||||
public List<EditorViewState> 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() {
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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<JResource> resList = getHierarchyResources(wrapper.getResources());
|
||||
for (JResource jRes : resList) {
|
||||
jRes.update();
|
||||
add(jRes);
|
||||
List<ResourceFile> resources = wrapper.getResources();
|
||||
if (!resources.isEmpty()) {
|
||||
add(getHierarchyResources(resources));
|
||||
}
|
||||
for (JNode customNode : customNodes) {
|
||||
add(customNode);
|
||||
}
|
||||
}
|
||||
|
||||
private List<JResource> getHierarchyResources(List<ResourceFile> resources) {
|
||||
if (resources.isEmpty()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
private JResource getHierarchyResources(List<ResourceFile> 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();
|
||||
|
@ -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<Path> openedFile = wrapper.getOpenPaths();
|
||||
if (openedFile != null) {
|
||||
int activeTab = tabbedPane.getSelectedIndex();
|
||||
List<EditorViewState> viewStates = tabbedPane.getEditorViewStates();
|
||||
open(openedFile, () -> restoreOpenTabs(viewStates, activeTab));
|
||||
saveOpenTabs();
|
||||
open(openedFile);
|
||||
}
|
||||
}
|
||||
|
||||
private void restoreOpenTabs(List<EditorViewState> 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<EditorViewState> 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());
|
||||
|
@ -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<EditorViewState> getEditorViewStates() {
|
||||
List<EditorViewState> 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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
* <li>Smali source code of the selected class</li>
|
||||
* </ul>
|
||||
*/
|
||||
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;
|
||||
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -0,0 +1,10 @@
|
||||
package jadx.gui.ui.panel;
|
||||
|
||||
import jadx.gui.ui.codearea.EditorViewState;
|
||||
|
||||
public interface IViewStateSupport {
|
||||
|
||||
EditorViewState getEditorViewState();
|
||||
|
||||
void restoreEditorViewState(EditorViewState viewState);
|
||||
}
|
@ -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;
|
||||
|
Loading…
x
Reference in New Issue
Block a user