From 84fe4712e92ee6bcc62b27eafaf1e484cd949077 Mon Sep 17 00:00:00 2001 From: MrIkso Date: Fri, 27 Dec 2024 01:58:50 +0200 Subject: [PATCH 1/2] feat(gui):improve search and usage dialog --- .../java/jadx/gui/search/ISearchMethod.java | 28 +- .../jadx/gui/search/MatchingPositions.java | 50 ++ .../java/jadx/gui/search/SearchManager.java | 39 ++ .../java/jadx/gui/search/SearchSettings.java | 28 +- .../search/providers/BaseSearchProvider.java | 5 +- .../search/providers/ClassSearchProvider.java | 31 +- .../search/providers/CodeSearchProvider.java | 15 +- .../providers/CommentSearchProvider.java | 134 +---- .../search/providers/FieldSearchProvider.java | 26 +- .../providers/MethodSearchProvider.java | 31 +- .../providers/ResourceSearchProvider.java | 16 +- .../jadx/gui/treemodel/CodeCommentNode.java | 52 ++ .../java/jadx/gui/treemodel/CodeNode.java | 8 +- .../main/java/jadx/gui/treemodel/JNode.java | 36 ++ .../jadx/gui/treemodel/JResSearchNode.java | 5 +- .../jadx/gui/treemodel/RefCommentNode.java | 93 ++++ .../jadx/gui/treemodel/SearchResultNode.java | 46 ++ .../gui/treemodel/SearchTreeCellRenderer.java | 28 + .../jadx/gui/treemodel/TreeCellRenderer.java | 31 ++ .../src/main/java/jadx/gui/ui/MainWindow.java | 42 +- .../gui/ui/dialog/CommonSearchDialog.java | 508 ++++++++++-------- .../java/jadx/gui/ui/dialog/SearchDialog.java | 53 +- .../java/jadx/gui/ui/dialog/UsageDialog.java | 28 +- .../java/jadx/gui/ui/tab/TabsController.java | 3 + .../main/java/jadx/gui/utils/TreeUtils.java | 83 +++ .../src/main/java/jadx/gui/utils/UiUtils.java | 26 + .../jadx/gui/utils/pkgs/PackageHelper.java | 6 +- 27 files changed, 937 insertions(+), 514 deletions(-) create mode 100644 jadx-gui/src/main/java/jadx/gui/search/MatchingPositions.java create mode 100644 jadx-gui/src/main/java/jadx/gui/search/SearchManager.java create mode 100644 jadx-gui/src/main/java/jadx/gui/treemodel/CodeCommentNode.java create mode 100644 jadx-gui/src/main/java/jadx/gui/treemodel/RefCommentNode.java create mode 100644 jadx-gui/src/main/java/jadx/gui/treemodel/SearchResultNode.java create mode 100644 jadx-gui/src/main/java/jadx/gui/treemodel/SearchTreeCellRenderer.java create mode 100644 jadx-gui/src/main/java/jadx/gui/treemodel/TreeCellRenderer.java create mode 100644 jadx-gui/src/main/java/jadx/gui/utils/TreeUtils.java diff --git a/jadx-gui/src/main/java/jadx/gui/search/ISearchMethod.java b/jadx-gui/src/main/java/jadx/gui/search/ISearchMethod.java index a446a69014b..4e4644bb1a2 100644 --- a/jadx-gui/src/main/java/jadx/gui/search/ISearchMethod.java +++ b/jadx-gui/src/main/java/jadx/gui/search/ISearchMethod.java @@ -3,25 +3,19 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; -import org.apache.commons.lang3.StringUtils; - public interface ISearchMethod { - int find(String input, String subStr, int start); + MatchingPositions find(String input, int start); static ISearchMethod build(SearchSettings searchSettings) { - if (searchSettings.isUseRegex()) { - Pattern pattern = searchSettings.getPattern(); - return (input, subStr, start) -> { - Matcher matcher = pattern.matcher(input); - if (matcher.find(start)) { - return matcher.start(); - } - return -1; - }; - } - if (searchSettings.isIgnoreCase()) { - return StringUtils::indexOfIgnoreCase; - } - return String::indexOf; + Pattern pattern = searchSettings.getPattern(); + return (input, start) -> { + Matcher matcher = pattern.matcher(input); + if (matcher.find(start)) { + int startIndex = matcher.start(); + int endIndex = matcher.end(); + return new MatchingPositions(/* lineText, */startIndex, endIndex); + } + return null; + }; } } diff --git a/jadx-gui/src/main/java/jadx/gui/search/MatchingPositions.java b/jadx-gui/src/main/java/jadx/gui/search/MatchingPositions.java new file mode 100644 index 00000000000..0d76ce57817 --- /dev/null +++ b/jadx-gui/src/main/java/jadx/gui/search/MatchingPositions.java @@ -0,0 +1,50 @@ +package jadx.gui.search; + +public class MatchingPositions { + private final String line; + private int startMath; + private int endMath; + + private int lineNumber; + + public MatchingPositions(String line) { + this.line = line; + } + + public MatchingPositions(String line, int lineNumber, int startMath, int endMath) { + this.line = line; + this.lineNumber = lineNumber; + this.startMath = startMath; + this.endMath = endMath; + } + + public MatchingPositions(String line, int startMath, int endMath) { + this.line = line; + this.lineNumber = -1; + this.startMath = startMath; + this.endMath = endMath; + } + + public MatchingPositions(int startMath, int endMath) { + this.line = null; + this.lineNumber = -1; + this.startMath = startMath; + this.endMath = endMath; + } + + public int getLineNumber() { + return lineNumber; + } + + public String getLine() { + return line; + } + + public int getEndMath() { + return endMath; + } + + public int getStartMath() { + return startMath; + } +} diff --git a/jadx-gui/src/main/java/jadx/gui/search/SearchManager.java b/jadx-gui/src/main/java/jadx/gui/search/SearchManager.java new file mode 100644 index 00000000000..cc060fa1d28 --- /dev/null +++ b/jadx-gui/src/main/java/jadx/gui/search/SearchManager.java @@ -0,0 +1,39 @@ +package jadx.gui.search; + +import java.util.regex.Pattern; + +public class SearchManager { + /** + * Generate the good pattern according to the different boolean + * + * @param exp the searched expression + * @param caseSensitive boolean + * @param wholeWord boolean + * @param useRegexp boolean + * @return the pattern + */ + public static Pattern generatePattern(String exp, boolean caseSensitive, boolean wholeWord, boolean useRegexp) { + String word = exp; + if (word != null && !word.isEmpty()) { + if (!useRegexp) { + word = word.replace("\\E", "\\E\\\\E\\Q"); + word = "\\Q" + word + "\\E"; + if (wholeWord && exp.matches("\\b.*\\b")) { + word = "\\b" + word + "\\b"; + } + } + + if (!caseSensitive) { + word = "(?i)" + word; + } + + if (useRegexp) { + word = "(?m)" + word; + } + + return Pattern.compile(word); + } else { + return Pattern.compile(""); + } + } +} diff --git a/jadx-gui/src/main/java/jadx/gui/search/SearchSettings.java b/jadx-gui/src/main/java/jadx/gui/search/SearchSettings.java index a5574ec78d1..a7a19fbe754 100644 --- a/jadx-gui/src/main/java/jadx/gui/search/SearchSettings.java +++ b/jadx-gui/src/main/java/jadx/gui/search/SearchSettings.java @@ -13,6 +13,7 @@ public class SearchSettings { private final String searchString; private final boolean useRegex; private final boolean ignoreCase; + private final boolean wholeWord; private final JavaPackage searchPackage; private JClass activeCls; @@ -20,37 +21,28 @@ public class SearchSettings { private Pattern regexPattern; private ISearchMethod searchMethod; - public SearchSettings(String searchString, boolean ignoreCase, boolean useRegex, JavaPackage searchPackage) { + public SearchSettings(String searchString, boolean ignoreCase, boolean wholeWord, boolean useRegex, JavaPackage searchPackage) { this.searchString = searchString; - this.useRegex = useRegex; this.ignoreCase = ignoreCase; + this.wholeWord = wholeWord; + this.useRegex = useRegex; this.searchPackage = searchPackage; } @Nullable public String prepare() { - if (useRegex) { - try { - int flags = ignoreCase ? Pattern.CASE_INSENSITIVE : 0; - this.regexPattern = Pattern.compile(searchString, flags); - } catch (Exception e) { - return "Invalid Regex: " + e.getMessage(); - } + try { + this.regexPattern = SearchManager.generatePattern(searchString, ignoreCase, wholeWord, useRegex); + } catch (Exception e) { + return "Invalid Regex: " + e.getMessage(); } + searchMethod = ISearchMethod.build(this); return null; } public boolean isMatch(String searchArea) { - return searchMethod.find(searchArea, this.searchString, 0) != -1; - } - - public boolean isUseRegex() { - return this.useRegex; - } - - public boolean isIgnoreCase() { - return this.ignoreCase; + return searchMethod.find(searchArea, 0) != null; } public JavaPackage getSearchPackage() { diff --git a/jadx-gui/src/main/java/jadx/gui/search/providers/BaseSearchProvider.java b/jadx-gui/src/main/java/jadx/gui/search/providers/BaseSearchProvider.java index 74436af2676..282acca589e 100644 --- a/jadx-gui/src/main/java/jadx/gui/search/providers/BaseSearchProvider.java +++ b/jadx-gui/src/main/java/jadx/gui/search/providers/BaseSearchProvider.java @@ -10,6 +10,7 @@ import jadx.core.dex.nodes.ICodeNode; import jadx.gui.search.ISearchMethod; import jadx.gui.search.ISearchProvider; +import jadx.gui.search.MatchingPositions; import jadx.gui.search.SearchSettings; import jadx.gui.treemodel.JClass; import jadx.gui.treemodel.JNode; @@ -41,8 +42,8 @@ public BaseSearchProvider(MainWindow mw, SearchSettings searchSettings, List ann instanceof InsnCodeOffset && ((InsnCodeOffset) ann).getOffset() == offset - ? new JumpPosition(node, pos) - : null); - if (jump != null) { - return jump; - } - return new JumpPosition(node); - } - } - - private static class RefCommentNode extends JNode { - private static final long serialVersionUID = 3887992236082515752L; - - protected final JNode node; - protected final String comment; - - public RefCommentNode(JNode node, String comment) { - this.node = node; - this.comment = comment; - } - - @Override - public JClass getRootClass() { - return node.getRootClass(); - } - - @Override - public JavaNode getJavaNode() { - return node.getJavaNode(); - } - - @Override - public JClass getJParent() { - return node.getJParent(); - } - - @Override - public Icon getIcon() { - return node.getIcon(); - } - - @Override - public String getSyntaxName() { - return SyntaxConstants.SYNTAX_STYLE_NONE; // comment is always plain text - } - - @Override - public String makeString() { - return node.makeString(); - } - - @Override - public String makeLongString() { - return node.makeLongString(); - } - - @Override - public String makeStringHtml() { - return node.makeStringHtml(); - } - - @Override - public String makeLongStringHtml() { - return node.makeLongStringHtml(); - } - - @Override - public boolean disableHtml() { - return node.disableHtml(); - } - - @Override - public int getPos() { - return node.getPos(); - } - - @Override - public String getTooltip() { - return node.getTooltip(); - } - - @Override - public String makeDescString() { - return comment; - } - - @Override - public boolean hasDescString() { - return true; - } - } - @Override public int progress() { return progress; diff --git a/jadx-gui/src/main/java/jadx/gui/search/providers/FieldSearchProvider.java b/jadx-gui/src/main/java/jadx/gui/search/providers/FieldSearchProvider.java index b41b5c4c008..b46bf4a10bd 100644 --- a/jadx-gui/src/main/java/jadx/gui/search/providers/FieldSearchProvider.java +++ b/jadx-gui/src/main/java/jadx/gui/search/providers/FieldSearchProvider.java @@ -8,6 +8,7 @@ import jadx.core.dex.info.FieldInfo; import jadx.core.dex.nodes.FieldNode; import jadx.gui.jobs.Cancelable; +import jadx.gui.search.MatchingPositions; import jadx.gui.search.SearchSettings; import jadx.gui.treemodel.JNode; import jadx.gui.ui.MainWindow; @@ -31,8 +32,14 @@ public FieldSearchProvider(MainWindow mw, SearchSettings searchSettings, List fields = cls.getClassNode().getFields(); if (fldNum < fields.size()) { FieldNode fld = fields.get(fldNum++); - if (checkField(fld.getFieldInfo())) { - return convert(fld); + MatchingPositions matchingPositions = checkField(fld.getFieldInfo()); + if (matchingPositions != null) { + JNode node = convert(fld); + MatchingPositions highlightPositions = isMatch(node.makeLongString()); + node.setHasHighlight(true); + node.setStart(highlightPositions.getStartMath()); + node.setEnd(highlightPositions.getEndMath()); + return node; } } else { clsNum++; @@ -44,10 +51,17 @@ public FieldSearchProvider(MainWindow mw, SearchSettings searchSettings, List methods = cls.getClassNode().getMethods(); if (mthNum < methods.size()) { MethodNode mth = methods.get(mthNum++); - if (checkMth(mth.getMethodInfo())) { - return convert(mth); + MatchingPositions matchingPositions = checkMth(mth.getMethodInfo()); + if (matchingPositions != null) { + JNode node = convert(mth); + MatchingPositions highlightPositions = isMatch(node.makeLongString()); + node.setHasHighlight(true); + node.setStart(highlightPositions.getStartMath()); + node.setEnd(highlightPositions.getEndMath()); + return node; } } else { clsNum++; @@ -44,11 +51,21 @@ public MethodSearchProvider(MainWindow mw, SearchSettings searchSettings, List ann instanceof InsnCodeOffset && ((InsnCodeOffset) ann).getOffset() == offset + ? new JumpPosition(node, pos) + : null); + if (jump != null) { + return jump; + } + return new JumpPosition(node); + } +} diff --git a/jadx-gui/src/main/java/jadx/gui/treemodel/CodeNode.java b/jadx-gui/src/main/java/jadx/gui/treemodel/CodeNode.java index 2ae107eca2f..da00c263d33 100644 --- a/jadx-gui/src/main/java/jadx/gui/treemodel/CodeNode.java +++ b/jadx-gui/src/main/java/jadx/gui/treemodel/CodeNode.java @@ -14,11 +14,17 @@ public class CodeNode extends JNode { private final transient String line; private final transient int pos; - public CodeNode(JClass rootCls, JNode jNode, String lineStr, int pos) { + public CodeNode(JClass rootCls, JNode jNode, String lineStr, int pos, int start, int end) { this.rootCls = rootCls; this.jNode = jNode; this.line = lineStr; this.pos = pos; + this.start = start; + this.end = end; + } + + public JNode getjNode() { + return jNode; } @Override diff --git a/jadx-gui/src/main/java/jadx/gui/treemodel/JNode.java b/jadx-gui/src/main/java/jadx/gui/treemodel/JNode.java index 88f7b18c984..d2b40f39a4d 100644 --- a/jadx-gui/src/main/java/jadx/gui/treemodel/JNode.java +++ b/jadx-gui/src/main/java/jadx/gui/treemodel/JNode.java @@ -1,5 +1,6 @@ package jadx.gui.treemodel; +import java.util.Arrays; import java.util.Comparator; import java.util.Enumeration; import java.util.function.Predicate; @@ -18,6 +19,8 @@ import jadx.gui.ui.MainWindow; import jadx.gui.ui.panel.ContentPanel; import jadx.gui.ui.tab.TabbedPane; +import jadx.gui.utils.TreeUtils; +import jadx.gui.utils.UiUtils; public abstract class JNode extends DefaultMutableTreeNode implements Comparable { @@ -25,6 +28,10 @@ public abstract class JNode extends DefaultMutableTreeNode implements Comparable public abstract JClass getJParent(); + protected int start = 0; + protected int end = 0; + protected boolean hasHighlight; + /** * Return top level JClass or self if already at top. */ @@ -81,6 +88,11 @@ public String makeStringHtml() { return makeString(); } + public String makeHighlightHtml() { + return TreeUtils.highlightString(makeLongString(), String.format("background:%s", UiUtils.getThemeAccentColor()), + Arrays.asList(start, end)); + } + public String makeDescString() { return null; } @@ -152,4 +164,28 @@ public int compareTo(@NotNull JNode other) { public String toString() { return makeString(); } + + public void setStart(int start) { + this.start = start; + } + + public int getStart() { + return start; + } + + public void setEnd(int end) { + this.end = end; + } + + public int getEnd() { + return end; + } + + public boolean isHasHighlight() { + return hasHighlight; + } + + public void setHasHighlight(boolean hasHighlight) { + this.hasHighlight = hasHighlight; + } } diff --git a/jadx-gui/src/main/java/jadx/gui/treemodel/JResSearchNode.java b/jadx-gui/src/main/java/jadx/gui/treemodel/JResSearchNode.java index 13a849457ac..487c3da38ca 100644 --- a/jadx-gui/src/main/java/jadx/gui/treemodel/JResSearchNode.java +++ b/jadx-gui/src/main/java/jadx/gui/treemodel/JResSearchNode.java @@ -10,10 +10,13 @@ public class JResSearchNode extends JNode { private final transient String text; private final transient int pos; - public JResSearchNode(JResource resNode, String text, int pos) { + public JResSearchNode(JResource resNode, String text, int pos, int start, int end) { this.pos = pos; this.text = text; this.resNode = resNode; + this.start = start; + this.end = end; + this.hasHighlight = true; } public JResource getResNode() { diff --git a/jadx-gui/src/main/java/jadx/gui/treemodel/RefCommentNode.java b/jadx-gui/src/main/java/jadx/gui/treemodel/RefCommentNode.java new file mode 100644 index 00000000000..4884e39028f --- /dev/null +++ b/jadx-gui/src/main/java/jadx/gui/treemodel/RefCommentNode.java @@ -0,0 +1,93 @@ +package jadx.gui.treemodel; + +import javax.swing.Icon; + +import org.fife.ui.rsyntaxtextarea.SyntaxConstants; + +import jadx.api.JavaNode; + +public class RefCommentNode extends JNode { + private static final long serialVersionUID = 3887992236082515752L; + + protected final JNode node; + protected final String comment; + + public RefCommentNode(JNode node, String comment) { + this.node = node; + this.comment = comment; + } + + public JNode getNode() { + return node; + } + + @Override + public JClass getRootClass() { + return node.getRootClass(); + } + + @Override + public JavaNode getJavaNode() { + return node.getJavaNode(); + } + + @Override + public JClass getJParent() { + return node.getJParent(); + } + + @Override + public Icon getIcon() { + return node.getIcon(); + } + + @Override + public String getSyntaxName() { + return SyntaxConstants.SYNTAX_STYLE_NONE; // comment is always plain text + } + + @Override + public String makeString() { + return node.makeString(); + } + + @Override + public String makeLongString() { + return node.makeLongString(); + } + + @Override + public String makeStringHtml() { + return node.makeStringHtml(); + } + + @Override + public String makeLongStringHtml() { + return node.makeLongStringHtml(); + } + + @Override + public boolean disableHtml() { + return node.disableHtml(); + } + + @Override + public int getPos() { + return node.getPos(); + } + + @Override + public String getTooltip() { + return node.getTooltip(); + } + + @Override + public String makeDescString() { + return comment; + } + + @Override + public boolean hasDescString() { + return true; + } +} diff --git a/jadx-gui/src/main/java/jadx/gui/treemodel/SearchResultNode.java b/jadx-gui/src/main/java/jadx/gui/treemodel/SearchResultNode.java new file mode 100644 index 00000000000..c1eacf9938b --- /dev/null +++ b/jadx-gui/src/main/java/jadx/gui/treemodel/SearchResultNode.java @@ -0,0 +1,46 @@ +package jadx.gui.treemodel; + +import javax.swing.Icon; + +public class SearchResultNode extends JNode { + + private final String label; + private final JNode realNode; + + public SearchResultNode(String str, JNode realNode, int start, int end) { + this.label = str; + this.realNode = realNode; + this.start = start; + this.end = end; + this.hasHighlight = true; + } + + @Override + public JClass getJParent() { + return null; + } + + @Override + public Icon getIcon() { + return null; + } + + @Override + public String makeString() { + return label; + } + + @Override + public String makeLongString() { + return makeString(); + } + + public JNode getRealNode() { + return realNode; + } + + @Override + public boolean disableHtml() { + return false; + } +} diff --git a/jadx-gui/src/main/java/jadx/gui/treemodel/SearchTreeCellRenderer.java b/jadx-gui/src/main/java/jadx/gui/treemodel/SearchTreeCellRenderer.java new file mode 100644 index 00000000000..5429d2fcfb6 --- /dev/null +++ b/jadx-gui/src/main/java/jadx/gui/treemodel/SearchTreeCellRenderer.java @@ -0,0 +1,28 @@ +package jadx.gui.treemodel; + +import java.awt.*; + +import javax.swing.*; +import javax.swing.tree.DefaultTreeCellRenderer; + +public class SearchTreeCellRenderer extends DefaultTreeCellRenderer { + + @Override + public Component getTreeCellRendererComponent(JTree tree, + Object value, boolean selected, boolean expanded, + boolean isLeaf, int row, boolean focused) { + Component c = super.getTreeCellRendererComponent(tree, value, selected, expanded, isLeaf, row, focused); + if (value instanceof JNode) { + JNode jNode = (JNode) value; + setText(jNode.isHasHighlight() ? jNode.makeHighlightHtml() : jNode.makeLongStringHtml()); + setIcon(jNode.getIcon()); + setToolTipText(jNode.getTooltip()); + } else { + setToolTipText(null); + } + if (value instanceof JPackage) { + setEnabled(((JPackage) value).isEnabled()); + } + return c; + } +} diff --git a/jadx-gui/src/main/java/jadx/gui/treemodel/TreeCellRenderer.java b/jadx-gui/src/main/java/jadx/gui/treemodel/TreeCellRenderer.java new file mode 100644 index 00000000000..40e5b5931a9 --- /dev/null +++ b/jadx-gui/src/main/java/jadx/gui/treemodel/TreeCellRenderer.java @@ -0,0 +1,31 @@ +package jadx.gui.treemodel; + +import java.awt.Component; + +import javax.swing.JTree; +import javax.swing.tree.DefaultTreeCellRenderer; + +import jadx.gui.utils.ui.NodeLabel; + +public class TreeCellRenderer extends DefaultTreeCellRenderer { + + @Override + public Component getTreeCellRendererComponent(JTree tree, + Object value, boolean selected, boolean expanded, + boolean isLeaf, int row, boolean focused) { + Component c = super.getTreeCellRendererComponent(tree, value, selected, expanded, isLeaf, row, focused); + if (value instanceof JNode) { + JNode jNode = (JNode) value; + NodeLabel.disableHtml(this, jNode.disableHtml()); + setText(jNode.makeStringHtml()); + setIcon(jNode.getIcon()); + setToolTipText(jNode.getTooltip()); + } else { + setToolTipText(null); + } + if (value instanceof JPackage) { + setEnabled(((JPackage) value).isEnabled()); + } + return c; + } +} diff --git a/jadx-gui/src/main/java/jadx/gui/ui/MainWindow.java b/jadx-gui/src/main/java/jadx/gui/ui/MainWindow.java index 3d04976e40e..8d998afea61 100644 --- a/jadx-gui/src/main/java/jadx/gui/ui/MainWindow.java +++ b/jadx-gui/src/main/java/jadx/gui/ui/MainWindow.java @@ -1,7 +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; @@ -62,7 +61,6 @@ import javax.swing.event.TreeExpansionEvent; import javax.swing.event.TreeWillExpandListener; import javax.swing.tree.DefaultMutableTreeNode; -import javax.swing.tree.DefaultTreeCellRenderer; import javax.swing.tree.DefaultTreeModel; import javax.swing.tree.TreeNode; import javax.swing.tree.TreePath; @@ -117,9 +115,9 @@ import jadx.gui.treemodel.JClass; import jadx.gui.treemodel.JLoadableNode; import jadx.gui.treemodel.JNode; -import jadx.gui.treemodel.JPackage; import jadx.gui.treemodel.JResource; import jadx.gui.treemodel.JRoot; +import jadx.gui.treemodel.TreeCellRenderer; import jadx.gui.ui.action.ActionModel; import jadx.gui.ui.action.JadxGuiAction; import jadx.gui.ui.codearea.AbstractCodeArea; @@ -157,12 +155,12 @@ import jadx.gui.utils.LafManager; import jadx.gui.utils.Link; import jadx.gui.utils.NLS; +import jadx.gui.utils.TreeUtils; import jadx.gui.utils.UiUtils; import jadx.gui.utils.dbg.UIWatchDog; import jadx.gui.utils.fileswatcher.LiveReloadWorker; import jadx.gui.utils.shortcut.ShortcutsController; import jadx.gui.utils.ui.ActionHandler; -import jadx.gui.utils.ui.NodeLabel; public class MainWindow extends JFrame implements ExportProjectDialog.ExportProjectDialogListener { private static final Logger LOG = LoggerFactory.getLogger(MainWindow.class); @@ -925,7 +923,7 @@ private boolean nodeClickAction(@Nullable Object obj) { } private void treeRightClickAction(MouseEvent e) { - JNode node = getJNodeUnderMouse(e); + JNode node = TreeUtils.getJNodeUnderMouse(tree, e); if (node == null) { return; } @@ -935,16 +933,6 @@ private void treeRightClickAction(MouseEvent e) { } } - @Nullable - private JNode getJNodeUnderMouse(MouseEvent mouseEvent) { - TreeNode treeNode = UiUtils.getTreeNodeUnderMouse(tree, mouseEvent); - if (treeNode instanceof JNode) { - return (JNode) treeNode; - } - - return null; - } - // TODO: extract tree component into new class public void selectNodeInTree(JNode node) { if (node.getParent() == null && treeRoot != null) { @@ -1327,7 +1315,7 @@ public void focusLost(FocusEvent e) { @Override public void mousePressed(MouseEvent e) { if (SwingUtilities.isLeftMouseButton(e)) { - if (!nodeClickAction(getJNodeUnderMouse(e))) { + if (!nodeClickAction(TreeUtils.getJNodeUnderMouse(tree, e))) { // click ignored -> switch to focusable mode tree.setFocusable(true); tree.requestFocus(); @@ -1345,27 +1333,7 @@ public void keyPressed(KeyEvent e) { } } }); - tree.setCellRenderer(new DefaultTreeCellRenderer() { - @Override - public Component getTreeCellRendererComponent(JTree tree, - Object value, boolean selected, boolean expanded, - boolean isLeaf, int row, boolean focused) { - Component c = super.getTreeCellRendererComponent(tree, value, selected, expanded, isLeaf, row, focused); - if (value instanceof JNode) { - JNode jNode = (JNode) value; - NodeLabel.disableHtml(this, jNode.disableHtml()); - setText(jNode.makeStringHtml()); - setIcon(jNode.getIcon()); - setToolTipText(jNode.getTooltip()); - } else { - setToolTipText(null); - } - if (value instanceof JPackage) { - setEnabled(((JPackage) value).isEnabled()); - } - return c; - } - }); + tree.setCellRenderer(new TreeCellRenderer()); tree.addTreeWillExpandListener(new TreeWillExpandListener() { @Override public void treeWillExpand(TreeExpansionEvent event) { diff --git a/jadx-gui/src/main/java/jadx/gui/ui/dialog/CommonSearchDialog.java b/jadx-gui/src/main/java/jadx/gui/ui/dialog/CommonSearchDialog.java index 8e0007d5d56..453ab544bf5 100644 --- a/jadx-gui/src/main/java/jadx/gui/ui/dialog/CommonSearchDialog.java +++ b/jadx-gui/src/main/java/jadx/gui/ui/dialog/CommonSearchDialog.java @@ -5,7 +5,6 @@ import java.awt.Component; import java.awt.Dimension; import java.awt.Font; -import java.awt.event.ActionEvent; import java.awt.event.KeyAdapter; import java.awt.event.KeyEvent; import java.awt.event.MouseAdapter; @@ -15,10 +14,10 @@ import java.util.ArrayList; import java.util.Collection; import java.util.Collections; -import java.util.Enumeration; +import java.util.Iterator; import java.util.List; +import java.util.Stack; -import javax.swing.AbstractAction; import javax.swing.BorderFactory; import javax.swing.Box; import javax.swing.BoxLayout; @@ -28,17 +27,11 @@ import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JScrollPane; -import javax.swing.JTable; -import javax.swing.ListSelectionModel; -import javax.swing.SwingConstants; +import javax.swing.JTree; import javax.swing.SwingUtilities; -import javax.swing.table.AbstractTableModel; -import javax.swing.table.TableCellRenderer; -import javax.swing.table.TableColumn; +import javax.swing.tree.DefaultTreeModel; +import javax.swing.tree.TreeNode; -import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea; -import org.fife.ui.rtextarea.SearchContext; -import org.fife.ui.rtextarea.SearchEngine; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.slf4j.Logger; @@ -46,19 +39,31 @@ import ch.qos.logback.classic.Level; +import jadx.api.ResourceType; import jadx.gui.logs.LogOptions; +import jadx.gui.treemodel.CodeCommentNode; +import jadx.gui.treemodel.CodeNode; +import jadx.gui.treemodel.JClass; +import jadx.gui.treemodel.JField; +import jadx.gui.treemodel.JMethod; import jadx.gui.treemodel.JNode; +import jadx.gui.treemodel.JPackage; import jadx.gui.treemodel.JResSearchNode; +import jadx.gui.treemodel.JResource; +import jadx.gui.treemodel.RefCommentNode; +import jadx.gui.treemodel.SearchResultNode; +import jadx.gui.treemodel.SearchTreeCellRenderer; +import jadx.gui.treemodel.TextNode; import jadx.gui.ui.MainWindow; -import jadx.gui.ui.codearea.AbstractCodeArea; import jadx.gui.ui.panel.ProgressPanel; import jadx.gui.ui.tab.TabsController; import jadx.gui.utils.CacheObject; import jadx.gui.utils.JNodeCache; import jadx.gui.utils.JumpPosition; import jadx.gui.utils.NLS; +import jadx.gui.utils.TreeUtils; import jadx.gui.utils.UiUtils; -import jadx.gui.utils.ui.NodeLabel; +import jadx.gui.utils.pkgs.PackageHelper; import static javax.swing.ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED; import static javax.swing.ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED; @@ -73,15 +78,13 @@ public abstract class CommonSearchDialog extends JFrame { protected final transient Font codeFont; protected final transient String windowTitle; - protected ResultsModel resultsModel; - protected ResultsTable resultsTable; + protected ResultsTree resultsTree; + protected JLabel resultsInfoLabel; protected JLabel progressInfoLabel; protected JLabel warnLabel; protected ProgressPanel progressPane; - private SearchContext highlightContext; - public CommonSearchDialog(MainWindow mainWindow, String title) { this.mainWindow = mainWindow; this.tabsController = mainWindow.getTabsController(); @@ -112,17 +115,8 @@ private void updateTitle(String searchText) { } } - public void updateHighlightContext(String text, boolean caseSensitive, boolean regexp, boolean wholeWord) { + public void updateHighlightContext(String text) { updateTitle(text); - highlightContext = new SearchContext(text); - highlightContext.setMatchCase(caseSensitive); - highlightContext.setWholeWord(wholeWord); - highlightContext.setRegularExpression(regexp); - highlightContext.setMarkAll(true); - } - - public void disableHighlight() { - highlightContext = null; } protected void registerInitOnOpen() { @@ -134,12 +128,23 @@ public void windowOpened(WindowEvent e) { }); } - protected void openSelectedItem() { - JNode node = getSelectedNode(); - if (node == null) { - return; + protected void openSelectedItem(MouseEvent event) { + JNode node = getSelectedNode(event); + if (node instanceof SearchResultNode) { + SearchResultNode node1 = (SearchResultNode) node; + openItem(node1.getRealNode()); + } else { + openItem(node); + } + } + + protected void openSelectedItem(@Nullable Object node) { + if (node instanceof SearchResultNode) { + SearchResultNode node1 = (SearchResultNode) node; + openItem(node1.getRealNode()); + } else { + openItem((JNode) node); } - openItem(node); } protected void openItem(JNode node) { @@ -155,17 +160,8 @@ protected void openItem(JNode node) { } @Nullable - private JNode getSelectedNode() { - try { - int selectedId = resultsTable.getSelectedRow(); - if (selectedId == -1 || selectedId >= resultsTable.getRowCount()) { - return null; - } - return (JNode) resultsModel.getValueAt(selectedId, 0); - } catch (Exception e) { - LOG.error("Failed to get results table selected object", e); - return null; - } + private JNode getSelectedNode(MouseEvent event) { + return TreeUtils.getJNodeUnderMouse(resultsTree, event); } @Override @@ -184,9 +180,6 @@ protected JPanel initButtonsPanel() { JButton cancelButton = new JButton(NLS.str("search_dialog.cancel")); cancelButton.addActionListener(event -> dispose()); - JButton openBtn = new JButton(NLS.str("search_dialog.open")); - openBtn.addActionListener(event -> openSelectedItem()); - getRootPane().setDefaultButton(openBtn); JCheckBox cbKeepOpen = new JCheckBox(NLS.str("search_dialog.keep_open")); cbKeepOpen.setSelected(mainWindow.getSettings().getKeepCommonDialogOpen()); @@ -203,55 +196,30 @@ protected JPanel initButtonsPanel() { buttonPane.add(progressPane); buttonPane.add(Box.createRigidArea(new Dimension(5, 0))); buttonPane.add(Box.createHorizontalGlue()); - buttonPane.add(openBtn); - buttonPane.add(Box.createRigidArea(new Dimension(10, 0))); buttonPane.add(cancelButton); return buttonPane; } - protected JPanel initResultsTable() { - ResultsTableCellRenderer renderer = new ResultsTableCellRenderer(); - resultsModel = new ResultsModel(); - resultsModel.addTableModelListener(e -> updateProgressLabel(false)); - - resultsTable = new ResultsTable(resultsModel, renderer); - resultsTable.setShowHorizontalLines(false); - resultsTable.setDragEnabled(false); - resultsTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); - resultsTable.setColumnSelectionAllowed(false); - resultsTable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF); - resultsTable.setAutoscrolls(false); - - resultsTable.setDefaultRenderer(Object.class, renderer); - Enumeration columns = resultsTable.getColumnModel().getColumns(); - while (columns.hasMoreElements()) { - TableColumn column = columns.nextElement(); - column.setCellRenderer(renderer); - } + protected JPanel initResultsTree() { + resultsTree = new ResultsTree(); + resultsTree.setCellRenderer(new SearchTreeCellRenderer()); + resultsTree.setDragEnabled(false); + resultsTree.setAutoscrolls(false); + resultsTree.setRootVisible(false); - resultsTable.addMouseListener(new MouseAdapter() { + resultsTree.addMouseListener(new MouseAdapter() { @Override public void mouseClicked(MouseEvent evt) { - if (evt.getClickCount() == 2) { - openSelectedItem(); + if (evt.getClickCount() == 2 && SwingUtilities.isLeftMouseButton(evt)) { + openSelectedItem(evt); } } }); - resultsTable.addKeyListener(new KeyAdapter() { + resultsTree.addKeyListener(new KeyAdapter() { @Override public void keyPressed(KeyEvent e) { if (e.getKeyCode() == KeyEvent.VK_ENTER) { - openSelectedItem(); - } - } - }); - // override copy action to copy long string of node column - resultsTable.getActionMap().put("copy", new AbstractAction() { - @Override - public void actionPerformed(ActionEvent e) { - JNode selectedNode = getSelectedNode(); - if (selectedNode != null) { - UiUtils.copyToClipboard(selectedNode.makeLongString()); + openSelectedItem(resultsTree.getLeadSelectionPath()); } } }); @@ -260,7 +228,7 @@ public void actionPerformed(ActionEvent e) { warnLabel.setForeground(Color.RED); warnLabel.setVisible(false); - JScrollPane scroll = new JScrollPane(resultsTable, VERTICAL_SCROLLBAR_AS_NEEDED, HORIZONTAL_SCROLLBAR_AS_NEEDED); + JScrollPane scroll = new JScrollPane(resultsTree, VERTICAL_SCROLLBAR_AS_NEEDED, HORIZONTAL_SCROLLBAR_AS_NEEDED); resultsInfoLabel = new JLabel(""); resultsInfoLabel.setFont(mainWindow.getSettings().getFont()); @@ -296,7 +264,7 @@ protected void addResultsActions(JPanel resultsActionsPanel) { } protected void updateProgressLabel(boolean complete) { - int count = resultsModel.getRowCount(); + int count = resultsTree.getFoundCount(); String statusText; if (complete) { statusText = NLS.str("search_dialog.results_complete", count); @@ -310,193 +278,275 @@ protected void showSearchState() { resultsInfoLabel.setText(NLS.str("search_dialog.tip_searching") + "..."); } - protected static final class ResultsTable extends JTable { - private static final long serialVersionUID = 3901184054736618969L; - private final transient ResultsModel model; + protected static final class ResultsTree extends JTree { + private final transient List rows = new ArrayList<>(); + private final JNode treeRoot; + private final DefaultTreeModel treeModel; + private transient int foundCount; + + public ResultsTree() { + setShowsRootHandles(true); + treeRoot = new TextNode(""); + treeModel = new DefaultTreeModel(treeRoot); + setModel(treeModel); + } - public ResultsTable(ResultsModel resultsModel, ResultsTableCellRenderer renderer) { - super(resultsModel); - this.model = resultsModel; - setRowHeight(renderer.getMaxRowHeight()); + public void updateTree() { + addNodes(); + treeModel.reload(); + TreeUtils.expandAllNodes(this); } - public void initColumnWidth() { - int columnCount = getColumnCount(); - int width = getParent().getWidth(); - int colWidth = model.isAddDescColumn() ? width / 2 : width; - columnModel.getColumn(0).setPreferredWidth(colWidth); - for (int col = 1; col < columnCount; col++) { - columnModel.getColumn(col).setPreferredWidth(width); - } + public void clear() { + foundCount = 0; + rows.clear(); + treeRoot.removeAllChildren(); + treeModel.reload(); } - public void updateTable() { - UiUtils.uiThreadGuard(); - int rowCount = getRowCount(); - if (rowCount == 0) { - updateUI(); + public void sort() { + Collections.sort(rows); + updateTree(); + } + + private void addNodes() { + if (rows.isEmpty()) { return; } - long start = System.currentTimeMillis(); - int width = getParent().getWidth(); - TableColumn firstColumn = columnModel.getColumn(0); - if (model.isAddDescColumn()) { - if (firstColumn.getWidth() > width * 0.8) { - // first column too big and hide second column, resize it - firstColumn.setPreferredWidth(width / 2); + for (JNode row : rows) { + if (row instanceof JMethod) { + treeRoot.add(row); } - TableColumn secondColumn = columnModel.getColumn(1); - int columnMaxWidth = width * 2; // set big enough size to skip per row check - if (secondColumn.getWidth() < columnMaxWidth) { - secondColumn.setPreferredWidth(columnMaxWidth); + if (row instanceof JField) { + treeRoot.add(row); } - } else { - firstColumn.setPreferredWidth(width); - } - updateUI(); - if (LOG.isDebugEnabled()) { - LOG.debug("Update results table in {}ms, count: {}", System.currentTimeMillis() - start, rowCount); - } - } - - @Override - public Object getValueAt(int row, int column) { - return model.getValueAt(row, column); - } - } + if (row instanceof JClass) { + JClass jClass = (JClass) row; + JPackage jPackage = findPackage(jClass); - protected static final class ResultsModel extends AbstractTableModel { - private static final long serialVersionUID = -7821286846923903208L; - private static final String[] COLUMN_NAMES = { NLS.str("search_dialog.col_node"), NLS.str("search_dialog.col_code") }; + // create new objects + JPackage existingPackage = findOrCreatePackage(jPackage); + JClass existingClass = findOrCreateClass(existingPackage, jClass); - private final transient List rows = new ArrayList<>(); - private transient boolean addDescColumn; - - public void addAll(Collection nodes) { - rows.addAll(nodes); - if (!addDescColumn) { - for (JNode row : rows) { - if (row.hasDescString()) { - addDescColumn = true; - break; + existingPackage.add(existingClass); + treeRoot.add(existingPackage); + } + if (row instanceof CodeNode) { + CodeNode node = (CodeNode) row; + processCodeNode(node, null); + } + if (row instanceof JResSearchNode) { + JResSearchNode jResSearchNode = (JResSearchNode) row; + JResource jResource = jResSearchNode.getResNode(); + // check if file node from resource table + JResource tableRes = isTableResource(jResource); + // file node + JResource newJResource = findOrCreateJResource(jResource); + SearchResultNode textNode = + new SearchResultNode(jResSearchNode.makeDescString(), row, jResSearchNode.getStart(), jResSearchNode.getEnd()); + + if (tableRes != null) { + newJResource = findOrCreateJResource(tableRes); + JResource subRes = findOrCreateSubJResource(jResource, newJResource); + subRes.add(textNode); + newJResource.add(subRes); + } else { + newJResource.add(textNode); } + treeRoot.add(newJResource); + } + if (row instanceof CodeCommentNode | row instanceof RefCommentNode) { + processCodeNode(null, (RefCommentNode) row); } } } - public void clear() { - addDescColumn = false; - rows.clear(); + public int getFoundCount() { + return foundCount; } - public void sort() { - Collections.sort(rows); + public void addAll(Collection nodes) { + foundCount += nodes.size(); + rows.clear(); + rows.addAll(nodes); } - public boolean isAddDescColumn() { - return addDescColumn; - } + private void processCodeNode(CodeNode codeNode, RefCommentNode codeCommentNode) { + JClass jClass = null; + JPackage jPackage = null; + JNode jMethod = null; + JNode addedNode = null; + if (codeNode != null) { + jClass = codeNode.getRootClass(); + jPackage = (JPackage) jClass.getParent(); + jMethod = codeNode.getjNode(); + addedNode = new SearchResultNode(codeNode.makeDescString(), codeNode, codeNode.getStart(), codeNode.getEnd()); + } else if (codeCommentNode != null) { + addedNode = new SearchResultNode(codeCommentNode.makeDescString(), codeCommentNode, 0, 0); + jMethod = codeCommentNode.getNode(); + if (jMethod instanceof JClass) { + jClass = (JClass) jMethod; + } else { + jClass = jMethod.getJParent(); + } + jPackage = PackageHelper.buildJPackage(codeCommentNode.getRootClass().getCls().getJavaPackage(), false); + } + JNode parentClass = jMethod.getJParent(); + // create new objects + JPackage existingPackage = findOrCreatePackage(jPackage); + JClass existingClass = findOrCreateClass(existingPackage, jClass); + + Stack stack = new Stack<>(); + if (jMethod instanceof JClass) { + stack.push(jMethod); + } + while (parentClass != null) { + stack.push(parentClass); + parentClass = parentClass.getJParent(); + } - @Override - public int getRowCount() { - return rows.size(); - } + while (!stack.isEmpty()) { + JNode currentClass = stack.pop(); + JClass newParentClass = findOrCreateSubClass((JClass) currentClass, existingClass); - @Override - public int getColumnCount() { - return 2; + if (jMethod instanceof JMethod) { + if (currentClass == jMethod.getJParent()) { + JMethod existingMethod = findOrCreateMethod((JMethod) jMethod, newParentClass); + existingMethod.add(addedNode); + } + } else if (jMethod instanceof JField) { + JField jField = (JField) jMethod; + jField.add(addedNode); + JClass jClassField = jField.getRootClass(); + newParentClass = findOrCreateSubClass(jClassField, newParentClass); + newParentClass.add(jField); + } else { + if (newParentClass.makeLongString().equals(jMethod.makeLongString())) { + newParentClass.add(addedNode); + } + } + existingClass = newParentClass; + } + treeRoot.add(existingPackage); } - @Override - public String getColumnName(int index) { - return COLUMN_NAMES[index]; + private JPackage findPackage(JNode jNode) { + JNode parentNode = (JNode) jNode.getParent(); + JNode jNodeJParent = jNode.getJParent(); + if (parentNode != null) { + return (JPackage) parentNode; + } else { + return findPackage(jNodeJParent); + } } - @Override - public Object getValueAt(int rowIndex, int columnIndex) { - return rows.get(rowIndex); + private JResource isTableResource(JNode jResource) { + if (jResource.getParent() instanceof JResource) { + JResource parent = (JResource) jResource.getParent(); + if (parent != null) { + if (parent.getResFile() != null && parent.getResFile().getType() == ResourceType.ARSC) { + return parent; + } else { + return isTableResource((JNode) parent.getParent()); + } + } + } + return null; } - } - protected final class ResultsTableCellRenderer implements TableCellRenderer { - private final NodeLabel label; - private final RSyntaxTextArea codeArea; - private final NodeLabel emptyLabel; - private final Color codeSelectedColor; - private final Color codeBackground; - - public ResultsTableCellRenderer() { - codeArea = AbstractCodeArea.getDefaultArea(mainWindow); - codeArea.setBorder(BorderFactory.createEmptyBorder(0, 10, 0, 10)); - codeArea.setRows(1); - codeBackground = codeArea.getBackground(); - codeSelectedColor = codeArea.getSelectionColor(); - label = new NodeLabel(); - label.setOpaque(true); - label.setFont(codeArea.getFont()); - label.setHorizontalAlignment(SwingConstants.LEFT); - emptyLabel = new NodeLabel(); - emptyLabel.setOpaque(true); + private JPackage findOrCreatePackage(JPackage jPackage) { + for (int i = 0; i < treeRoot.getChildCount(); i++) { + if (treeRoot.getChildAt(i) instanceof JPackage) { + JPackage existingPackage = (JPackage) treeRoot.getChildAt(i); + if (existingPackage.getPkg().getFullName().equals(jPackage.getPkg().getFullName())) { + return existingPackage; + } + } + } + + JPackage newPackage = new JPackage( + jPackage.getPkg(), + jPackage.isEnabled(), + jPackage.getClasses(), + jPackage.getSubPackages(), + jPackage.isSynthetic()); + newPackage.setName(jPackage.getPkg().getFullName()); + return newPackage; } - @Override - public Component getTableCellRendererComponent(JTable table, Object obj, - boolean isSelected, boolean hasFocus, int row, int column) { - if (obj == null || table == null) { - return emptyLabel; + private JResource findOrCreateJResource(JResource jResource) { + for (int i = 0; i < treeRoot.getChildCount(); i++) { + if (treeRoot.getChildAt(i) instanceof JResource) { + JResource subChildren = (JResource) treeRoot.getChildAt(i); + if (subChildren.makeLongString().equals(jResource.makeLongString())) { + return subChildren; + } + } } - Component comp = makeCell((JNode) obj, column); - updateSelection(table, comp, column, isSelected); - return comp; + return new JResource(jResource.getResFile(), jResource.getName(), jResource.getType()); } - private void updateSelection(JTable table, Component comp, int column, boolean isSelected) { - if (column == 1) { - if (isSelected) { - comp.setBackground(codeSelectedColor); - } else { - comp.setBackground(codeBackground); - } - } else { - if (isSelected) { - comp.setBackground(table.getSelectionBackground()); - comp.setForeground(table.getSelectionForeground()); - } else { - comp.setBackground(table.getBackground()); - comp.setForeground(table.getForeground()); + private JResource findOrCreateSubJResource(JResource jResource, JResource jParent) { + if (jParent.makeLongString().equals(jResource.makeLongString())) { + return jParent; + } + for (Iterator iterator = jParent.children().asIterator(); iterator.hasNext();) { + JNode subChildren = (JNode) iterator.next(); + if (subChildren instanceof JResource && subChildren.makeLongString().equals(jResource.makeLongString())) { + return (JResource) subChildren; } } + return new JResource(jResource.getResFile(), jResource.getName(), jResource.getType()); } - private Component makeCell(JNode node, int column) { - if (column == 0) { - label.disableHtml(node.disableHtml()); - label.setText(node.makeLongStringHtml()); - label.setToolTipText(node.getTooltip()); - label.setIcon(node.getIcon()); - return label; + private JClass findOrCreateSubClass(JClass jClass, JClass jParent) { + if (jParent.makeLongString().equals(jClass.makeLongString())) { + return jParent; } - if (!node.hasDescString()) { - return emptyLabel; - } - codeArea.setSyntaxEditingStyle(node.getSyntaxName()); - String descStr = node.makeDescString(); - codeArea.setText(descStr); - codeArea.setColumns(descStr.length() + 1); - if (highlightContext != null) { - SearchEngine.markAll(codeArea, highlightContext); + for (Iterator iterator = jParent.children().asIterator(); iterator.hasNext();) { + JNode subChildren = (JNode) iterator.next(); + if (subChildren instanceof JClass && subChildren.makeLongString().equals(jClass.makeLongString())) { + return (JClass) subChildren; + } } - return codeArea; + + JClass newClass = new JClass(jClass.getCls(), jClass.getJParent()); + newClass.setStart(jClass.getStart()); + newClass.setEnd(jClass.getEnd()); + newClass.setHasHighlight(jClass.isHasHighlight()); + jParent.add(newClass); + return newClass; } - public int getMaxRowHeight() { - label.setText("Text"); - codeArea.setText("Text"); - return Math.max(getCompHeight(label), getCompHeight(codeArea)); + private JClass findOrCreateClass(JPackage jPackage, JClass jClass) { + for (Iterator it = jPackage.children().asIterator(); it.hasNext();) { + JNode child = (JNode) it.next(); + if (child instanceof JClass) { + if (child.makeLongString().equals(jClass.makeLongString())) { + return (JClass) child; + } + } + } + + JClass newClass = new JClass(jClass.getCls(), jClass.getJParent()); + newClass.setStart(jClass.getStart()); + newClass.setEnd(jClass.getEnd()); + newClass.setHasHighlight(jClass.isHasHighlight()); + jPackage.add(newClass); + return newClass; } - private int getCompHeight(Component comp) { - return Math.max(comp.getHeight(), comp.getPreferredSize().height); + private JMethod findOrCreateMethod(JMethod jMethod, JClass jClass) { + for (Iterator it = jClass.children().asIterator(); it.hasNext();) { + JNode child = (JNode) it.next(); + if (child instanceof JMethod && child.makeLongString().equals(jMethod.makeLongString())) { + return (JMethod) child; + } + } + + JMethod newMethod = new JMethod(jMethod.getJavaMethod(), jMethod.getJParent()); + jClass.add(newMethod); + return newMethod; } } diff --git a/jadx-gui/src/main/java/jadx/gui/ui/dialog/SearchDialog.java b/jadx-gui/src/main/java/jadx/gui/ui/dialog/SearchDialog.java index b3f04498a09..3d38c535d5f 100644 --- a/jadx-gui/src/main/java/jadx/gui/ui/dialog/SearchDialog.java +++ b/jadx-gui/src/main/java/jadx/gui/ui/dialog/SearchDialog.java @@ -70,10 +70,8 @@ import static jadx.gui.ui.dialog.SearchDialog.SearchOptions.CODE; import static jadx.gui.ui.dialog.SearchDialog.SearchOptions.COMMENT; import static jadx.gui.ui.dialog.SearchDialog.SearchOptions.FIELD; -import static jadx.gui.ui.dialog.SearchDialog.SearchOptions.IGNORE_CASE; import static jadx.gui.ui.dialog.SearchDialog.SearchOptions.METHOD; import static jadx.gui.ui.dialog.SearchDialog.SearchOptions.RESOURCE; -import static jadx.gui.ui.dialog.SearchDialog.SearchOptions.USE_REGEX; public class SearchDialog extends CommonSearchDialog { private static final Logger LOG = LoggerFactory.getLogger(SearchDialog.class); @@ -127,6 +125,7 @@ public enum SearchOptions { COMMENT, IGNORE_CASE, + WHOLE_WORD, USE_REGEX, ACTIVE_TAB } @@ -143,7 +142,6 @@ public enum SearchOptions { private transient JButton loadAllButton; private transient JButton loadMoreButton; private transient JButton stopBtn; - private transient JButton sortBtn; private transient Disposable searchDisposable; private transient SearchEventEmitter searchEmitter; @@ -178,7 +176,7 @@ public void dispose() { if (searchDisposable != null && !searchDisposable.isDisposed()) { searchDisposable.dispose(); } - resultsModel.clear(); + resultsTree.clear(); removeActiveTabListener(); searchBackgroundExecutor.execute(() -> { stopSearchTask(); @@ -197,7 +195,7 @@ private Set buildOptions(SearchPreset preset) { case TEXT: if (searchOptions.isEmpty()) { searchOptions.add(SearchOptions.CODE); - searchOptions.add(IGNORE_CASE); + searchOptions.add(SearchOptions.IGNORE_CASE); } break; @@ -225,7 +223,6 @@ protected void openInit() { packageField.setText(searchPackage); } searchField.requestFocus(); - resultsTable.initColumnWidth(); if (options.contains(COMMENT)) { // show all comments on empty input @@ -288,8 +285,9 @@ private void initUI() { JPanel searchOptions = new JPanel(new FlowLayout(FlowLayout.LEFT)); searchOptions.setBorder(BorderFactory.createTitledBorder(NLS.str("search_dialog.options"))); - searchOptions.add(makeOptionsCheckBox(NLS.str("search_dialog.ignorecase"), IGNORE_CASE)); - searchOptions.add(makeOptionsCheckBox(NLS.str("search_dialog.regex"), USE_REGEX)); + searchOptions.add(makeOptionsCheckBox(NLS.str("search_dialog.ignorecase"), SearchOptions.IGNORE_CASE)); + searchOptions.add(makeOptionsCheckBox(NLS.str("search.whole_word"), SearchOptions.WHOLE_WORD)); + searchOptions.add(makeOptionsCheckBox(NLS.str("search_dialog.regex"), SearchOptions.USE_REGEX)); searchOptions.add(makeOptionsCheckBox(NLS.str("search_dialog.active_tab"), SearchOptions.ACTIVE_TAB)); packageField = new JTextField(); @@ -315,7 +313,7 @@ private void initUI() { searchPane.add(optionsPanel); initCommon(); - JPanel resultsPanel = initResultsTable(); + JPanel resultsPanel = initResultsTree(); JPanel buttonPane = initButtonsPanel(); JPanel contentPanel = new JPanel(); @@ -362,25 +360,12 @@ protected void addResultsActions(JPanel resultsActionsPanel) { stopBtn.addActionListener(e -> pauseSearch()); stopBtn.setEnabled(false); - sortBtn = new JButton(NLS.str("search_dialog.sort_results")); - sortBtn.addActionListener(e -> { - synchronized (pendingResults) { - resultsModel.sort(); - resultsTable.updateTable(); - } - }); - sortBtn.setEnabled(false); - resultsActionsPanel.add(loadAllButton); resultsActionsPanel.add(Box.createRigidArea(new Dimension(10, 0))); resultsActionsPanel.add(loadMoreButton); resultsActionsPanel.add(Box.createRigidArea(new Dimension(10, 0))); resultsActionsPanel.add(stopBtn); - resultsActionsPanel.add(Box.createRigidArea(new Dimension(10, 0))); - resultsActionsPanel.add(stopBtn); super.addResultsActions(resultsActionsPanel); - resultsActionsPanel.add(Box.createRigidArea(new Dimension(10, 0))); - resultsActionsPanel.add(sortBtn); } private class SearchEventEmitter { @@ -457,8 +442,9 @@ private SearchTask prepareSearch(String text) { return null; } LOG.debug("Building search for '{}', options: {}", text, options); - boolean ignoreCase = options.contains(IGNORE_CASE); - boolean useRegex = options.contains(USE_REGEX); + boolean ignoreCase = options.contains(SearchOptions.IGNORE_CASE); + boolean wholeWord = options.contains(SearchOptions.WHOLE_WORD); + boolean useRegex = options.contains(SearchOptions.USE_REGEX); // Find the JavaPackage for the searched package string String packageText = packageField.getText(); @@ -481,7 +467,7 @@ private SearchTask prepareSearch(String text) { packageField.setBackground(searchFieldDefaultBgColor); } - SearchSettings searchSettings = new SearchSettings(text, ignoreCase, useRegex, searchPackage); + SearchSettings searchSettings = new SearchSettings(text, ignoreCase, wholeWord, useRegex, searchPackage); String error = searchSettings.prepare(); if (error == null) { if (Objects.equals(searchField.getBackground(), SEARCH_FIELD_ERROR_COLOR)) { @@ -609,8 +595,7 @@ private void loadMoreResults(boolean all) { private void resetSearch() { UiUtils.uiThreadGuard(); - resultsModel.clear(); - resultsTable.updateTable(); + resultsTree.clear(); synchronized (pendingResults) { pendingResults.clear(); } @@ -624,7 +609,6 @@ private void resetSearch() { private void prepareForSearch() { UiUtils.uiThreadGuard(); stopBtn.setEnabled(true); - sortBtn.setEnabled(false); showSearchState(); progressStartCommon(); } @@ -641,15 +625,15 @@ private void updateTable() { synchronized (pendingResults) { UiUtils.uiThreadGuard(); Collections.sort(pendingResults); - resultsModel.addAll(pendingResults); + resultsTree.addAll(pendingResults); pendingResults.clear(); - resultsTable.updateTable(); + resultsTree.updateTree(); } } private void updateTableHighlight() { String text = searchField.getText(); - updateHighlightContext(text, !options.contains(IGNORE_CASE), options.contains(USE_REGEX), false); + updateHighlightContext(text); cache.setLastSearch(text); cache.setLastSearchPackage(packageField.getText()); cache.getLastSearchOptions().put(searchPreset, options); @@ -669,7 +653,7 @@ public void updateProgressLabel(String text) { UiUtils.uiRun(() -> progressInfoLabel.setText(text)); } - private void searchFinished(ITaskInfo status, Boolean complete) { + private void searchFinished(ITaskInfo status, boolean complete) { UiUtils.uiThreadGuard(); LOG.debug("Search complete: {}, complete: {}", status, complete); loadAllButton.setEnabled(!complete); @@ -678,7 +662,6 @@ private void searchFinished(ITaskInfo status, Boolean complete) { progressFinishedCommon(); updateTable(); updateProgressLabel(complete); - sortBtn.setEnabled(resultsModel.getRowCount() != 0); } private void unloadTempData() { @@ -703,14 +686,14 @@ private JCheckBox makeOptionsCheckBox(String name, final SearchOptions opt) { @Override protected void loadFinished() { - resultsTable.setEnabled(true); + resultsTree.setEnabled(true); searchField.setEnabled(true); searchEmitter.emitSearch(); } @Override protected void loadStart() { - resultsTable.setEnabled(false); + resultsTree.setEnabled(false); searchField.setEnabled(false); } diff --git a/jadx-gui/src/main/java/jadx/gui/ui/dialog/UsageDialog.java b/jadx-gui/src/main/java/jadx/gui/ui/dialog/UsageDialog.java index 5155c4a6047..d89aa2afeee 100644 --- a/jadx-gui/src/main/java/jadx/gui/ui/dialog/UsageDialog.java +++ b/jadx-gui/src/main/java/jadx/gui/ui/dialog/UsageDialog.java @@ -23,6 +23,8 @@ import jadx.core.dex.visitors.prepare.CollectConstValues; import jadx.gui.JadxWrapper; import jadx.gui.jobs.TaskStatus; +import jadx.gui.search.MatchingPositions; +import jadx.gui.search.SearchSettings; import jadx.gui.settings.JadxSettings; import jadx.gui.treemodel.CodeNode; import jadx.gui.treemodel.JClass; @@ -141,7 +143,7 @@ private void processUsage(JavaNode searchNode, JavaClass topUseClass) { String code = codeInfo.getCodeStr(); JadxWrapper wrapper = mainWindow.getWrapper(); for (int pos : usePositions) { - String line = CodeUtils.getLineForPos(code, pos); + String line = CodeUtils.getLineForPos(code, pos).trim(); if (line.startsWith("import ")) { continue; } @@ -149,26 +151,32 @@ private void processUsage(JavaNode searchNode, JavaClass topUseClass) { JavaNode enclosingNode = wrapper.getEnclosingNode(codeInfo, pos); JClass rootJCls = nodeCache.makeFrom(topUseClass); JNode usageJNode = enclosingNode == null ? rootJCls : nodeCache.makeFrom(enclosingNode); - usageList.add(new CodeNode(rootJCls, usageJNode, line.trim(), pos)); + + // build pos for highlight + SearchSettings searchSettings = new SearchSettings(node.getName(), false, true, false, null); + searchSettings.prepare(); + MatchingPositions positions1 = searchSettings.getSearchMethod().find(line, 0); + int startHighlight = positions1.getStartMath(); + int endHighlight = positions1.getEndMath(); + usageList.add(new CodeNode(rootJCls, usageJNode, line, pos, startHighlight, endHighlight)); } } @Override protected void loadFinished() { - resultsTable.setEnabled(true); - resultsModel.clear(); + resultsTree.setEnabled(true); + resultsTree.clear(); Collections.sort(usageList); - resultsModel.addAll(usageList); - updateHighlightContext(node.getName(), true, false, true); - resultsTable.initColumnWidth(); - resultsTable.updateTable(); + resultsTree.addAll(usageList); + updateHighlightContext(node.getName()); + resultsTree.updateTree(); updateProgressLabel(true); } @Override protected void loadStart() { - resultsTable.setEnabled(false); + resultsTree.setEnabled(false); } private void initUI() { @@ -187,7 +195,7 @@ private void initUI() { searchPane.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10)); initCommon(); - JPanel resultsPanel = initResultsTable(); + JPanel resultsPanel = initResultsTree(); JPanel buttonPane = initButtonsPanel(); JPanel contentPanel = new JPanel(); diff --git a/jadx-gui/src/main/java/jadx/gui/ui/tab/TabsController.java b/jadx-gui/src/main/java/jadx/gui/ui/tab/TabsController.java index 74f41ef725d..c2f9084783a 100644 --- a/jadx-gui/src/main/java/jadx/gui/ui/tab/TabsController.java +++ b/jadx-gui/src/main/java/jadx/gui/ui/tab/TabsController.java @@ -85,6 +85,9 @@ public void selectTab(JNode node) { * Jump to node definition */ public void codeJump(JNode node) { + if (node == null) { + return; + } JClass parentCls = node.getJParent(); if (parentCls != null) { JavaClass cls = node.getJParent().getCls(); diff --git a/jadx-gui/src/main/java/jadx/gui/utils/TreeUtils.java b/jadx-gui/src/main/java/jadx/gui/utils/TreeUtils.java new file mode 100644 index 00000000000..522a171ae82 --- /dev/null +++ b/jadx-gui/src/main/java/jadx/gui/utils/TreeUtils.java @@ -0,0 +1,83 @@ +package jadx.gui.utils; + +import java.awt.event.MouseEvent; +import java.util.List; + +import javax.swing.JTree; +import javax.swing.tree.TreeNode; + +import org.jetbrains.annotations.Nullable; + +import jadx.gui.treemodel.JNode; + +public class TreeUtils { + + @Nullable + public static JNode getJNodeUnderMouse(JTree tree, MouseEvent mouseEvent) { + TreeNode treeNode = UiUtils.getTreeNodeUnderMouse(tree, mouseEvent); + if (treeNode instanceof JNode) { + return (JNode) treeNode; + } + + return null; + } + + public static void expandAllNodes(JTree tree) { + int j = tree.getRowCount(); + int i = 0; + while (i < j) { + tree.expandRow(i); + i += 1; + j = tree.getRowCount(); + } + } + + public static String highlightString(String s, String styleTag, List ranges) { + StringBuilder sb = new StringBuilder(""); + int lastIndex = 0; + for (int i = 0; i < ranges.size(); i += 2) { + int rangeStart = ranges.get(i); + int rangeEnd = ranges.get(i + 1); + appendString2html(sb, s.substring(lastIndex, rangeStart)); + sb.append(""); + appendString2html(sb, s.substring(rangeStart, rangeEnd)); + sb.append(""); + lastIndex = rangeEnd; + } + appendString2html(sb, s.substring(lastIndex)); + sb.append(""); + return sb.toString(); + } + + public static void appendString2html(StringBuilder sb, String s) { + for (int i = 0; i < s.length(); i++) { + char c = s.charAt(i); + String r; + switch (c) { + case '"': + r = """; + break; + // case '\'': r = "'"; break; + case '&': + r = "&"; + break; + case '<': + r = "<"; + break; + case '>': + r = ">"; + break; + case ' ': + r = " "; // Maintain amount of whitespace in line + break; + default: + r = String.valueOf(c); + break; + } + sb.append(r); + } + } + +} 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 63bb4ffc21f..0467b7033a7 100644 --- a/jadx-gui/src/main/java/jadx/gui/utils/UiUtils.java +++ b/jadx-gui/src/main/java/jadx/gui/utils/UiUtils.java @@ -1,5 +1,6 @@ package jadx.gui.utils; +import java.awt.Color; import java.awt.Component; import java.awt.Image; import java.awt.MouseInfo; @@ -30,6 +31,7 @@ import javax.swing.KeyStroke; import javax.swing.RootPaneContainer; import javax.swing.SwingUtilities; +import javax.swing.UIManager; import javax.swing.tree.TreeNode; import javax.swing.tree.TreePath; @@ -148,6 +150,30 @@ public static String escapeHtml(String str) { return str.replace("<", "<").replace(">", ">"); } + public static String getThemeAccentColor() { + return toHexColor(UIManager.getColor("textHighlight")); + } + + /** + * Converts a given color to it's hexidecimal equivalent. + * + * @param color Color to get hexidecimal string from. + * @return Hexidecimal string representing the given color, in the form "#abcdef". + */ + public static String toHexColor(final Color color) { + return "#" + colorToHexCode(color); + } + + /** + * Gets the RGB hex color code of the passed color. + * + * @param color The color to get a hex code from. + * @return A lower-cased string of the RGB hex code of color. + */ + public static String colorToHexCode(final Color color) { + return String.format("%06x", color.getRGB() & 0xFFFFFF); + } + public static String typeStr(ArgType type) { if (type == null) { return "null"; diff --git a/jadx-gui/src/main/java/jadx/gui/utils/pkgs/PackageHelper.java b/jadx-gui/src/main/java/jadx/gui/utils/pkgs/PackageHelper.java index 6d732006067..e0d91f23666 100644 --- a/jadx-gui/src/main/java/jadx/gui/utils/pkgs/PackageHelper.java +++ b/jadx-gui/src/main/java/jadx/gui/utils/pkgs/PackageHelper.java @@ -28,8 +28,8 @@ public class PackageHelper { private static final Comparator PKG_COMPARATOR = Comparator.comparing(JPackage::getName, String.CASE_INSENSITIVE_ORDER); private final JadxWrapper wrapper; - private List excludedPackages; - private JNodeCache nodeCache; + private static List excludedPackages; + private static JNodeCache nodeCache; private final Map pkgInfoMap = new HashMap<>(); @@ -167,7 +167,7 @@ private static JPackage mergeMiddlePackages(JPackage jPkg, List merged return jPkg; } - private JPackage buildJPackage(JavaPackage javaPkg, boolean synthetic) { + public static JPackage buildJPackage(JavaPackage javaPkg, boolean synthetic) { boolean pkgEnabled = isPkgEnabled(javaPkg.getRawFullName(), excludedPackages); List classes; if (synthetic) { From 470e4afd2125b6ae8d968de5f3a9acd9fc3afc55 Mon Sep 17 00:00:00 2001 From: MrIkso Date: Fri, 27 Dec 2024 18:36:42 +0200 Subject: [PATCH 2/2] fix(gui): resolve spotless check --- jadx-gui/src/main/resources/i18n/Messages_de_DE.properties | 4 ---- jadx-gui/src/main/resources/i18n/Messages_en_US.properties | 4 ---- jadx-gui/src/main/resources/i18n/Messages_es_ES.properties | 4 ---- jadx-gui/src/main/resources/i18n/Messages_id_ID.properties | 4 ---- jadx-gui/src/main/resources/i18n/Messages_ko_KR.properties | 4 ---- jadx-gui/src/main/resources/i18n/Messages_pt_BR.properties | 4 ---- jadx-gui/src/main/resources/i18n/Messages_ru_RU.properties | 4 ---- jadx-gui/src/main/resources/i18n/Messages_zh_CN.properties | 4 ---- jadx-gui/src/main/resources/i18n/Messages_zh_TW.properties | 4 ---- 9 files changed, 36 deletions(-) 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 9238c008006..00541a79816 100644 --- a/jadx-gui/src/main/resources/i18n/Messages_de_DE.properties +++ b/jadx-gui/src/main/resources/i18n/Messages_de_DE.properties @@ -127,7 +127,6 @@ file_dialog.supported_files=Unterstützte Dateien file_dialog.load_dir_title=Verzeichnis laden file_dialog.load_dir_confirm=Alle Dateien aus dem Verzeichnis laden? -search_dialog.open=Öffnen search_dialog.cancel=Beenden search_dialog.open_by_name=Nach Text suchen: #search_dialog.search_button=Search @@ -148,9 +147,6 @@ search_dialog.results_complete=%d gefunden (komplett) #search_dialog.resources_load_errors=Load errors: %d #search_dialog.resources_skip_by_size=Skipped by size: %d #search_dialog.resources_check_logs=(click to check logs) -search_dialog.col_node=Knoten -search_dialog.col_code=Code -#search_dialog.sort_results=Sort results search_dialog.regex=Regex search_dialog.active_tab=Nur aktiver Tab search_dialog.comments=Kommentare 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 561096507c1..13136631cf4 100644 --- a/jadx-gui/src/main/resources/i18n/Messages_en_US.properties +++ b/jadx-gui/src/main/resources/i18n/Messages_en_US.properties @@ -127,7 +127,6 @@ file_dialog.supported_files=Supported files file_dialog.load_dir_title=Load directory file_dialog.load_dir_confirm=Load all files from directory? -search_dialog.open=Open search_dialog.cancel=Cancel search_dialog.open_by_name=Search for text: search_dialog.search_button=Search @@ -148,9 +147,6 @@ search_dialog.results_complete=Found %d (complete) search_dialog.resources_load_errors=Load errors: %d search_dialog.resources_skip_by_size=Skipped by size: %d search_dialog.resources_check_logs=(click to check logs) -search_dialog.col_node=Node -search_dialog.col_code=Code -search_dialog.sort_results=Sort results search_dialog.regex=Regex search_dialog.active_tab=Active tab only search_dialog.comments=Comments 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 6724e1fde51..2c27fad1fac 100644 --- a/jadx-gui/src/main/resources/i18n/Messages_es_ES.properties +++ b/jadx-gui/src/main/resources/i18n/Messages_es_ES.properties @@ -127,7 +127,6 @@ nav.forward=Adelante #file_dialog.load_dir_title=Load directory #file_dialog.load_dir_confirm=Load all files from directory? -search_dialog.open=Abrir search_dialog.cancel=Cancelar search_dialog.open_by_name=Buscar texto: #search_dialog.search_button=Search @@ -148,9 +147,6 @@ search_dialog.ignorecase=Ignorar minúsculas/mayúsculas #search_dialog.resources_load_errors=Load errors: %d #search_dialog.resources_skip_by_size=Skipped by size: %d #search_dialog.resources_check_logs=(click to check logs) -search_dialog.col_node=Nodo -search_dialog.col_code=Código -#search_dialog.sort_results=Sort results search_dialog.regex=Regex #search_dialog.active_tab=Active tab only #search_dialog.comments=Comments diff --git a/jadx-gui/src/main/resources/i18n/Messages_id_ID.properties b/jadx-gui/src/main/resources/i18n/Messages_id_ID.properties index 423921636eb..3fbf760b116 100644 --- a/jadx-gui/src/main/resources/i18n/Messages_id_ID.properties +++ b/jadx-gui/src/main/resources/i18n/Messages_id_ID.properties @@ -127,7 +127,6 @@ file_dialog.supported_files=Berkas yang didukung file_dialog.load_dir_title=Muat direktori file_dialog.load_dir_confirm=Muat semua berkas dari direktori? -search_dialog.open=Buka search_dialog.cancel=Batal search_dialog.open_by_name=Cari teks: search_dialog.search_button=Cari @@ -148,9 +147,6 @@ search_dialog.results_complete=Ditemukan %d (lengkap) search_dialog.resources_load_errors=Kesalahan pemuatan: %d search_dialog.resources_skip_by_size=Dilewati karena ukuran: %d search_dialog.resources_check_logs=(klik untuk memeriksa log) -search_dialog.col_node=Node -search_dialog.col_code=Kode -search_dialog.sort_results=Sortir hasil search_dialog.regex=Regex search_dialog.active_tab=Hanya tab aktif search_dialog.comments=Komentar 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 538984d5167..3a252819148 100644 --- a/jadx-gui/src/main/resources/i18n/Messages_ko_KR.properties +++ b/jadx-gui/src/main/resources/i18n/Messages_ko_KR.properties @@ -127,7 +127,6 @@ file_dialog.supported_files=지원되는 파일 file_dialog.load_dir_title=디렉토리 불러오기 file_dialog.load_dir_confirm=디렉토리에서 모든 파일을 불러오시겠습니까? -search_dialog.open=열기 search_dialog.cancel=취소 search_dialog.open_by_name=텍스트 검색 : search_dialog.search_button=검색 @@ -148,9 +147,6 @@ search_dialog.results_complete=%d개 찾음 (검색 완료) #search_dialog.resources_load_errors=Load errors: %d #search_dialog.resources_skip_by_size=Skipped by size: %d #search_dialog.resources_check_logs=(click to check logs) -search_dialog.col_node=노드 -search_dialog.col_code=코드 -search_dialog.sort_results=결과 정렬 search_dialog.regex=정규식 search_dialog.active_tab=열려 있는 탭에서만 검색 search_dialog.comments=주석 diff --git a/jadx-gui/src/main/resources/i18n/Messages_pt_BR.properties b/jadx-gui/src/main/resources/i18n/Messages_pt_BR.properties index a205688befd..42d144cdb88 100644 --- a/jadx-gui/src/main/resources/i18n/Messages_pt_BR.properties +++ b/jadx-gui/src/main/resources/i18n/Messages_pt_BR.properties @@ -127,7 +127,6 @@ file_dialog.supported_files=Arquivos suportados file_dialog.load_dir_title=Carregar diretório file_dialog.load_dir_confirm=Carregar todos arquivos do diretório? -search_dialog.open=Abrir search_dialog.cancel=Cancelar search_dialog.open_by_name=Buscar por texto: search_dialog.search_button=Buscar @@ -148,9 +147,6 @@ search_dialog.results_complete=Encontradas %d (completos) #search_dialog.resources_load_errors=Load errors: %d #search_dialog.resources_skip_by_size=Skipped by size: %d #search_dialog.resources_check_logs=(click to check logs) -search_dialog.col_node=Nó -search_dialog.col_code=Código -search_dialog.sort_results=Ordenar resultados search_dialog.regex=Expressão regular search_dialog.active_tab=Apenas abas ativas search_dialog.comments=Comentários diff --git a/jadx-gui/src/main/resources/i18n/Messages_ru_RU.properties b/jadx-gui/src/main/resources/i18n/Messages_ru_RU.properties index e94bfe92d74..dedebf61e72 100644 --- a/jadx-gui/src/main/resources/i18n/Messages_ru_RU.properties +++ b/jadx-gui/src/main/resources/i18n/Messages_ru_RU.properties @@ -127,7 +127,6 @@ file_dialog.supported_files=Поддерж. файлы file_dialog.load_dir_title=Импортировать папку file_dialog.load_dir_confirm=Импортировать все файлы из директории? -search_dialog.open=Открыть search_dialog.cancel=Отмена search_dialog.open_by_name=Поиск по тексту: search_dialog.search_button=Поиск @@ -148,9 +147,6 @@ search_dialog.results_complete=Найдено %d (поиск завершен) search_dialog.resources_load_errors=Ошибки загрузки: %d search_dialog.resources_skip_by_size=Пропущено из-за размера: %d search_dialog.resources_check_logs=(нажмите для просмотра логов) -search_dialog.col_node=Вхождения -search_dialog.col_code=Код -search_dialog.sort_results=Сортировка результатов search_dialog.regex=Регулярные выражения search_dialog.active_tab=Только активные вкладки search_dialog.comments=Комментарии 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 728cfeba5de..7322b822a3f 100644 --- a/jadx-gui/src/main/resources/i18n/Messages_zh_CN.properties +++ b/jadx-gui/src/main/resources/i18n/Messages_zh_CN.properties @@ -127,7 +127,6 @@ file_dialog.supported_files=支持的文件 file_dialog.load_dir_title=加载目录 file_dialog.load_dir_confirm=从目录中加载所有文件? -search_dialog.open=转到 search_dialog.cancel=取消 search_dialog.open_by_name=搜索文本: search_dialog.search_button=搜索 @@ -148,9 +147,6 @@ search_dialog.results_complete=全部找到 %d search_dialog.resources_load_errors=加载错误:%d search_dialog.resources_skip_by_size=按大小跳过:%d search_dialog.resources_check_logs=(点击查看日志) -search_dialog.col_node=节点 -search_dialog.col_code=代码 -search_dialog.sort_results=结果分类 search_dialog.regex=正则表达式 search_dialog.active_tab=只在当前页搜索 search_dialog.comments=注释 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 c5c932fd0d5..9d2697e88b2 100644 --- a/jadx-gui/src/main/resources/i18n/Messages_zh_TW.properties +++ b/jadx-gui/src/main/resources/i18n/Messages_zh_TW.properties @@ -127,7 +127,6 @@ file_dialog.supported_files=支援的檔案 file_dialog.load_dir_title=載入目錄 file_dialog.load_dir_confirm=要載入目錄中的所有檔案嗎? -search_dialog.open=開啟 search_dialog.cancel=取消 search_dialog.open_by_name=搜尋文字: search_dialog.search_button=搜尋 @@ -148,9 +147,6 @@ search_dialog.results_complete=找到 %d (完整) search_dialog.resources_load_errors=載入錯誤:%d search_dialog.resources_skip_by_size=因大小跳過:%d search_dialog.resources_check_logs=(點擊以查看記錄檔) -search_dialog.col_node=無 -search_dialog.col_code=程式碼 -search_dialog.sort_results=排序結果 search_dialog.regex=Regex search_dialog.active_tab=僅使用中分頁 search_dialog.comments=註解