未验证 提交 e9b80608 编写于 作者: S Skylot

refactor(gui): improve node action in code area

上级 1c2b2c07
......@@ -27,7 +27,10 @@ public class JadxGUI {
if (!settings.overrideProvided(args)) {
return;
}
LogHelper.initLogLevel(settings);
LogHelper.setLogLevelsForDecompileStage();
printSystemInfo();
LafManager.init(settings);
NLS.setLocale(settings.getLangLocale());
ExceptionDialog.registerUncaughtExceptionHandler();
......
......@@ -8,8 +8,6 @@ import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.AbstractAction;
import javax.swing.JCheckBoxMenuItem;
......@@ -25,6 +23,7 @@ import javax.swing.text.DefaultCaret;
import org.fife.ui.rsyntaxtextarea.AbstractTokenMakerFactory;
import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea;
import org.fife.ui.rsyntaxtextarea.Token;
import org.fife.ui.rsyntaxtextarea.TokenMakerFactory;
import org.fife.ui.rtextarea.SearchContext;
import org.fife.ui.rtextarea.SearchEngine;
......@@ -193,51 +192,17 @@ public abstract class AbstractCodeArea extends RSyntaxTextArea {
return getWordByPosition(getCaretPosition());
}
public int getWordStart(int pos) {
int start = Math.max(0, pos - 1);
try {
if (!StringUtils.isWordSeparator(getText(start, 1).charAt(0))) {
do {
start--;
} while (start >= 0 && !StringUtils.isWordSeparator(getText(start, 1).charAt(0)));
}
start++;
} catch (BadLocationException e) {
LOG.error("Failed to find word start", e);
start = -1;
}
return start;
}
public int getWordEnd(int pos, int max) {
int end = pos;
try {
if (!StringUtils.isWordSeparator(getText(end, 1).charAt(0))) {
do {
end++;
} while (end < max && !StringUtils.isWordSeparator(getText(end, 1).charAt(0)));
}
} catch (BadLocationException e) {
LOG.error("Failed to find word end", e);
end = max;
}
return end;
}
@Nullable
public String getWordByPosition(int pos) {
int len = getDocument().getLength();
int start = getWordStart(pos);
int end = getWordEnd(pos, len);
try {
if (end <= start) {
return null;
Token token = modelToToken(pos);
if (token != null) {
return token.getLexeme();
}
return getText(start, end - start);
} catch (BadLocationException e) {
LOG.error("Failed to get word at pos: {}, start: {}, end: {}", pos, start, end, e);
return null;
} catch (Exception e) {
LOG.error("Failed to get word at pos: {}", pos, e);
}
return null;
}
/**
......@@ -326,23 +291,6 @@ 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
*/
......
......@@ -5,9 +5,7 @@ import java.awt.event.InputEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.JPopupMenu;
import javax.swing.event.PopupMenuEvent;
import javax.swing.text.BadLocationException;
import org.fife.ui.rsyntaxtextarea.RSyntaxDocument;
import org.fife.ui.rsyntaxtextarea.Token;
......@@ -89,33 +87,19 @@ public final class CodeArea extends AbstractCodeArea {
}
private void addMenuItems() {
FindUsageAction findUsage = new FindUsageAction(this);
GoToDeclarationAction goToDeclaration = new GoToDeclarationAction(this);
RenameAction rename = new RenameAction(this);
CommentAction comment = new CommentAction(this);
FridaAction frida = new FridaAction(this);
XposedAction xposed = new XposedAction(this);
JPopupMenu popup = getPopupMenu();
JNodePopupBuilder popup = new JNodePopupBuilder(this, getPopupMenu());
popup.addSeparator();
popup.add(findUsage);
popup.add(goToDeclaration);
popup.add(comment);
popup.add(new FindUsageAction(this));
popup.add(new GoToDeclarationAction(this));
popup.add(new CommentAction(this));
popup.add(new CommentSearchAction(this));
popup.add(rename);
popup.add(new RenameAction(this));
popup.addSeparator();
popup.add(frida);
popup.add(xposed);
popup.addPopupMenuListener(findUsage);
popup.addPopupMenuListener(goToDeclaration);
popup.addPopupMenuListener(comment);
popup.addPopupMenuListener(rename);
popup.addPopupMenuListener(frida);
popup.addPopupMenuListener(xposed);
popup.add(new FridaAction(this));
popup.add(new XposedAction(this));
// move caret on mouse right button click
popup.addPopupMenuListener(new DefaultPopupMenuListener() {
popup.getMenu().addPopupMenuListener(new DefaultPopupMenuListener() {
@Override
public void popupMenuWillBecomeVisible(PopupMenuEvent e) {
CodeArea codeArea = CodeArea.this;
......@@ -186,43 +170,25 @@ public final class CodeArea extends AbstractCodeArea {
return nodeCache.makeFrom(javaNode);
}
@SuppressWarnings("deprecation")
public CodePosition getMouseCodePos() {
try {
Point mousePos = UiUtils.getMousePosition(this);
return buildCodePosFromOffset(this.viewToModel(mousePos));
} catch (Exception e) {
LOG.error("Failed to get offset at mouse position", e);
return null;
}
}
@Nullable
public CodePosition getCaretCodePos() {
try {
return buildCodePosFromOffset(getCaretPosition());
} catch (Exception e) {
LOG.warn("Failed to get caret position", e);
public JNode getNodeUnderCaret() {
int caretPos = getCaretPosition();
Token token = modelToToken(caretPos);
if (token == null) {
return null;
}
}
private CodePosition buildCodePosFromOffset(int offset) throws BadLocationException {
int start = getWordStart(offset);
int start = adjustOffsetForToken(token);
if (start == -1) {
start = offset;
start = caretPos;
}
int line = getLineOfOffset(start);
int lineOffset = start - getLineStartOffset(line);
return new CodePosition(line + 1, lineOffset + 1, start);
return getJNodeAtOffset(start);
}
public JNode getNodeUnderCaret() {
int start = getWordStart(getCaretPosition());
if (start == -1) {
start = getCaretPosition();
}
return getJNodeAtOffset(start);
@Nullable
public JNode getNodeUnderMouse() {
Point pos = UiUtils.getMousePosition(this);
int offset = adjustOffsetForToken(viewToToken(pos));
return getJNodeAtOffset(offset);
}
@Nullable
......
......@@ -4,7 +4,6 @@ import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import javax.swing.AbstractAction;
import javax.swing.KeyStroke;
import javax.swing.event.PopupMenuEvent;
import org.jetbrains.annotations.Nullable;
......@@ -48,16 +47,9 @@ public class CommentAction extends AbstractAction implements DefaultPopupMenuLis
} else {
this.topCls = null;
}
KeyStroke key = getKeyStroke(KeyEvent.VK_SEMICOLON, 0);
codeArea.getInputMap().put(key, "popup.add_comment");
codeArea.getActionMap().put("popup.add_comment", new AbstractAction() {
@Override
public void actionPerformed(ActionEvent e) {
int line = codeArea.getCaretLineNumber() + 1;
ICodeComment codeComment = getCommentRef(line);
showCommentDialog(codeComment);
}
UiUtils.addKeyBinding(codeArea, getKeyStroke(KeyEvent.VK_SEMICOLON, 0), "popup.add_comment", () -> {
int line = codeArea.getCaretLineNumber() + 1;
showCommentDialog(getCommentRef(line));
});
}
......
......@@ -4,7 +4,6 @@ import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.KeyStroke;
import jadx.gui.ui.dialog.SearchDialog;
......@@ -19,18 +18,11 @@ public class CommentSearchAction extends AbstractAction {
private final CodeArea codeArea;
public CommentSearchAction(CodeArea codeArea) {
super(NLS.str("popup.search_comment") + " (Ctrl + ;)");
this.codeArea = codeArea;
KeyStroke key = getKeyStroke(KeyEvent.VK_SEMICOLON, UiUtils.ctrlButton());
putValue(Action.NAME, NLS.str("popup.search_comment") + " (Ctrl + ;)");
codeArea.getInputMap().put(key, "popup.search_comment");
codeArea.getActionMap().put("popup.search_comment", new AbstractAction() {
@Override
public void actionPerformed(ActionEvent e) {
startSearch();
}
});
UiUtils.addKeyBinding(codeArea, key, "popup.search_comment", this::startSearch);
}
@Override
......
package jadx.gui.ui.codearea;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import javax.swing.AbstractAction;
import javax.swing.KeyStroke;
import org.jetbrains.annotations.Nullable;
import jadx.gui.treemodel.JNode;
import jadx.gui.ui.dialog.UsageDialog;
import jadx.gui.utils.NLS;
import static javax.swing.KeyStroke.getKeyStroke;
public final class FindUsageAction extends JNodeMenuAction<JNode> {
public final class FindUsageAction extends JNodeAction {
private static final long serialVersionUID = 4692546569977976384L;
public FindUsageAction(CodeArea codeArea) {
super(NLS.str("popup.find_usage") + " (x)", codeArea);
KeyStroke key = getKeyStroke(KeyEvent.VK_X, 0);
codeArea.getInputMap().put(key, "trigger usage");
codeArea.getActionMap().put("trigger usage", new AbstractAction() {
@Override
public void actionPerformed(ActionEvent e) {
node = codeArea.getNodeUnderCaret();
showUsageDialog();
}
});
}
private void showUsageDialog() {
if (node != null) {
UsageDialog usageDialog = new UsageDialog(codeArea.getMainWindow(), node);
usageDialog.setVisible(true);
node = null;
}
}
@Override
public void actionPerformed(ActionEvent e) {
showUsageDialog();
addKeyBinding(getKeyStroke(KeyEvent.VK_X, 0), "trigger usage");
}
@Nullable
@Override
public JNode getNodeByOffset(int offset) {
return codeArea.getJNodeAtOffset(offset);
public void runAction(JNode node) {
UsageDialog usageDialog = new UsageDialog(getCodeArea().getMainWindow(), node);
usageDialog.setVisible(true);
}
}
package jadx.gui.ui.codearea;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.util.*;
import java.util.Comparator;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
import javax.swing.*;
import javax.swing.JOptionPane;
import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
......@@ -30,50 +30,41 @@ import jadx.gui.utils.UiUtils;
import static javax.swing.KeyStroke.getKeyStroke;
public final class FridaAction extends JNodeMenuAction<JNode> {
public final class FridaAction extends JNodeAction {
private static final Logger LOG = LoggerFactory.getLogger(FridaAction.class);
private static final long serialVersionUID = -3084073927621269039L;
private final Map<String, Boolean> isInitial = new HashMap<>();
public FridaAction(CodeArea codeArea) {
super(NLS.str("popup.frida") + " (f)", codeArea);
KeyStroke key = getKeyStroke(KeyEvent.VK_F, 0);
codeArea.getInputMap().put(key, "trigger frida");
codeArea.getActionMap().put("trigger frida", new AbstractAction() {
@Override
public void actionPerformed(ActionEvent e) {
node = getNodeByOffset(codeArea.getWordStart(codeArea.getCaretPosition()));
copyFridaSnippet();
}
});
addKeyBinding(getKeyStroke(KeyEvent.VK_F, 0), "trigger frida");
}
@Override
public void actionPerformed(ActionEvent e) {
node = codeArea.getNodeUnderCaret();
copyFridaSnippet();
}
private void copyFridaSnippet() {
public void runAction(JNode node) {
try {
String fridaSnippet = generateFridaSnippet();
String fridaSnippet = generateFridaSnippet(node);
LOG.info("Frida snippet:\n{}", fridaSnippet);
UiUtils.copyToClipboard(fridaSnippet);
} catch (Exception e) {
LOG.error("Failed to generate Frida code snippet", e);
JOptionPane.showMessageDialog(codeArea.getMainWindow(), e.getLocalizedMessage(), NLS.str("error_dialog.title"),
JOptionPane.showMessageDialog(getCodeArea().getMainWindow(), e.getLocalizedMessage(), NLS.str("error_dialog.title"),
JOptionPane.ERROR_MESSAGE);
}
}
private String generateFridaSnippet() {
@Override
public boolean isActionEnabled(JNode node) {
return node instanceof JMethod || node instanceof JClass || node instanceof JField;
}
private String generateFridaSnippet(JNode node) {
if (node instanceof JMethod) {
return generateMethodSnippet((JMethod) node);
} else if (node instanceof JClass) {
}
if (node instanceof JClass) {
return generateClassSnippet((JClass) node);
} else if (node instanceof JField) {
}
if (node instanceof JField) {
return generateFieldSnippet((JField) node);
}
throw new JadxRuntimeException("Unsupported node type: " + (node != null ? node.getClass() : "null"));
......@@ -86,7 +77,6 @@ public final class FridaAction extends JNodeMenuAction<JNode> {
if (methodInfo.isConstructor()) {
methodName = "$init";
}
String rawClassName = javaMethod.getDeclaringClass().getRawName();
String shortClassName = javaMethod.getDeclaringClass().getName();
String functionUntilImplementation;
......@@ -114,23 +104,14 @@ public final class FridaAction extends JNodeMenuAction<JNode> {
+ "};",
functionUntilImplementation, functionParametersString, methodName, methodName, functionParametersString, methodName);
String finalFridaCode;
if (isInitial.getOrDefault(rawClassName, true)) {
String classSnippet = generateClassSnippet(jMth.getJParent());
finalFridaCode = classSnippet + "\n" + functionParameterAndBody;
} else {
finalFridaCode = functionParameterAndBody;
}
return finalFridaCode;
return generateClassSnippet(jMth.getJParent()) + "\n" + functionParameterAndBody;
}
private String generateClassSnippet(JClass jc) {
JavaClass javaClass = jc.getCls();
String rawClassName = javaClass.getRawName();
String shortClassName = javaClass.getName();
String finalFridaCode = String.format("let %s = Java.use(\"%s\");", shortClassName, rawClassName);
isInitial.put(rawClassName, false);
return finalFridaCode;
return String.format("let %s = Java.use(\"%s\");", shortClassName, rawClassName);
}
private String generateFieldSnippet(JField jf) {
......@@ -168,10 +149,4 @@ public final class FridaAction extends JNodeMenuAction<JNode> {
}
return parsedArgType.append("'").toString();
}
@Nullable
@Override
public JNode getNodeByOffset(int offset) {
return codeArea.getJNodeAtOffset(offset);
}
}
package jadx.gui.ui.codearea;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import javax.swing.AbstractAction;
import javax.swing.KeyStroke;
import org.jetbrains.annotations.Nullable;
import jadx.api.CodePosition;
import jadx.gui.treemodel.JNode;
import jadx.gui.utils.JumpPosition;
import jadx.gui.utils.NLS;
import static javax.swing.KeyStroke.getKeyStroke;
public final class GoToDeclarationAction extends JNodeMenuAction<JumpPosition> {
public final class GoToDeclarationAction extends JNodeAction {
private static final long serialVersionUID = -1186470538894941301L;
private transient @Nullable JumpPosition declPos;
public GoToDeclarationAction(CodeArea codeArea) {
super(NLS.str("popup.go_to_declaration") + " (d)", codeArea);
KeyStroke key = getKeyStroke(KeyEvent.VK_D, 0);
codeArea.getInputMap().put(key, "trigger goto decl");
codeArea.getActionMap().put("trigger goto decl", new AbstractAction() {
@Override
public void actionPerformed(ActionEvent e) {
node = getNodeByOffset(codeArea.getWordStart(codeArea.getCaretPosition()));
doJump();
}
});
}
private void doJump() {
if (node != null) {
codeArea.getContentPanel().getTabbedPane().codeJump(node);
node = null;
}
addKeyBinding(getKeyStroke(KeyEvent.VK_D, 0), "trigger goto decl");
}
@Override
public void actionPerformed(ActionEvent e) {
doJump();
public boolean isActionEnabled(JNode node) {
declPos = null;
if (node == null) {
return false;
}
CodePosition defPos = getCodeArea().getDecompiler().getDefinitionPosition(node.getJavaNode());
if (defPos == null) {
return false;
}
declPos = new JumpPosition(node.getRootClass(), defPos);
return true;
}
@Nullable
@Override
public JumpPosition getNodeByOffset(int offset) {
return codeArea.getDefPosForNodeAtOffset(offset);
public void runAction(JNode node) {
if (declPos != null) {
getCodeArea().getContentPanel().getTabbedPane().codeJump(declPos);
}
}
}
package jadx.gui.ui.codearea;
import java.awt.event.ActionEvent;
import javax.swing.AbstractAction;
import javax.swing.KeyStroke;
import org.jetbrains.annotations.Nullable;
import jadx.gui.treemodel.JNode;
import jadx.gui.utils.UiUtils;
/**
* Add menu and key binding actions for JNode in code area
*/
public abstract class JNodeAction extends AbstractAction {
private static final long serialVersionUID = -2600154727884853550L;
private final transient CodeArea codeArea;
private transient @Nullable JNode node;
public JNodeAction(String name, CodeArea codeArea) {
super(name);
this.codeArea = codeArea;
}
public abstract void runAction(JNode node);
public boolean isActionEnabled(JNode node) {
return node != null;
}
public void addKeyBinding(KeyStroke key, String id) {
UiUtils.addKeyBinding(codeArea, key, id, new AbstractAction() {
@Override
public void actionPerformed(ActionEvent e) {
node = codeArea.getNodeUnderCaret();
if (isActionEnabled(node)) {
runAction(node);
}
}
});
}
@Override
public void actionPerformed(ActionEvent e) {
runAction(node);
}
public void changeNode(JNode node) {
this.node = node;
setEnabled(isActionEnabled(node));
}
public CodeArea getCodeArea() {
return codeArea;
}
}
package jadx.gui.ui.codearea;
import java.awt.Point;
import java.awt.event.ActionEvent;
import javax.swing.AbstractAction;
import javax.swing.event.PopupMenuEvent;
import javax.swing.event.PopupMenuListener;
import org.fife.ui.rsyntaxtextarea.Token;
import org.jetbrains.annotations.Nullable;
import jadx.gui.utils.UiUtils;
public abstract class JNodeMenuAction<T> extends AbstractAction implements PopupMenuListener {
private static final long serialVersionUID = -2600154727884853550L;
protected final transient CodeArea codeArea;
@Nullable
protected transient T node;
public JNodeMenuAction(String name, CodeArea codeArea) {
super(name);
this.codeArea = codeArea;
}
@Override
public abstract void actionPerformed(ActionEvent e);
@Nullable
public abstract T getNodeByOffset(int offset);
@Override
public void popupMenuWillBecomeVisible(PopupMenuEvent e) {
node = getNode();
setEnabled(node != null);
}
@Nullable
private T getNode() {
Point pos = UiUtils.getMousePosition(codeArea);
Token token = codeArea.viewToToken(pos);
int offset = codeArea.adjustOffsetForToken(token);
return getNodeByOffset(offset);
}
@Override
public void popupMenuWillBecomeInvisible(PopupMenuEvent e) {
// do nothing
}
@Override
public void popupMenuCanceled(PopupMenuEvent e) {
// do nothing
}
}
package jadx.gui.ui.codearea;
import javax.swing.Action;
import javax.swing.JPopupMenu;
import javax.swing.event.PopupMenuListener;
public class JNodePopupBuilder {
private final JPopupMenu menu;
private final JNodePopupListener popupListener;
public JNodePopupBuilder(CodeArea codeArea, JPopupMenu popupMenu) {
menu = popupMenu;
popupListener = new JNodePopupListener(codeArea);
popupMenu.addPopupMenuListener(popupListener);
}
public void addSeparator() {
menu.addSeparator();
}
public void add(JNodeAction nodeAction) {
menu.add(nodeAction);
popupListener.addActions(nodeAction);
}
public void add(Action action) {
menu.add(action);
if (action instanceof PopupMenuListener) {
menu.addPopupMenuListener((PopupMenuListener) action);
}
}
public JPopupMenu getMenu() {
return menu;
}
}
package jadx.gui.ui.codearea;
import java.util.ArrayList;
import java.util.List;
import javax.swing.event.PopupMenuEvent;
import jadx.gui.treemodel.JNode;
import jadx.gui.utils.DefaultPopupMenuListener;
public final class JNodePopupListener implements DefaultPopupMenuListener {
private final CodeArea codeArea;
private final List<JNodeAction> actions = new ArrayList<>();
public JNodePopupListener(CodeArea codeArea) {
this.codeArea = codeArea;
}
@Override
public void popupMenuWillBecomeVisible(PopupMenuEvent e) {
JNode node = codeArea.getNodeUnderMouse();
actions.forEach(action -> action.changeNode(node));
}
public void addActions(JNodeAction action) {
actions.add(action);
}
}
package jadx.gui.ui.codearea;
import java.awt.event.ActionEvent;
import javax.swing.AbstractAction;
import javax.swing.KeyStroke;
import javax.swing.event.PopupMenuEvent;
import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import jadx.gui.treemodel.JNode;
import jadx.gui.ui.dialog.RenameDialog;
import jadx.gui.utils.NLS;
import jadx.gui.utils.UiUtils;
import static java.awt.event.KeyEvent.VK_N;
import static javax.swing.KeyStroke.getKeyStroke;
public final class RenameAction extends JNodeMenuAction<JNode> {
public final class RenameAction extends JNodeAction {
private static final long serialVersionUID = -4680872086148463289L;
private static final Logger LOG = LoggerFactory.getLogger(RenameAction.class);
public RenameAction(CodeArea codeArea) {
super(NLS.str("popup.rename") + " (n)", codeArea);
KeyStroke key = getKeyStroke(VK_N, 0);
String renameActionId = "trigger rename";
codeArea.getInputMap().put(key, renameActionId);
codeArea.getActionMap().put(renameActionId, new AbstractAction() {
@Override
public void actionPerformed(ActionEvent e) {
node = codeArea.getNodeUnderCaret();
showRenameDialog();
}
});
}
private void showRenameDialog() {
if (node == null) {
LOG.info("node == null!");
UiUtils.showMessageBox(codeArea.getMainWindow(), NLS.str("msg.rename_node_disabled"));
return;
}
if (!node.canRename()) {
UiUtils.showMessageBox(codeArea.getMainWindow(),
NLS.str("msg.rename_node_failed", node.getJavaNode().getFullName()));
LOG.warn("Can't rename node: {}", node);
return;
}
RenameDialog.rename(codeArea.getMainWindow(), codeArea.getNode(), node);
node = null;
}
@Override
public void popupMenuWillBecomeVisible(PopupMenuEvent e) {
super.popupMenuWillBecomeVisible(e);
setEnabled(node != null && node.canRename());
addKeyBinding(getKeyStroke(VK_N, 0), "trigger rename");
}
@Override
public void actionPerformed(ActionEvent e) {
showRenameDialog();
public boolean isActionEnabled(JNode node) {
return node != null && node.canRename();
}
@Nullable
@Override
public JNode getNodeByOffset(int offset) {
return codeArea.getJNodeAtOffset(offset);
public void runAction(JNode node) {
RenameDialog.rename(getCodeArea().getMainWindow(), getCodeArea().getNode(), node);
}
}
package jadx.gui.ui.codearea;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.util.List;
import java.util.stream.Collectors;
import javax.swing.AbstractAction;
import javax.swing.JOptionPane;
import javax.swing.KeyStroke;
import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
......@@ -26,42 +22,34 @@ import jadx.gui.utils.UiUtils;
import static javax.swing.KeyStroke.getKeyStroke;
public class XposedAction extends JNodeMenuAction<JNode> {
public class XposedAction extends JNodeAction {
private static final Logger LOG = LoggerFactory.getLogger(XposedAction.class);
private static final long serialVersionUID = 2641585141624592578L;
public XposedAction(CodeArea codeArea) {
super(NLS.str("popup.xposed") + " (y)", codeArea);
KeyStroke key = getKeyStroke(KeyEvent.VK_Y, 0);
codeArea.getInputMap().put(key, "trigger xposed");
codeArea.getActionMap().put("trigger xposed", new AbstractAction() {
@Override
public void actionPerformed(ActionEvent e) {
node = getNodeByOffset(codeArea.getWordStart(codeArea.getCaretPosition()));
copyXposedSnippet();
}
});
addKeyBinding(getKeyStroke(KeyEvent.VK_Y, 0), "trigger xposed");
}
@Override
public void actionPerformed(ActionEvent e) {
node = codeArea.getNodeUnderCaret();
copyXposedSnippet();
}
private void copyXposedSnippet() {
public void runAction(JNode node) {
try {
String xposedSnippet = generateXposedSnippet();
String xposedSnippet = generateXposedSnippet(node);
LOG.info("Xposed snippet:\n{}", xposedSnippet);
UiUtils.copyToClipboard(xposedSnippet);
} catch (Exception e) {
LOG.error("Failed to generate Xposed code snippet", e);
JOptionPane.showMessageDialog(codeArea.getMainWindow(), e.getLocalizedMessage(), NLS.str("error_dialog.title"),
JOptionPane.showMessageDialog(getCodeArea().getMainWindow(), e.getLocalizedMessage(), NLS.str("error_dialog.title"),
JOptionPane.ERROR_MESSAGE);
}
}
private String generateXposedSnippet() {
@Override
public boolean isActionEnabled(JNode node) {
return node instanceof JMethod || node instanceof JClass;
}
private String generateXposedSnippet(JNode node) {
if (node instanceof JMethod) {
return generateMethodSnippet((JMethod) node);
}
......@@ -111,10 +99,4 @@ public class XposedAction extends JNodeMenuAction<JNode> {
+ "Class %sClass=classLoader.loadClass(\"%s\");",
shortClassName, rawClassName);
}
@Nullable
@Override
public JNode getNodeByOffset(int offset) {
return codeArea.getJNodeAtOffset(offset);
}
}
......@@ -9,12 +9,14 @@ import java.awt.Window;
import java.awt.datatransfer.Clipboard;
import java.awt.datatransfer.StringSelection;
import java.awt.datatransfer.Transferable;
import java.awt.event.ActionEvent;
import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.Icon;
import javax.swing.ImageIcon;
......@@ -90,6 +92,15 @@ public class UiUtils {
return Toolkit.getDefaultToolkit().createImage(resource);
}
public static void addKeyBinding(JComponent comp, KeyStroke key, String id, Runnable action) {
addKeyBinding(comp, key, id, new AbstractAction() {
@Override
public void actionPerformed(ActionEvent e) {
action.run();
}
});
}
public static void addKeyBinding(JComponent comp, KeyStroke key, String id, Action action) {
comp.getInputMap().put(key, id);
comp.getActionMap().put(id, action);
......
......@@ -182,8 +182,6 @@ msg.index_not_initialized=Index nicht initialisiert, Suche wird deaktiviert!
msg.project_error_title=Fehler
msg.project_error=Projekt konnte nicht geladen werden
msg.cmd_select_class_error=Klasse\n%s auswählen nicht möglich\nSie existiert nicht.
msg.rename_node_disabled=Dieser Knotenpunkt kann nicht umbenannt werden
msg.rename_node_failed=%s umbenennen nicht möglich
msg.cant_add_comment=Kann hier keinen Kommentar hinzufügen
popup.bytecode_col=Dalvik Bytecode anzeigen
......
......@@ -182,8 +182,6 @@ msg.index_not_initialized=Index not initialized, search will be disabled!
msg.project_error_title=Error
msg.project_error=Project could not be loaded
msg.cmd_select_class_error=Failed to select the class\n%s\nThe class does not exist.
msg.rename_node_disabled=Can't rename this node
msg.rename_node_failed=Can't rename %s
msg.cant_add_comment=Can't add comment here
popup.bytecode_col=Show Dalvik Bytecode
......
......@@ -182,8 +182,6 @@ msg.index_not_initialized=Índice no inicializado, ¡la bósqueda se desactivar
#msg.project_error_title=
#msg.project_error=
#msg.cmd_select_class_error=
#msg.rename_node_disabled=
#msg.rename_node_failed=
#msg.cant_add_comment=Can't add comment here
#popup.bytecode_col=
......
......@@ -182,8 +182,6 @@ msg.index_not_initialized=인덱스가 초기화되지 않았습니다. 검색
msg.project_error_title=오류
msg.project_error=프로젝트를 로드 할 수 없습니다.
msg.cmd_select_class_error=클래스를 선택하지 못했습니다.\n%s\n클래스가 없습니다.
msg.rename_node_disabled=이 노드의 이름을 바꿀 수 없습니다.
msg.rename_node_failed=%s의 이름을 바꿀 수 없습니다.
msg.cant_add_comment=여기에 주석을 추가할수 없음
popup.bytecode_col=Dalvik Bytecode 보이기
......
......@@ -182,8 +182,6 @@ msg.index_not_initialized=索引尚未初始化,无法进行搜索!
msg.project_error_title=错误
msg.project_error=项目无法加载
msg.cmd_select_class_error=无法选择类\n%s\n该类不存在。
msg.rename_node_disabled=无法重命名此节点
msg.rename_node_failed=无法重命名 %s
msg.cant_add_comment=无法在此添加注释
popup.bytecode_col=显示Dalvik字节码
......
......@@ -182,8 +182,6 @@ msg.index_not_initialized=索引尚未初始化,搜尋將被停用!
msg.project_error_title=錯誤
msg.project_error=無法載入專案
msg.cmd_select_class_error=無法選擇類別\n%s\n類別不存在。
msg.rename_node_disabled=無法重新命名此節點
msg.rename_node_failed=無法重新命名 %s
msg.cant_add_comment=無法在此新增註解
popup.bytecode_col=顯示 Dalvik 位元組碼
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册