gui: add search bar
This commit is contained in:
@@ -4,7 +4,10 @@ import jadx.cli.JadxArgs;
|
||||
import jadx.gui.treemodel.JClass;
|
||||
import jadx.gui.treemodel.JNode;
|
||||
import jadx.gui.treemodel.JRoot;
|
||||
import jadx.gui.utils.NLS;
|
||||
import jadx.gui.utils.Utils;
|
||||
|
||||
import javax.swing.AbstractAction;
|
||||
import javax.swing.ImageIcon;
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JFileChooser;
|
||||
@@ -18,9 +21,9 @@ import javax.swing.JSplitPane;
|
||||
import javax.swing.JToggleButton;
|
||||
import javax.swing.JToolBar;
|
||||
import javax.swing.JTree;
|
||||
import javax.swing.KeyStroke;
|
||||
import javax.swing.event.TreeSelectionEvent;
|
||||
import javax.swing.event.TreeSelectionListener;
|
||||
import javax.swing.filechooser.FileFilter;
|
||||
import javax.swing.filechooser.FileNameExtensionFilter;
|
||||
import javax.swing.text.BadLocationException;
|
||||
import javax.swing.tree.DefaultMutableTreeNode;
|
||||
@@ -32,6 +35,7 @@ import java.awt.Color;
|
||||
import java.awt.Component;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.awt.event.InputEvent;
|
||||
import java.awt.event.KeyEvent;
|
||||
import java.io.File;
|
||||
|
||||
@@ -50,12 +54,15 @@ public class MainWindow extends JFrame {
|
||||
private static final ImageIcon ICON_OPEN = Utils.openIcon("folder");
|
||||
private static final ImageIcon ICON_CLOSE = Utils.openIcon("cross");
|
||||
private static final ImageIcon ICON_FLAT_PKG = Utils.openIcon("empty_logical_package_obj");
|
||||
private static final ImageIcon ICON_SEARCH = Utils.openIcon("magnifier");
|
||||
|
||||
private final JadxWrapper wrapper;
|
||||
private JPanel mainPanel;
|
||||
private JTree tree;
|
||||
private DefaultTreeModel treeModel;
|
||||
private RSyntaxTextArea textArea;
|
||||
private JToolBar searchToolBar;
|
||||
private SearchBar searchBar;
|
||||
|
||||
public MainWindow(JadxArgs jadxArgs) {
|
||||
this.wrapper = new JadxWrapper(jadxArgs);
|
||||
@@ -87,6 +94,10 @@ public class MainWindow extends JFrame {
|
||||
}
|
||||
}
|
||||
|
||||
private void toggleSearch() {
|
||||
searchBar.toggle();
|
||||
}
|
||||
|
||||
private void treeClickAction() {
|
||||
Object obj = tree.getLastSelectedPathComponent();
|
||||
if (obj instanceof JNode) {
|
||||
@@ -94,8 +105,8 @@ public class MainWindow extends JFrame {
|
||||
if (node.getJParent() != null) {
|
||||
textArea.setText(node.getJParent().getCode());
|
||||
scrollToLine(node.getLine());
|
||||
} else if (node.getClass() == JClass.class){
|
||||
textArea.setText(((JClass)node).getCode());
|
||||
} else if (node.getClass() == JClass.class) {
|
||||
textArea.setText(((JClass) node).getCode());
|
||||
scrollToLine(node.getLine());
|
||||
}
|
||||
}
|
||||
@@ -149,7 +160,7 @@ public class MainWindow extends JFrame {
|
||||
toolbar.add(openButton);
|
||||
toolbar.addSeparator();
|
||||
|
||||
JToggleButton flatPkgButton = new JToggleButton(ICON_FLAT_PKG);
|
||||
final JToggleButton flatPkgButton = new JToggleButton(ICON_FLAT_PKG);
|
||||
flatPkgButton.addActionListener(new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
@@ -158,15 +169,35 @@ public class MainWindow extends JFrame {
|
||||
});
|
||||
flatPkgButton.setToolTipText(NLS.str("tree.flatten"));
|
||||
toolbar.add(flatPkgButton);
|
||||
toolbar.addSeparator();
|
||||
|
||||
final JButton searchButton = new JButton(ICON_SEARCH);
|
||||
searchButton.addActionListener(new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
toggleSearch();
|
||||
}
|
||||
});
|
||||
searchButton.setToolTipText(NLS.str("search"));
|
||||
toolbar.add(searchButton);
|
||||
|
||||
KeyStroke key = KeyStroke.getKeyStroke(KeyEvent.VK_F, InputEvent.CTRL_MASK);
|
||||
Utils.addKeyBinding(textArea, key, "SearchAction", new AbstractAction() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
toggleSearch();
|
||||
}
|
||||
});
|
||||
|
||||
toolbar.addSeparator();
|
||||
|
||||
add(toolbar, BorderLayout.NORTH);
|
||||
mainPanel.add(toolbar, BorderLayout.NORTH);
|
||||
}
|
||||
|
||||
private void initUI() {
|
||||
mainPanel = new JPanel(new BorderLayout());
|
||||
JSplitPane splitPane = new JSplitPane();
|
||||
splitPane.setDividerLocation(200);
|
||||
mainPanel.add(splitPane);
|
||||
|
||||
DefaultMutableTreeNode treeRoot = new DefaultMutableTreeNode("Please open file");
|
||||
@@ -198,7 +229,7 @@ public class MainWindow extends JFrame {
|
||||
JScrollPane treeScrollPane = new JScrollPane(tree);
|
||||
splitPane.setLeftComponent(treeScrollPane);
|
||||
|
||||
textArea = new RSyntaxTextArea(20, 60);
|
||||
textArea = new RSyntaxTextArea();
|
||||
textArea.setSyntaxEditingStyle(SyntaxConstants.SYNTAX_STYLE_JAVA);
|
||||
textArea.setMarkOccurrences(true);
|
||||
textArea.setBackground(BACKGROUND);
|
||||
@@ -206,9 +237,15 @@ public class MainWindow extends JFrame {
|
||||
textArea.setAntiAliasingEnabled(true);
|
||||
// textArea.setHyperlinksEnabled(true);
|
||||
textArea.setTabSize(4);
|
||||
|
||||
RTextScrollPane scrollPane = new RTextScrollPane(textArea);
|
||||
scrollPane.setFoldIndicatorEnabled(true);
|
||||
splitPane.setRightComponent(scrollPane);
|
||||
|
||||
JPanel textPanel = new JPanel(new BorderLayout());
|
||||
searchBar = new SearchBar(textArea);
|
||||
textPanel.add(searchBar.getToolBar(), BorderLayout.NORTH);
|
||||
textPanel.add(scrollPane);
|
||||
splitPane.setRightComponent(textPanel);
|
||||
|
||||
setContentPane(mainPanel);
|
||||
setTitle(DEFAULT_TITLE);
|
||||
@@ -220,12 +257,10 @@ public class MainWindow extends JFrame {
|
||||
private class OpenListener implements ActionListener {
|
||||
public void actionPerformed(ActionEvent event) {
|
||||
JFileChooser fileChooser = new JFileChooser();
|
||||
FileFilter filter = new FileNameExtensionFilter("dex files", "dex", "apk", "jar");
|
||||
fileChooser.addChoosableFileFilter(filter);
|
||||
fileChooser.addChoosableFileFilter(new FileNameExtensionFilter("dex files", "dex", "apk", "jar"));
|
||||
int ret = fileChooser.showDialog(mainPanel, "Open file");
|
||||
if (ret == JFileChooser.APPROVE_OPTION) {
|
||||
File file = fileChooser.getSelectedFile();
|
||||
openFile(file);
|
||||
openFile(fileChooser.getSelectedFile());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,205 @@
|
||||
package jadx.gui;
|
||||
|
||||
import jadx.gui.utils.NLS;
|
||||
import jadx.gui.utils.Utils;
|
||||
|
||||
import javax.swing.Icon;
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JCheckBox;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JTextField;
|
||||
import javax.swing.JToolBar;
|
||||
import javax.swing.text.BadLocationException;
|
||||
import java.awt.Color;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.awt.event.KeyEvent;
|
||||
import java.awt.event.KeyListener;
|
||||
|
||||
import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea;
|
||||
import org.fife.ui.rtextarea.SearchContext;
|
||||
import org.fife.ui.rtextarea.SearchEngine;
|
||||
|
||||
public class SearchBar {
|
||||
private static final Color COLOR_BG_ERROR = new Color(0xFFDFDE);
|
||||
private static final Color COLOR_BG_WARN = new Color(0xFFFDD9);
|
||||
private static final Color COLOR_BG_NORMAL = new Color(0xFFFFFF);
|
||||
|
||||
private static final Icon ICON_UP = Utils.openIcon("arrow_up");
|
||||
private static final Icon ICON_DOWN = Utils.openIcon("arrow_down");
|
||||
private static final Icon ICON_CLOSE = Utils.openIcon("cross");
|
||||
|
||||
private final RSyntaxTextArea rTextArea;
|
||||
private final JToolBar toolBar;
|
||||
|
||||
private final JTextField searchField;
|
||||
private final JCheckBox markAllCB;
|
||||
private final JCheckBox regexCB;
|
||||
|
||||
private final JCheckBox wholeWordCB;
|
||||
private final JCheckBox matchCaseCB;
|
||||
|
||||
public SearchBar(RSyntaxTextArea textArea) {
|
||||
rTextArea = textArea;
|
||||
toolBar = new JToolBar();
|
||||
|
||||
JLabel findLabel = new JLabel(NLS.str("search.find") + ":");
|
||||
toolBar.add(findLabel);
|
||||
|
||||
searchField = new JTextField(30);
|
||||
searchField.addKeyListener(new KeyListener() {
|
||||
@Override
|
||||
public void keyTyped(KeyEvent e) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void keyPressed(KeyEvent e) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void keyReleased(KeyEvent e) {
|
||||
switch (e.getKeyCode()) {
|
||||
case KeyEvent.VK_ENTER:
|
||||
// skip
|
||||
break;
|
||||
case KeyEvent.VK_ESCAPE:
|
||||
toggle();
|
||||
break;
|
||||
default:
|
||||
search(0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
searchField.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
search(1);
|
||||
}
|
||||
});
|
||||
toolBar.add(searchField);
|
||||
|
||||
JButton prevButton = new JButton(NLS.str("search.previous"));
|
||||
prevButton.setIcon(ICON_UP);
|
||||
prevButton.addActionListener(new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
search(-1);
|
||||
}
|
||||
});
|
||||
toolBar.add(prevButton);
|
||||
|
||||
JButton nextButton = new JButton(NLS.str("search.next"));
|
||||
nextButton.setIcon(ICON_DOWN);
|
||||
nextButton.addActionListener(new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
search(1);
|
||||
}
|
||||
});
|
||||
toolBar.add(nextButton);
|
||||
|
||||
markAllCB = new JCheckBox(NLS.str("search.mark_all"));
|
||||
markAllCB.addActionListener(new ForwardListener());
|
||||
toolBar.add(markAllCB);
|
||||
|
||||
regexCB = new JCheckBox(NLS.str("search.regex"));
|
||||
regexCB.addActionListener(new ForwardListener());
|
||||
toolBar.add(regexCB);
|
||||
|
||||
matchCaseCB = new JCheckBox(NLS.str("search.match_case"));
|
||||
matchCaseCB.addActionListener(new ForwardListener());
|
||||
toolBar.add(matchCaseCB);
|
||||
|
||||
wholeWordCB = new JCheckBox(NLS.str("search.whole_word"));
|
||||
wholeWordCB.addActionListener(new ForwardListener());
|
||||
toolBar.add(wholeWordCB);
|
||||
|
||||
JButton closeButton = new JButton();
|
||||
closeButton.setIcon(ICON_CLOSE);
|
||||
closeButton.addActionListener(new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
toggle();
|
||||
}
|
||||
});
|
||||
toolBar.add(closeButton);
|
||||
|
||||
toolBar.setFloatable(false);
|
||||
toolBar.setVisible(false);
|
||||
}
|
||||
|
||||
public JToolBar getToolBar() {
|
||||
return toolBar;
|
||||
}
|
||||
|
||||
public boolean toggle() {
|
||||
boolean visible = !toolBar.isVisible();
|
||||
toolBar.setVisible(visible);
|
||||
|
||||
if (visible) {
|
||||
searchField.requestFocus();
|
||||
} else {
|
||||
rTextArea.requestFocus();
|
||||
}
|
||||
return visible;
|
||||
}
|
||||
|
||||
private void search(int direction) {
|
||||
String text = searchField.getText();
|
||||
if (text.length() == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
boolean forward = (direction >= 0);
|
||||
boolean matchCase = matchCaseCB.isSelected();
|
||||
boolean regex = regexCB.isSelected();
|
||||
boolean wholeWord = wholeWordCB.isSelected();
|
||||
|
||||
if (markAllCB.isSelected()) {
|
||||
rTextArea.markAll(text, matchCase, wholeWord, regex);
|
||||
} else {
|
||||
rTextArea.clearMarkAllHighlights();
|
||||
}
|
||||
|
||||
SearchContext context = new SearchContext();
|
||||
context.setSearchFor(text);
|
||||
context.setMatchCase(matchCase);
|
||||
context.setRegularExpression(regex);
|
||||
context.setSearchForward(forward);
|
||||
context.setWholeWord(wholeWord);
|
||||
|
||||
// TODO hack: move cursor before previous search for not jump to next occurrence
|
||||
if (direction == 0 && !searchField.getBackground().equals(COLOR_BG_ERROR)) {
|
||||
try {
|
||||
int caretPos = rTextArea.getCaretPosition();
|
||||
int lineNum = rTextArea.getLineOfOffset(caretPos) - 1;
|
||||
if (lineNum > 1) {
|
||||
rTextArea.setCaretPosition(rTextArea.getLineStartOffset(lineNum));
|
||||
}
|
||||
} catch (BadLocationException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
boolean found = SearchEngine.find(rTextArea, context);
|
||||
if (!found) {
|
||||
int pos = SearchEngine.getNextMatchPos(text, rTextArea.getText(), forward, matchCase, wholeWord);
|
||||
if (pos != -1) {
|
||||
rTextArea.setCaretPosition(forward ? 0 : rTextArea.getDocument().getLength() - 1);
|
||||
search(direction);
|
||||
searchField.setBackground(COLOR_BG_WARN);
|
||||
return;
|
||||
}
|
||||
searchField.setBackground(COLOR_BG_ERROR);
|
||||
} else {
|
||||
searchField.setBackground(COLOR_BG_NORMAL);
|
||||
}
|
||||
}
|
||||
|
||||
private class ForwardListener implements ActionListener {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
search(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,7 @@ import jadx.api.JavaClass;
|
||||
import jadx.api.JavaField;
|
||||
import jadx.api.JavaMethod;
|
||||
import jadx.core.dex.info.AccessInfo;
|
||||
import jadx.gui.Utils;
|
||||
import jadx.gui.utils.Utils;
|
||||
|
||||
import javax.swing.Icon;
|
||||
import javax.swing.ImageIcon;
|
||||
|
||||
@@ -2,7 +2,7 @@ package jadx.gui.treemodel;
|
||||
|
||||
import jadx.api.JavaField;
|
||||
import jadx.core.dex.info.AccessInfo;
|
||||
import jadx.gui.Utils;
|
||||
import jadx.gui.utils.Utils;
|
||||
|
||||
import javax.swing.Icon;
|
||||
import javax.swing.ImageIcon;
|
||||
|
||||
@@ -2,7 +2,7 @@ package jadx.gui.treemodel;
|
||||
|
||||
import jadx.api.JavaMethod;
|
||||
import jadx.core.dex.info.AccessInfo;
|
||||
import jadx.gui.Utils;
|
||||
import jadx.gui.utils.Utils;
|
||||
|
||||
import javax.swing.Icon;
|
||||
import javax.swing.ImageIcon;
|
||||
|
||||
@@ -2,7 +2,7 @@ package jadx.gui.treemodel;
|
||||
|
||||
import jadx.api.JavaClass;
|
||||
import jadx.api.JavaPackage;
|
||||
import jadx.gui.Utils;
|
||||
import jadx.gui.utils.Utils;
|
||||
|
||||
import javax.swing.Icon;
|
||||
import javax.swing.ImageIcon;
|
||||
|
||||
@@ -2,7 +2,7 @@ package jadx.gui.treemodel;
|
||||
|
||||
import jadx.api.JavaPackage;
|
||||
import jadx.gui.JadxWrapper;
|
||||
import jadx.gui.Utils;
|
||||
import jadx.gui.utils.Utils;
|
||||
|
||||
import javax.swing.Icon;
|
||||
import javax.swing.ImageIcon;
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package jadx.gui;
|
||||
package jadx.gui.utils;
|
||||
|
||||
import java.util.Locale;
|
||||
import java.util.ResourceBundle;
|
||||
+9
-1
@@ -1,8 +1,11 @@
|
||||
package jadx.gui;
|
||||
package jadx.gui.utils;
|
||||
|
||||
import jadx.core.utils.exceptions.JadxRuntimeException;
|
||||
|
||||
import javax.swing.AbstractAction;
|
||||
import javax.swing.ImageIcon;
|
||||
import javax.swing.JComponent;
|
||||
import javax.swing.KeyStroke;
|
||||
import java.net.URL;
|
||||
|
||||
public class Utils {
|
||||
@@ -15,4 +18,9 @@ public class Utils {
|
||||
}
|
||||
return new ImageIcon(resource);
|
||||
}
|
||||
|
||||
public static void addKeyBinding(JComponent comp, KeyStroke key, String id, AbstractAction action) {
|
||||
comp.getInputMap().put(key, id);
|
||||
comp.getActionMap().put(id, action);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user