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;
import javax.swing.KeyStroke;
import javax.swing.ProgressMonitor;
import javax.swing.event.TreeExpansionEvent;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;
import javax.swing.event.TreeWillExpandListener;
import javax.swing.filechooser.FileNameExtensionFilter;
import javax.swing.plaf.basic.BasicButtonUI;
......@@ -40,14 +38,15 @@ import javax.swing.tree.ExpandVetoException;
import javax.swing.tree.TreePath;
import javax.swing.tree.TreeSelectionModel;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.DisplayMode;
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.ActionListener;
import java.awt.event.InputEvent;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
......@@ -55,8 +54,6 @@ import java.io.File;
import java.util.HashMap;
import java.util.Map;
import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea;
import org.fife.ui.rsyntaxtextarea.SyntaxConstants;
import org.fife.ui.rtextarea.RTextScrollPane;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
......@@ -66,7 +63,6 @@ public class MainWindow extends JFrame {
private static final Logger LOG = LoggerFactory.getLogger(MainWindow.class);
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 WINDOW_RATIO = 1 - BORDER_RATIO * 2;
......@@ -158,17 +154,8 @@ public class MainWindow extends JFrame {
}
}
private JPanel newCodePane() {
RSyntaxTextArea textArea = new RSyntaxTextArea();
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);
private JPanel newCodePane(final JClass cls) {
JadxTextArea textArea = new JadxTextArea(this, cls);
RTextScrollPane scrollPane = new RTextScrollPane(textArea);
scrollPane.setFoldIndicatorEnabled(true);
......@@ -191,27 +178,25 @@ public class MainWindow extends JFrame {
return (SearchBar) panel.getComponent(0);
}
private JTextArea getTextArea(JPanel panel) {
private JadxTextArea getTextArea(JPanel panel) {
RTextScrollPane scrollPane = (RTextScrollPane) panel.getComponent(1);
return scrollPane.getTextArea();
return (JadxTextArea) scrollPane.getTextArea();
}
private void showCode(JClass cls, int line) {
cls.load();
void showCode(JClass cls, int line) {
JPanel panel = (JPanel) openTabs.get(cls);
if (panel != null) {
panel = (JPanel) openTabs.get(cls);
tabbedPane.setSelectedComponent(panel);
} else {
panel = newCodePane();
panel = newCodePane(cls);
tabbedPane.add(panel);
openTabs.put(cls, panel);
int id = tabbedPane.indexOfComponent(panel);
tabbedPane.setTabComponentAt(id, makeTabComponent(cls, panel));
tabbedPane.setSelectedIndex(id);
}
JTextArea textArea = getTextArea(panel);
textArea.setText(cls.getCode());
JadxTextArea textArea = getTextArea(panel);
scrollToLine(textArea, line);
}
......@@ -366,13 +351,20 @@ public class MainWindow extends JFrame {
treeModel = new DefaultTreeModel(treeRoot);
tree = new JTree(treeModel);
tree.getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);
tree.addTreeSelectionListener(new TreeSelectionListener() {
tree.addMouseListener(new MouseAdapter() {
@Override
public void valueChanged(TreeSelectionEvent event) {
public void mouseClicked(MouseEvent e) {
treeClickAction();
}
});
tree.addKeyListener(new KeyAdapter() {
@Override
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_ENTER) {
treeClickAction();
}
}
});
tree.setCellRenderer(new DefaultTreeCellRenderer() {
@Override
public Component getTreeCellRendererComponent(JTree tree,
......@@ -386,7 +378,6 @@ public class MainWindow extends JFrame {
}
});
tree.addTreeWillExpandListener(new TreeWillExpandListener() {
@Override
public void treeWillExpand(TreeExpansionEvent event) throws ExpandVetoException {
TreePath path = event.getPath();
......@@ -413,9 +404,10 @@ public class MainWindow extends JFrame {
}
public void setLocationAndPosition() {
Dimension dimension = Toolkit.getDefaultToolkit().getScreenSize();
double w = dimension.getWidth();
double h = dimension.getHeight();
GraphicsDevice gd = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice();
DisplayMode mode = gd.getDisplayMode();
int w = mode.getWidth();
int h = mode.getHeight();
setLocation((int) (w * BORDER_RATIO), (int) (h * BORDER_RATIO));
setSize((int) (w * WINDOW_RATIO), (int) (h * WINDOW_RATIO));
}
......
......@@ -112,6 +112,16 @@ public class JClass extends JNode {
}
@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() {
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