Commit 4b73d24d authored by Jan S's avatar Jan S Committed by skylot

fix(gui): separate SearchBar for Java and Smali code areas (PR #653)

parent 65818dcc
......@@ -2,6 +2,8 @@ package jadx.gui.ui;
import javax.swing.*;
import org.jetbrains.annotations.Nullable;
import jadx.gui.treemodel.JNode;
public abstract class ContentPanel extends JPanel {
......@@ -25,4 +27,17 @@ public abstract class ContentPanel extends JPanel {
public JNode getNode() {
return node;
}
/**
* Allows to show a tool tip on the tab e.g. for displaying a long path of the
* selected entry inside the APK file.
*
* If <code>null</code> is returned no tool tip will be displayed.
*
* @return
*/
@Nullable
public String getTabTooltip() {
return null;
}
}
......@@ -23,8 +23,10 @@ import jadx.gui.treemodel.JCertificate;
import jadx.gui.treemodel.JClass;
import jadx.gui.treemodel.JNode;
import jadx.gui.treemodel.JResource;
import jadx.gui.ui.codearea.CodeArea;
import jadx.gui.ui.codearea.CodePanel;
import jadx.gui.ui.codearea.AbstractCodeArea;
import jadx.gui.ui.codearea.AbstractCodeContentPanel;
import jadx.gui.ui.codearea.ClassCodeContentPanel;
import jadx.gui.ui.codearea.CodeContentPanel;
import jadx.gui.utils.JumpManager;
import jadx.gui.utils.JumpPosition;
import jadx.gui.utils.NLS;
......@@ -66,13 +68,13 @@ public class TabbedPane extends JTabbedPane {
}
private void showCode(final JumpPosition pos) {
final CodePanel contentPanel = (CodePanel) getContentPanel(pos.getNode());
final AbstractCodeContentPanel contentPanel = (AbstractCodeContentPanel) getContentPanel(pos.getNode());
if (contentPanel == null) {
return;
}
SwingUtilities.invokeLater(() -> {
setSelectedComponent(contentPanel);
CodeArea codeArea = contentPanel.getCodeArea();
AbstractCodeArea codeArea = contentPanel.getCodeArea();
int line = pos.getLine();
if (line < 0) {
try {
......@@ -115,8 +117,8 @@ public class TabbedPane extends JTabbedPane {
@Nullable
private JumpPosition getCurrentPosition() {
ContentPanel selectedCodePanel = getSelectedCodePanel();
if (selectedCodePanel instanceof CodePanel) {
return ((CodePanel) selectedCodePanel).getCodeArea().getCurrentPosition();
if (selectedCodePanel instanceof CodeContentPanel) {
return ((CodeContentPanel) selectedCodePanel).getCodeArea().getCurrentPosition();
}
return null;
}
......@@ -168,6 +170,7 @@ public class TabbedPane extends JTabbedPane {
if (resFile.getType() == ResourceType.IMG) {
return new ImagePanel(this, res);
}
return new CodeContentPanel(this, node);
} else {
return null;
}
......@@ -178,7 +181,7 @@ public class TabbedPane extends JTabbedPane {
if (node instanceof JCertificate) {
return new CertificatePanel(this, node);
}
return new CodePanel(this, node);
return new ClassCodeContentPanel(this, node);
}
@Nullable
......@@ -194,6 +197,10 @@ public class TabbedPane extends JTabbedPane {
panel.setOpaque(false);
final JLabel label = new JLabel(name);
String toolTip = contentPanel.getTabTooltip();
if (toolTip != null) {
label.setToolTipText(toolTip);
}
label.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 10));
label.setIcon(node.getIcon());
......
package jadx.gui.ui.codearea;
import java.awt.Dimension;
import java.awt.Point;
import java.awt.Rectangle;
import javax.swing.JViewport;
import javax.swing.SwingUtilities;
import javax.swing.text.BadLocationException;
import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea;
import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import jadx.gui.settings.JadxSettings;
import jadx.gui.treemodel.JNode;
import jadx.gui.ui.ContentPanel;
import jadx.gui.ui.MainWindow;
import jadx.gui.utils.JumpPosition;
public abstract class AbstractCodeArea extends RSyntaxTextArea {
private static final Logger LOG = LoggerFactory.getLogger(AbstractCodeArea.class);
protected final ContentPanel contentPanel;
protected final JNode node;
public AbstractCodeArea(ContentPanel contentPanel) {
this.contentPanel = contentPanel;
this.node = contentPanel.getNode();
}
/**
* Implement in this method the code that loads and sets the content to be displayed
*/
public abstract void load();
public void loadSettings() {
loadCommonSettings(contentPanel.getTabbedPane().getMainWindow(), this);
}
public void scrollToLine(int line) {
int lineNum = line - 1;
if (lineNum < 0) {
lineNum = 0;
}
setCaretAtLine(lineNum);
centerCurrentLine();
forceCurrentLineHighlightRepaint();
}
private void setCaretAtLine(int line) {
try {
setCaretPosition(getLineStartOffset(line));
} catch (BadLocationException e) {
LOG.debug("Can't scroll to {}", line, e);
}
}
public void centerCurrentLine() {
JViewport viewport = (JViewport) SwingUtilities.getAncestorOfClass(JViewport.class, this);
if (viewport == null) {
return;
}
try {
Rectangle r = modelToView(getCaretPosition());
if (r == null) {
return;
}
int extentHeight = viewport.getExtentSize().height;
Dimension viewSize = viewport.getViewSize();
if (viewSize == null) {
return;
}
int viewHeight = viewSize.height;
int y = Math.max(0, r.y - extentHeight / 2);
y = Math.min(y, viewHeight - extentHeight);
viewport.setViewPosition(new Point(0, y));
} catch (BadLocationException e) {
LOG.debug("Can't center current line", e);
}
}
public JumpPosition getCurrentPosition() {
return new JumpPosition(node, getCaretLineNumber() + 1);
}
@Nullable
Integer getSourceLine(int line) {
return node.getSourceLine(line);
}
public ContentPanel getContentPanel() {
return contentPanel;
}
public JNode getNode() {
return node;
}
public static void loadCommonSettings(MainWindow mainWindow, RSyntaxTextArea area) {
area.setAntiAliasingEnabled(true);
mainWindow.getEditorTheme().apply(area);
JadxSettings settings = mainWindow.getSettings();
area.setFont(settings.getFont());
}
}
package jadx.gui.ui.codearea;
import jadx.gui.treemodel.JNode;
import jadx.gui.ui.ContentPanel;
import jadx.gui.ui.TabbedPane;
/**
* The abstract base class for a content panel that show text based code or a.g. a resource
*/
public abstract class AbstractCodeContentPanel extends ContentPanel {
protected AbstractCodeContentPanel(TabbedPane panel, JNode jnode) {
super(panel, jnode);
}
public abstract AbstractCodeArea getCodeArea();
}
package jadx.gui.ui.codearea;
import java.awt.BorderLayout;
import javax.swing.JTabbedPane;
import jadx.gui.treemodel.JNode;
import jadx.gui.ui.TabbedPane;
import jadx.gui.utils.NLS;
/**
* Displays one class with two different view:
*
* <ul>
* <li>Java source code of the selected class (default)</li>
* <li>Smali source code of the selected class</li>
* </ul>
*/
public final class ClassCodeContentPanel extends AbstractCodeContentPanel {
private static final long serialVersionUID = -7229931102504634591L;
private final CodePanel javaCodePanel;
private final CodePanel smaliCodePanel;
private JTabbedPane areaTabbedPane = new JTabbedPane(JTabbedPane.BOTTOM);
public ClassCodeContentPanel(TabbedPane panel, JNode jnode) {
super(panel, jnode);
javaCodePanel = new CodePanel(this, new CodeArea(this));
smaliCodePanel = new CodePanel(this, new SmaliArea(this));
setLayout(new BorderLayout());
areaTabbedPane.add(javaCodePanel, NLS.str("tabs.code"));
areaTabbedPane.add(smaliCodePanel, NLS.str("tabs.smali"));
add(areaTabbedPane);
javaCodePanel.load();
areaTabbedPane.addChangeListener(e -> {
CodePanel selectedPanel = (CodePanel) areaTabbedPane.getSelectedComponent();
selectedPanel.load();
});
}
@Override
public void loadSettings() {
javaCodePanel.loadSettings();
smaliCodePanel.loadSettings();
updateUI();
}
@Override
public TabbedPane getTabbedPane() {
return tabbedPane;
}
@Override
public JNode getNode() {
return node;
}
@Override
public AbstractCodeArea getCodeArea() {
return javaCodePanel.getCodeArea();
}
}
package jadx.gui.ui.codearea;
import java.awt.Dimension;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.JPopupMenu;
import javax.swing.JViewport;
import javax.swing.SwingUtilities;
import javax.swing.text.BadLocationException;
import javax.swing.text.Caret;
import javax.swing.text.DefaultCaret;
......@@ -23,23 +17,23 @@ import org.slf4j.LoggerFactory;
import jadx.api.CodePosition;
import jadx.api.JavaNode;
import jadx.gui.settings.JadxSettings;
import jadx.gui.treemodel.JClass;
import jadx.gui.treemodel.JNode;
import jadx.gui.ui.ContentPanel;
import jadx.gui.ui.MainWindow;
import jadx.gui.utils.JumpPosition;
public final class CodeArea extends RSyntaxTextArea {
/**
* The {@link AbstractCodeArea} implementation used for displaying Java code and text based
* resources (e.g. AndroidManifest.xml)
*/
public final class CodeArea extends AbstractCodeArea {
private static final Logger LOG = LoggerFactory.getLogger(CodeArea.class);
private static final long serialVersionUID = 6312736869579635796L;
private final CodePanel contentPanel;
private final JNode node;
CodeArea(CodePanel panel) {
this.contentPanel = panel;
this.node = panel.getNode();
CodeArea(ContentPanel contentPanel) {
super(contentPanel);
setMarkOccurrences(true);
setEditable(false);
......@@ -63,7 +57,14 @@ public final class CodeArea extends RSyntaxTextArea {
addMenuItems(jClsNode);
}
registerWordHighlighter();
setText(node.getContent());
}
@Override
public void load() {
if (getText().isEmpty()) {
setText(node.getContent());
setCaretPosition(0);
}
}
private void registerWordHighlighter() {
......@@ -106,18 +107,6 @@ public final class CodeArea extends RSyntaxTextArea {
popup.addPopupMenuListener(goToDeclaration);
}
public void loadSettings() {
loadCommonSettings(contentPanel.getTabbedPane().getMainWindow(), this);
}
public static void loadCommonSettings(MainWindow mainWindow, RSyntaxTextArea area) {
area.setAntiAliasingEnabled(true);
mainWindow.getEditorTheme().apply(area);
JadxSettings settings = mainWindow.getSettings();
area.setFont(settings.getFont());
}
public static RSyntaxTextArea getDefaultArea(MainWindow mainWindow) {
RSyntaxTextArea area = new RSyntaxTextArea();
loadCommonSettings(mainWindow, area);
......@@ -156,56 +145,4 @@ public final class CodeArea extends RSyntaxTextArea {
return null;
}
public JumpPosition getCurrentPosition() {
return new JumpPosition(node, getCaretLineNumber() + 1);
}
@Nullable
Integer getSourceLine(int line) {
return node.getSourceLine(line);
}
public void scrollToLine(int line) {
int lineNum = line - 1;
if (lineNum < 0) {
lineNum = 0;
}
setCaretAtLine(lineNum);
centerCurrentLine();
forceCurrentLineHighlightRepaint();
}
public void centerCurrentLine() {
JViewport viewport = (JViewport) SwingUtilities.getAncestorOfClass(JViewport.class, this);
if (viewport == null) {
return;
}
try {
Rectangle r = modelToView(getCaretPosition());
if (r == null) {
return;
}
int extentHeight = viewport.getExtentSize().height;
Dimension viewSize = viewport.getViewSize();
if (viewSize == null) {
return;
}
int viewHeight = viewSize.height;
int y = Math.max(0, r.y - extentHeight / 2);
y = Math.min(y, viewHeight - extentHeight);
viewport.setViewPosition(new Point(0, y));
} catch (BadLocationException e) {
LOG.debug("Can't center current line", e);
}
}
private void setCaretAtLine(int line) {
try {
setCaretPosition(getLineStartOffset(line));
} catch (BadLocationException e) {
LOG.debug("Can't scroll to {}", line, e);
}
}
}
package jadx.gui.ui.codearea;
import java.awt.BorderLayout;
import jadx.gui.treemodel.JNode;
import jadx.gui.ui.TabbedPane;
public final class CodeContentPanel extends AbstractCodeContentPanel {
private static final long serialVersionUID = 5310536092010045565L;
private final CodePanel codePanel;
public CodeContentPanel(TabbedPane panel, JNode jnode) {
super(panel, jnode);
setLayout(new BorderLayout());
codePanel = new CodePanel(this, new CodeArea(this));
add(codePanel, BorderLayout.CENTER);
codePanel.load();
}
@Override
public void loadSettings() {
codePanel.loadSettings();
updateUI();
}
@Override
public TabbedPane getTabbedPane() {
return tabbedPane;
}
@Override
public JNode getNode() {
return node;
}
SearchBar getSearchBar() {
return codePanel.getSearchBar();
}
@Override
public AbstractCodeArea getCodeArea() {
return codePanel.getCodeArea();
}
@Override
public String getTabTooltip() {
String s = node.getName();
JNode n = (JNode) node.getParent();
while (n != null) {
String name = n.getName();
if (name == null) {
break;
}
s = name + '/' + s;
n = (JNode) n.getParent();
}
return '/' + s;
}
}
......@@ -14,16 +14,17 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import jadx.gui.treemodel.JClass;
import jadx.gui.ui.ContentPanel;
import jadx.gui.utils.JumpPosition;
public class CodeLinkGenerator implements LinkGenerator, HyperlinkListener {
private static final Logger LOG = LoggerFactory.getLogger(CodeLinkGenerator.class);
private final CodePanel contentPanel;
private final ContentPanel contentPanel;
private final CodeArea codeArea;
private final JClass jCls;
public CodeLinkGenerator(CodePanel contentPanel, CodeArea codeArea, JClass cls) {
public CodeLinkGenerator(ContentPanel contentPanel, CodeArea codeArea, JClass cls) {
this.contentPanel = contentPanel;
this.codeArea = codeArea;
this.jCls = cls;
......
......@@ -5,60 +5,51 @@ import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import javax.swing.AbstractAction;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTabbedPane;
import javax.swing.KeyStroke;
import jadx.gui.treemodel.JClass;
import jadx.gui.treemodel.JNode;
import jadx.gui.treemodel.JResource;
import jadx.gui.ui.ContentPanel;
import jadx.gui.ui.TabbedPane;
import jadx.gui.utils.NLS;
import jadx.gui.utils.Utils;
public final class CodePanel extends ContentPanel {
private static final long serialVersionUID = 5310536092010045565L;
/**
* A panel combining a {@link SearchBar and a scollable {@link CodeArea}
*/
public class CodePanel extends JPanel {
private final SearchBar searchBar;
private final CodeArea codeArea;
private final SmaliArea smaliArea;
private final AbstractCodeArea codeArea;
private final JScrollPane codeScrollPane;
private final JScrollPane smaliScrollPane;
private JTabbedPane areaTabbedPane = new JTabbedPane(JTabbedPane.BOTTOM);
public CodePanel(TabbedPane panel, JNode jnode) {
super(panel, jnode);
codeArea = new CodeArea(this);
smaliArea = new SmaliArea(this);
public CodePanel(ContentPanel contentPanel, AbstractCodeArea codeArea) {
this.codeArea = codeArea;
searchBar = new SearchBar(codeArea);
codeScrollPane = new JScrollPane(codeArea);
smaliScrollPane = new JScrollPane(smaliArea);
initLineNumbers();
setLayout(new BorderLayout());
add(searchBar, BorderLayout.NORTH);
areaTabbedPane.add(codeScrollPane, NLS.str("tabs.code"));
areaTabbedPane.add(smaliScrollPane, NLS.str("tabs.smali"));
add(areaTabbedPane);
add(codeScrollPane, BorderLayout.CENTER);
initLineNumbers();
KeyStroke key = KeyStroke.getKeyStroke(KeyEvent.VK_F, Utils.ctrlButton());
SearchAction searchAction = new SearchAction();
Utils.addKeyBinding(codeArea, key, "SearchAction", searchAction);
Utils.addKeyBinding(smaliArea, key, "SearchAction", searchAction);
areaTabbedPane.addChangeListener(e -> {
if (areaTabbedPane.getSelectedComponent() == smaliScrollPane) {
smaliArea.load();
searchBar.setRTextArea(smaliArea);
} else if (areaTabbedPane.getSelectedComponent() == codeScrollPane) {
searchBar.setRTextArea(codeArea);
Utils.addKeyBinding(codeArea, key, "SearchAction", new AbstractAction() {
@Override
public void actionPerformed(ActionEvent e) {
searchBar.toggle();
}
});
}
public void loadSettings() {
codeArea.loadSettings();
initLineNumbers();
}
public void load() {
codeArea.load();
}
private void initLineNumbers() {
// TODO: fix slow line rendering on big files
if (codeArea.getDocument().getLength() <= 100_000) {
......@@ -69,48 +60,22 @@ public final class CodePanel extends ContentPanel {
}
private boolean isUseSourceLines() {
if (node instanceof JClass) {
return true;
}
if (node instanceof JResource) {
JResource resNode = (JResource) node;
if (codeArea.getNode() instanceof JResource) {
JResource resNode = (JResource) codeArea.getNode();
return !resNode.getLineMapping().isEmpty();
}
return false;
}
private class SearchAction extends AbstractAction {
private static final long serialVersionUID = 8650568214755387093L;
@Override
public void actionPerformed(ActionEvent e) {
searchBar.toggle();
}
}
@Override
public void loadSettings() {
codeArea.loadSettings();
initLineNumbers();
updateUI();
}
@Override
public TabbedPane getTabbedPane() {
return tabbedPane;
}
@Override
public JNode getNode() {
return node;
}
SearchBar getSearchBar() {
public SearchBar getSearchBar() {
return searchBar;
}
public CodeArea getCodeArea() {
public AbstractCodeArea getCodeArea() {
return codeArea;
}
public JScrollPane getCodeScrollPane() {
return codeScrollPane;
}
}
......@@ -12,6 +12,7 @@ import org.fife.ui.rsyntaxtextarea.Token;
import jadx.api.JavaNode;
import jadx.gui.treemodel.JClass;
import jadx.gui.treemodel.JNode;
import jadx.gui.ui.ContentPanel;
import jadx.gui.ui.MainWindow;
import jadx.gui.ui.UsageDialog;
import jadx.gui.utils.NLS;
......@@ -19,13 +20,13 @@ import jadx.gui.utils.NLS;
public final class FindUsageAction extends AbstractAction implements PopupMenuListener {
private static final long serialVersionUID = 4692546569977976384L;
private final transient CodePanel contentPanel;
private final transient ContentPanel contentPanel;
private final transient CodeArea codeArea;
private final transient JClass jCls;
private transient JavaNode node;
public FindUsageAction(CodePanel contentPanel, CodeArea codeArea, JClass jCls) {
public FindUsageAction(ContentPanel contentPanel, CodeArea codeArea, JClass jCls) {
super(NLS.str("popup.find_usage"));
this.contentPanel = contentPanel;
this.codeArea = codeArea;
......
......@@ -12,19 +12,20 @@ import org.fife.ui.rsyntaxtextarea.Token;
import jadx.api.JavaNode;
import jadx.gui.treemodel.JClass;
import jadx.gui.treemodel.JNode;
import jadx.gui.ui.ContentPanel;
import jadx.gui.ui.MainWindow;
import jadx.gui.utils.JumpPosition;
import jadx.gui.utils.NLS;
public final class GoToDeclarationAction extends AbstractAction implements PopupMenuListener {
private static final long serialVersionUID = -1186470538894941301L;
private final transient CodePanel contentPanel;
private final transient ContentPanel contentPanel;
private final transient CodeArea codeArea;
private final transient JClass jCls;
private transient JavaNode node;
public GoToDeclarationAction(CodePanel contentPanel, CodeArea codeArea, JClass jCls) {
public GoToDeclarationAction(ContentPanel contentPanel, CodeArea codeArea, JClass jCls) {
super(NLS.str("popup.go_to_declaration"));
this.contentPanel = contentPanel;
this.codeArea = codeArea;
......
......@@ -33,7 +33,7 @@ public class LineNumbers extends JPanel implements CaretListener {
private static final int NUM_HEIGHT = Integer.MAX_VALUE - 1000000;
private static final Map<?, ?> DESKTOP_HINTS = (Map<?, ?>) Toolkit.getDefaultToolkit().getDesktopProperty("awt.font.desktophints");
private final CodeArea codeArea;
private final AbstractCodeArea codeArea;
private boolean useSourceLines = true;
private int lastDigits;
......@@ -44,9 +44,9 @@ public class LineNumbers extends JPanel implements CaretListener {
private final transient Color currentColor;
private final transient Border border;
public LineNumbers(CodeArea component) {
this.codeArea = component;
setFont(component.getFont());
public LineNumbers(AbstractCodeArea codeArea) {
this.codeArea = codeArea;
setFont(codeArea.getFont());
SyntaxScheme syntaxScheme = codeArea.getSyntaxScheme();
numberColor = syntaxScheme.getStyle(Token.LITERAL_NUMBER_DECIMAL_INT).foreground;
currentColor = syntaxScheme.getStyle(Token.LITERAL_STRING_DOUBLE_QUOTE).foreground;
......@@ -57,7 +57,7 @@ public class LineNumbers extends JPanel implements CaretListener {
setBorderGap(5);
setPreferredWidth();
component.addCaretListener(this);
codeArea.addCaretListener(this);
addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
......
package jadx.gui.ui.codearea;
import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea;
import jadx.gui.ui.ContentPanel;
import jadx.gui.treemodel.JNode;
public final class SmaliArea extends RSyntaxTextArea {
public final class SmaliArea extends AbstractCodeArea {
private static final long serialVersionUID = 1334485631870306494L;
private final JNode node;
SmaliArea(CodePanel panel) {
node = panel.getNode();
SmaliArea(ContentPanel contentPanel) {
super(contentPanel);
setEditable(false);
}
void load() {
@Override
public void load() {
if (getText().isEmpty()) {
setText(node.getSmali());
setCaretPosition(0);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment