Commit ed64b8c1 authored by Skylot's avatar Skylot

gui: add hyperlinks for jump to definitions

parent 2a60ac47
package jadx.gui;
import jadx.api.CodePosition;
import jadx.gui.treemodel.JClass;
import javax.swing.event.HyperlinkEvent;
import javax.swing.event.HyperlinkListener;
import javax.swing.text.BadLocationException;
import java.awt.Color;
import org.fife.ui.rsyntaxtextarea.LinkGenerator;
import org.fife.ui.rsyntaxtextarea.LinkGeneratorResult;
import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea;
import org.fife.ui.rsyntaxtextarea.SyntaxConstants;
import org.fife.ui.rsyntaxtextarea.SyntaxScheme;
import org.fife.ui.rsyntaxtextarea.Token;
import org.fife.ui.rsyntaxtextarea.TokenTypes;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class JadxTextArea extends RSyntaxTextArea {
private static final Logger LOG = LoggerFactory.getLogger(JadxTextArea.class);
private static final Color BACKGROUND = new Color(0xf7f7f7);
private static final Color JUMP_FOREGROUND = new Color(0x785523);
private static final Color JUMP_BACKGROUND = new Color(0xE6E6FF);
private final JClass cls;
private final MainWindow rootWindow;
public JadxTextArea(MainWindow mainWindow, JClass cls) {
this.rootWindow = mainWindow;
this.cls = cls;
setSyntaxEditingStyle(SyntaxConstants.SYNTAX_STYLE_JAVA);
SyntaxScheme scheme = getSyntaxScheme();
scheme.getStyle(Token.FUNCTION).foreground = Color.BLACK;
setMarkOccurrences(true);
setBackground(BACKGROUND);
setAntiAliasingEnabled(true);
setEditable(false);
getCaret().setVisible(true);
setHyperlinksEnabled(true);
CodeLinkGenerator codeLinkProcessor = new CodeLinkGenerator(cls);
setLinkGenerator(codeLinkProcessor);
addHyperlinkListener(codeLinkProcessor);
setText(cls.getCode());
}
private boolean isJumpToken(Token token) {
if (token.getType() == TokenTypes.IDENTIFIER) {
CodePosition pos = getCodePosition(cls, this, token.getOffset());
if (pos != null) {
return true;
}
}
return false;
}
@Override
public boolean getUnderlineForToken(Token t) {
if (isJumpToken(t)) {
return true;
}
return super.getUnderlineForToken(t);
}
static CodePosition getCodePosition(JClass jCls, RSyntaxTextArea textArea, int offset) {
try {
int line = textArea.getLineOfOffset(offset);
int lineOffset = offset - textArea.getLineStartOffset(line);
return jCls.getCls().getDefinitionPosition(line + 1, lineOffset + 1);
} catch (BadLocationException e) {
LOG.error("Can't get line by offset", e);
return null;
}
}
private class CodeLinkGenerator implements LinkGenerator, HyperlinkListener {
private final JClass jCls;
public CodeLinkGenerator(JClass cls) {
this.jCls = cls;
}
@Override
public LinkGeneratorResult isLinkAtOffset(RSyntaxTextArea textArea, int offset) {
try {
Token token = textArea.modelToToken(offset);
if (token != null) {
offset = token.getOffset();
}
final CodePosition defPos = getCodePosition(jCls, textArea, offset);
if (defPos != null) {
final int sourceOffset = offset;
return new LinkGeneratorResult() {
@Override
public HyperlinkEvent execute() {
return new HyperlinkEvent(defPos, HyperlinkEvent.EventType.ACTIVATED, null,
defPos.getJavaClass().getFullName());
}
@Override
public int getSourceOffset() {
return sourceOffset;
}
};
}
} catch (Exception e) {
LOG.error("isLinkAtOffset error", e);
}
return null;
}
@Override
public void hyperlinkUpdate(HyperlinkEvent e) {
Object obj = e.getSource();
if (obj instanceof CodePosition) {
CodePosition pos = (CodePosition) obj;
JClass cls = new JClass(pos.getJavaClass());
rootWindow.showCode(cls, pos.getLine());
LOG.debug("Code jump to: {}", pos);
}
}
}
}
...@@ -27,8 +27,6 @@ import javax.swing.JTree; ...@@ -27,8 +27,6 @@ import javax.swing.JTree;
import javax.swing.KeyStroke; import javax.swing.KeyStroke;
import javax.swing.ProgressMonitor; import javax.swing.ProgressMonitor;
import javax.swing.event.TreeExpansionEvent; import javax.swing.event.TreeExpansionEvent;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;
import javax.swing.event.TreeWillExpandListener; import javax.swing.event.TreeWillExpandListener;
import javax.swing.filechooser.FileNameExtensionFilter; import javax.swing.filechooser.FileNameExtensionFilter;
import javax.swing.plaf.basic.BasicButtonUI; import javax.swing.plaf.basic.BasicButtonUI;
...@@ -40,14 +38,15 @@ import javax.swing.tree.ExpandVetoException; ...@@ -40,14 +38,15 @@ import javax.swing.tree.ExpandVetoException;
import javax.swing.tree.TreePath; import javax.swing.tree.TreePath;
import javax.swing.tree.TreeSelectionModel; import javax.swing.tree.TreeSelectionModel;
import java.awt.BorderLayout; import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component; import java.awt.Component;
import java.awt.Dimension; import java.awt.DisplayMode;
import java.awt.FlowLayout; import java.awt.FlowLayout;
import java.awt.Toolkit; import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.event.ActionEvent; import java.awt.event.ActionEvent;
import java.awt.event.ActionListener; import java.awt.event.ActionListener;
import java.awt.event.InputEvent; import java.awt.event.InputEvent;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent; import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter; import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent; import java.awt.event.MouseEvent;
...@@ -55,8 +54,6 @@ import java.io.File; ...@@ -55,8 +54,6 @@ import java.io.File;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea;
import org.fife.ui.rsyntaxtextarea.SyntaxConstants;
import org.fife.ui.rtextarea.RTextScrollPane; import org.fife.ui.rtextarea.RTextScrollPane;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
...@@ -66,7 +63,6 @@ public class MainWindow extends JFrame { ...@@ -66,7 +63,6 @@ public class MainWindow extends JFrame {
private static final Logger LOG = LoggerFactory.getLogger(MainWindow.class); private static final Logger LOG = LoggerFactory.getLogger(MainWindow.class);
private static final String DEFAULT_TITLE = "jadx-gui"; private static final String DEFAULT_TITLE = "jadx-gui";
private static final Color BACKGROUND = new Color(0xf7f7f7);
private static final double BORDER_RATIO = 0.15; private static final double BORDER_RATIO = 0.15;
private static final double WINDOW_RATIO = 1 - BORDER_RATIO * 2; private static final double WINDOW_RATIO = 1 - BORDER_RATIO * 2;
...@@ -158,17 +154,8 @@ public class MainWindow extends JFrame { ...@@ -158,17 +154,8 @@ public class MainWindow extends JFrame {
} }
} }
private JPanel newCodePane() { private JPanel newCodePane(final JClass cls) {
RSyntaxTextArea textArea = new RSyntaxTextArea(); JadxTextArea textArea = new JadxTextArea(this, cls);
textArea.setSyntaxEditingStyle(SyntaxConstants.SYNTAX_STYLE_JAVA);
textArea.setMarkOccurrences(true);
textArea.setBackground(BACKGROUND);
textArea.setCodeFoldingEnabled(true);
textArea.setAntiAliasingEnabled(true);
// textArea.setEditable(false);
// textArea.setHyperlinksEnabled(true);
textArea.setTabSize(4);
RTextScrollPane scrollPane = new RTextScrollPane(textArea); RTextScrollPane scrollPane = new RTextScrollPane(textArea);
scrollPane.setFoldIndicatorEnabled(true); scrollPane.setFoldIndicatorEnabled(true);
...@@ -191,27 +178,25 @@ public class MainWindow extends JFrame { ...@@ -191,27 +178,25 @@ public class MainWindow extends JFrame {
return (SearchBar) panel.getComponent(0); return (SearchBar) panel.getComponent(0);
} }
private JTextArea getTextArea(JPanel panel) { private JadxTextArea getTextArea(JPanel panel) {
RTextScrollPane scrollPane = (RTextScrollPane) panel.getComponent(1); RTextScrollPane scrollPane = (RTextScrollPane) panel.getComponent(1);
return scrollPane.getTextArea(); return (JadxTextArea) scrollPane.getTextArea();
} }
private void showCode(JClass cls, int line) { void showCode(JClass cls, int line) {
cls.load();
JPanel panel = (JPanel) openTabs.get(cls); JPanel panel = (JPanel) openTabs.get(cls);
if (panel != null) { if (panel != null) {
panel = (JPanel) openTabs.get(cls); panel = (JPanel) openTabs.get(cls);
tabbedPane.setSelectedComponent(panel); tabbedPane.setSelectedComponent(panel);
} else { } else {
panel = newCodePane(); panel = newCodePane(cls);
tabbedPane.add(panel); tabbedPane.add(panel);
openTabs.put(cls, panel); openTabs.put(cls, panel);
int id = tabbedPane.indexOfComponent(panel); int id = tabbedPane.indexOfComponent(panel);
tabbedPane.setTabComponentAt(id, makeTabComponent(cls, panel)); tabbedPane.setTabComponentAt(id, makeTabComponent(cls, panel));
tabbedPane.setSelectedIndex(id); tabbedPane.setSelectedIndex(id);
} }
JTextArea textArea = getTextArea(panel); JadxTextArea textArea = getTextArea(panel);
textArea.setText(cls.getCode());
scrollToLine(textArea, line); scrollToLine(textArea, line);
} }
...@@ -366,13 +351,20 @@ public class MainWindow extends JFrame { ...@@ -366,13 +351,20 @@ public class MainWindow extends JFrame {
treeModel = new DefaultTreeModel(treeRoot); treeModel = new DefaultTreeModel(treeRoot);
tree = new JTree(treeModel); tree = new JTree(treeModel);
tree.getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION); tree.getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);
tree.addTreeSelectionListener(new TreeSelectionListener() { tree.addMouseListener(new MouseAdapter() {
@Override @Override
public void valueChanged(TreeSelectionEvent event) { public void mouseClicked(MouseEvent e) {
treeClickAction(); treeClickAction();
} }
}); });
tree.addKeyListener(new KeyAdapter() {
@Override
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_ENTER) {
treeClickAction();
}
}
});
tree.setCellRenderer(new DefaultTreeCellRenderer() { tree.setCellRenderer(new DefaultTreeCellRenderer() {
@Override @Override
public Component getTreeCellRendererComponent(JTree tree, public Component getTreeCellRendererComponent(JTree tree,
...@@ -386,7 +378,6 @@ public class MainWindow extends JFrame { ...@@ -386,7 +378,6 @@ public class MainWindow extends JFrame {
} }
}); });
tree.addTreeWillExpandListener(new TreeWillExpandListener() { tree.addTreeWillExpandListener(new TreeWillExpandListener() {
@Override @Override
public void treeWillExpand(TreeExpansionEvent event) throws ExpandVetoException { public void treeWillExpand(TreeExpansionEvent event) throws ExpandVetoException {
TreePath path = event.getPath(); TreePath path = event.getPath();
...@@ -413,9 +404,10 @@ public class MainWindow extends JFrame { ...@@ -413,9 +404,10 @@ public class MainWindow extends JFrame {
} }
public void setLocationAndPosition() { public void setLocationAndPosition() {
Dimension dimension = Toolkit.getDefaultToolkit().getScreenSize(); GraphicsDevice gd = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice();
double w = dimension.getWidth(); DisplayMode mode = gd.getDisplayMode();
double h = dimension.getHeight(); int w = mode.getWidth();
int h = mode.getHeight();
setLocation((int) (w * BORDER_RATIO), (int) (h * BORDER_RATIO)); setLocation((int) (w * BORDER_RATIO), (int) (h * BORDER_RATIO));
setSize((int) (w * WINDOW_RATIO), (int) (h * WINDOW_RATIO)); setSize((int) (w * WINDOW_RATIO), (int) (h * WINDOW_RATIO));
} }
......
...@@ -112,6 +112,16 @@ public class JClass extends JNode { ...@@ -112,6 +112,16 @@ public class JClass extends JNode {
} }
@Override @Override
public int hashCode() {
return cls.hashCode();
}
@Override
public boolean equals(Object obj) {
return this == obj || obj instanceof JClass && cls.equals(((JClass) obj).cls);
}
@Override
public String toString() { public String toString() {
return cls.getShortName(); return cls.getShortName();
} }
......
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