feat(gui): add class links for AndroidManifest.xml and other minor fixes
This commit is contained in:
@@ -0,0 +1,11 @@
|
||||
package jadx.api;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public interface ICodeInfo {
|
||||
String getCodeStr();
|
||||
|
||||
Map<Integer, Integer> getLineMapping();
|
||||
|
||||
Map<CodePosition, Object> getAnnotations();
|
||||
}
|
||||
@@ -15,6 +15,7 @@ import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jf.baksmali.Adaptors.ClassDefinition;
|
||||
import org.jf.baksmali.BaksmaliOptions;
|
||||
import org.jf.dexlib2.DexFileFactory;
|
||||
@@ -28,6 +29,7 @@ import org.slf4j.LoggerFactory;
|
||||
import jadx.core.Jadx;
|
||||
import jadx.core.ProcessClass;
|
||||
import jadx.core.dex.attributes.AFlag;
|
||||
import jadx.core.dex.attributes.nodes.LineAttrNode;
|
||||
import jadx.core.dex.nodes.ClassNode;
|
||||
import jadx.core.dex.nodes.FieldNode;
|
||||
import jadx.core.dex.nodes.MethodNode;
|
||||
@@ -390,6 +392,47 @@ public final class JadxDecompiler {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
JavaNode convertNode(Object obj) {
|
||||
if (!(obj instanceof LineAttrNode)) {
|
||||
return null;
|
||||
}
|
||||
if (obj instanceof ClassNode) {
|
||||
return getClassesMap().get(obj);
|
||||
}
|
||||
if (obj instanceof MethodNode) {
|
||||
return getJavaMethodByNode(((MethodNode) obj));
|
||||
}
|
||||
if (obj instanceof FieldNode) {
|
||||
return getJavaFieldByNode((FieldNode) obj);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public JavaNode getJavaNodeAtPosition(ICodeInfo codeInfo, int line, int offset) {
|
||||
Map<CodePosition, Object> map = codeInfo.getAnnotations();
|
||||
if (map.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
Object obj = map.get(new CodePosition(line, offset));
|
||||
if (obj == null) {
|
||||
return null;
|
||||
}
|
||||
return convertNode(obj);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public CodePosition getDefinitionPosition(JavaNode javaNode) {
|
||||
JavaClass jCls = javaNode.getTopParentClass();
|
||||
jCls.decompile();
|
||||
int defLine = javaNode.getDecompiledLine();
|
||||
if (defLine == 0) {
|
||||
return null;
|
||||
}
|
||||
return new CodePosition(jCls, defLine, 0);
|
||||
}
|
||||
|
||||
public JadxArgs getArgs() {
|
||||
return args;
|
||||
}
|
||||
|
||||
@@ -152,7 +152,7 @@ public final class JavaClass implements JavaNode {
|
||||
CodePosition codePosition = entry.getKey();
|
||||
Object obj = entry.getValue();
|
||||
if (obj instanceof LineAttrNode) {
|
||||
JavaNode node = convertNode(obj);
|
||||
JavaNode node = getRootDecompiler().convertNode(obj);
|
||||
if (node != null) {
|
||||
resultMap.put(codePosition, node);
|
||||
}
|
||||
@@ -161,45 +161,15 @@ public final class JavaClass implements JavaNode {
|
||||
return resultMap;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private JavaNode convertNode(Object obj) {
|
||||
if (!(obj instanceof LineAttrNode)) {
|
||||
return null;
|
||||
}
|
||||
if (obj instanceof ClassNode) {
|
||||
return getRootDecompiler().getClassesMap().get(obj);
|
||||
}
|
||||
if (obj instanceof MethodNode) {
|
||||
return getRootDecompiler().getJavaMethodByNode(((MethodNode) obj));
|
||||
}
|
||||
if (obj instanceof FieldNode) {
|
||||
return getRootDecompiler().getJavaFieldByNode((FieldNode) obj);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public JavaNode getJavaNodeAtPosition(int line, int offset) {
|
||||
Map<CodePosition, Object> map = getCodeAnnotations();
|
||||
if (map.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
Object obj = map.get(new CodePosition(line, offset));
|
||||
if (obj == null) {
|
||||
return null;
|
||||
}
|
||||
return convertNode(obj);
|
||||
decompile();
|
||||
return getRootDecompiler().getJavaNodeAtPosition(cls.getCode(), line, offset);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public CodePosition getDefinitionPosition(JavaNode javaNode) {
|
||||
JavaClass jCls = javaNode.getTopParentClass();
|
||||
jCls.decompile();
|
||||
int defLine = javaNode.getDecompiledLine();
|
||||
if (defLine == 0) {
|
||||
return null;
|
||||
}
|
||||
return new CodePosition(jCls, defLine, 0);
|
||||
public CodePosition getDefinitionPosition() {
|
||||
return getRootDecompiler().getDefinitionPosition(this);
|
||||
}
|
||||
|
||||
public Integer getSourceLine(int decompiledLine) {
|
||||
@@ -250,6 +220,7 @@ public final class JavaClass implements JavaNode {
|
||||
return methods;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getDecompiledLine() {
|
||||
return cls.getDecompiledLine();
|
||||
}
|
||||
|
||||
@@ -164,10 +164,8 @@ public final class ResourcesLoader {
|
||||
}
|
||||
|
||||
public static CodeWriter loadToCodeWriter(InputStream is) throws IOException {
|
||||
CodeWriter cw = new CodeWriter();
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream(READ_BUFFER_SIZE);
|
||||
copyStream(is, baos);
|
||||
cw.add(baos.toString("UTF-8"));
|
||||
return cw;
|
||||
return new CodeWriter(baos.toString("UTF-8"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,12 +12,13 @@ import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import jadx.api.CodePosition;
|
||||
import jadx.api.ICodeInfo;
|
||||
import jadx.core.dex.attributes.nodes.LineAttrNode;
|
||||
import jadx.core.utils.StringUtils;
|
||||
import jadx.core.utils.files.FileUtils;
|
||||
import jadx.core.utils.files.ZipSecurity;
|
||||
|
||||
public class CodeWriter {
|
||||
public class CodeWriter implements ICodeInfo {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(CodeWriter.class);
|
||||
|
||||
public static final String NL = System.getProperty("line.separator");
|
||||
@@ -242,6 +243,7 @@ public class CodeWriter {
|
||||
return annotations.put(pos, obj);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<CodePosition, Object> getAnnotations() {
|
||||
return annotations;
|
||||
}
|
||||
@@ -260,6 +262,7 @@ public class CodeWriter {
|
||||
lineMap.put(decompiledLine, sourceLine);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<Integer, Integer> getLineMapping() {
|
||||
return lineMap;
|
||||
}
|
||||
@@ -293,7 +296,11 @@ public class CodeWriter {
|
||||
return buf.length();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCodeStr() {
|
||||
if (code == null) {
|
||||
throw new NullPointerException("Code not set");
|
||||
}
|
||||
return code;
|
||||
}
|
||||
|
||||
|
||||
@@ -164,6 +164,20 @@ public class RootNode {
|
||||
return resolveClass(clsInfo);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public ClassNode searchClassByFullAlias(String fullName) {
|
||||
for (DexNode dexNode : dexNodes) {
|
||||
for (ClassNode cls : dexNode.getClasses()) {
|
||||
ClassInfo classInfo = cls.getClassInfo();
|
||||
if (classInfo.getFullName().equals(fullName)
|
||||
|| classInfo.getAliasFullName().equals(fullName)) {
|
||||
return cls;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public List<ClassNode> searchClassByShortName(String shortName) {
|
||||
List<ClassNode> list = new ArrayList<>();
|
||||
for (DexNode dexNode : dexNodes) {
|
||||
|
||||
@@ -15,6 +15,7 @@ import org.slf4j.LoggerFactory;
|
||||
import jadx.api.ResourcesLoader;
|
||||
import jadx.core.codegen.CodeWriter;
|
||||
import jadx.core.dex.info.ConstStorage;
|
||||
import jadx.core.dex.nodes.ClassNode;
|
||||
import jadx.core.dex.nodes.RootNode;
|
||||
import jadx.core.utils.StringUtils;
|
||||
import jadx.core.utils.exceptions.JadxRuntimeException;
|
||||
@@ -32,7 +33,6 @@ import jadx.core.xmlgen.entry.ValuesParser;
|
||||
* Check Element chunk size
|
||||
*/
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public class BinaryXMLParser extends CommonBinaryParser {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(BinaryXMLParser.class);
|
||||
@@ -306,6 +306,7 @@ public class BinaryXMLParser extends CommonBinaryParser {
|
||||
if (isDeobfCandidateAttr(shortNsName, attrName)) {
|
||||
decodedAttr = deobfClassName(decodedAttr);
|
||||
}
|
||||
attachClassNode(writer, attrName, decodedAttr);
|
||||
writer.add(StringUtils.escapeXML(decodedAttr));
|
||||
} else {
|
||||
decodeAttribute(attributeNS, attrValDataType, attrValData,
|
||||
@@ -401,6 +402,7 @@ public class BinaryXMLParser extends CommonBinaryParser {
|
||||
if (isDeobfCandidateAttr(shortNsName, attrName)) {
|
||||
str = deobfClassName(str);
|
||||
}
|
||||
attachClassNode(writer, attrName, str);
|
||||
writer.add(str != null ? StringUtils.escapeXML(str) : "null");
|
||||
}
|
||||
}
|
||||
@@ -459,9 +461,24 @@ public class BinaryXMLParser extends CommonBinaryParser {
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private void attachClassNode(CodeWriter writer, String attrName, String clsName) {
|
||||
if (clsName == null || !attrName.equals("name")) {
|
||||
return;
|
||||
}
|
||||
String clsFullName;
|
||||
if (clsName.startsWith(".")) {
|
||||
clsFullName = appPackageName + clsName;
|
||||
} else {
|
||||
clsFullName = clsName;
|
||||
}
|
||||
ClassNode classNode = rootNode.searchClassByFullAlias(clsFullName);
|
||||
if (classNode != null) {
|
||||
writer.attachAnnotation(classNode);
|
||||
}
|
||||
}
|
||||
|
||||
private String deobfClassName(String className) {
|
||||
String newName = XmlDeobf.deobfClassName(rootNode, className,
|
||||
appPackageName);
|
||||
String newName = XmlDeobf.deobfClassName(rootNode, className, appPackageName);
|
||||
if (newName != null) {
|
||||
return newName;
|
||||
}
|
||||
|
||||
@@ -3,6 +3,8 @@ package jadx.core.xmlgen;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import jadx.core.dex.info.ClassInfo;
|
||||
import jadx.core.dex.nodes.ClassNode;
|
||||
import jadx.core.dex.nodes.RootNode;
|
||||
@@ -17,9 +19,8 @@ public class XmlDeobf {
|
||||
private XmlDeobf() {
|
||||
}
|
||||
|
||||
public static String deobfClassName(RootNode rootNode, String potencialClassName,
|
||||
String packageName) {
|
||||
|
||||
@Nullable
|
||||
public static String deobfClassName(RootNode rootNode, String potencialClassName, String packageName) {
|
||||
if (packageName != null && potencialClassName.startsWith(".")) {
|
||||
potencialClassName = packageName + potencialClassName;
|
||||
}
|
||||
|
||||
@@ -131,6 +131,10 @@ public class JadxWrapper {
|
||||
return openFile;
|
||||
}
|
||||
|
||||
public JadxDecompiler getDecompiler() {
|
||||
return decompiler;
|
||||
}
|
||||
|
||||
public JadxArgs getArgs() {
|
||||
return decompiler.getArgs();
|
||||
}
|
||||
|
||||
@@ -3,7 +3,9 @@ package jadx.gui.treemodel;
|
||||
import javax.swing.*;
|
||||
|
||||
import org.fife.ui.rsyntaxtextarea.SyntaxConstants;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import jadx.api.ICodeInfo;
|
||||
import jadx.api.JavaClass;
|
||||
import jadx.api.JavaField;
|
||||
import jadx.api.JavaMethod;
|
||||
@@ -76,6 +78,12 @@ public class JClass extends JLoadableNode {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable ICodeInfo getCodeInfo() {
|
||||
load();
|
||||
return cls.getClassNode().getCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getContent() {
|
||||
return cls.getCode();
|
||||
@@ -147,11 +155,6 @@ public class JClass extends JLoadableNode {
|
||||
return cls.getDecompiledLine();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer getSourceLine(int line) {
|
||||
return cls.getSourceLine(line);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return cls.hashCode();
|
||||
|
||||
@@ -4,7 +4,10 @@ import javax.swing.*;
|
||||
import javax.swing.tree.DefaultMutableTreeNode;
|
||||
|
||||
import org.fife.ui.rsyntaxtextarea.SyntaxConstants;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import jadx.api.ICodeInfo;
|
||||
import jadx.api.JadxDecompiler;
|
||||
import jadx.api.JavaNode;
|
||||
|
||||
public abstract class JNode extends DefaultMutableTreeNode {
|
||||
@@ -40,10 +43,28 @@ public abstract class JNode extends DefaultMutableTreeNode {
|
||||
return 0;
|
||||
}
|
||||
|
||||
public Integer getSourceLine(int line) {
|
||||
@Nullable
|
||||
public ICodeInfo getCodeInfo() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public final Integer getSourceLine(int line) {
|
||||
ICodeInfo codeInfo = getCodeInfo();
|
||||
if (codeInfo == null) {
|
||||
return null;
|
||||
}
|
||||
return codeInfo.getLineMapping().get(line);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public JavaNode getJavaNodeAtPosition(JadxDecompiler decompiler, int line, int offset) {
|
||||
ICodeInfo codeInfo = getCodeInfo();
|
||||
if (codeInfo == null) {
|
||||
return null;
|
||||
}
|
||||
return decompiler.getJavaNodeAtPosition(codeInfo, line, offset);
|
||||
}
|
||||
|
||||
public abstract Icon getIcon();
|
||||
|
||||
public String getName() {
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package jadx.gui.treemodel;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -10,7 +9,9 @@ import javax.swing.*;
|
||||
|
||||
import org.fife.ui.rsyntaxtextarea.SyntaxConstants;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import jadx.api.ICodeInfo;
|
||||
import jadx.api.ResourceFile;
|
||||
import jadx.api.ResourceFileContent;
|
||||
import jadx.api.ResourceType;
|
||||
@@ -44,8 +45,7 @@ public class JResource extends JLoadableNode implements Comparable<JResource> {
|
||||
private final transient ResourceFile resFile;
|
||||
|
||||
private transient boolean loaded;
|
||||
private transient String content;
|
||||
private transient Map<Integer, Integer> lineMapping = Collections.emptyMap();
|
||||
private transient ICodeInfo content;
|
||||
|
||||
public JResource(ResourceFile resFile, String name, JResType type) {
|
||||
this(resFile, name, name, type);
|
||||
@@ -98,10 +98,19 @@ public class JResource extends JLoadableNode implements Comparable<JResource> {
|
||||
return files;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable ICodeInfo getCodeInfo() {
|
||||
getContent();
|
||||
return content;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized String getContent() {
|
||||
if (loaded) {
|
||||
return content;
|
||||
if (content == null) {
|
||||
return null;
|
||||
}
|
||||
return content.getCodeStr();
|
||||
}
|
||||
if (resFile == null || type != JResType.FILE) {
|
||||
return null;
|
||||
@@ -111,6 +120,7 @@ public class JResource extends JLoadableNode implements Comparable<JResource> {
|
||||
}
|
||||
ResContainer rc = resFile.loadContent();
|
||||
if (rc == null) {
|
||||
loaded = true;
|
||||
return null;
|
||||
}
|
||||
if (rc.getDataType() == ResContainer.DataType.RES_TABLE) {
|
||||
@@ -118,36 +128,35 @@ public class JResource extends JLoadableNode implements Comparable<JResource> {
|
||||
for (ResContainer subFile : rc.getSubFiles()) {
|
||||
loadSubNodes(this, subFile, 1);
|
||||
}
|
||||
loaded = true;
|
||||
return content;
|
||||
} else {
|
||||
// single node
|
||||
content = loadCurrentSingleRes(rc);
|
||||
}
|
||||
// single node
|
||||
return loadCurrentSingleRes(rc);
|
||||
loaded = true;
|
||||
return content.getCodeStr();
|
||||
}
|
||||
|
||||
private String loadCurrentSingleRes(ResContainer rc) {
|
||||
private ICodeInfo loadCurrentSingleRes(ResContainer rc) {
|
||||
switch (rc.getDataType()) {
|
||||
case TEXT:
|
||||
case RES_TABLE:
|
||||
CodeWriter cw = rc.getText();
|
||||
lineMapping = cw.getLineMapping();
|
||||
return cw.toString();
|
||||
return rc.getText();
|
||||
|
||||
case RES_LINK:
|
||||
try {
|
||||
return ResourcesLoader.decodeStream(rc.getResLink(), (size, is) -> {
|
||||
if (size > 10 * 1024 * 1024L) {
|
||||
return "File too large for view";
|
||||
return new CodeWriter("File too large for view");
|
||||
}
|
||||
return ResourcesLoader.loadToCodeWriter(is).toString();
|
||||
return ResourcesLoader.loadToCodeWriter(is);
|
||||
});
|
||||
} catch (Exception e) {
|
||||
return "Failed to load resource file: \n" + jadx.core.utils.Utils.getStackTrace(e);
|
||||
return new CodeWriter("Failed to load resource file: \n" + jadx.core.utils.Utils.getStackTrace(e));
|
||||
}
|
||||
|
||||
case DECODED_DATA:
|
||||
default:
|
||||
return "Unexpected resource type: " + rc;
|
||||
return new CodeWriter("Unexpected resource type: " + rc);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -192,14 +201,6 @@ public class JResource extends JLoadableNode implements Comparable<JResource> {
|
||||
return resDir;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer getSourceLine(int line) {
|
||||
if (lineMapping == null) {
|
||||
return null;
|
||||
}
|
||||
return lineMapping.get(line);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSyntaxName() {
|
||||
if (resFile == null) {
|
||||
@@ -291,10 +292,6 @@ public class JResource extends JLoadableNode implements Comparable<JResource> {
|
||||
return resFile;
|
||||
}
|
||||
|
||||
public Map<Integer, Integer> getLineMapping() {
|
||||
return lineMapping;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JClass getJParent() {
|
||||
return null;
|
||||
|
||||
@@ -32,7 +32,7 @@ import jadx.gui.jobs.BackgroundJob;
|
||||
import jadx.gui.jobs.BackgroundWorker;
|
||||
import jadx.gui.jobs.DecompileJob;
|
||||
import jadx.gui.treemodel.JNode;
|
||||
import jadx.gui.ui.codearea.CodeArea;
|
||||
import jadx.gui.ui.codearea.AbstractCodeArea;
|
||||
import jadx.gui.utils.CacheObject;
|
||||
import jadx.gui.utils.JumpPosition;
|
||||
import jadx.gui.utils.NLS;
|
||||
@@ -403,7 +403,7 @@ public abstract class CommonSearchDialog extends JDialog {
|
||||
private final Map<Integer, Component> componentCache = new HashMap<>();
|
||||
|
||||
public ResultsTableCellRenderer() {
|
||||
RSyntaxTextArea area = CodeArea.getDefaultArea(mainWindow);
|
||||
RSyntaxTextArea area = AbstractCodeArea.getDefaultArea(mainWindow);
|
||||
this.font = area.getFont();
|
||||
this.codeSelectedColor = area.getSelectionColor();
|
||||
this.codeBackground = area.getBackground();
|
||||
@@ -455,7 +455,7 @@ public abstract class CommonSearchDialog extends JDialog {
|
||||
if (!node.hasDescString()) {
|
||||
return emptyLabel;
|
||||
}
|
||||
RSyntaxTextArea textArea = CodeArea.getDefaultArea(mainWindow);
|
||||
RSyntaxTextArea textArea = AbstractCodeArea.getDefaultArea(mainWindow);
|
||||
textArea.setLayout(new GridLayout(1, 1));
|
||||
textArea.setEditable(false);
|
||||
textArea.setSyntaxEditingStyle(SyntaxConstants.SYNTAX_STYLE_JAVA);
|
||||
|
||||
@@ -16,7 +16,7 @@ import jadx.core.utils.Utils;
|
||||
import jadx.core.utils.exceptions.JadxRuntimeException;
|
||||
import jadx.core.xmlgen.ResContainer;
|
||||
import jadx.gui.treemodel.JResource;
|
||||
import jadx.gui.ui.codearea.CodeArea;
|
||||
import jadx.gui.ui.codearea.AbstractCodeArea;
|
||||
|
||||
public class ImagePanel extends ContentPanel {
|
||||
private static final long serialVersionUID = 4071356367073142688L;
|
||||
@@ -29,7 +29,7 @@ public class ImagePanel extends ContentPanel {
|
||||
ImageViewer imageViewer = new ImageViewer(img);
|
||||
add(imageViewer.getComponent());
|
||||
} catch (Exception e) {
|
||||
RSyntaxTextArea textArea = CodeArea.getDefaultArea(panel.getMainWindow());
|
||||
RSyntaxTextArea textArea = AbstractCodeArea.getDefaultArea(panel.getMainWindow());
|
||||
textArea.setText("Image load error: \n" + Utils.getStackTrace(e));
|
||||
add(textArea);
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea;
|
||||
import ch.qos.logback.classic.Level;
|
||||
|
||||
import jadx.gui.settings.JadxSettings;
|
||||
import jadx.gui.ui.codearea.CodeArea;
|
||||
import jadx.gui.ui.codearea.AbstractCodeArea;
|
||||
import jadx.gui.utils.NLS;
|
||||
import jadx.gui.utils.logs.ILogListener;
|
||||
import jadx.gui.utils.logs.LogCollector;
|
||||
@@ -31,7 +31,7 @@ class LogViewer extends JDialog {
|
||||
}
|
||||
|
||||
public final void initUI(MainWindow mainWindow) {
|
||||
textPane = CodeArea.getDefaultArea(mainWindow);
|
||||
textPane = AbstractCodeArea.getDefaultArea(mainWindow);
|
||||
textPane.setBorder(BorderFactory.createEmptyBorder(15, 15, 15, 15));
|
||||
|
||||
JPanel controlPane = new JPanel();
|
||||
|
||||
@@ -1,12 +1,6 @@
|
||||
package jadx.gui.ui;
|
||||
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.Component;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.DisplayMode;
|
||||
import java.awt.Font;
|
||||
import java.awt.GraphicsDevice;
|
||||
import java.awt.GraphicsEnvironment;
|
||||
import java.awt.*;
|
||||
import java.awt.dnd.DnDConstants;
|
||||
import java.awt.dnd.DropTarget;
|
||||
import java.awt.event.ActionEvent;
|
||||
@@ -29,27 +23,7 @@ import java.util.Locale;
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
|
||||
import javax.swing.AbstractAction;
|
||||
import javax.swing.Action;
|
||||
import javax.swing.Box;
|
||||
import javax.swing.ImageIcon;
|
||||
import javax.swing.JCheckBoxMenuItem;
|
||||
import javax.swing.JFileChooser;
|
||||
import javax.swing.JFrame;
|
||||
import javax.swing.JMenu;
|
||||
import javax.swing.JMenuBar;
|
||||
import javax.swing.JMenuItem;
|
||||
import javax.swing.JOptionPane;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JPopupMenu;
|
||||
import javax.swing.JScrollPane;
|
||||
import javax.swing.JSplitPane;
|
||||
import javax.swing.JToggleButton;
|
||||
import javax.swing.JToolBar;
|
||||
import javax.swing.JTree;
|
||||
import javax.swing.ProgressMonitor;
|
||||
import javax.swing.SwingUtilities;
|
||||
import javax.swing.WindowConstants;
|
||||
import javax.swing.*;
|
||||
import javax.swing.event.MenuEvent;
|
||||
import javax.swing.event.MenuListener;
|
||||
import javax.swing.event.TreeExpansionEvent;
|
||||
@@ -518,7 +492,7 @@ public class MainWindow extends JFrame {
|
||||
if (resFile != null && JResource.isSupportedForView(resFile.getType())) {
|
||||
tabbedPane.showResource(res);
|
||||
}
|
||||
} else if ((obj instanceof JCertificate) || (obj instanceof ApkSignature)) {
|
||||
} else if (obj instanceof JCertificate || obj instanceof ApkSignature) {
|
||||
tabbedPane.showSimpleNode((JNode) obj);
|
||||
} else if (obj instanceof JNode) {
|
||||
JNode node = (JNode) obj;
|
||||
@@ -533,10 +507,13 @@ public class MainWindow extends JFrame {
|
||||
}
|
||||
|
||||
private void treeRightClickAction(MouseEvent e) {
|
||||
Object obj = tree.getLastSelectedPathComponent();
|
||||
if (obj instanceof JPackage) {
|
||||
JPackagePopUp menu = new JPackagePopUp((JPackage) obj);
|
||||
menu.show(e.getComponent(), e.getX(), e.getY());
|
||||
TreePath path = tree.getPathForLocation(e.getX(), e.getY());
|
||||
if (path != null) {
|
||||
Object obj = path.getLastPathComponent();
|
||||
if (obj instanceof JPackage) {
|
||||
JPackagePopUp menu = new JPackagePopUp((JPackage) obj);
|
||||
menu.show(e.getComponent(), e.getX(), e.getY());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -42,7 +42,7 @@ public class TabbedPane extends JTabbedPane {
|
||||
|
||||
private final transient MainWindow mainWindow;
|
||||
private final transient Map<JNode, ContentPanel> openTabs = new LinkedHashMap<>();
|
||||
private transient JumpManager jumps = new JumpManager();
|
||||
private final transient JumpManager jumps = new JumpManager();
|
||||
|
||||
TabbedPane(MainWindow window) {
|
||||
this.mainWindow = window;
|
||||
@@ -216,7 +216,7 @@ public class TabbedPane extends JTabbedPane {
|
||||
button.setBorderPainted(false);
|
||||
button.addActionListener(e -> closeCodePanel(contentPanel));
|
||||
|
||||
panel.addMouseListener(new MouseAdapter() {
|
||||
MouseAdapter clickAdapter = new MouseAdapter() {
|
||||
@Override
|
||||
public void mouseClicked(MouseEvent e) {
|
||||
if (SwingUtilities.isMiddleMouseButton(e)) {
|
||||
@@ -228,7 +228,9 @@ public class TabbedPane extends JTabbedPane {
|
||||
setSelectedComponent(contentPanel);
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
panel.addMouseListener(clickAdapter);
|
||||
label.addMouseListener(clickAdapter);
|
||||
|
||||
panel.add(label);
|
||||
panel.add(button);
|
||||
|
||||
@@ -1,14 +1,17 @@
|
||||
package jadx.gui.ui.codearea;
|
||||
|
||||
import java.awt.Dimension;
|
||||
import java.awt.Point;
|
||||
import java.awt.Rectangle;
|
||||
import java.awt.*;
|
||||
import java.awt.event.MouseAdapter;
|
||||
import java.awt.event.MouseEvent;
|
||||
|
||||
import javax.swing.JViewport;
|
||||
import javax.swing.SwingUtilities;
|
||||
import javax.swing.*;
|
||||
import javax.swing.text.BadLocationException;
|
||||
import javax.swing.text.Caret;
|
||||
import javax.swing.text.DefaultCaret;
|
||||
|
||||
import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea;
|
||||
import org.fife.ui.rtextarea.SearchContext;
|
||||
import org.fife.ui.rtextarea.SearchEngine;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@@ -30,6 +33,19 @@ public abstract class AbstractCodeArea extends RSyntaxTextArea {
|
||||
public AbstractCodeArea(ContentPanel contentPanel) {
|
||||
this.contentPanel = contentPanel;
|
||||
this.node = contentPanel.getNode();
|
||||
|
||||
setMarkOccurrences(true);
|
||||
setEditable(false);
|
||||
setCodeFoldingEnabled(false);
|
||||
loadSettings();
|
||||
|
||||
Caret caret = getCaret();
|
||||
if (caret instanceof DefaultCaret) {
|
||||
((DefaultCaret) caret).setUpdatePolicy(DefaultCaret.ALWAYS_UPDATE);
|
||||
}
|
||||
caret.setVisible(true);
|
||||
|
||||
registerWordHighlighter();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -37,6 +53,22 @@ public abstract class AbstractCodeArea extends RSyntaxTextArea {
|
||||
*/
|
||||
public abstract void load();
|
||||
|
||||
public static RSyntaxTextArea getDefaultArea(MainWindow mainWindow) {
|
||||
RSyntaxTextArea area = new RSyntaxTextArea();
|
||||
area.setEditable(false);
|
||||
area.setCodeFoldingEnabled(false);
|
||||
loadCommonSettings(mainWindow, area);
|
||||
return area;
|
||||
}
|
||||
|
||||
public static void loadCommonSettings(MainWindow mainWindow, RSyntaxTextArea area) {
|
||||
area.setAntiAliasingEnabled(true);
|
||||
mainWindow.getEditorTheme().apply(area);
|
||||
|
||||
JadxSettings settings = mainWindow.getSettings();
|
||||
area.setFont(settings.getFont());
|
||||
}
|
||||
|
||||
public void loadSettings() {
|
||||
loadCommonSettings(contentPanel.getTabbedPane().getMainWindow(), this);
|
||||
}
|
||||
@@ -85,6 +117,34 @@ public abstract class AbstractCodeArea extends RSyntaxTextArea {
|
||||
}
|
||||
}
|
||||
|
||||
private void registerWordHighlighter() {
|
||||
addMouseListener(new MouseAdapter() {
|
||||
@Override
|
||||
public void mouseClicked(MouseEvent evt) {
|
||||
if (evt.getClickCount() % 2 == 0 && !evt.isConsumed()) {
|
||||
evt.consume();
|
||||
String str = getSelectedText();
|
||||
if (str != null) {
|
||||
highlightAllMatches(str);
|
||||
}
|
||||
} else {
|
||||
highlightAllMatches(null);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @param str - if null -> reset current highlights
|
||||
*/
|
||||
private void highlightAllMatches(@Nullable String str) {
|
||||
SearchContext context = new SearchContext(str);
|
||||
context.setMarkAll(true);
|
||||
context.setMatchCase(true);
|
||||
context.setWholeWord(true);
|
||||
SearchEngine.markAll(this, context);
|
||||
}
|
||||
|
||||
public JumpPosition getCurrentPosition() {
|
||||
return new JumpPosition(node, getCaretLineNumber() + 1);
|
||||
}
|
||||
@@ -101,12 +161,4 @@ public abstract class AbstractCodeArea extends RSyntaxTextArea {
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
package jadx.gui.ui.codearea;
|
||||
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.*;
|
||||
|
||||
import javax.swing.JTabbedPane;
|
||||
import javax.swing.*;
|
||||
import javax.swing.border.EmptyBorder;
|
||||
|
||||
import jadx.gui.treemodel.JNode;
|
||||
import jadx.gui.ui.TabbedPane;
|
||||
@@ -19,18 +20,22 @@ import jadx.gui.utils.NLS;
|
||||
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);
|
||||
private final transient CodePanel javaCodePanel;
|
||||
private final transient CodePanel smaliCodePanel;
|
||||
// private final transient JTabbedPane areaTabbedPane;
|
||||
|
||||
public ClassCodeContentPanel(TabbedPane panel, JNode jnode) {
|
||||
super(panel, jnode);
|
||||
|
||||
javaCodePanel = new CodePanel(this, new CodeArea(this));
|
||||
smaliCodePanel = new CodePanel(this, new SmaliArea(this));
|
||||
javaCodePanel = new CodePanel(new CodeArea(this));
|
||||
smaliCodePanel = new CodePanel(new SmaliArea(this));
|
||||
|
||||
setLayout(new BorderLayout());
|
||||
setBorder(new EmptyBorder(0, 0, 0, 0));
|
||||
|
||||
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);
|
||||
|
||||
@@ -1,26 +1,17 @@
|
||||
package jadx.gui.ui.codearea;
|
||||
|
||||
import java.awt.event.MouseAdapter;
|
||||
import java.awt.event.MouseEvent;
|
||||
|
||||
import javax.swing.JPopupMenu;
|
||||
import javax.swing.text.Caret;
|
||||
import javax.swing.text.DefaultCaret;
|
||||
import javax.swing.*;
|
||||
|
||||
import org.fife.ui.rsyntaxtextarea.RSyntaxDocument;
|
||||
import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea;
|
||||
import org.fife.ui.rtextarea.SearchContext;
|
||||
import org.fife.ui.rtextarea.SearchEngine;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import jadx.api.CodePosition;
|
||||
import jadx.api.JadxDecompiler;
|
||||
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;
|
||||
|
||||
/**
|
||||
@@ -34,29 +25,18 @@ public final class CodeArea extends AbstractCodeArea {
|
||||
|
||||
CodeArea(ContentPanel contentPanel) {
|
||||
super(contentPanel);
|
||||
|
||||
setMarkOccurrences(true);
|
||||
setEditable(false);
|
||||
loadSettings();
|
||||
|
||||
Caret caret = getCaret();
|
||||
if (caret instanceof DefaultCaret) {
|
||||
((DefaultCaret) caret).setUpdatePolicy(DefaultCaret.ALWAYS_UPDATE);
|
||||
}
|
||||
caret.setVisible(true);
|
||||
|
||||
setSyntaxEditingStyle(node.getSyntaxName());
|
||||
|
||||
if (node instanceof JClass) {
|
||||
JClass jClsNode = (JClass) this.node;
|
||||
((RSyntaxDocument) getDocument()).setSyntaxStyle(new JadxTokenMaker(this, jClsNode));
|
||||
|
||||
setHyperlinksEnabled(true);
|
||||
CodeLinkGenerator codeLinkProcessor = new CodeLinkGenerator(contentPanel, this, jClsNode);
|
||||
setLinkGenerator(codeLinkProcessor);
|
||||
addHyperlinkListener(codeLinkProcessor);
|
||||
((RSyntaxDocument) getDocument()).setSyntaxStyle(new JadxTokenMaker(this));
|
||||
addMenuItems(jClsNode);
|
||||
}
|
||||
registerWordHighlighter();
|
||||
|
||||
setHyperlinksEnabled(true);
|
||||
CodeLinkGenerator codeLinkProcessor = new CodeLinkGenerator(this);
|
||||
setLinkGenerator(codeLinkProcessor);
|
||||
addHyperlinkListener(codeLinkProcessor);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -67,36 +47,8 @@ public final class CodeArea extends AbstractCodeArea {
|
||||
}
|
||||
}
|
||||
|
||||
private void registerWordHighlighter() {
|
||||
addMouseListener(new MouseAdapter() {
|
||||
@Override
|
||||
public void mouseClicked(MouseEvent evt) {
|
||||
if (evt.getClickCount() % 2 == 0 && !evt.isConsumed()) {
|
||||
evt.consume();
|
||||
String str = getSelectedText();
|
||||
if (str != null) {
|
||||
highlightAllMatches(str);
|
||||
}
|
||||
} else {
|
||||
highlightAllMatches(null);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @param str - if null -> reset current highlights
|
||||
*/
|
||||
private void highlightAllMatches(@Nullable String str) {
|
||||
SearchContext context = new SearchContext(str);
|
||||
context.setMarkAll(true);
|
||||
context.setMatchCase(true);
|
||||
context.setWholeWord(true);
|
||||
SearchEngine.markAll(this, context);
|
||||
}
|
||||
|
||||
private void addMenuItems(JClass jCls) {
|
||||
FindUsageAction findUsage = new FindUsageAction(contentPanel, this, jCls);
|
||||
FindUsageAction findUsage = new FindUsageAction(contentPanel, this);
|
||||
GoToDeclarationAction goToDeclaration = new GoToDeclarationAction(contentPanel, this, jCls);
|
||||
|
||||
JPopupMenu popup = getPopupMenu();
|
||||
@@ -107,22 +59,16 @@ public final class CodeArea extends AbstractCodeArea {
|
||||
popup.addPopupMenuListener(goToDeclaration);
|
||||
}
|
||||
|
||||
public static RSyntaxTextArea getDefaultArea(MainWindow mainWindow) {
|
||||
RSyntaxTextArea area = new RSyntaxTextArea();
|
||||
loadCommonSettings(mainWindow, area);
|
||||
return area;
|
||||
}
|
||||
|
||||
/**
|
||||
* Search node by offset in {@code jCls} code and return its definition position
|
||||
* (useful for jumps from usage)
|
||||
*/
|
||||
public JumpPosition getDefPosForNodeAtOffset(JClass jCls, int offset) {
|
||||
JavaNode foundNode = getJavaNodeAtOffset(jCls, offset);
|
||||
public JumpPosition getDefPosForNodeAtOffset(int offset) {
|
||||
JavaNode foundNode = getJavaNodeAtOffset(offset);
|
||||
if (foundNode == null) {
|
||||
return null;
|
||||
}
|
||||
CodePosition pos = jCls.getCls().getDefinitionPosition(foundNode);
|
||||
CodePosition pos = getDecompiler().getDefinitionPosition(foundNode);
|
||||
if (pos == null) {
|
||||
return null;
|
||||
}
|
||||
@@ -133,16 +79,19 @@ public final class CodeArea extends AbstractCodeArea {
|
||||
/**
|
||||
* Search referenced java node by offset in {@code jCls} code
|
||||
*/
|
||||
public JavaNode getJavaNodeAtOffset(JClass jCls, int offset) {
|
||||
public JavaNode getJavaNodeAtOffset(int offset) {
|
||||
try {
|
||||
// TODO: add direct mapping for code offset to CodeWriter (instead of line and line offset pair)
|
||||
int line = this.getLineOfOffset(offset);
|
||||
int lineOffset = offset - this.getLineStartOffset(line);
|
||||
return jCls.getCls().getJavaNodeAtPosition(line + 1, lineOffset + 1);
|
||||
return node.getJavaNodeAtPosition(getDecompiler(), line + 1, lineOffset + 1);
|
||||
} catch (Exception e) {
|
||||
LOG.error("Can't get java node by offset: {}", offset, e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private JadxDecompiler getDecompiler() {
|
||||
return contentPanel.getTabbedPane().getMainWindow().getWrapper().getDecompiler();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package jadx.gui.ui.codearea;
|
||||
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.*;
|
||||
|
||||
import jadx.gui.treemodel.JNode;
|
||||
import jadx.gui.ui.TabbedPane;
|
||||
@@ -13,7 +13,7 @@ public final class CodeContentPanel extends AbstractCodeContentPanel {
|
||||
public CodeContentPanel(TabbedPane panel, JNode jnode) {
|
||||
super(panel, jnode);
|
||||
setLayout(new BorderLayout());
|
||||
codePanel = new CodePanel(this, new CodeArea(this));
|
||||
codePanel = new CodePanel(new CodeArea(this));
|
||||
add(codePanel, BorderLayout.CENTER);
|
||||
codePanel.load();
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import jadx.gui.treemodel.JClass;
|
||||
import jadx.gui.treemodel.JNode;
|
||||
import jadx.gui.ui.ContentPanel;
|
||||
import jadx.gui.utils.JumpPosition;
|
||||
|
||||
@@ -22,29 +23,40 @@ public class CodeLinkGenerator implements LinkGenerator, HyperlinkListener {
|
||||
|
||||
private final ContentPanel contentPanel;
|
||||
private final CodeArea codeArea;
|
||||
private final JClass jCls;
|
||||
private final JNode jNode;
|
||||
|
||||
public CodeLinkGenerator(ContentPanel contentPanel, CodeArea codeArea, JClass cls) {
|
||||
this.contentPanel = contentPanel;
|
||||
public CodeLinkGenerator(CodeArea codeArea) {
|
||||
this.contentPanel = codeArea.getContentPanel();
|
||||
this.codeArea = codeArea;
|
||||
this.jCls = cls;
|
||||
this.jNode = codeArea.getNode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public LinkGeneratorResult isLinkAtOffset(RSyntaxTextArea textArea, int offset) {
|
||||
try {
|
||||
if (jNode.getCodeInfo() == null) {
|
||||
return null;
|
||||
}
|
||||
Token token = textArea.modelToToken(offset);
|
||||
if (token == null) {
|
||||
return null;
|
||||
}
|
||||
int type = token.getType();
|
||||
final int sourceOffset;
|
||||
if (type == TokenTypes.IDENTIFIER) {
|
||||
sourceOffset = token.getOffset();
|
||||
} else if (type == TokenTypes.ANNOTATION && token.length() > 1) {
|
||||
sourceOffset = token.getOffset() + 1;
|
||||
if (jNode instanceof JClass) {
|
||||
if (type == TokenTypes.IDENTIFIER) {
|
||||
sourceOffset = token.getOffset();
|
||||
} else if (type == TokenTypes.ANNOTATION && token.length() > 1) {
|
||||
sourceOffset = token.getOffset() + 1;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
} else {
|
||||
return null;
|
||||
if (type == TokenTypes.MARKUP_TAG_ATTRIBUTE_VALUE) {
|
||||
sourceOffset = token.getOffset() + 1; // skip quote at start (")
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
// fast skip
|
||||
if (token.length() == 1) {
|
||||
@@ -53,11 +65,11 @@ public class CodeLinkGenerator implements LinkGenerator, HyperlinkListener {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
final JumpPosition defPos = codeArea.getDefPosForNodeAtOffset(jCls, sourceOffset);
|
||||
final JumpPosition defPos = codeArea.getDefPosForNodeAtOffset(sourceOffset);
|
||||
if (defPos == null) {
|
||||
return null;
|
||||
}
|
||||
if (Objects.equals(defPos.getNode().getRootClass(), jCls)
|
||||
if (Objects.equals(defPos.getNode().getRootClass(), jNode)
|
||||
&& defPos.getLine() == textArea.getLineOfOffset(sourceOffset) + 1) {
|
||||
// ignore self jump
|
||||
return null;
|
||||
|
||||
@@ -3,14 +3,14 @@ package jadx.gui.ui.codearea;
|
||||
import java.awt.*;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.KeyEvent;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.border.EmptyBorder;
|
||||
|
||||
import org.fife.ui.rtextarea.RTextScrollPane;
|
||||
|
||||
import jadx.gui.treemodel.JNode;
|
||||
import jadx.gui.treemodel.JResource;
|
||||
import jadx.gui.ui.ContentPanel;
|
||||
import jadx.api.ICodeInfo;
|
||||
import jadx.gui.utils.UiUtils;
|
||||
|
||||
/**
|
||||
@@ -21,14 +21,15 @@ public class CodePanel extends JPanel {
|
||||
|
||||
private final SearchBar searchBar;
|
||||
private final AbstractCodeArea codeArea;
|
||||
private final RTextScrollPane codeScrollPane;
|
||||
private final JScrollPane codeScrollPane;
|
||||
|
||||
public CodePanel(ContentPanel contentPanel, AbstractCodeArea codeArea) {
|
||||
public CodePanel(AbstractCodeArea codeArea) {
|
||||
this.codeArea = codeArea;
|
||||
searchBar = new SearchBar(codeArea);
|
||||
codeScrollPane = new RTextScrollPane(codeArea, false);
|
||||
codeScrollPane = new JScrollPane(codeArea);
|
||||
|
||||
setLayout(new BorderLayout());
|
||||
setBorder(new EmptyBorder(0, 0, 0, 0));
|
||||
add(searchBar, BorderLayout.NORTH);
|
||||
add(codeScrollPane, BorderLayout.CENTER);
|
||||
|
||||
@@ -60,12 +61,19 @@ public class CodePanel extends JPanel {
|
||||
}
|
||||
|
||||
private boolean isUseSourceLines() {
|
||||
JNode node = codeArea.getNode();
|
||||
if (node instanceof JResource) {
|
||||
JResource resNode = (JResource) node;
|
||||
return !resNode.getLineMapping().isEmpty();
|
||||
if (codeArea instanceof SmaliArea) {
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
ICodeInfo codeInfo = codeArea.getNode().getCodeInfo();
|
||||
if (codeInfo == null) {
|
||||
return false;
|
||||
}
|
||||
Map<Integer, Integer> lineMapping = codeInfo.getLineMapping();
|
||||
if (lineMapping.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
Set<Integer> uniqueSourceLines = new HashSet<>(lineMapping.values());
|
||||
return uniqueSourceLines.size() > 3;
|
||||
}
|
||||
|
||||
public SearchBar getSearchBar() {
|
||||
|
||||
@@ -10,7 +10,6 @@ import javax.swing.event.PopupMenuListener;
|
||||
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;
|
||||
@@ -22,15 +21,13 @@ public final class FindUsageAction extends AbstractAction implements PopupMenuLi
|
||||
|
||||
private final transient ContentPanel contentPanel;
|
||||
private final transient CodeArea codeArea;
|
||||
private final transient JClass jCls;
|
||||
|
||||
private transient JavaNode node;
|
||||
|
||||
public FindUsageAction(ContentPanel contentPanel, CodeArea codeArea, JClass jCls) {
|
||||
public FindUsageAction(ContentPanel contentPanel, CodeArea codeArea) {
|
||||
super(NLS.str("popup.find_usage"));
|
||||
this.contentPanel = contentPanel;
|
||||
this.codeArea = codeArea;
|
||||
this.jCls = jCls;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -51,7 +48,7 @@ public final class FindUsageAction extends AbstractAction implements PopupMenuLi
|
||||
if (pos != null) {
|
||||
Token token = codeArea.viewToToken(pos);
|
||||
if (token != null) {
|
||||
node = codeArea.getJavaNodeAtOffset(jCls, token.getOffset());
|
||||
node = codeArea.getJavaNodeAtOffset(token.getOffset());
|
||||
}
|
||||
}
|
||||
setEnabled(node != null);
|
||||
|
||||
@@ -49,7 +49,7 @@ public final class GoToDeclarationAction extends AbstractAction implements Popup
|
||||
if (pos != null) {
|
||||
Token token = codeArea.viewToToken(pos);
|
||||
if (token != null) {
|
||||
node = codeArea.getJavaNodeAtOffset(jCls, token.getOffset());
|
||||
node = codeArea.getJavaNodeAtOffset(token.getOffset());
|
||||
}
|
||||
}
|
||||
setEnabled(node != null);
|
||||
|
||||
@@ -13,17 +13,14 @@ import org.slf4j.LoggerFactory;
|
||||
|
||||
import jadx.api.JavaClass;
|
||||
import jadx.api.JavaNode;
|
||||
import jadx.gui.treemodel.JClass;
|
||||
|
||||
public final class JadxTokenMaker extends JavaTokenMaker {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(JadxTokenMaker.class);
|
||||
|
||||
private final CodeArea codeArea;
|
||||
private final JClass jCls;
|
||||
|
||||
public JadxTokenMaker(CodeArea codeArea, JClass jCls) {
|
||||
public JadxTokenMaker(CodeArea codeArea) {
|
||||
this.codeArea = codeArea;
|
||||
this.jCls = jCls;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -62,7 +59,7 @@ public final class JadxTokenMaker extends JavaTokenMaker {
|
||||
if (annotation) {
|
||||
offset++;
|
||||
}
|
||||
JavaNode javaNode = codeArea.getJavaNodeAtOffset(jCls, offset);
|
||||
JavaNode javaNode = codeArea.getJavaNodeAtOffset(offset);
|
||||
if (javaNode instanceof JavaClass) {
|
||||
String name = javaNode.getName();
|
||||
String lexeme = current.getLexeme();
|
||||
|
||||
@@ -20,6 +20,8 @@ import org.jetbrains.annotations.Nullable;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import jadx.api.ICodeInfo;
|
||||
|
||||
public class LineNumbers extends JPanel implements CaretListener {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(LineNumbers.class);
|
||||
|
||||
@@ -41,6 +43,7 @@ public class LineNumbers extends JPanel implements CaretListener {
|
||||
|
||||
private transient Insets textAreaInsets;
|
||||
private transient Rectangle visibleRect = new Rectangle();
|
||||
private transient ICodeInfo codeInfo;
|
||||
|
||||
public LineNumbers(AbstractCodeArea codeArea) {
|
||||
this.codeArea = codeArea;
|
||||
@@ -60,10 +63,8 @@ public class LineNumbers extends JPanel implements CaretListener {
|
||||
addMouseListener(new MouseAdapter() {
|
||||
@Override
|
||||
public void mouseClicked(MouseEvent e) {
|
||||
if (e.getClickCount() == 2) {
|
||||
useSourceLines = !useSourceLines;
|
||||
repaint();
|
||||
}
|
||||
useSourceLines = !useSourceLines;
|
||||
repaint();
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -96,6 +97,8 @@ public class LineNumbers extends JPanel implements CaretListener {
|
||||
|
||||
@Override
|
||||
public void paintComponent(Graphics g) {
|
||||
codeInfo = codeArea.getNode().getCodeInfo();
|
||||
|
||||
visibleRect = g.getClipBounds(visibleRect);
|
||||
if (visibleRect == null) {
|
||||
visibleRect = getVisibleRect();
|
||||
@@ -131,8 +134,7 @@ public class LineNumbers extends JPanel implements CaretListener {
|
||||
int y = actualTopY + ascent;
|
||||
int endY = visibleRect.y + visibleRect.height + ascent;
|
||||
|
||||
Element rootElement = codeArea.getDocument().getDefaultRootElement();
|
||||
int currentLine = 1 + rootElement.getElementIndex(codeArea.getCaretPosition());
|
||||
int currentLine = 1 + codeArea.getCaretLineNumber();
|
||||
int lineNum = topLine + 1;
|
||||
int linesCount = codeArea.getLineCount();
|
||||
boolean isCurLine = updateColor(g, false, true);
|
||||
@@ -186,7 +188,7 @@ public class LineNumbers extends JPanel implements CaretListener {
|
||||
if (!useSourceLines) {
|
||||
return String.valueOf(lineNumber);
|
||||
}
|
||||
Integer sourceLine = codeArea.getSourceLine(lineNumber);
|
||||
Integer sourceLine = codeInfo.getLineMapping().get(lineNumber);
|
||||
if (sourceLine == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -1,13 +1,20 @@
|
||||
package jadx.gui.ui.codearea;
|
||||
|
||||
import org.fife.ui.rsyntaxtextarea.SyntaxConstants;
|
||||
|
||||
import jadx.gui.treemodel.JNode;
|
||||
import jadx.gui.treemodel.TextNode;
|
||||
import jadx.gui.ui.ContentPanel;
|
||||
|
||||
public final class SmaliArea extends AbstractCodeArea {
|
||||
private static final long serialVersionUID = 1334485631870306494L;
|
||||
|
||||
private final JNode textNode;
|
||||
|
||||
SmaliArea(ContentPanel contentPanel) {
|
||||
super(contentPanel);
|
||||
setEditable(false);
|
||||
this.textNode = new TextNode(node.getName());
|
||||
setSyntaxEditingStyle(SyntaxConstants.SYNTAX_STYLE_JAVA);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -17,4 +24,10 @@ public final class SmaliArea extends AbstractCodeArea {
|
||||
setCaretPosition(0);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public JNode getNode() {
|
||||
// this area contains only smali without other node attributes
|
||||
return textNode;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user