feat(gui): add split view for different decompilation modes
This commit is contained in:
@@ -105,7 +105,6 @@ public class BackgroundExecutor {
|
||||
protected TaskStatus doInBackground() throws Exception {
|
||||
progressPane.changeLabel(this, task.getTitle() + "… ");
|
||||
progressPane.changeCancelBtnVisible(this, task.canBeCanceled());
|
||||
progressPane.changeVisibility(this, true);
|
||||
|
||||
runJobs();
|
||||
return status;
|
||||
@@ -116,6 +115,9 @@ public class BackgroundExecutor {
|
||||
jobsCount = jobs.size();
|
||||
LOG.debug("Starting background task '{}', jobs count: {}, time limit: {} ms, memory check: {}",
|
||||
task.getTitle(), jobsCount, task.timeLimit(), task.checkMemoryUsage());
|
||||
if (jobsCount != 1) {
|
||||
progressPane.changeVisibility(this, true);
|
||||
}
|
||||
status = TaskStatus.STARTED;
|
||||
int threadsCount = mainWindow.getSettings().getThreadsCount();
|
||||
executor = (ThreadPoolExecutor) Executors.newFixedThreadPool(threadsCount);
|
||||
@@ -146,6 +148,10 @@ public class BackgroundExecutor {
|
||||
setProgress(calcProgress(executor.getCompletedTaskCount()));
|
||||
k++;
|
||||
Thread.sleep(k < 20 ? 100 : 1000); // faster update for short tasks
|
||||
if (jobsCount == 1 && k == 3) {
|
||||
// small delay before show progress to reduce blinking on short tasks
|
||||
progressPane.changeVisibility(this, true);
|
||||
}
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
LOG.debug("Task wait interrupted");
|
||||
|
||||
@@ -32,7 +32,6 @@ import jadx.api.JadxArgs;
|
||||
import jadx.api.args.DeobfuscationMapFileMode;
|
||||
import jadx.cli.JadxCLIArgs;
|
||||
import jadx.cli.LogHelper;
|
||||
import jadx.core.utils.exceptions.JadxRuntimeException;
|
||||
import jadx.gui.ui.MainWindow;
|
||||
import jadx.gui.ui.codearea.EditorTheme;
|
||||
import jadx.gui.utils.FontUtils;
|
||||
@@ -686,7 +685,7 @@ public class JadxSettings extends JadxCLIArgs {
|
||||
fromVersion++;
|
||||
}
|
||||
if (fromVersion != CURRENT_SETTINGS_VERSION) {
|
||||
throw new JadxRuntimeException("Incorrect settings upgrade");
|
||||
LOG.warn("Incorrect settings upgrade. Expected version: {}, got: {}", CURRENT_SETTINGS_VERSION, fromVersion);
|
||||
}
|
||||
settingsVersion = CURRENT_SETTINGS_VERSION;
|
||||
sync();
|
||||
|
||||
@@ -63,7 +63,7 @@ public class JClass extends JLoadableNode implements Comparable<JClass> {
|
||||
return !cls.getClassNode().contains(AFlag.DONT_RENAME);
|
||||
}
|
||||
|
||||
public synchronized void load() {
|
||||
private synchronized void load() {
|
||||
if (loaded) {
|
||||
return;
|
||||
}
|
||||
@@ -122,7 +122,6 @@ public class JClass extends JLoadableNode implements Comparable<JClass> {
|
||||
return new ClassCodeContentPanel(tabbedPane, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSmali() {
|
||||
return cls.getSmali();
|
||||
}
|
||||
|
||||
@@ -12,9 +12,6 @@ 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.ui.TabbedPane;
|
||||
import jadx.gui.ui.codearea.ClassCodeContentPanel;
|
||||
import jadx.gui.ui.panel.ContentPanel;
|
||||
import jadx.gui.utils.OverlayIcon;
|
||||
import jadx.gui.utils.UiUtils;
|
||||
|
||||
@@ -65,11 +62,6 @@ public class JMethod extends JNode {
|
||||
return mth.getDecompiledLine();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ContentPanel getContentPanel(TabbedPane tabbedPane) {
|
||||
return new ClassCodeContentPanel(tabbedPane, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Icon getIcon() {
|
||||
AccessInfo accessFlags = mth.getAccessFlags();
|
||||
|
||||
@@ -38,10 +38,6 @@ public abstract class JNode extends DefaultMutableTreeNode {
|
||||
return null;
|
||||
}
|
||||
|
||||
public String getSmali() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public String getSyntaxName() {
|
||||
return SyntaxConstants.SYNTAX_STYLE_NONE;
|
||||
}
|
||||
|
||||
@@ -23,7 +23,9 @@ import javax.swing.text.BadLocationException;
|
||||
import javax.swing.text.Caret;
|
||||
import javax.swing.text.DefaultCaret;
|
||||
|
||||
import org.fife.ui.rsyntaxtextarea.AbstractTokenMakerFactory;
|
||||
import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea;
|
||||
import org.fife.ui.rsyntaxtextarea.TokenMakerFactory;
|
||||
import org.fife.ui.rtextarea.SearchContext;
|
||||
import org.fife.ui.rtextarea.SearchEngine;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
@@ -31,6 +33,7 @@ import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import jadx.core.utils.StringUtils;
|
||||
import jadx.core.utils.exceptions.JadxRuntimeException;
|
||||
import jadx.gui.settings.JadxSettings;
|
||||
import jadx.gui.treemodel.JClass;
|
||||
import jadx.gui.treemodel.JNode;
|
||||
@@ -47,12 +50,24 @@ public abstract class AbstractCodeArea extends RSyntaxTextArea {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(AbstractCodeArea.class);
|
||||
|
||||
public static final String SYNTAX_STYLE_SMALI = "text/smali";
|
||||
|
||||
static {
|
||||
TokenMakerFactory tokenMakerFactory = TokenMakerFactory.getDefaultInstance();
|
||||
if (tokenMakerFactory instanceof AbstractTokenMakerFactory) {
|
||||
AbstractTokenMakerFactory atmf = (AbstractTokenMakerFactory) tokenMakerFactory;
|
||||
atmf.putMapping(SYNTAX_STYLE_SMALI, "jadx.gui.ui.codearea.SmaliTokenMaker");
|
||||
} else {
|
||||
throw new JadxRuntimeException("Unexpected TokenMakerFactory instance: " + tokenMakerFactory.getClass());
|
||||
}
|
||||
}
|
||||
|
||||
protected final ContentPanel contentPanel;
|
||||
protected final JNode node;
|
||||
|
||||
public AbstractCodeArea(ContentPanel contentPanel) {
|
||||
public AbstractCodeArea(ContentPanel contentPanel, JNode node) {
|
||||
this.contentPanel = contentPanel;
|
||||
this.node = contentPanel.getNode();
|
||||
this.node = node;
|
||||
|
||||
setMarkOccurrences(false);
|
||||
setEditable(false);
|
||||
|
||||
@@ -1,21 +1,28 @@
|
||||
package jadx.gui.ui.codearea;
|
||||
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.Point;
|
||||
|
||||
import javax.swing.JCheckBox;
|
||||
import javax.swing.JSplitPane;
|
||||
import javax.swing.JTabbedPane;
|
||||
import javax.swing.JToolBar;
|
||||
import javax.swing.border.EmptyBorder;
|
||||
|
||||
import org.fife.ui.rsyntaxtextarea.AbstractTokenMakerFactory;
|
||||
import org.fife.ui.rsyntaxtextarea.TokenMakerFactory;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import jadx.gui.treemodel.JNode;
|
||||
import jadx.api.DecompilationMode;
|
||||
import jadx.gui.jobs.BackgroundExecutor;
|
||||
import jadx.gui.treemodel.JClass;
|
||||
import jadx.gui.ui.TabbedPane;
|
||||
import jadx.gui.ui.codearea.mode.JCodeMode;
|
||||
import jadx.gui.ui.panel.IViewStateSupport;
|
||||
import jadx.gui.utils.NLS;
|
||||
|
||||
import static com.formdev.flatlaf.FlatClientProperties.TABBED_PANE_TRAILING_COMPONENT;
|
||||
|
||||
/**
|
||||
* Displays one class with two different view:
|
||||
*
|
||||
@@ -32,32 +39,77 @@ public final class ClassCodeContentPanel extends AbstractCodeContentPanel implem
|
||||
private final transient CodePanel smaliCodePanel;
|
||||
private final transient JTabbedPane areaTabbedPane;
|
||||
|
||||
public ClassCodeContentPanel(TabbedPane panel, JNode jnode) {
|
||||
super(panel, jnode);
|
||||
private boolean splitView = false;
|
||||
|
||||
// FIXME I don't know the project very well, so need to get the right place
|
||||
AbstractTokenMakerFactory atmf = (AbstractTokenMakerFactory) TokenMakerFactory.getDefaultInstance();
|
||||
atmf.putMapping("text/smali", "jadx.gui.ui.codearea.SmaliTokenMaker");
|
||||
public ClassCodeContentPanel(TabbedPane panel, JClass jCls) {
|
||||
super(panel, jCls);
|
||||
|
||||
javaCodePanel = new CodePanel(new CodeArea(this));
|
||||
smaliCodePanel = new CodePanel(new SmaliArea(this));
|
||||
javaCodePanel = new CodePanel(new CodeArea(this, jCls));
|
||||
smaliCodePanel = new CodePanel(new SmaliArea(this, jCls));
|
||||
areaTabbedPane = buildTabbedPane(jCls, false);
|
||||
addCustomControls(areaTabbedPane);
|
||||
|
||||
initView();
|
||||
javaCodePanel.load();
|
||||
}
|
||||
|
||||
private void initView() {
|
||||
removeAll();
|
||||
setLayout(new BorderLayout());
|
||||
setBorder(new EmptyBorder(0, 0, 0, 0));
|
||||
if (splitView) {
|
||||
JTabbedPane splitPaneView = buildTabbedPane(((JClass) node), true);
|
||||
JSplitPane splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, areaTabbedPane, splitPaneView);
|
||||
add(splitPane);
|
||||
splitPane.setDividerLocation(0.5);
|
||||
splitPaneView.setSelectedIndex(1);
|
||||
} else {
|
||||
add(areaTabbedPane);
|
||||
}
|
||||
invalidate();
|
||||
}
|
||||
|
||||
areaTabbedPane = new JTabbedPane(JTabbedPane.BOTTOM);
|
||||
private JTabbedPane buildTabbedPane(JClass jCls, boolean split) {
|
||||
JTabbedPane areaTabbedPane = new JTabbedPane(JTabbedPane.BOTTOM);
|
||||
areaTabbedPane.setBorder(new EmptyBorder(0, 0, 0, 0));
|
||||
areaTabbedPane.setTabLayoutPolicy(JTabbedPane.SCROLL_TAB_LAYOUT);
|
||||
areaTabbedPane.add(javaCodePanel, NLS.str("tabs.code"));
|
||||
areaTabbedPane.add(smaliCodePanel, NLS.str("tabs.smali"));
|
||||
add(areaTabbedPane);
|
||||
|
||||
javaCodePanel.load();
|
||||
|
||||
if (split) {
|
||||
areaTabbedPane.add(new CodePanel(new CodeArea(this, jCls)), NLS.str("tabs.code"));
|
||||
areaTabbedPane.add(new CodePanel(new SmaliArea(this, jCls)), NLS.str("tabs.smali"));
|
||||
} else {
|
||||
areaTabbedPane.add(javaCodePanel, NLS.str("tabs.code"));
|
||||
areaTabbedPane.add(smaliCodePanel, NLS.str("tabs.smali"));
|
||||
}
|
||||
areaTabbedPane.add(new CodePanel(new CodeArea(this, new JCodeMode(jCls, DecompilationMode.SIMPLE))), "Simple");
|
||||
areaTabbedPane.add(new CodePanel(new CodeArea(this, new JCodeMode(jCls, DecompilationMode.FALLBACK))), "Fallback");
|
||||
areaTabbedPane.addChangeListener(e -> {
|
||||
CodePanel selectedPanel = (CodePanel) areaTabbedPane.getSelectedComponent();
|
||||
// TODO: to run background load extract ui update to other method
|
||||
selectedPanel.load();
|
||||
// execInBackground(selectedPanel::load);
|
||||
});
|
||||
return areaTabbedPane;
|
||||
}
|
||||
|
||||
private void addCustomControls(JTabbedPane tabbedPane) {
|
||||
JCheckBox splitCheckBox = new JCheckBox("Split view", splitView);
|
||||
splitCheckBox.addItemListener(e -> {
|
||||
splitView = splitCheckBox.isSelected();
|
||||
this.initView();
|
||||
});
|
||||
|
||||
JToolBar trailing = new JToolBar();
|
||||
trailing.setFloatable(false);
|
||||
trailing.setBorder(null);
|
||||
// trailing.add(Box.createHorizontalGlue());
|
||||
trailing.addSeparator(new Dimension(50, 1));
|
||||
trailing.add(splitCheckBox);
|
||||
tabbedPane.putClientProperty(TABBED_PANE_TRAILING_COMPONENT, trailing);
|
||||
}
|
||||
|
||||
private void execInBackground(Runnable runnable) {
|
||||
BackgroundExecutor bgExec = this.tabbedPane.getMainWindow().getBackgroundExecutor();
|
||||
bgExec.execute("Loading", runnable);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -39,10 +39,9 @@ public final class CodeArea extends AbstractCodeArea {
|
||||
|
||||
private static final long serialVersionUID = 6312736869579635796L;
|
||||
|
||||
CodeArea(ContentPanel contentPanel) {
|
||||
super(contentPanel);
|
||||
CodeArea(ContentPanel contentPanel, JNode node) {
|
||||
super(contentPanel, node);
|
||||
setSyntaxEditingStyle(node.getSyntaxName());
|
||||
|
||||
boolean isJavaCode = node instanceof JClass;
|
||||
if (isJavaCode) {
|
||||
((RSyntaxDocument) getDocument()).setSyntaxStyle(new JadxTokenMaker(this));
|
||||
|
||||
@@ -15,7 +15,7 @@ public final class CodeContentPanel extends AbstractCodeContentPanel implements
|
||||
public CodeContentPanel(TabbedPane panel, JNode jnode) {
|
||||
super(panel, jnode);
|
||||
setLayout(new BorderLayout());
|
||||
codePanel = new CodePanel(new CodeArea(this));
|
||||
codePanel = new CodePanel(new CodeArea(this, jnode));
|
||||
add(codePanel, BorderLayout.CENTER);
|
||||
codePanel.load();
|
||||
}
|
||||
|
||||
@@ -60,8 +60,8 @@ public final class SmaliArea extends AbstractCodeArea {
|
||||
private boolean curVersion = false;
|
||||
private SmaliModel model;
|
||||
|
||||
SmaliArea(ContentPanel contentPanel) {
|
||||
super(contentPanel);
|
||||
SmaliArea(ContentPanel contentPanel, JClass node) {
|
||||
super(contentPanel, node);
|
||||
this.textNode = new TextNode(node.getName());
|
||||
|
||||
cbUseSmaliV2 = new JCheckBoxMenuItem(NLS.str("popup.bytecode_col"),
|
||||
@@ -106,6 +106,10 @@ public final class SmaliArea extends AbstractCodeArea {
|
||||
return textNode;
|
||||
}
|
||||
|
||||
public JClass getJClass() {
|
||||
return ((JClass) node);
|
||||
}
|
||||
|
||||
private void switchModel() {
|
||||
if (model != null) {
|
||||
model.unload();
|
||||
@@ -166,12 +170,12 @@ public final class SmaliArea extends AbstractCodeArea {
|
||||
public NormalModel() {
|
||||
Theme theme = getContentPanel().getTabbedPane().getMainWindow().getEditorTheme();
|
||||
setSyntaxScheme(theme.scheme);
|
||||
setSyntaxEditingStyle("text/smali");
|
||||
setSyntaxEditingStyle(SYNTAX_STYLE_SMALI);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void load() {
|
||||
setText(node.getSmali());
|
||||
setText(getJClass().getSmali());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
package jadx.gui.ui.codearea.mode;
|
||||
|
||||
import javax.swing.Icon;
|
||||
|
||||
import org.fife.ui.rsyntaxtextarea.SyntaxConstants;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import jadx.api.DecompilationMode;
|
||||
import jadx.api.ICodeInfo;
|
||||
import jadx.core.dex.nodes.ClassNode;
|
||||
import jadx.gui.treemodel.JClass;
|
||||
import jadx.gui.treemodel.JNode;
|
||||
|
||||
public class JCodeMode extends JNode {
|
||||
|
||||
private final JClass jCls;
|
||||
private final DecompilationMode mode;
|
||||
|
||||
private @Nullable ICodeInfo codeInfo;
|
||||
|
||||
public JCodeMode(JClass jClass, DecompilationMode mode) {
|
||||
this.jCls = jClass;
|
||||
this.mode = mode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JClass getJParent() {
|
||||
return jCls.getJParent();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Icon getIcon() {
|
||||
return jCls.getIcon();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String makeString() {
|
||||
return jCls.makeString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull ICodeInfo getCodeInfo() {
|
||||
if (codeInfo != null) {
|
||||
return codeInfo;
|
||||
}
|
||||
ClassNode cls = jCls.getCls().getClassNode();
|
||||
codeInfo = cls.decompileWithMode(mode);
|
||||
return codeInfo;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getContent() {
|
||||
return getCodeInfo().getCodeStr();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSyntaxName() {
|
||||
return SyntaxConstants.SYNTAX_STYLE_JAVA;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user