Commit 324f544b authored by Skylot's avatar Skylot

gui: show source line numbers

parent 0a1981f9
......@@ -130,6 +130,11 @@ public final class JavaClass {
return null;
}
public Integer getSourceLine(int decompiledLine) {
decompile();
return cls.getCode().getLineMapping().get(decompiledLine);
}
public String getFullName() {
return cls.getFullName();
}
......
......@@ -63,12 +63,10 @@ public class ClassGen {
addClassCode(clsBody);
CodeWriter clsCode = new CodeWriter();
if (!"".equals(cls.getPackage())) {
clsCode.add("package ").add(cls.getPackage()).add(';');
clsCode.newLine();
}
int importsCount = imports.size();
if (importsCount != 0) {
List<String> sortImports = new ArrayList<String>(importsCount);
......@@ -85,7 +83,6 @@ public class ClassGen {
sortImports.clear();
imports.clear();
}
clsCode.add(clsBody);
return clsCode;
}
......
......@@ -37,17 +37,13 @@ public class CodeWriter {
private int line = 1;
private int offset = 0;
private Map<CodePosition, Object> annotations = Collections.emptyMap();
private Map<Integer, Integer> lineMap = Collections.emptyMap();
public CodeWriter() {
this.indent = 0;
this.indentStr = "";
}
public CodeWriter(int indent) {
this.indent = indent;
updateIndent();
}
public CodeWriter startLine() {
addLine();
addLineIndent();
......@@ -68,11 +64,6 @@ public class CodeWriter {
return this;
}
public CodeWriter add(Object obj) {
add(obj.toString());
return this;
}
public CodeWriter add(String str) {
buf.append(str);
offset += str.length();
......@@ -91,6 +82,9 @@ public class CodeWriter {
CodePosition pos = entry.getKey();
attachAnnotation(entry.getValue(), new CodePosition(line + pos.getLine(), pos.getOffset()));
}
for (Map.Entry<Integer, Integer> entry : code.lineMap.entrySet()) {
attachSourceLine(line + entry.getKey(), entry.getValue());
}
line += code.line;
offset = code.offset;
buf.append(code);
......@@ -102,6 +96,11 @@ public class CodeWriter {
return this;
}
public CodeWriter addIndent() {
add(INDENT);
return this;
}
private void addLine() {
buf.append(NL);
line++;
......@@ -114,11 +113,6 @@ public class CodeWriter {
return this;
}
public CodeWriter addIndent() {
add(INDENT);
return this;
}
private void updateIndent() {
int curIndent = indent;
if (curIndent < INDENT_CACHE.length) {
......@@ -178,17 +172,32 @@ public class CodeWriter {
return attachAnnotation(obj, new CodePosition(line, offset + 1));
}
private Object attachAnnotation(Object obj, CodePosition pos) {
if (annotations.isEmpty()) {
annotations = new HashMap<CodePosition, Object>();
private void attachSourceLine(int decompiledLine, int sourceLine) {
if (lineMap.isEmpty()) {
lineMap = new HashMap<Integer, Integer>();
}
return annotations.put(pos, obj);
lineMap.put(decompiledLine, sourceLine);
}
public Map<CodePosition, Object> getAnnotations() {
return annotations;
}
public void attachSourceLine(int sourceLine) {
attachSourceLine(line, sourceLine);
}
public Map<Integer, Integer> getLineMapping() {
return lineMap;
}
private Object attachAnnotation(Object obj, CodePosition pos) {
if (annotations.isEmpty()) {
annotations = new HashMap<CodePosition, Object>();
}
return annotations.put(pos, obj);
}
public void finish() {
buf.trimToSize();
Iterator<Map.Entry<CodePosition, Object>> it = annotations.entrySet().iterator();
......@@ -206,9 +215,8 @@ public class CodeWriter {
private static String removeFirstEmptyLine(String str) {
if (str.startsWith(NL)) {
return str.substring(NL.length());
} else {
return str;
}
return str;
}
public int length() {
......
......@@ -198,7 +198,7 @@ public class InsnGen {
} else {
code.startLine();
if (insn.getSourceLine() != 0) {
code.attachAnnotation(insn.getSourceLine());
code.attachSourceLine(insn.getSourceLine());
}
if (insn.getResult() != null && insn.getType() != InsnType.ARITH_ONEARG) {
assignVar(code, insn);
......@@ -469,7 +469,8 @@ public class InsnGen {
code.add(") {");
code.incIndent();
for (int i = 0; i < sw.getCasesCount(); i++) {
code.startLine("case ").add(sw.getKeys()[i]).add(": goto ");
String key = sw.getKeys()[i].toString();
code.startLine("case ").add(key).add(": goto ");
code.add(MethodGen.getLabelName(sw.getTargets()[i])).add(';');
}
code.startLine("default: goto ");
......
......@@ -120,7 +120,7 @@ public class DotGraphVisitor extends AbstractVisitor {
if (region instanceof IRegion) {
IRegion r = (IRegion) region;
dot.startLine("subgraph " + makeName(region) + " {");
dot.startLine("label = \"").add(r);
dot.startLine("label = \"").add(r.toString());
String attrs = attributesString(r);
if (attrs.length() != 0) {
dot.add(" | ").add(attrs);
......@@ -146,7 +146,7 @@ public class DotGraphVisitor extends AbstractVisitor {
dot.add("color=red,");
}
dot.add("label=\"{");
dot.add(block.getId()).add("\\:\\ ");
dot.add(String.valueOf(block.getId())).add("\\:\\ ");
dot.add(InsnUtils.formatOffset(block.getStartOffset()));
if (attrs.length() != 0) {
dot.add('|').add(attrs);
......@@ -210,7 +210,7 @@ public class DotGraphVisitor extends AbstractVisitor {
}
return str.toString();
} else {
CodeWriter code = new CodeWriter(0);
CodeWriter code = new CodeWriter();
MethodGen.addFallbackInsns(code, mth, block.getInstructions(), false);
String str = escape(code.newLine().toString());
if (str.startsWith(NL)) {
......
......@@ -30,7 +30,7 @@ class CodeArea extends RSyntaxTextArea {
private static final long serialVersionUID = 6312736869579635796L;
private static final Color BACKGROUND = new Color(0xf7f7f7);
public 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);
......@@ -96,7 +96,11 @@ class CodeArea extends RSyntaxTextArea {
}
Position getCurrentPosition() {
return new Position(cls, getCaretLineNumber());
return new Position(cls, getCaretLineNumber() + 1);
}
Integer getSourceLine(int line) {
return cls.getCls().getSourceLine(line);
}
void scrollToLine(int line) {
......
......@@ -5,14 +5,13 @@ import jadx.gui.utils.Utils;
import javax.swing.AbstractAction;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.KeyStroke;
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
import org.fife.ui.rtextarea.RTextScrollPane;
class CodePanel extends JPanel {
private static final long serialVersionUID = 5310536092010045565L;
......@@ -21,7 +20,7 @@ class CodePanel extends JPanel {
private final JClass jClass;
private final SearchBar searchBar;
private final CodeArea codeArea;
private final RTextScrollPane scrollPane;
private final JScrollPane scrollPane;
CodePanel(TabbedPane panel, JClass cls) {
tabbedPane = panel;
......@@ -29,8 +28,8 @@ class CodePanel extends JPanel {
codeArea = new CodeArea(this);
searchBar = new SearchBar(codeArea);
scrollPane = new RTextScrollPane(codeArea);
scrollPane.setFoldIndicatorEnabled(true);
scrollPane = new JScrollPane(codeArea);
scrollPane.setRowHeaderView(new LineNumbers(codeArea));
setLayout(new BorderLayout());
add(searchBar, BorderLayout.NORTH);
......@@ -65,7 +64,7 @@ class CodePanel extends JPanel {
return codeArea;
}
RTextScrollPane getScrollPane() {
JScrollPane getScrollPane() {
return scrollPane;
}
}
package jadx.gui.ui;
import javax.swing.JPanel;
import javax.swing.border.Border;
import javax.swing.border.CompoundBorder;
import javax.swing.border.EmptyBorder;
import javax.swing.border.MatteBorder;
import javax.swing.event.CaretEvent;
import javax.swing.event.CaretListener;
import javax.swing.text.AttributeSet;
import javax.swing.text.BadLocationException;
import javax.swing.text.Element;
import javax.swing.text.StyleConstants;
import javax.swing.text.Utilities;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Insets;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.HashMap;
public class LineNumbers extends JPanel implements CaretListener {
private final static Border OUTER = new MatteBorder(0, 0, 0, 1, Color.LIGHT_GRAY);
private final static int HEIGHT = Integer.MAX_VALUE - 1000000;
public static final Color FOREGROUND = Color.GRAY;
public static final Color BACKGROUND = CodeArea.BACKGROUND;
public static final Color CURRENT_LINE_FOREGROUND = new Color(227, 0, 0);
private CodeArea codeArea;
private boolean useSourceLines = true;
private int lastDigits;
private int lastLine;
private HashMap<String, FontMetrics> fonts;
public LineNumbers(CodeArea component) {
this.codeArea = component;
setFont(component.getFont());
setBackground(BACKGROUND);
setForeground(FOREGROUND);
setBorderGap(5);
setPreferredWidth();
component.addCaretListener(this);
addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
if (e.getClickCount() == 2) {
useSourceLines = !useSourceLines;
repaint();
}
}
});
}
public void setBorderGap(int borderGap) {
Border inner = new EmptyBorder(0, borderGap, 0, borderGap);
setBorder(new CompoundBorder(OUTER, inner));
lastDigits = 0;
}
private void setPreferredWidth() {
Element root = codeArea.getDocument().getDefaultRootElement();
int lines = root.getElementCount();
int digits = Math.max(String.valueOf(lines).length(), 3);
if (lastDigits != digits) {
lastDigits = digits;
FontMetrics fontMetrics = getFontMetrics(getFont());
int width = fontMetrics.charWidth('0') * digits;
Insets insets = getInsets();
int preferredWidth = insets.left + insets.right + width;
Dimension d = getPreferredSize();
if (d != null) {
d.setSize(preferredWidth, HEIGHT);
setPreferredSize(d);
setSize(d);
}
}
}
@Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
FontMetrics fontMetrics = codeArea.getFontMetrics(codeArea.getFont());
Insets insets = getInsets();
int availableWidth = getSize().width - insets.left - insets.right;
Rectangle clip = g.getClipBounds();
int rowStartOffset = codeArea.viewToModel(new Point(0, clip.y));
int endOffset = codeArea.viewToModel(new Point(0, clip.y + clip.height));
while (rowStartOffset <= endOffset) {
try {
if (isCurrentLine(rowStartOffset)) {
g.setColor(CURRENT_LINE_FOREGROUND);
} else {
g.setColor(FOREGROUND);
}
String lineNumber = getTextLineNumber(rowStartOffset);
int stringWidth = fontMetrics.stringWidth(lineNumber);
int x = availableWidth - stringWidth + insets.left;
int y = getOffsetY(rowStartOffset, fontMetrics);
g.drawString(lineNumber, x, y);
rowStartOffset = Utilities.getRowEnd(codeArea, rowStartOffset) + 1;
} catch (Exception e) {
break;
}
}
}
private boolean isCurrentLine(int rowStartOffset) {
int caretPosition = codeArea.getCaretPosition();
Element root = codeArea.getDocument().getDefaultRootElement();
return root.getElementIndex(rowStartOffset) == root.getElementIndex(caretPosition);
}
protected String getTextLineNumber(int rowStartOffset) {
Element root = codeArea.getDocument().getDefaultRootElement();
int index = root.getElementIndex(rowStartOffset);
Element line = root.getElement(index);
if (line.getStartOffset() == rowStartOffset) {
int lineNumber = index + 1;
if (useSourceLines) {
Integer sourceLine = codeArea.getSourceLine(lineNumber);
if (sourceLine != null) {
return String.valueOf(sourceLine);
}
} else {
return String.valueOf(lineNumber);
}
}
return "";
}
private int getOffsetY(int rowStartOffset, FontMetrics fontMetrics) throws BadLocationException {
Rectangle r = codeArea.modelToView(rowStartOffset);
if (r == null) {
throw new BadLocationException("Can't get Y offset", rowStartOffset);
}
int lineHeight = fontMetrics.getHeight();
int y = r.y + r.height;
int descent = 0;
if (r.height == lineHeight) {
descent = fontMetrics.getDescent();
} else {
if (fonts == null) {
fonts = new HashMap<String, FontMetrics>();
}
Element root = codeArea.getDocument().getDefaultRootElement();
int index = root.getElementIndex(rowStartOffset);
Element line = root.getElement(index);
for (int i = 0; i < line.getElementCount(); i++) {
Element child = line.getElement(i);
AttributeSet as = child.getAttributes();
String fontFamily = (String) as.getAttribute(StyleConstants.FontFamily);
Integer fontSize = (Integer) as.getAttribute(StyleConstants.FontSize);
String key = fontFamily + fontSize;
FontMetrics fm = fonts.get(key);
if (fm == null) {
Font font = new Font(fontFamily, Font.PLAIN, fontSize);
fm = codeArea.getFontMetrics(font);
fonts.put(key, fm);
}
descent = Math.max(descent, fm.getDescent());
}
}
return y - descent;
}
@Override
public void caretUpdate(CaretEvent e) {
int caretPosition = codeArea.getCaretPosition();
Element root = codeArea.getDocument().getDefaultRootElement();
int currentLine = root.getElementIndex(caretPosition);
if (lastLine != currentLine) {
repaint();
lastLine = currentLine;
}
}
}
......@@ -54,13 +54,13 @@ public class MainWindow extends JFrame {
private static final double BORDER_RATIO = 0.15;
private static final double WINDOW_RATIO = 1 - BORDER_RATIO * 2;
private static final double SPLIT_PANE_RESIZE_WEIGHT = 0.15;
private static final ImageIcon ICON_OPEN = Utils.openIcon("folder");
private static final ImageIcon ICON_SAVE_ALL = Utils.openIcon("disk_multiple");
private static final ImageIcon ICON_CLOSE = Utils.openIcon("cross");
private static final ImageIcon ICON_FLAT_PKG = Utils.openIcon("empty_logical_package_obj");
private static final ImageIcon ICON_SEARCH = Utils.openIcon("magnifier");
private static final ImageIcon ICON_BACK = Utils.openIcon("icon_back");
private static final ImageIcon ICON_FORWARD = Utils.openIcon("icon_forward");
......@@ -248,7 +248,7 @@ public class MainWindow extends JFrame {
private void initUI() {
mainPanel = new JPanel(new BorderLayout());
JSplitPane splitPane = new JSplitPane();
splitPane.setResizeWeight(0.2);
splitPane.setResizeWeight(SPLIT_PANE_RESIZE_WEIGHT);
mainPanel.add(splitPane);
DefaultMutableTreeNode treeRoot = new DefaultMutableTreeNode("Please open file");
......
......@@ -36,6 +36,7 @@ class TestJumpManager extends Specification {
then:
noExceptionThrown()
jm.getPrev() == mock1
jm.getPrev() == null
jm.getNext() == mock2
jm.getNext() == null
}
......
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