feat(gui): add split view for different decompilation modes

This commit is contained in:
Skylot
2022-03-22 10:59:15 +00:00
parent d8306cb1c0
commit fe91d774fa
11 changed files with 167 additions and 44 deletions
@@ -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;
}
}