From e9b8060889d0d59eb77759c82d97e9bed2061457 Mon Sep 17 00:00:00 2001 From: Skylot Date: Sat, 26 Mar 2022 15:31:29 +0000 Subject: [PATCH] refactor(gui): improve node action in code area --- jadx-gui/src/main/java/jadx/gui/JadxGUI.java | 3 + .../gui/ui/codearea/AbstractCodeArea.java | 66 ++--------------- .../java/jadx/gui/ui/codearea/CodeArea.java | 74 +++++-------------- .../jadx/gui/ui/codearea/CommentAction.java | 14 +--- .../gui/ui/codearea/CommentSearchAction.java | 12 +-- .../jadx/gui/ui/codearea/FindUsageAction.java | 37 ++-------- .../jadx/gui/ui/codearea/FridaAction.java | 67 ++++++----------- .../ui/codearea/GoToDeclarationAction.java | 47 ++++++------ .../jadx/gui/ui/codearea/JNodeAction.java | 58 +++++++++++++++ .../jadx/gui/ui/codearea/JNodeMenuAction.java | 56 -------------- .../gui/ui/codearea/JNodePopupBuilder.java | 36 +++++++++ .../gui/ui/codearea/JNodePopupListener.java | 28 +++++++ .../jadx/gui/ui/codearea/RenameAction.java | 57 ++------------ .../jadx/gui/ui/codearea/XposedAction.java | 40 +++------- .../src/main/java/jadx/gui/utils/UiUtils.java | 11 +++ .../resources/i18n/Messages_de_DE.properties | 2 - .../resources/i18n/Messages_en_US.properties | 2 - .../resources/i18n/Messages_es_ES.properties | 2 - .../resources/i18n/Messages_ko_KR.properties | 2 - .../resources/i18n/Messages_zh_CN.properties | 2 - .../resources/i18n/Messages_zh_TW.properties | 2 - 21 files changed, 232 insertions(+), 386 deletions(-) create mode 100644 jadx-gui/src/main/java/jadx/gui/ui/codearea/JNodeAction.java delete mode 100644 jadx-gui/src/main/java/jadx/gui/ui/codearea/JNodeMenuAction.java create mode 100644 jadx-gui/src/main/java/jadx/gui/ui/codearea/JNodePopupBuilder.java create mode 100644 jadx-gui/src/main/java/jadx/gui/ui/codearea/JNodePopupListener.java diff --git a/jadx-gui/src/main/java/jadx/gui/JadxGUI.java b/jadx-gui/src/main/java/jadx/gui/JadxGUI.java index 548fe16e..8132ca32 100644 --- a/jadx-gui/src/main/java/jadx/gui/JadxGUI.java +++ b/jadx-gui/src/main/java/jadx/gui/JadxGUI.java @@ -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(); diff --git a/jadx-gui/src/main/java/jadx/gui/ui/codearea/AbstractCodeArea.java b/jadx-gui/src/main/java/jadx/gui/ui/codearea/AbstractCodeArea.java index 4090a9fd..4fc98a92 100644 --- a/jadx-gui/src/main/java/jadx/gui/ui/codearea/AbstractCodeArea.java +++ b/jadx-gui/src/main/java/jadx/gui/ui/codearea/AbstractCodeArea.java @@ -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 */ diff --git a/jadx-gui/src/main/java/jadx/gui/ui/codearea/CodeArea.java b/jadx-gui/src/main/java/jadx/gui/ui/codearea/CodeArea.java index ec144062..2e134689 100644 --- a/jadx-gui/src/main/java/jadx/gui/ui/codearea/CodeArea.java +++ b/jadx-gui/src/main/java/jadx/gui/ui/codearea/CodeArea.java @@ -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 diff --git a/jadx-gui/src/main/java/jadx/gui/ui/codearea/CommentAction.java b/jadx-gui/src/main/java/jadx/gui/ui/codearea/CommentAction.java index 4f27d88e..0312ae43 100644 --- a/jadx-gui/src/main/java/jadx/gui/ui/codearea/CommentAction.java +++ b/jadx-gui/src/main/java/jadx/gui/ui/codearea/CommentAction.java @@ -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)); }); } diff --git a/jadx-gui/src/main/java/jadx/gui/ui/codearea/CommentSearchAction.java b/jadx-gui/src/main/java/jadx/gui/ui/codearea/CommentSearchAction.java index d78e1f46..347627bf 100644 --- a/jadx-gui/src/main/java/jadx/gui/ui/codearea/CommentSearchAction.java +++ b/jadx-gui/src/main/java/jadx/gui/ui/codearea/CommentSearchAction.java @@ -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 diff --git a/jadx-gui/src/main/java/jadx/gui/ui/codearea/FindUsageAction.java b/jadx-gui/src/main/java/jadx/gui/ui/codearea/FindUsageAction.java index 176b63c1..7a13b24f 100644 --- a/jadx-gui/src/main/java/jadx/gui/ui/codearea/FindUsageAction.java +++ b/jadx-gui/src/main/java/jadx/gui/ui/codearea/FindUsageAction.java @@ -1,51 +1,24 @@ 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 { +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); } } diff --git a/jadx-gui/src/main/java/jadx/gui/ui/codearea/FridaAction.java b/jadx-gui/src/main/java/jadx/gui/ui/codearea/FridaAction.java index fac5c2dc..d7ffdd87 100644 --- a/jadx-gui/src/main/java/jadx/gui/ui/codearea/FridaAction.java +++ b/jadx-gui/src/main/java/jadx/gui/ui/codearea/FridaAction.java @@ -1,13 +1,13 @@ 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 { +public final class FridaAction extends JNodeAction { private static final Logger LOG = LoggerFactory.getLogger(FridaAction.class); private static final long serialVersionUID = -3084073927621269039L; - private final Map 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 { 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 { + "};", 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 { } return parsedArgType.append("'").toString(); } - - @Nullable - @Override - public JNode getNodeByOffset(int offset) { - return codeArea.getJNodeAtOffset(offset); - } } diff --git a/jadx-gui/src/main/java/jadx/gui/ui/codearea/GoToDeclarationAction.java b/jadx-gui/src/main/java/jadx/gui/ui/codearea/GoToDeclarationAction.java index 4198bb21..d76f7bcd 100644 --- a/jadx-gui/src/main/java/jadx/gui/ui/codearea/GoToDeclarationAction.java +++ b/jadx-gui/src/main/java/jadx/gui/ui/codearea/GoToDeclarationAction.java @@ -1,49 +1,44 @@ 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 { +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); + } } } diff --git a/jadx-gui/src/main/java/jadx/gui/ui/codearea/JNodeAction.java b/jadx-gui/src/main/java/jadx/gui/ui/codearea/JNodeAction.java new file mode 100644 index 00000000..8d686fe8 --- /dev/null +++ b/jadx-gui/src/main/java/jadx/gui/ui/codearea/JNodeAction.java @@ -0,0 +1,58 @@ +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; + } +} diff --git a/jadx-gui/src/main/java/jadx/gui/ui/codearea/JNodeMenuAction.java b/jadx-gui/src/main/java/jadx/gui/ui/codearea/JNodeMenuAction.java deleted file mode 100644 index 2c83b360..00000000 --- a/jadx-gui/src/main/java/jadx/gui/ui/codearea/JNodeMenuAction.java +++ /dev/null @@ -1,56 +0,0 @@ -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 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 - } -} diff --git a/jadx-gui/src/main/java/jadx/gui/ui/codearea/JNodePopupBuilder.java b/jadx-gui/src/main/java/jadx/gui/ui/codearea/JNodePopupBuilder.java new file mode 100644 index 00000000..7ec572b9 --- /dev/null +++ b/jadx-gui/src/main/java/jadx/gui/ui/codearea/JNodePopupBuilder.java @@ -0,0 +1,36 @@ +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; + } +} diff --git a/jadx-gui/src/main/java/jadx/gui/ui/codearea/JNodePopupListener.java b/jadx-gui/src/main/java/jadx/gui/ui/codearea/JNodePopupListener.java new file mode 100644 index 00000000..8fb929e4 --- /dev/null +++ b/jadx-gui/src/main/java/jadx/gui/ui/codearea/JNodePopupListener.java @@ -0,0 +1,28 @@ +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 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); + } +} diff --git a/jadx-gui/src/main/java/jadx/gui/ui/codearea/RenameAction.java b/jadx-gui/src/main/java/jadx/gui/ui/codearea/RenameAction.java index 64813764..ae3c573e 100644 --- a/jadx-gui/src/main/java/jadx/gui/ui/codearea/RenameAction.java +++ b/jadx-gui/src/main/java/jadx/gui/ui/codearea/RenameAction.java @@ -1,72 +1,27 @@ 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 { +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); } } diff --git a/jadx-gui/src/main/java/jadx/gui/ui/codearea/XposedAction.java b/jadx-gui/src/main/java/jadx/gui/ui/codearea/XposedAction.java index f98d4948..6a9e304d 100644 --- a/jadx-gui/src/main/java/jadx/gui/ui/codearea/XposedAction.java +++ b/jadx-gui/src/main/java/jadx/gui/ui/codearea/XposedAction.java @@ -1,15 +1,11 @@ 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 { +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 { + "Class %sClass=classLoader.loadClass(\"%s\");", shortClassName, rawClassName); } - - @Nullable - @Override - public JNode getNodeByOffset(int offset) { - return codeArea.getJNodeAtOffset(offset); - } } diff --git a/jadx-gui/src/main/java/jadx/gui/utils/UiUtils.java b/jadx-gui/src/main/java/jadx/gui/utils/UiUtils.java index f0ca4396..704f35e6 100644 --- a/jadx-gui/src/main/java/jadx/gui/utils/UiUtils.java +++ b/jadx-gui/src/main/java/jadx/gui/utils/UiUtils.java @@ -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); diff --git a/jadx-gui/src/main/resources/i18n/Messages_de_DE.properties b/jadx-gui/src/main/resources/i18n/Messages_de_DE.properties index 86f9d2de..a8f71f12 100644 --- a/jadx-gui/src/main/resources/i18n/Messages_de_DE.properties +++ b/jadx-gui/src/main/resources/i18n/Messages_de_DE.properties @@ -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 diff --git a/jadx-gui/src/main/resources/i18n/Messages_en_US.properties b/jadx-gui/src/main/resources/i18n/Messages_en_US.properties index a3630230..5df00d9e 100644 --- a/jadx-gui/src/main/resources/i18n/Messages_en_US.properties +++ b/jadx-gui/src/main/resources/i18n/Messages_en_US.properties @@ -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 diff --git a/jadx-gui/src/main/resources/i18n/Messages_es_ES.properties b/jadx-gui/src/main/resources/i18n/Messages_es_ES.properties index 2bf28b28..98d05756 100644 --- a/jadx-gui/src/main/resources/i18n/Messages_es_ES.properties +++ b/jadx-gui/src/main/resources/i18n/Messages_es_ES.properties @@ -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= diff --git a/jadx-gui/src/main/resources/i18n/Messages_ko_KR.properties b/jadx-gui/src/main/resources/i18n/Messages_ko_KR.properties index 53b95983..09f3b59a 100644 --- a/jadx-gui/src/main/resources/i18n/Messages_ko_KR.properties +++ b/jadx-gui/src/main/resources/i18n/Messages_ko_KR.properties @@ -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 보이기 diff --git a/jadx-gui/src/main/resources/i18n/Messages_zh_CN.properties b/jadx-gui/src/main/resources/i18n/Messages_zh_CN.properties index b14cdbfe..baef645e 100644 --- a/jadx-gui/src/main/resources/i18n/Messages_zh_CN.properties +++ b/jadx-gui/src/main/resources/i18n/Messages_zh_CN.properties @@ -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字节码 diff --git a/jadx-gui/src/main/resources/i18n/Messages_zh_TW.properties b/jadx-gui/src/main/resources/i18n/Messages_zh_TW.properties index 4b399379..ffb0f821 100644 --- a/jadx-gui/src/main/resources/i18n/Messages_zh_TW.properties +++ b/jadx-gui/src/main/resources/i18n/Messages_zh_TW.properties @@ -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 位元組碼 -- GitLab