mirror of
https://github.com/skylot/jadx.git
synced 2024-11-23 21:00:06 +00:00
* feat(gui): allow user to customize shortcuts * internal: fixed other constructor for jadx action * make code area actions customizable * show warning dialog when mouse button is commonly used * applied code formatting * code formatting and and moved string to resources * moved action related classes to their own package * added fix for actions with modifiers in macos * ignore left click in shortcut edit * applied code formatting * warn user when a duplicate shortcut is entered * save shortcut when key is pressed (instead of typed) * fix node under mouse being ignored Co-authored-by: skylot <118523+skylot@users.noreply.github.com> * add missing import * applied code formatting * added custom shortcuts support to script content panel * save shortcut when key is released (instead of pressed) * enable custom shortcut in script autocomplete * fix duplicate shortcut warning when the shortcut is set again at the same action * fixed mouse buttons shortcut not working for code area * fix exception with mouse button shortcuts * fix action getting fired twice * added variants for forward and back nav actions * fix exception when shortcut is not saved * fix mouse button shortcut for auto complete action * consume mouse event if bound to an action * workaround not being able to extend HashMap * fix exception in script code area when using mouse button shortcut * minor pref serialiazation improvement * fix action buttons not working (like run action) * fix exception with plugin actinos * fixed nullptr when adding an action with null actionmodel to jadxmenu * fix plugin action name not showing --------- Co-authored-by: skylot <118523+skylot@users.noreply.github.com>
This commit is contained in:
parent
868e99e0b5
commit
68b84ea786
@ -34,12 +34,14 @@ public class CodePopupAction {
|
|||||||
|
|
||||||
public NodeAction(CodePopupAction data, CodeArea codeArea) {
|
public NodeAction(CodePopupAction data, CodeArea codeArea) {
|
||||||
super(data.name, codeArea);
|
super(data.name, codeArea);
|
||||||
|
setName(data.name);
|
||||||
|
setShortcutComponent(codeArea);
|
||||||
if (data.keyBinding != null) {
|
if (data.keyBinding != null) {
|
||||||
KeyStroke key = KeyStroke.getKeyStroke(data.keyBinding);
|
KeyStroke key = KeyStroke.getKeyStroke(data.keyBinding);
|
||||||
if (key == null) {
|
if (key == null) {
|
||||||
throw new IllegalArgumentException("Failed to parse key stroke: " + data.keyBinding);
|
throw new IllegalArgumentException("Failed to parse key stroke: " + data.keyBinding);
|
||||||
}
|
}
|
||||||
addKeyBinding(key, data.name);
|
setKeyBinding(key);
|
||||||
}
|
}
|
||||||
this.data = data;
|
this.data = data;
|
||||||
}
|
}
|
||||||
|
@ -1,23 +1,21 @@
|
|||||||
package jadx.gui.plugins.script;
|
package jadx.gui.plugins.script;
|
||||||
|
|
||||||
import java.awt.event.KeyEvent;
|
|
||||||
|
|
||||||
import javax.swing.KeyStroke;
|
|
||||||
|
|
||||||
import org.fife.ui.autocomplete.AutoCompletion;
|
import org.fife.ui.autocomplete.AutoCompletion;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
import jadx.api.ICodeInfo;
|
import jadx.api.ICodeInfo;
|
||||||
import jadx.gui.settings.JadxSettings;
|
import jadx.gui.settings.JadxSettings;
|
||||||
import jadx.gui.treemodel.JInputScript;
|
import jadx.gui.treemodel.JInputScript;
|
||||||
|
import jadx.gui.ui.action.JadxAutoCompletion;
|
||||||
import jadx.gui.ui.codearea.AbstractCodeArea;
|
import jadx.gui.ui.codearea.AbstractCodeArea;
|
||||||
import jadx.gui.ui.panel.ContentPanel;
|
import jadx.gui.ui.panel.ContentPanel;
|
||||||
import jadx.gui.utils.UiUtils;
|
import jadx.gui.utils.shortcut.ShortcutsController;
|
||||||
|
|
||||||
public class ScriptCodeArea extends AbstractCodeArea {
|
public class ScriptCodeArea extends AbstractCodeArea {
|
||||||
|
|
||||||
private final JInputScript scriptNode;
|
private final JInputScript scriptNode;
|
||||||
private final AutoCompletion autoCompletion;
|
private final AutoCompletion autoCompletion;
|
||||||
|
private final ShortcutsController shortcutsController;
|
||||||
|
|
||||||
public ScriptCodeArea(ContentPanel contentPanel, JInputScript node) {
|
public ScriptCodeArea(ContentPanel contentPanel, JInputScript node) {
|
||||||
super(contentPanel, node);
|
super(contentPanel, node);
|
||||||
@ -27,6 +25,7 @@ public class ScriptCodeArea extends AbstractCodeArea {
|
|||||||
setCodeFoldingEnabled(true);
|
setCodeFoldingEnabled(true);
|
||||||
setCloseCurlyBraces(true);
|
setCloseCurlyBraces(true);
|
||||||
|
|
||||||
|
shortcutsController = contentPanel.getTabbedPane().getMainWindow().getShortcutsController();
|
||||||
JadxSettings settings = contentPanel.getTabbedPane().getMainWindow().getSettings();
|
JadxSettings settings = contentPanel.getTabbedPane().getMainWindow().getSettings();
|
||||||
autoCompletion = addAutoComplete(settings);
|
autoCompletion = addAutoComplete(settings);
|
||||||
}
|
}
|
||||||
@ -34,12 +33,12 @@ public class ScriptCodeArea extends AbstractCodeArea {
|
|||||||
private AutoCompletion addAutoComplete(JadxSettings settings) {
|
private AutoCompletion addAutoComplete(JadxSettings settings) {
|
||||||
ScriptCompleteProvider provider = new ScriptCompleteProvider(this);
|
ScriptCompleteProvider provider = new ScriptCompleteProvider(this);
|
||||||
provider.setAutoActivationRules(false, ".");
|
provider.setAutoActivationRules(false, ".");
|
||||||
AutoCompletion ac = new AutoCompletion(provider);
|
JadxAutoCompletion ac = new JadxAutoCompletion(provider);
|
||||||
ac.setListCellRenderer(new ScriptCompletionRenderer(settings));
|
ac.setListCellRenderer(new ScriptCompletionRenderer(settings));
|
||||||
ac.setTriggerKey(KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, UiUtils.ctrlButton()));
|
|
||||||
ac.setAutoActivationEnabled(true);
|
ac.setAutoActivationEnabled(true);
|
||||||
ac.setAutoCompleteSingleChoices(true);
|
ac.setAutoCompleteSingleChoices(true);
|
||||||
ac.install(this);
|
ac.install(this);
|
||||||
|
shortcutsController.bindImmediate(ac);
|
||||||
return ac;
|
return ac;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -80,6 +79,7 @@ public class ScriptCodeArea extends AbstractCodeArea {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void dispose() {
|
public void dispose() {
|
||||||
|
shortcutsController.unbindActionsForComponent(this);
|
||||||
autoCompletion.uninstall();
|
autoCompletion.uninstall();
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
@ -29,13 +29,14 @@ import jadx.gui.settings.LineNumbersMode;
|
|||||||
import jadx.gui.treemodel.JInputScript;
|
import jadx.gui.treemodel.JInputScript;
|
||||||
import jadx.gui.ui.MainWindow;
|
import jadx.gui.ui.MainWindow;
|
||||||
import jadx.gui.ui.TabbedPane;
|
import jadx.gui.ui.TabbedPane;
|
||||||
|
import jadx.gui.ui.action.ActionModel;
|
||||||
|
import jadx.gui.ui.action.JadxGuiAction;
|
||||||
import jadx.gui.ui.codearea.AbstractCodeArea;
|
import jadx.gui.ui.codearea.AbstractCodeArea;
|
||||||
import jadx.gui.ui.codearea.AbstractCodeContentPanel;
|
import jadx.gui.ui.codearea.AbstractCodeContentPanel;
|
||||||
import jadx.gui.ui.codearea.SearchBar;
|
import jadx.gui.ui.codearea.SearchBar;
|
||||||
import jadx.gui.utils.Icons;
|
import jadx.gui.utils.Icons;
|
||||||
import jadx.gui.utils.NLS;
|
import jadx.gui.utils.NLS;
|
||||||
import jadx.gui.utils.UiUtils;
|
import jadx.gui.utils.UiUtils;
|
||||||
import jadx.gui.utils.ui.ActionHandler;
|
|
||||||
import jadx.gui.utils.ui.NodeLabel;
|
import jadx.gui.utils.ui.NodeLabel;
|
||||||
import jadx.plugins.script.ide.ScriptAnalyzeResult;
|
import jadx.plugins.script.ide.ScriptAnalyzeResult;
|
||||||
import jadx.plugins.script.ide.ScriptServices;
|
import jadx.plugins.script.ide.ScriptServices;
|
||||||
@ -89,15 +90,14 @@ public class ScriptContentPanel extends AbstractCodeContentPanel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private JPanel buildScriptActionsPanel() {
|
private JPanel buildScriptActionsPanel() {
|
||||||
ActionHandler runAction = new ActionHandler(this::runScript);
|
JadxGuiAction runAction = new JadxGuiAction(ActionModel.SCRIPT_RUN, this::runScript);
|
||||||
runAction.setNameAndDesc(NLS.str("script.run"));
|
JadxGuiAction saveAction = new JadxGuiAction(ActionModel.SCRIPT_SAVE, scriptArea::save);
|
||||||
runAction.setIcon(Icons.RUN);
|
|
||||||
runAction.attachKeyBindingFor(scriptArea, KeyStroke.getKeyStroke(KeyEvent.VK_F8, 0));
|
|
||||||
|
|
||||||
ActionHandler saveAction = new ActionHandler(scriptArea::save);
|
runAction.setShortcutComponent(scriptArea);
|
||||||
saveAction.setNameAndDesc(NLS.str("script.save"));
|
saveAction.setShortcutComponent(scriptArea);
|
||||||
saveAction.setIcon(Icons.SAVE_ALL);
|
|
||||||
saveAction.setKeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_S, UiUtils.ctrlButton()));
|
tabbedPane.getMainWindow().getShortcutsController().bindImmediate(runAction);
|
||||||
|
tabbedPane.getMainWindow().getShortcutsController().bindImmediate(saveAction);
|
||||||
|
|
||||||
JButton save = saveAction.makeButton();
|
JButton save = saveAction.makeButton();
|
||||||
scriptArea.getScriptNode().addChangeListener(save::setEnabled);
|
scriptArea.getScriptNode().addChangeListener(save::setEnabled);
|
||||||
|
@ -37,12 +37,15 @@ import jadx.cli.JadxCLIArgs;
|
|||||||
import jadx.cli.LogHelper;
|
import jadx.cli.LogHelper;
|
||||||
import jadx.gui.cache.code.CodeCacheMode;
|
import jadx.gui.cache.code.CodeCacheMode;
|
||||||
import jadx.gui.cache.usage.UsageCacheMode;
|
import jadx.gui.cache.usage.UsageCacheMode;
|
||||||
|
import jadx.gui.settings.data.ShortcutsWrapper;
|
||||||
import jadx.gui.ui.MainWindow;
|
import jadx.gui.ui.MainWindow;
|
||||||
|
import jadx.gui.ui.action.ActionModel;
|
||||||
import jadx.gui.ui.codearea.EditorTheme;
|
import jadx.gui.ui.codearea.EditorTheme;
|
||||||
import jadx.gui.utils.FontUtils;
|
import jadx.gui.utils.FontUtils;
|
||||||
import jadx.gui.utils.LafManager;
|
import jadx.gui.utils.LafManager;
|
||||||
import jadx.gui.utils.LangLocale;
|
import jadx.gui.utils.LangLocale;
|
||||||
import jadx.gui.utils.NLS;
|
import jadx.gui.utils.NLS;
|
||||||
|
import jadx.gui.utils.shortcut.Shortcut;
|
||||||
|
|
||||||
public class JadxSettings extends JadxCLIArgs {
|
public class JadxSettings extends JadxCLIArgs {
|
||||||
private static final Logger LOG = LoggerFactory.getLogger(JadxSettings.class);
|
private static final Logger LOG = LoggerFactory.getLogger(JadxSettings.class);
|
||||||
@ -73,6 +76,10 @@ public class JadxSettings extends JadxCLIArgs {
|
|||||||
private boolean autoStartJobs = false;
|
private boolean autoStartJobs = false;
|
||||||
private String excludedPackages = "";
|
private String excludedPackages = "";
|
||||||
private boolean autoSaveProject = true;
|
private boolean autoSaveProject = true;
|
||||||
|
private Map<ActionModel, Shortcut> shortcuts = new HashMap<>();
|
||||||
|
|
||||||
|
@JadxSettingsAdapter.GsonExclude
|
||||||
|
private ShortcutsWrapper shortcutsWrapper = null;
|
||||||
|
|
||||||
private boolean showHeapUsageBar = false;
|
private boolean showHeapUsageBar = false;
|
||||||
private boolean alwaysSelectOpened = false;
|
private boolean alwaysSelectOpened = false;
|
||||||
@ -442,6 +449,14 @@ public class JadxSettings extends JadxCLIArgs {
|
|||||||
this.autoSaveProject = autoSaveProject;
|
this.autoSaveProject = autoSaveProject;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ShortcutsWrapper getShortcuts() {
|
||||||
|
if (shortcutsWrapper == null) {
|
||||||
|
shortcutsWrapper = new ShortcutsWrapper();
|
||||||
|
shortcutsWrapper.updateShortcuts(shortcuts);
|
||||||
|
}
|
||||||
|
return shortcutsWrapper;
|
||||||
|
}
|
||||||
|
|
||||||
public void setExportAsGradleProject(boolean exportAsGradleProject) {
|
public void setExportAsGradleProject(boolean exportAsGradleProject) {
|
||||||
this.exportAsGradleProject = exportAsGradleProject;
|
this.exportAsGradleProject = exportAsGradleProject;
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,22 @@
|
|||||||
|
package jadx.gui.settings.data;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import jadx.gui.ui.action.ActionModel;
|
||||||
|
import jadx.gui.utils.shortcut.Shortcut;
|
||||||
|
|
||||||
|
public class ShortcutsWrapper {
|
||||||
|
private Map<ActionModel, Shortcut> shortcuts;
|
||||||
|
|
||||||
|
public void updateShortcuts(Map<ActionModel, Shortcut> shortcuts) {
|
||||||
|
this.shortcuts = shortcuts;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Shortcut get(ActionModel actionModel) {
|
||||||
|
return shortcuts.getOrDefault(actionModel, actionModel.getDefaultShortcut());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void put(ActionModel actionModel, Shortcut shortcut) {
|
||||||
|
shortcuts.put(actionModel, shortcut);
|
||||||
|
}
|
||||||
|
}
|
@ -62,6 +62,7 @@ import jadx.gui.settings.JadxSettingsAdapter;
|
|||||||
import jadx.gui.settings.LineNumbersMode;
|
import jadx.gui.settings.LineNumbersMode;
|
||||||
import jadx.gui.settings.ui.cache.CacheSettingsGroup;
|
import jadx.gui.settings.ui.cache.CacheSettingsGroup;
|
||||||
import jadx.gui.settings.ui.plugins.PluginsSettings;
|
import jadx.gui.settings.ui.plugins.PluginsSettings;
|
||||||
|
import jadx.gui.settings.ui.shortcut.ShortcutsSettingsGroup;
|
||||||
import jadx.gui.ui.MainWindow;
|
import jadx.gui.ui.MainWindow;
|
||||||
import jadx.gui.ui.codearea.EditorTheme;
|
import jadx.gui.ui.codearea.EditorTheme;
|
||||||
import jadx.gui.utils.FontUtils;
|
import jadx.gui.utils.FontUtils;
|
||||||
@ -126,6 +127,7 @@ public class JadxSettingsWindow extends JDialog {
|
|||||||
groups.add(makeRenameGroup());
|
groups.add(makeRenameGroup());
|
||||||
groups.add(new CacheSettingsGroup(this));
|
groups.add(new CacheSettingsGroup(this));
|
||||||
groups.add(makeAppearanceGroup());
|
groups.add(makeAppearanceGroup());
|
||||||
|
groups.add(new ShortcutsSettingsGroup(this, settings));
|
||||||
groups.add(makeSearchResGroup());
|
groups.add(makeSearchResGroup());
|
||||||
groups.add(makeProjectGroup());
|
groups.add(makeProjectGroup());
|
||||||
groups.add(new PluginsSettings(mainWindow, settings).build());
|
groups.add(new PluginsSettings(mainWindow, settings).build());
|
||||||
@ -640,6 +642,7 @@ public class JadxSettingsWindow extends JDialog {
|
|||||||
enableComponents(this, false);
|
enableComponents(this, false);
|
||||||
SwingUtilities.invokeLater(() -> {
|
SwingUtilities.invokeLater(() -> {
|
||||||
if (shouldReload()) {
|
if (shouldReload()) {
|
||||||
|
mainWindow.getShortcutsController().loadSettings();
|
||||||
mainWindow.reopen();
|
mainWindow.reopen();
|
||||||
}
|
}
|
||||||
if (!settings.getLangLocale().equals(prevLang)) {
|
if (!settings.getLangLocale().equals(prevLang)) {
|
||||||
|
@ -0,0 +1,193 @@
|
|||||||
|
package jadx.gui.settings.ui.shortcut;
|
||||||
|
|
||||||
|
import java.awt.AWTEvent;
|
||||||
|
import java.awt.KeyboardFocusManager;
|
||||||
|
import java.awt.Toolkit;
|
||||||
|
import java.awt.event.FocusEvent;
|
||||||
|
import java.awt.event.FocusListener;
|
||||||
|
import java.awt.event.KeyEvent;
|
||||||
|
import java.awt.event.MouseEvent;
|
||||||
|
|
||||||
|
import javax.swing.BoxLayout;
|
||||||
|
import javax.swing.Icon;
|
||||||
|
import javax.swing.JButton;
|
||||||
|
import javax.swing.JOptionPane;
|
||||||
|
import javax.swing.JPanel;
|
||||||
|
import javax.swing.JTextField;
|
||||||
|
import javax.swing.UIManager;
|
||||||
|
|
||||||
|
import jadx.gui.settings.JadxSettings;
|
||||||
|
import jadx.gui.settings.ui.JadxSettingsWindow;
|
||||||
|
import jadx.gui.ui.action.ActionModel;
|
||||||
|
import jadx.gui.utils.NLS;
|
||||||
|
import jadx.gui.utils.UiUtils;
|
||||||
|
import jadx.gui.utils.shortcut.Shortcut;
|
||||||
|
|
||||||
|
public class ShortcutEdit extends JPanel {
|
||||||
|
private static final Icon CLEAR_ICON = UiUtils.openSvgIcon("ui/close");
|
||||||
|
|
||||||
|
private final ActionModel actionModel;
|
||||||
|
private final JadxSettingsWindow settingsWindow;
|
||||||
|
private final JadxSettings settings;
|
||||||
|
private final TextField textField;
|
||||||
|
|
||||||
|
public Shortcut shortcut;
|
||||||
|
|
||||||
|
public ShortcutEdit(ActionModel actionModel, JadxSettingsWindow settingsWindow, JadxSettings settings) {
|
||||||
|
this.actionModel = actionModel;
|
||||||
|
this.settings = settings;
|
||||||
|
this.settingsWindow = settingsWindow;
|
||||||
|
|
||||||
|
textField = new TextField();
|
||||||
|
JButton clearButton = new JButton(CLEAR_ICON);
|
||||||
|
|
||||||
|
setLayout(new BoxLayout(this, BoxLayout.X_AXIS));
|
||||||
|
add(textField);
|
||||||
|
add(clearButton);
|
||||||
|
|
||||||
|
clearButton.addActionListener(e -> {
|
||||||
|
setShortcut(Shortcut.none());
|
||||||
|
saveShortcut();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setShortcut(Shortcut shortcut) {
|
||||||
|
this.shortcut = shortcut;
|
||||||
|
textField.reload();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void saveShortcut() {
|
||||||
|
settings.getShortcuts().put(actionModel, shortcut);
|
||||||
|
settingsWindow.needReload();
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean verifyShortcut(Shortcut shortcut) {
|
||||||
|
ActionModel otherAction = null;
|
||||||
|
for (ActionModel a : ActionModel.values()) {
|
||||||
|
if (actionModel != a && shortcut.equals(settings.getShortcuts().get(a))) {
|
||||||
|
otherAction = a;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (otherAction != null) {
|
||||||
|
int dialogResult = JOptionPane.showConfirmDialog(
|
||||||
|
this,
|
||||||
|
NLS.str("msg.duplicate_shortcut",
|
||||||
|
shortcut,
|
||||||
|
otherAction.getName(),
|
||||||
|
otherAction.getCategory().getName()),
|
||||||
|
NLS.str("msg.warning_title"),
|
||||||
|
JOptionPane.YES_NO_OPTION);
|
||||||
|
if (dialogResult != 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private class TextField extends JTextField {
|
||||||
|
private Shortcut tempShortcut;
|
||||||
|
|
||||||
|
public TextField() {
|
||||||
|
KeyboardFocusManager.getCurrentKeyboardFocusManager().addKeyEventDispatcher(ev -> {
|
||||||
|
if (!isListening()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ev.getID() == KeyEvent.KEY_PRESSED) {
|
||||||
|
Shortcut pressedShortcut = Shortcut.keyboard(ev.getKeyCode(), ev.getModifiersEx());
|
||||||
|
if (pressedShortcut.isValidKeyboard()) {
|
||||||
|
tempShortcut = pressedShortcut;
|
||||||
|
refresh(tempShortcut);
|
||||||
|
} else {
|
||||||
|
tempShortcut = null;
|
||||||
|
}
|
||||||
|
} else if (ev.getID() == KeyEvent.KEY_RELEASED) {
|
||||||
|
removeFocus();
|
||||||
|
}
|
||||||
|
ev.consume();
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
|
||||||
|
addFocusListener(new FocusListener() {
|
||||||
|
@Override
|
||||||
|
public void focusGained(FocusEvent ev) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void focusLost(FocusEvent ev) {
|
||||||
|
if (tempShortcut != null) {
|
||||||
|
if (verifyShortcut(tempShortcut)) {
|
||||||
|
shortcut = tempShortcut;
|
||||||
|
saveShortcut();
|
||||||
|
} else {
|
||||||
|
reload();
|
||||||
|
}
|
||||||
|
tempShortcut = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Toolkit.getDefaultToolkit().addAWTEventListener(event -> {
|
||||||
|
if (!isListening()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event instanceof MouseEvent) {
|
||||||
|
MouseEvent mouseEvent = (MouseEvent) event;
|
||||||
|
if (mouseEvent.getID() == MouseEvent.MOUSE_PRESSED) {
|
||||||
|
int mouseButton = mouseEvent.getButton();
|
||||||
|
|
||||||
|
if (mouseButton <= MouseEvent.BUTTON1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mouseButton <= MouseEvent.BUTTON3) {
|
||||||
|
int dialogResult = JOptionPane.showConfirmDialog(
|
||||||
|
this,
|
||||||
|
NLS.str("msg.common_mouse_shortcut"),
|
||||||
|
NLS.str("msg.warning_title"),
|
||||||
|
JOptionPane.YES_NO_OPTION);
|
||||||
|
if (dialogResult != 0) {
|
||||||
|
((MouseEvent) event).consume();
|
||||||
|
tempShortcut = null;
|
||||||
|
removeFocus();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
((MouseEvent) event).consume();
|
||||||
|
tempShortcut = Shortcut.mouse(mouseButton);
|
||||||
|
refresh(tempShortcut);
|
||||||
|
removeFocus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, AWTEvent.MOUSE_EVENT_MASK);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void reload() {
|
||||||
|
refresh(shortcut);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void refresh(Shortcut displayedShortcut) {
|
||||||
|
if (displayedShortcut == null || displayedShortcut.isNone()) {
|
||||||
|
setText("None");
|
||||||
|
setForeground(UIManager.getColor("TextArea.inactiveForeground"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setText(displayedShortcut.toString());
|
||||||
|
setForeground(UIManager.getColor("TextArea.foreground"));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void removeFocus() {
|
||||||
|
// triggers focusLost
|
||||||
|
getRootPane().requestFocus();
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isListening() {
|
||||||
|
return isFocusOwner();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,60 @@
|
|||||||
|
package jadx.gui.settings.ui.shortcut;
|
||||||
|
|
||||||
|
import java.awt.BorderLayout;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import javax.swing.JComponent;
|
||||||
|
import javax.swing.JLabel;
|
||||||
|
import javax.swing.JPanel;
|
||||||
|
|
||||||
|
import jadx.api.plugins.gui.ISettingsGroup;
|
||||||
|
import jadx.gui.settings.JadxSettings;
|
||||||
|
import jadx.gui.settings.ui.JadxSettingsWindow;
|
||||||
|
import jadx.gui.settings.ui.SettingsGroup;
|
||||||
|
import jadx.gui.ui.action.ActionCategory;
|
||||||
|
import jadx.gui.ui.action.ActionModel;
|
||||||
|
import jadx.gui.utils.NLS;
|
||||||
|
import jadx.gui.utils.shortcut.Shortcut;
|
||||||
|
|
||||||
|
public class ShortcutsSettingsGroup implements ISettingsGroup {
|
||||||
|
private final JadxSettingsWindow settingsWindow;
|
||||||
|
private final JadxSettings settings;
|
||||||
|
|
||||||
|
public ShortcutsSettingsGroup(JadxSettingsWindow settingsWindow, JadxSettings settings) {
|
||||||
|
this.settingsWindow = settingsWindow;
|
||||||
|
this.settings = settings;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getTitle() {
|
||||||
|
return NLS.str("preferences.shortcuts");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JComponent buildComponent() {
|
||||||
|
JPanel panel = new JPanel();
|
||||||
|
panel.setLayout(new BorderLayout());
|
||||||
|
panel.add(new JLabel(NLS.str("preferences.select_shortcuts")), BorderLayout.NORTH);
|
||||||
|
return panel;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<ISettingsGroup> getSubGroups() {
|
||||||
|
return Arrays.stream(ActionCategory.values())
|
||||||
|
.map(this::makeShortcutsGroup)
|
||||||
|
.collect(Collectors.toUnmodifiableList());
|
||||||
|
}
|
||||||
|
|
||||||
|
private SettingsGroup makeShortcutsGroup(ActionCategory category) {
|
||||||
|
SettingsGroup group = new SettingsGroup(category.getName());
|
||||||
|
for (ActionModel actionModel : ActionModel.select(category)) {
|
||||||
|
Shortcut shortcut = settings.getShortcuts().get(actionModel);
|
||||||
|
ShortcutEdit edit = new ShortcutEdit(actionModel, settingsWindow, settings);
|
||||||
|
edit.setShortcut(shortcut);
|
||||||
|
group.addRow(actionModel.getName(), edit);
|
||||||
|
}
|
||||||
|
return group;
|
||||||
|
}
|
||||||
|
}
|
@ -1,6 +1,5 @@
|
|||||||
package jadx.gui.ui;
|
package jadx.gui.ui;
|
||||||
|
|
||||||
import java.awt.AWTEvent;
|
|
||||||
import java.awt.BorderLayout;
|
import java.awt.BorderLayout;
|
||||||
import java.awt.Component;
|
import java.awt.Component;
|
||||||
import java.awt.Dimension;
|
import java.awt.Dimension;
|
||||||
@ -9,14 +8,12 @@ import java.awt.Font;
|
|||||||
import java.awt.GraphicsDevice;
|
import java.awt.GraphicsDevice;
|
||||||
import java.awt.GraphicsEnvironment;
|
import java.awt.GraphicsEnvironment;
|
||||||
import java.awt.Rectangle;
|
import java.awt.Rectangle;
|
||||||
import java.awt.Toolkit;
|
|
||||||
import java.awt.dnd.DnDConstants;
|
import java.awt.dnd.DnDConstants;
|
||||||
import java.awt.dnd.DropTarget;
|
import java.awt.dnd.DropTarget;
|
||||||
import java.awt.event.ActionEvent;
|
import java.awt.event.ActionEvent;
|
||||||
import java.awt.event.ActionListener;
|
import java.awt.event.ActionListener;
|
||||||
import java.awt.event.FocusAdapter;
|
import java.awt.event.FocusAdapter;
|
||||||
import java.awt.event.FocusEvent;
|
import java.awt.event.FocusEvent;
|
||||||
import java.awt.event.InputEvent;
|
|
||||||
import java.awt.event.KeyAdapter;
|
import java.awt.event.KeyAdapter;
|
||||||
import java.awt.event.KeyEvent;
|
import java.awt.event.KeyEvent;
|
||||||
import java.awt.event.MouseAdapter;
|
import java.awt.event.MouseAdapter;
|
||||||
@ -45,10 +42,10 @@ import javax.swing.Action;
|
|||||||
import javax.swing.Box;
|
import javax.swing.Box;
|
||||||
import javax.swing.ImageIcon;
|
import javax.swing.ImageIcon;
|
||||||
import javax.swing.JCheckBoxMenuItem;
|
import javax.swing.JCheckBoxMenuItem;
|
||||||
|
import javax.swing.JDialog;
|
||||||
import javax.swing.JFileChooser;
|
import javax.swing.JFileChooser;
|
||||||
import javax.swing.JFrame;
|
import javax.swing.JFrame;
|
||||||
import javax.swing.JMenu;
|
import javax.swing.JMenu;
|
||||||
import javax.swing.JMenuBar;
|
|
||||||
import javax.swing.JOptionPane;
|
import javax.swing.JOptionPane;
|
||||||
import javax.swing.JPanel;
|
import javax.swing.JPanel;
|
||||||
import javax.swing.JPopupMenu;
|
import javax.swing.JPopupMenu;
|
||||||
@ -118,6 +115,8 @@ import jadx.gui.treemodel.JNode;
|
|||||||
import jadx.gui.treemodel.JPackage;
|
import jadx.gui.treemodel.JPackage;
|
||||||
import jadx.gui.treemodel.JResource;
|
import jadx.gui.treemodel.JResource;
|
||||||
import jadx.gui.treemodel.JRoot;
|
import jadx.gui.treemodel.JRoot;
|
||||||
|
import jadx.gui.ui.action.ActionModel;
|
||||||
|
import jadx.gui.ui.action.JadxGuiAction;
|
||||||
import jadx.gui.ui.codearea.AbstractCodeArea;
|
import jadx.gui.ui.codearea.AbstractCodeArea;
|
||||||
import jadx.gui.ui.codearea.AbstractCodeContentPanel;
|
import jadx.gui.ui.codearea.AbstractCodeContentPanel;
|
||||||
import jadx.gui.ui.codearea.EditorTheme;
|
import jadx.gui.ui.codearea.EditorTheme;
|
||||||
@ -128,6 +127,9 @@ import jadx.gui.ui.dialog.LogViewerDialog;
|
|||||||
import jadx.gui.ui.dialog.SearchDialog;
|
import jadx.gui.ui.dialog.SearchDialog;
|
||||||
import jadx.gui.ui.filedialog.FileDialogWrapper;
|
import jadx.gui.ui.filedialog.FileDialogWrapper;
|
||||||
import jadx.gui.ui.filedialog.FileOpenMode;
|
import jadx.gui.ui.filedialog.FileOpenMode;
|
||||||
|
import jadx.gui.ui.menu.HiddenMenuItem;
|
||||||
|
import jadx.gui.ui.menu.JadxMenu;
|
||||||
|
import jadx.gui.ui.menu.JadxMenuBar;
|
||||||
import jadx.gui.ui.panel.ContentPanel;
|
import jadx.gui.ui.panel.ContentPanel;
|
||||||
import jadx.gui.ui.panel.IssuesPanel;
|
import jadx.gui.ui.panel.IssuesPanel;
|
||||||
import jadx.gui.ui.panel.JDebuggerPanel;
|
import jadx.gui.ui.panel.JDebuggerPanel;
|
||||||
@ -141,18 +143,16 @@ import jadx.gui.update.data.Release;
|
|||||||
import jadx.gui.utils.CacheObject;
|
import jadx.gui.utils.CacheObject;
|
||||||
import jadx.gui.utils.FontUtils;
|
import jadx.gui.utils.FontUtils;
|
||||||
import jadx.gui.utils.ILoadListener;
|
import jadx.gui.utils.ILoadListener;
|
||||||
import jadx.gui.utils.Icons;
|
|
||||||
import jadx.gui.utils.LafManager;
|
import jadx.gui.utils.LafManager;
|
||||||
import jadx.gui.utils.Link;
|
import jadx.gui.utils.Link;
|
||||||
import jadx.gui.utils.NLS;
|
import jadx.gui.utils.NLS;
|
||||||
import jadx.gui.utils.SystemInfo;
|
|
||||||
import jadx.gui.utils.UiUtils;
|
import jadx.gui.utils.UiUtils;
|
||||||
import jadx.gui.utils.fileswatcher.LiveReloadWorker;
|
import jadx.gui.utils.fileswatcher.LiveReloadWorker;
|
||||||
|
import jadx.gui.utils.shortcut.ShortcutsController;
|
||||||
import jadx.gui.utils.ui.ActionHandler;
|
import jadx.gui.utils.ui.ActionHandler;
|
||||||
import jadx.gui.utils.ui.NodeLabel;
|
import jadx.gui.utils.ui.NodeLabel;
|
||||||
|
|
||||||
import static io.reactivex.internal.functions.Functions.EMPTY_RUNNABLE;
|
import static io.reactivex.internal.functions.Functions.EMPTY_RUNNABLE;
|
||||||
import static javax.swing.KeyStroke.getKeyStroke;
|
|
||||||
|
|
||||||
public class MainWindow extends JFrame {
|
public class MainWindow extends JFrame {
|
||||||
private static final Logger LOG = LoggerFactory.getLogger(MainWindow.class);
|
private static final Logger LOG = LoggerFactory.getLogger(MainWindow.class);
|
||||||
@ -191,8 +191,8 @@ public class MainWindow extends JFrame {
|
|||||||
|
|
||||||
private transient @NotNull JadxProject project;
|
private transient @NotNull JadxProject project;
|
||||||
|
|
||||||
private transient Action newProjectAction;
|
private transient JadxGuiAction newProjectAction;
|
||||||
private transient Action saveProjectAction;
|
private transient JadxGuiAction saveProjectAction;
|
||||||
|
|
||||||
private transient JPanel mainPanel;
|
private transient JPanel mainPanel;
|
||||||
private transient JSplitPane treeSplitPane;
|
private transient JSplitPane treeSplitPane;
|
||||||
@ -227,7 +227,10 @@ public class MainWindow extends JFrame {
|
|||||||
private final List<ILoadListener> loadListeners = new ArrayList<>();
|
private final List<ILoadListener> loadListeners = new ArrayList<>();
|
||||||
private final List<Consumer<JRoot>> treeUpdateListener = new ArrayList<>();
|
private final List<Consumer<JRoot>> treeUpdateListener = new ArrayList<>();
|
||||||
private boolean loaded;
|
private boolean loaded;
|
||||||
|
private boolean settingsOpen = false;
|
||||||
|
|
||||||
|
private ShortcutsController shortcutsController;
|
||||||
|
private JadxMenuBar menuBar;
|
||||||
private JMenu pluginsMenu;
|
private JMenu pluginsMenu;
|
||||||
|
|
||||||
private final transient RenameMappingsGui renameMappings;
|
private final transient RenameMappingsGui renameMappings;
|
||||||
@ -240,6 +243,7 @@ public class MainWindow extends JFrame {
|
|||||||
this.liveReloadWorker = new LiveReloadWorker(this);
|
this.liveReloadWorker = new LiveReloadWorker(this);
|
||||||
this.renameMappings = new RenameMappingsGui(this);
|
this.renameMappings = new RenameMappingsGui(this);
|
||||||
this.cacheManager = new CacheManager(settings);
|
this.cacheManager = new CacheManager(settings);
|
||||||
|
this.shortcutsController = new ShortcutsController(settings);
|
||||||
|
|
||||||
resetCache();
|
resetCache();
|
||||||
FontUtils.registerBundledFonts();
|
FontUtils.registerBundledFonts();
|
||||||
@ -247,8 +251,8 @@ public class MainWindow extends JFrame {
|
|||||||
initUI();
|
initUI();
|
||||||
this.backgroundExecutor = new BackgroundExecutor(settings, progressPane);
|
this.backgroundExecutor = new BackgroundExecutor(settings, progressPane);
|
||||||
initMenuAndToolbar();
|
initMenuAndToolbar();
|
||||||
registerMouseNavigationButtons();
|
|
||||||
UiUtils.setWindowIcons(this);
|
UiUtils.setWindowIcons(this);
|
||||||
|
shortcutsController.registerMouseEventListener(this);
|
||||||
loadSettings();
|
loadSettings();
|
||||||
|
|
||||||
update();
|
update();
|
||||||
@ -472,6 +476,8 @@ public class MainWindow extends JFrame {
|
|||||||
saveAll();
|
saveAll();
|
||||||
closeAll();
|
closeAll();
|
||||||
loadFiles(EMPTY_RUNNABLE);
|
loadFiles(EMPTY_RUNNABLE);
|
||||||
|
|
||||||
|
menuBar.reloadShortcuts();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void openProject(Path path, Runnable onFinish) {
|
private void openProject(Path path, Runnable onFinish) {
|
||||||
@ -865,99 +871,75 @@ public class MainWindow extends JFrame {
|
|||||||
tree.requestFocus();
|
tree.requestFocus();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void textSearch() {
|
||||||
|
ContentPanel panel = tabbedPane.getSelectedContentPanel();
|
||||||
|
if (panel instanceof AbstractCodeContentPanel) {
|
||||||
|
AbstractCodeArea codeArea = ((AbstractCodeContentPanel) panel).getCodeArea();
|
||||||
|
String preferText = codeArea.getSelectedText();
|
||||||
|
if (StringUtils.isEmpty(preferText)) {
|
||||||
|
preferText = codeArea.getWordUnderCaret();
|
||||||
|
}
|
||||||
|
if (!StringUtils.isEmpty(preferText)) {
|
||||||
|
SearchDialog.searchText(MainWindow.this, preferText);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SearchDialog.search(MainWindow.this, SearchDialog.SearchPreset.TEXT);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void gotoMainActivity() {
|
||||||
|
AndroidManifestParser parser = new AndroidManifestParser(
|
||||||
|
AndroidManifestParser.getAndroidManifest(getWrapper().getResources()),
|
||||||
|
EnumSet.of(AppAttribute.MAIN_ACTIVITY));
|
||||||
|
if (!parser.isManifestFound()) {
|
||||||
|
JOptionPane.showMessageDialog(MainWindow.this,
|
||||||
|
NLS.str("error_dialog.not_found", "AndroidManifest.xml"),
|
||||||
|
NLS.str("error_dialog.title"),
|
||||||
|
JOptionPane.ERROR_MESSAGE);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
ApplicationParams results = parser.parse();
|
||||||
|
if (results.getMainActivityName() == null) {
|
||||||
|
throw new JadxRuntimeException("Failed to get main activity name from manifest");
|
||||||
|
}
|
||||||
|
JavaClass mainActivityClass = results.getMainActivity(getWrapper().getDecompiler());
|
||||||
|
if (mainActivityClass == null) {
|
||||||
|
throw new JadxRuntimeException("Failed to find main activity class: " + results.getMainActivityName());
|
||||||
|
}
|
||||||
|
tabbedPane.codeJump(getCacheObject().getNodeCache().makeFrom(mainActivityClass));
|
||||||
|
} catch (Exception e) {
|
||||||
|
LOG.error("Main activity not found", e);
|
||||||
|
JOptionPane.showMessageDialog(MainWindow.this,
|
||||||
|
NLS.str("error_dialog.not_found", "Main Activity"),
|
||||||
|
NLS.str("error_dialog.title"),
|
||||||
|
JOptionPane.ERROR_MESSAGE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void initMenuAndToolbar() {
|
private void initMenuAndToolbar() {
|
||||||
ActionHandler openAction = new ActionHandler(this::openFileDialog);
|
JadxGuiAction openAction = new JadxGuiAction(ActionModel.OPEN, this::openFileDialog);
|
||||||
openAction.setNameAndDesc(NLS.str("file.open_action"));
|
JadxGuiAction openProject = new JadxGuiAction(ActionModel.OPEN_PROJECT, this::openProjectDialog);
|
||||||
openAction.setIcon(Icons.OPEN);
|
|
||||||
openAction.setKeyBinding(getKeyStroke(KeyEvent.VK_O, UiUtils.ctrlButton()));
|
|
||||||
|
|
||||||
ActionHandler openProject = new ActionHandler(this::openProjectDialog);
|
JadxGuiAction addFilesAction = new JadxGuiAction(ActionModel.ADD_FILES, () -> addFiles());
|
||||||
openProject.setNameAndDesc(NLS.str("file.open_project"));
|
newProjectAction = new JadxGuiAction(ActionModel.NEW_PROJECT, this::newProject);
|
||||||
openProject.setIcon(Icons.OPEN_PROJECT);
|
saveProjectAction = new JadxGuiAction(ActionModel.SAVE_PROJECT, this::saveProject);
|
||||||
openProject.setKeyBinding(getKeyStroke(KeyEvent.VK_O, InputEvent.SHIFT_DOWN_MASK | UiUtils.ctrlButton()));
|
JadxGuiAction saveProjectAsAction = new JadxGuiAction(ActionModel.SAVE_PROJECT_AS, this::saveProjectAs);
|
||||||
|
JadxGuiAction reloadAction = new JadxGuiAction(ActionModel.RELOAD, () -> UiUtils.uiRun(this::reopen));
|
||||||
|
JadxGuiAction liveReloadAction = new JadxGuiAction(ActionModel.LIVE_RELOAD,
|
||||||
|
() -> updateLiveReload(!project.isEnableLiveReload()));
|
||||||
|
|
||||||
Action addFilesAction = new AbstractAction(NLS.str("file.add_files_action"), ICON_ADD_FILES) {
|
liveReloadMenuItem = new JCheckBoxMenuItem(liveReloadAction);
|
||||||
@Override
|
|
||||||
public void actionPerformed(ActionEvent e) {
|
|
||||||
addFiles();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
addFilesAction.putValue(Action.SHORT_DESCRIPTION, NLS.str("file.add_files_action"));
|
|
||||||
|
|
||||||
newProjectAction = new AbstractAction(NLS.str("file.new_project"), Icons.NEW_PROJECT) {
|
|
||||||
@Override
|
|
||||||
public void actionPerformed(ActionEvent e) {
|
|
||||||
newProject();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
newProjectAction.putValue(Action.SHORT_DESCRIPTION, NLS.str("file.new_project"));
|
|
||||||
|
|
||||||
saveProjectAction = new AbstractAction(NLS.str("file.save_project")) {
|
|
||||||
@Override
|
|
||||||
public void actionPerformed(ActionEvent e) {
|
|
||||||
saveProject();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
saveProjectAction.putValue(Action.SHORT_DESCRIPTION, NLS.str("file.save_project"));
|
|
||||||
|
|
||||||
Action saveProjectAsAction = new AbstractAction(NLS.str("file.save_project_as")) {
|
|
||||||
@Override
|
|
||||||
public void actionPerformed(ActionEvent e) {
|
|
||||||
saveProjectAs();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
saveProjectAsAction.putValue(Action.SHORT_DESCRIPTION, NLS.str("file.save_project_as"));
|
|
||||||
|
|
||||||
ActionHandler reload = new ActionHandler(ev -> UiUtils.uiRun(this::reopen));
|
|
||||||
reload.setNameAndDesc(NLS.str("file.reload"));
|
|
||||||
reload.setIcon(ICON_RELOAD);
|
|
||||||
reload.setKeyBinding(getKeyStroke(KeyEvent.VK_F5, 0));
|
|
||||||
|
|
||||||
ActionHandler liveReload = new ActionHandler(ev -> updateLiveReload(!project.isEnableLiveReload()));
|
|
||||||
liveReload.setName(NLS.str("file.live_reload"));
|
|
||||||
liveReload.setShortDescription(NLS.str("file.live_reload_desc"));
|
|
||||||
liveReload.setKeyBinding(getKeyStroke(KeyEvent.VK_F5, InputEvent.SHIFT_DOWN_MASK));
|
|
||||||
|
|
||||||
liveReloadMenuItem = new JCheckBoxMenuItem(liveReload);
|
|
||||||
liveReloadMenuItem.setState(project.isEnableLiveReload());
|
liveReloadMenuItem.setState(project.isEnableLiveReload());
|
||||||
|
|
||||||
Action saveAllAction = new AbstractAction(NLS.str("file.save_all"), Icons.SAVE_ALL) {
|
JadxGuiAction saveAllAction = new JadxGuiAction(ActionModel.SAVE_ALL, () -> saveAll(false));
|
||||||
@Override
|
JadxGuiAction exportAction = new JadxGuiAction(ActionModel.EXPORT, () -> saveAll(true));
|
||||||
public void actionPerformed(ActionEvent e) {
|
|
||||||
saveAll(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
saveAllAction.putValue(Action.SHORT_DESCRIPTION, NLS.str("file.save_all"));
|
|
||||||
saveAllAction.putValue(Action.ACCELERATOR_KEY, getKeyStroke(KeyEvent.VK_E, UiUtils.ctrlButton()));
|
|
||||||
|
|
||||||
Action exportAction = new AbstractAction(NLS.str("file.export_gradle"), ICON_EXPORT) {
|
JMenu recentProjects = new JadxMenu(NLS.str("menu.recent_projects"), shortcutsController);
|
||||||
@Override
|
|
||||||
public void actionPerformed(ActionEvent e) {
|
|
||||||
saveAll(true);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
exportAction.putValue(Action.SHORT_DESCRIPTION, NLS.str("file.export_gradle"));
|
|
||||||
exportAction.putValue(Action.ACCELERATOR_KEY, getKeyStroke(KeyEvent.VK_E, UiUtils.ctrlButton() | KeyEvent.SHIFT_DOWN_MASK));
|
|
||||||
|
|
||||||
JMenu recentProjects = new JMenu(NLS.str("menu.recent_projects"));
|
|
||||||
recentProjects.addMenuListener(new RecentProjectsMenuListener(this, recentProjects));
|
recentProjects.addMenuListener(new RecentProjectsMenuListener(this, recentProjects));
|
||||||
|
|
||||||
Action prefsAction = new AbstractAction(NLS.str("menu.preferences"), ICON_PREF) {
|
JadxGuiAction prefsAction = new JadxGuiAction(ActionModel.PREFS, this::openSettings);
|
||||||
@Override
|
JadxGuiAction exitAction = new JadxGuiAction(ActionModel.EXIT, this::closeWindow);
|
||||||
public void actionPerformed(ActionEvent e) {
|
|
||||||
new JadxSettingsWindow(MainWindow.this, settings).setVisible(true);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
prefsAction.putValue(Action.SHORT_DESCRIPTION, NLS.str("menu.preferences"));
|
|
||||||
prefsAction.putValue(Action.ACCELERATOR_KEY, getKeyStroke(KeyEvent.VK_P,
|
|
||||||
UiUtils.ctrlButton() | KeyEvent.SHIFT_DOWN_MASK));
|
|
||||||
|
|
||||||
Action exitAction = new AbstractAction(NLS.str("file.exit"), ICON_EXIT) {
|
|
||||||
@Override
|
|
||||||
public void actionPerformed(ActionEvent e) {
|
|
||||||
closeWindow();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
isFlattenPackage = settings.isFlattenPackage();
|
isFlattenPackage = settings.isFlattenPackage();
|
||||||
flatPkgMenuItem = new JCheckBoxMenuItem(NLS.str("menu.flatten"), ICON_FLAT_PKG);
|
flatPkgMenuItem = new JCheckBoxMenuItem(NLS.str("menu.flatten"), ICON_FLAT_PKG);
|
||||||
@ -983,109 +965,17 @@ public class MainWindow extends JFrame {
|
|||||||
dockLog.setState(settings.isDockLogViewer());
|
dockLog.setState(settings.isDockLogViewer());
|
||||||
dockLog.addActionListener(event -> settings.setDockLogViewer(!settings.isDockLogViewer()));
|
dockLog.addActionListener(event -> settings.setDockLogViewer(!settings.isDockLogViewer()));
|
||||||
|
|
||||||
Action syncAction = new AbstractAction(NLS.str("menu.sync"), ICON_SYNC) {
|
JadxGuiAction syncAction = new JadxGuiAction(ActionModel.SYNC, this::syncWithEditor);
|
||||||
@Override
|
JadxGuiAction textSearchAction = new JadxGuiAction(ActionModel.TEXT_SEARCH, this::textSearch);
|
||||||
public void actionPerformed(ActionEvent e) {
|
JadxGuiAction clsSearchAction = new JadxGuiAction(ActionModel.CLASS_SEARCH,
|
||||||
syncWithEditor();
|
() -> SearchDialog.search(MainWindow.this, SearchDialog.SearchPreset.CLASS));
|
||||||
}
|
JadxGuiAction commentSearchAction = new JadxGuiAction(ActionModel.COMMENT_SEARCH,
|
||||||
};
|
() -> SearchDialog.search(MainWindow.this, SearchDialog.SearchPreset.COMMENT));
|
||||||
syncAction.putValue(Action.SHORT_DESCRIPTION, NLS.str("menu.sync"));
|
JadxGuiAction gotoMainActivityAction = new JadxGuiAction(ActionModel.GOTO_MAIN_ACTIVITY,
|
||||||
syncAction.putValue(Action.ACCELERATOR_KEY, getKeyStroke(KeyEvent.VK_T, UiUtils.ctrlButton()));
|
this::gotoMainActivity);
|
||||||
|
JadxGuiAction decompileAllAction = new JadxGuiAction(ActionModel.DECOMPILE_ALL, this::requestFullDecompilation);
|
||||||
Action textSearchAction = new AbstractAction(NLS.str("menu.text_search"), ICON_SEARCH) {
|
JadxGuiAction resetCacheAction = new JadxGuiAction(ActionModel.RESET_CACHE, this::resetCodeCache);
|
||||||
@Override
|
JadxGuiAction deobfAction = new JadxGuiAction(ActionModel.DEOBF, this::toggleDeobfuscation);
|
||||||
public void actionPerformed(ActionEvent e) {
|
|
||||||
ContentPanel panel = tabbedPane.getSelectedContentPanel();
|
|
||||||
if (panel instanceof AbstractCodeContentPanel) {
|
|
||||||
AbstractCodeArea codeArea = ((AbstractCodeContentPanel) panel).getCodeArea();
|
|
||||||
String preferText = codeArea.getSelectedText();
|
|
||||||
if (StringUtils.isEmpty(preferText)) {
|
|
||||||
preferText = codeArea.getWordUnderCaret();
|
|
||||||
}
|
|
||||||
if (!StringUtils.isEmpty(preferText)) {
|
|
||||||
SearchDialog.searchText(MainWindow.this, preferText);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
SearchDialog.search(MainWindow.this, SearchDialog.SearchPreset.TEXT);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
textSearchAction.putValue(Action.SHORT_DESCRIPTION, NLS.str("menu.text_search"));
|
|
||||||
textSearchAction.putValue(Action.ACCELERATOR_KEY,
|
|
||||||
getKeyStroke(KeyEvent.VK_F, UiUtils.ctrlButton() | KeyEvent.SHIFT_DOWN_MASK));
|
|
||||||
|
|
||||||
Action clsSearchAction = new AbstractAction(NLS.str("menu.class_search"), ICON_FIND) {
|
|
||||||
@Override
|
|
||||||
public void actionPerformed(ActionEvent e) {
|
|
||||||
SearchDialog.search(MainWindow.this, SearchDialog.SearchPreset.CLASS);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
clsSearchAction.putValue(Action.SHORT_DESCRIPTION, NLS.str("menu.class_search"));
|
|
||||||
clsSearchAction.putValue(Action.ACCELERATOR_KEY, getKeyStroke(KeyEvent.VK_N, UiUtils.ctrlButton()));
|
|
||||||
|
|
||||||
Action commentSearchAction = new AbstractAction(NLS.str("menu.comment_search"), ICON_COMMENT_SEARCH) {
|
|
||||||
@Override
|
|
||||||
public void actionPerformed(ActionEvent e) {
|
|
||||||
SearchDialog.search(MainWindow.this, SearchDialog.SearchPreset.COMMENT);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
commentSearchAction.putValue(Action.SHORT_DESCRIPTION, NLS.str("menu.comment_search"));
|
|
||||||
commentSearchAction.putValue(Action.ACCELERATOR_KEY, getKeyStroke(KeyEvent.VK_SEMICOLON,
|
|
||||||
UiUtils.ctrlButton() | KeyEvent.SHIFT_DOWN_MASK));
|
|
||||||
|
|
||||||
Action gotoMainActivityAction = new AbstractAction(NLS.str("menu.goto_main_activity"), ICON_MAIN_ACTIVITY) {
|
|
||||||
@Override
|
|
||||||
public void actionPerformed(ActionEvent ev) {
|
|
||||||
AndroidManifestParser parser = new AndroidManifestParser(
|
|
||||||
AndroidManifestParser.getAndroidManifest(getWrapper().getResources()),
|
|
||||||
EnumSet.of(AppAttribute.MAIN_ACTIVITY));
|
|
||||||
if (!parser.isManifestFound()) {
|
|
||||||
JOptionPane.showMessageDialog(MainWindow.this,
|
|
||||||
NLS.str("error_dialog.not_found", "AndroidManifest.xml"),
|
|
||||||
NLS.str("error_dialog.title"),
|
|
||||||
JOptionPane.ERROR_MESSAGE);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
ApplicationParams results = parser.parse();
|
|
||||||
if (results.getMainActivityName() == null) {
|
|
||||||
throw new JadxRuntimeException("Failed to get main activity name from manifest");
|
|
||||||
}
|
|
||||||
JavaClass mainActivityClass = results.getMainActivity(getWrapper().getDecompiler());
|
|
||||||
if (mainActivityClass == null) {
|
|
||||||
throw new JadxRuntimeException("Failed to find main activity class: " + results.getMainActivityName());
|
|
||||||
}
|
|
||||||
tabbedPane.codeJump(getCacheObject().getNodeCache().makeFrom(mainActivityClass));
|
|
||||||
} catch (Exception e) {
|
|
||||||
LOG.error("Main activity not found", e);
|
|
||||||
JOptionPane.showMessageDialog(MainWindow.this,
|
|
||||||
NLS.str("error_dialog.not_found", "Main Activity"),
|
|
||||||
NLS.str("error_dialog.title"),
|
|
||||||
JOptionPane.ERROR_MESSAGE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
gotoMainActivityAction.putValue(Action.SHORT_DESCRIPTION, NLS.str("menu.goto_main_activity"));
|
|
||||||
gotoMainActivityAction.putValue(Action.ACCELERATOR_KEY, getKeyStroke(KeyEvent.VK_M,
|
|
||||||
UiUtils.ctrlButton() | KeyEvent.SHIFT_DOWN_MASK));
|
|
||||||
|
|
||||||
ActionHandler decompileAllAction = new ActionHandler(ev -> requestFullDecompilation());
|
|
||||||
decompileAllAction.setNameAndDesc(NLS.str("menu.decompile_all"));
|
|
||||||
decompileAllAction.setIcon(ICON_DECOMPILE_ALL);
|
|
||||||
|
|
||||||
ActionHandler resetCacheAction = new ActionHandler(ev -> resetCodeCache());
|
|
||||||
resetCacheAction.setNameAndDesc(NLS.str("menu.reset_cache"));
|
|
||||||
resetCacheAction.setIcon(Icons.RESET);
|
|
||||||
|
|
||||||
Action deobfAction = new AbstractAction(NLS.str("menu.deobfuscation"), ICON_DEOBF) {
|
|
||||||
@Override
|
|
||||||
public void actionPerformed(ActionEvent e) {
|
|
||||||
toggleDeobfuscation();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
deobfAction.putValue(Action.SHORT_DESCRIPTION, NLS.str("preferences.deobfuscation"));
|
|
||||||
deobfAction.putValue(Action.ACCELERATOR_KEY,
|
|
||||||
getKeyStroke(KeyEvent.VK_D, UiUtils.ctrlButton() | KeyEvent.ALT_DOWN_MASK));
|
|
||||||
|
|
||||||
deobfToggleBtn = new JToggleButton(deobfAction);
|
deobfToggleBtn = new JToggleButton(deobfAction);
|
||||||
deobfToggleBtn.setSelected(settings.isDeobfuscationOn());
|
deobfToggleBtn.setSelected(settings.isDeobfuscationOn());
|
||||||
@ -1094,54 +984,19 @@ public class MainWindow extends JFrame {
|
|||||||
deobfMenuItem = new JCheckBoxMenuItem(deobfAction);
|
deobfMenuItem = new JCheckBoxMenuItem(deobfAction);
|
||||||
deobfMenuItem.setState(settings.isDeobfuscationOn());
|
deobfMenuItem.setState(settings.isDeobfuscationOn());
|
||||||
|
|
||||||
ActionHandler showLog = new ActionHandler(ev -> showLogViewer(LogOptions.current()));
|
JadxGuiAction showLogAction = new JadxGuiAction(ActionModel.SHOW_LOG,
|
||||||
showLog.setNameAndDesc(NLS.str("menu.log"));
|
() -> showLogViewer(LogOptions.current()));
|
||||||
showLog.setIcon(ICON_LOG);
|
JadxGuiAction aboutAction = new JadxGuiAction(ActionModel.ABOUT, () -> new AboutDialog().setVisible(true));
|
||||||
showLog.setKeyBinding(getKeyStroke(KeyEvent.VK_L, UiUtils.ctrlButton() | KeyEvent.SHIFT_DOWN_MASK));
|
JadxGuiAction backAction = new JadxGuiAction(ActionModel.BACK, tabbedPane::navBack);
|
||||||
|
JadxGuiAction backVariantAction = new JadxGuiAction(ActionModel.BACK_V, tabbedPane::navBack);
|
||||||
|
JadxGuiAction forwardAction = new JadxGuiAction(ActionModel.FORWARD, tabbedPane::navForward);
|
||||||
|
JadxGuiAction forwardVariantAction = new JadxGuiAction(ActionModel.FORWARD_V, tabbedPane::navForward);
|
||||||
|
JadxGuiAction quarkAction = new JadxGuiAction(ActionModel.QUARK,
|
||||||
|
() -> new QuarkDialog(MainWindow.this).setVisible(true));
|
||||||
|
JadxGuiAction openDeviceAction = new JadxGuiAction(ActionModel.OPEN_DEVICE,
|
||||||
|
() -> new ADBDialog(MainWindow.this).setVisible(true));
|
||||||
|
|
||||||
Action aboutAction = new AbstractAction(NLS.str("menu.about"), ICON_INFO) {
|
JMenu file = new JadxMenu(NLS.str("menu.file"), shortcutsController);
|
||||||
@Override
|
|
||||||
public void actionPerformed(ActionEvent e) {
|
|
||||||
new AboutDialog().setVisible(true);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
Action backAction = new AbstractAction(NLS.str("nav.back"), ICON_BACK) {
|
|
||||||
@Override
|
|
||||||
public void actionPerformed(ActionEvent e) {
|
|
||||||
tabbedPane.navBack();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
backAction.putValue(Action.SHORT_DESCRIPTION, NLS.str("nav.back"));
|
|
||||||
backAction.putValue(Action.ACCELERATOR_KEY, getKeyStroke(KeyEvent.VK_ESCAPE, 0));
|
|
||||||
|
|
||||||
Action forwardAction = new AbstractAction(NLS.str("nav.forward"), ICON_FORWARD) {
|
|
||||||
@Override
|
|
||||||
public void actionPerformed(ActionEvent e) {
|
|
||||||
tabbedPane.navForward();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
forwardAction.putValue(Action.SHORT_DESCRIPTION, NLS.str("nav.forward"));
|
|
||||||
forwardAction.putValue(Action.ACCELERATOR_KEY, getKeyStroke(KeyEvent.VK_RIGHT, KeyEvent.ALT_DOWN_MASK, SystemInfo.IS_MAC));
|
|
||||||
|
|
||||||
Action quarkAction = new AbstractAction("Quark Engine", ICON_QUARK) {
|
|
||||||
@Override
|
|
||||||
public void actionPerformed(ActionEvent e) {
|
|
||||||
new QuarkDialog(MainWindow.this).setVisible(true);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
quarkAction.putValue(Action.SHORT_DESCRIPTION, "Quark Engine");
|
|
||||||
|
|
||||||
Action openDeviceAction = new AbstractAction(NLS.str("debugger.process_selector"), ICON_DEBUGGER) {
|
|
||||||
@Override
|
|
||||||
public void actionPerformed(ActionEvent e) {
|
|
||||||
ADBDialog dialog = new ADBDialog(MainWindow.this);
|
|
||||||
dialog.setVisible(true);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
openDeviceAction.putValue(Action.SHORT_DESCRIPTION, NLS.str("debugger.process_selector"));
|
|
||||||
|
|
||||||
JMenu file = new JMenu(NLS.str("menu.file"));
|
|
||||||
file.setMnemonic(KeyEvent.VK_F);
|
file.setMnemonic(KeyEvent.VK_F);
|
||||||
file.add(openAction);
|
file.add(openAction);
|
||||||
file.add(openProject);
|
file.add(openProject);
|
||||||
@ -1151,7 +1006,7 @@ public class MainWindow extends JFrame {
|
|||||||
file.add(saveProjectAction);
|
file.add(saveProjectAction);
|
||||||
file.add(saveProjectAsAction);
|
file.add(saveProjectAsAction);
|
||||||
file.addSeparator();
|
file.addSeparator();
|
||||||
file.add(reload);
|
file.add(reloadAction);
|
||||||
file.add(liveReloadMenuItem);
|
file.add(liveReloadMenuItem);
|
||||||
renameMappings.addMenuActions(file);
|
renameMappings.addMenuActions(file);
|
||||||
file.addSeparator();
|
file.addSeparator();
|
||||||
@ -1164,7 +1019,7 @@ public class MainWindow extends JFrame {
|
|||||||
file.addSeparator();
|
file.addSeparator();
|
||||||
file.add(exitAction);
|
file.add(exitAction);
|
||||||
|
|
||||||
JMenu view = new JMenu(NLS.str("menu.view"));
|
JMenu view = new JadxMenu(NLS.str("menu.view"), shortcutsController);
|
||||||
view.setMnemonic(KeyEvent.VK_V);
|
view.setMnemonic(KeyEvent.VK_V);
|
||||||
view.add(flatPkgMenuItem);
|
view.add(flatPkgMenuItem);
|
||||||
view.add(syncAction);
|
view.add(syncAction);
|
||||||
@ -1172,7 +1027,7 @@ public class MainWindow extends JFrame {
|
|||||||
view.add(alwaysSelectOpened);
|
view.add(alwaysSelectOpened);
|
||||||
view.add(dockLog);
|
view.add(dockLog);
|
||||||
|
|
||||||
JMenu nav = new JMenu(NLS.str("menu.navigation"));
|
JMenu nav = new JadxMenu(NLS.str("menu.navigation"), shortcutsController);
|
||||||
nav.setMnemonic(KeyEvent.VK_N);
|
nav.setMnemonic(KeyEvent.VK_N);
|
||||||
nav.add(textSearchAction);
|
nav.add(textSearchAction);
|
||||||
nav.add(clsSearchAction);
|
nav.add(clsSearchAction);
|
||||||
@ -1182,11 +1037,11 @@ public class MainWindow extends JFrame {
|
|||||||
nav.add(backAction);
|
nav.add(backAction);
|
||||||
nav.add(forwardAction);
|
nav.add(forwardAction);
|
||||||
|
|
||||||
pluginsMenu = new JMenu(NLS.str("menu.plugins"));
|
pluginsMenu = new JadxMenu(NLS.str("menu.plugins"), shortcutsController);
|
||||||
pluginsMenu.setMnemonic(KeyEvent.VK_P);
|
pluginsMenu.setMnemonic(KeyEvent.VK_P);
|
||||||
resetPluginsMenu();
|
resetPluginsMenu();
|
||||||
|
|
||||||
JMenu tools = new JMenu(NLS.str("menu.tools"));
|
JMenu tools = new JadxMenu(NLS.str("menu.tools"), shortcutsController);
|
||||||
tools.setMnemonic(KeyEvent.VK_T);
|
tools.setMnemonic(KeyEvent.VK_T);
|
||||||
tools.add(decompileAllAction);
|
tools.add(decompileAllAction);
|
||||||
tools.add(resetCacheAction);
|
tools.add(resetCacheAction);
|
||||||
@ -1194,9 +1049,9 @@ public class MainWindow extends JFrame {
|
|||||||
tools.add(quarkAction);
|
tools.add(quarkAction);
|
||||||
tools.add(openDeviceAction);
|
tools.add(openDeviceAction);
|
||||||
|
|
||||||
JMenu help = new JMenu(NLS.str("menu.help"));
|
JMenu help = new JadxMenu(NLS.str("menu.help"), shortcutsController);
|
||||||
help.setMnemonic(KeyEvent.VK_H);
|
help.setMnemonic(KeyEvent.VK_H);
|
||||||
help.add(showLog);
|
help.add(showLogAction);
|
||||||
if (Jadx.isDevVersion()) {
|
if (Jadx.isDevVersion()) {
|
||||||
help.add(new AbstractAction("Show sample error report") {
|
help.add(new AbstractAction("Show sample error report") {
|
||||||
@Override
|
@Override
|
||||||
@ -1207,7 +1062,7 @@ public class MainWindow extends JFrame {
|
|||||||
}
|
}
|
||||||
help.add(aboutAction);
|
help.add(aboutAction);
|
||||||
|
|
||||||
JMenuBar menuBar = new JMenuBar();
|
menuBar = new JadxMenuBar();
|
||||||
menuBar.add(file);
|
menuBar.add(file);
|
||||||
menuBar.add(view);
|
menuBar.add(view);
|
||||||
menuBar.add(nav);
|
menuBar.add(nav);
|
||||||
@ -1231,7 +1086,7 @@ public class MainWindow extends JFrame {
|
|||||||
toolbar.add(openAction);
|
toolbar.add(openAction);
|
||||||
toolbar.add(addFilesAction);
|
toolbar.add(addFilesAction);
|
||||||
toolbar.addSeparator();
|
toolbar.addSeparator();
|
||||||
toolbar.add(reload);
|
toolbar.add(reloadAction);
|
||||||
toolbar.addSeparator();
|
toolbar.addSeparator();
|
||||||
toolbar.add(saveAllAction);
|
toolbar.add(saveAllAction);
|
||||||
toolbar.add(exportAction);
|
toolbar.add(exportAction);
|
||||||
@ -1251,7 +1106,7 @@ public class MainWindow extends JFrame {
|
|||||||
toolbar.add(quarkAction);
|
toolbar.add(quarkAction);
|
||||||
toolbar.add(openDeviceAction);
|
toolbar.add(openDeviceAction);
|
||||||
toolbar.addSeparator();
|
toolbar.addSeparator();
|
||||||
toolbar.add(showLog);
|
toolbar.add(showLogAction);
|
||||||
toolbar.addSeparator();
|
toolbar.addSeparator();
|
||||||
toolbar.add(prefsAction);
|
toolbar.add(prefsAction);
|
||||||
toolbar.addSeparator();
|
toolbar.addSeparator();
|
||||||
@ -1260,18 +1115,26 @@ public class MainWindow extends JFrame {
|
|||||||
|
|
||||||
mainPanel.add(toolbar, BorderLayout.NORTH);
|
mainPanel.add(toolbar, BorderLayout.NORTH);
|
||||||
|
|
||||||
|
nav.add(new HiddenMenuItem(backVariantAction));
|
||||||
|
nav.add(new HiddenMenuItem(forwardVariantAction));
|
||||||
|
|
||||||
|
shortcutsController.bind(backVariantAction);
|
||||||
|
shortcutsController.bind(forwardVariantAction);
|
||||||
|
|
||||||
addLoadListener(loaded -> {
|
addLoadListener(loaded -> {
|
||||||
textSearchAction.setEnabled(loaded);
|
textSearchAction.setEnabled(loaded);
|
||||||
clsSearchAction.setEnabled(loaded);
|
clsSearchAction.setEnabled(loaded);
|
||||||
commentSearchAction.setEnabled(loaded);
|
commentSearchAction.setEnabled(loaded);
|
||||||
gotoMainActivityAction.setEnabled(loaded);
|
gotoMainActivityAction.setEnabled(loaded);
|
||||||
backAction.setEnabled(loaded);
|
backAction.setEnabled(loaded);
|
||||||
|
backVariantAction.setEnabled(loaded);
|
||||||
forwardAction.setEnabled(loaded);
|
forwardAction.setEnabled(loaded);
|
||||||
|
forwardVariantAction.setEnabled(loaded);
|
||||||
syncAction.setEnabled(loaded);
|
syncAction.setEnabled(loaded);
|
||||||
saveAllAction.setEnabled(loaded);
|
saveAllAction.setEnabled(loaded);
|
||||||
exportAction.setEnabled(loaded);
|
exportAction.setEnabled(loaded);
|
||||||
saveProjectAsAction.setEnabled(loaded);
|
saveProjectAsAction.setEnabled(loaded);
|
||||||
reload.setEnabled(loaded);
|
reloadAction.setEnabled(loaded);
|
||||||
decompileAllAction.setEnabled(loaded);
|
decompileAllAction.setEnabled(loaded);
|
||||||
deobfAction.setEnabled(loaded);
|
deobfAction.setEnabled(loaded);
|
||||||
quarkAction.setEnabled(loaded);
|
quarkAction.setEnabled(loaded);
|
||||||
@ -1402,42 +1265,6 @@ public class MainWindow extends JFrame {
|
|||||||
setTitle(DEFAULT_TITLE);
|
setTitle(DEFAULT_TITLE);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void registerMouseNavigationButtons() {
|
|
||||||
Toolkit toolkit = Toolkit.getDefaultToolkit();
|
|
||||||
toolkit.addAWTEventListener(event -> {
|
|
||||||
if (event instanceof MouseEvent) {
|
|
||||||
MouseEvent mouseEvent = (MouseEvent) event;
|
|
||||||
if (mouseEvent.getID() == MouseEvent.MOUSE_PRESSED) {
|
|
||||||
int rawButton = mouseEvent.getButton();
|
|
||||||
if (rawButton <= 3) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
int button = remapMouseButton(rawButton);
|
|
||||||
switch (button) {
|
|
||||||
case 4:
|
|
||||||
tabbedPane.navBack();
|
|
||||||
break;
|
|
||||||
case 5:
|
|
||||||
tabbedPane.navForward();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, AWTEvent.MOUSE_EVENT_MASK);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static int remapMouseButton(int rawButton) {
|
|
||||||
if (SystemInfo.IS_LINUX) {
|
|
||||||
if (rawButton == 6) {
|
|
||||||
return 4;
|
|
||||||
}
|
|
||||||
if (rawButton == 7) {
|
|
||||||
return 5;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return rawButton;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String[] getPathExpansion(TreePath path) {
|
private static String[] getPathExpansion(TreePath path) {
|
||||||
List<String> pathList = new ArrayList<>();
|
List<String> pathList = new ArrayList<>();
|
||||||
while (path != null) {
|
while (path != null) {
|
||||||
@ -1515,6 +1342,23 @@ public class MainWindow extends JFrame {
|
|||||||
return editorTheme;
|
return editorTheme;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void openSettings() {
|
||||||
|
settingsOpen = true;
|
||||||
|
|
||||||
|
JDialog settingsWindow = new JadxSettingsWindow(MainWindow.this, settings);
|
||||||
|
settingsWindow.setVisible(true);
|
||||||
|
settingsWindow.addWindowListener(new WindowAdapter() {
|
||||||
|
@Override
|
||||||
|
public void windowClosed(WindowEvent e) {
|
||||||
|
settingsOpen = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isSettingsOpen() {
|
||||||
|
return settingsOpen;
|
||||||
|
}
|
||||||
|
|
||||||
public void loadSettings() {
|
public void loadSettings() {
|
||||||
// queue update to not interrupt current UI tasks
|
// queue update to not interrupt current UI tasks
|
||||||
UiUtils.uiRun(this::updateUiSettings);
|
UiUtils.uiRun(this::updateUiSettings);
|
||||||
@ -1535,6 +1379,8 @@ public class MainWindow extends JFrame {
|
|||||||
if (logPanel != null) {
|
if (logPanel != null) {
|
||||||
logPanel.loadSettings();
|
logPanel.loadSettings();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
shortcutsController.loadSettings();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void closeWindow() {
|
private void closeWindow() {
|
||||||
@ -1638,6 +1484,10 @@ public class MainWindow extends JFrame {
|
|||||||
return debuggerPanel;
|
return debuggerPanel;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ShortcutsController getShortcutsController() {
|
||||||
|
return shortcutsController;
|
||||||
|
}
|
||||||
|
|
||||||
public void showDebuggerPanel() {
|
public void showDebuggerPanel() {
|
||||||
initDebuggerPanel();
|
initDebuggerPanel();
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,22 @@
|
|||||||
|
package jadx.gui.ui.action;
|
||||||
|
|
||||||
|
import jadx.gui.utils.NLS;
|
||||||
|
|
||||||
|
public enum ActionCategory {
|
||||||
|
MENU_TOOLBAR("action_category.menu_toolbar"),
|
||||||
|
CODE_AREA("action_category.code_area"),
|
||||||
|
PLUGIN_SCRIPT("action_category.plugin_script");
|
||||||
|
|
||||||
|
private final String nameRes;
|
||||||
|
|
||||||
|
ActionCategory(String nameRes) {
|
||||||
|
this.nameRes = nameRes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
if (nameRes != null) {
|
||||||
|
return NLS.str(nameRes);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
154
jadx-gui/src/main/java/jadx/gui/ui/action/ActionModel.java
Normal file
154
jadx-gui/src/main/java/jadx/gui/ui/action/ActionModel.java
Normal file
@ -0,0 +1,154 @@
|
|||||||
|
package jadx.gui.ui.action;
|
||||||
|
|
||||||
|
import java.awt.event.InputEvent;
|
||||||
|
import java.awt.event.KeyEvent;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import javax.swing.ImageIcon;
|
||||||
|
|
||||||
|
import io.reactivex.annotations.NonNull;
|
||||||
|
|
||||||
|
import jadx.gui.utils.NLS;
|
||||||
|
import jadx.gui.utils.UiUtils;
|
||||||
|
import jadx.gui.utils.shortcut.Shortcut;
|
||||||
|
|
||||||
|
import static jadx.gui.ui.action.ActionCategory.*;
|
||||||
|
|
||||||
|
public enum ActionModel {
|
||||||
|
ABOUT(MENU_TOOLBAR, "menu.about", "menu.about", "ui/showInfos",
|
||||||
|
Shortcut.keyboard(KeyEvent.VK_F1)),
|
||||||
|
OPEN(MENU_TOOLBAR, "file.open_action", "file.open_action", "ui/openDisk",
|
||||||
|
Shortcut.keyboard(KeyEvent.VK_O, KeyEvent.CTRL_DOWN_MASK)),
|
||||||
|
OPEN_PROJECT(MENU_TOOLBAR, "file.open_project", "file.open_project", "ui/projectDirectory",
|
||||||
|
Shortcut.keyboard(KeyEvent.VK_O, InputEvent.SHIFT_DOWN_MASK | UiUtils.ctrlButton())),
|
||||||
|
ADD_FILES(MENU_TOOLBAR, "file.add_files_action", "file.add_files_action", "ui/addFile",
|
||||||
|
Shortcut.none()),
|
||||||
|
NEW_PROJECT(MENU_TOOLBAR, "file.new_project", "file.new_project", "ui/newFolder",
|
||||||
|
Shortcut.none()),
|
||||||
|
SAVE_PROJECT(MENU_TOOLBAR, "file.save_project", "file.save_project", null,
|
||||||
|
Shortcut.none()),
|
||||||
|
SAVE_PROJECT_AS(MENU_TOOLBAR, "file.save_project_as", "file.save_project_as", null,
|
||||||
|
Shortcut.none()),
|
||||||
|
RELOAD(MENU_TOOLBAR, "file.reload", "file.reload", "ui/refresh",
|
||||||
|
Shortcut.keyboard(KeyEvent.VK_F5)),
|
||||||
|
LIVE_RELOAD(MENU_TOOLBAR, "file.live_reload", "file.live_reload_desc", null,
|
||||||
|
Shortcut.keyboard(KeyEvent.VK_F5, InputEvent.SHIFT_DOWN_MASK)),
|
||||||
|
SAVE_ALL(MENU_TOOLBAR, "file.save_all", "file.save_all", "ui/menu-saveall",
|
||||||
|
Shortcut.keyboard(KeyEvent.VK_E, UiUtils.ctrlButton())),
|
||||||
|
EXPORT(MENU_TOOLBAR, "file.export_gradle", "file.export_gradle", "ui/export",
|
||||||
|
Shortcut.keyboard(KeyEvent.VK_E, UiUtils.ctrlButton() | KeyEvent.SHIFT_DOWN_MASK)),
|
||||||
|
PREFS(MENU_TOOLBAR, "menu.preferences", "menu.preferences", "ui/settings",
|
||||||
|
Shortcut.keyboard(KeyEvent.VK_P, UiUtils.ctrlButton() | KeyEvent.SHIFT_DOWN_MASK)),
|
||||||
|
EXIT(MENU_TOOLBAR, "file.exit", "file.exit", "ui/exit",
|
||||||
|
Shortcut.none()),
|
||||||
|
SYNC(MENU_TOOLBAR, "menu.sync", "menu.sync", "ui/pagination",
|
||||||
|
Shortcut.keyboard(KeyEvent.VK_T, UiUtils.ctrlButton())),
|
||||||
|
TEXT_SEARCH(MENU_TOOLBAR, "menu.text_search", "menu.text_search", "ui/find",
|
||||||
|
Shortcut.keyboard(KeyEvent.VK_F, UiUtils.ctrlButton() | KeyEvent.SHIFT_DOWN_MASK)),
|
||||||
|
CLASS_SEARCH(MENU_TOOLBAR, "menu.class_search", "menu.class_search", "ui/ejbFinderMethod",
|
||||||
|
Shortcut.keyboard(KeyEvent.VK_N, UiUtils.ctrlButton())),
|
||||||
|
COMMENT_SEARCH(MENU_TOOLBAR, "menu.comment_search", "menu.comment_search", "ui/usagesFinder",
|
||||||
|
Shortcut.keyboard(KeyEvent.VK_SEMICOLON, UiUtils.ctrlButton() | KeyEvent.SHIFT_DOWN_MASK)),
|
||||||
|
GOTO_MAIN_ACTIVITY(MENU_TOOLBAR, "menu.goto_main_activity", "menu.goto_main_activity", "ui/home",
|
||||||
|
Shortcut.keyboard(KeyEvent.VK_M, UiUtils.ctrlButton() | KeyEvent.SHIFT_DOWN_MASK)),
|
||||||
|
DECOMPILE_ALL(MENU_TOOLBAR, "menu.decompile_all", "menu.decompile_all", "ui/runAll",
|
||||||
|
Shortcut.none()),
|
||||||
|
RESET_CACHE(MENU_TOOLBAR, "menu.reset_cache", "menu.reset_cache", "ui/reset",
|
||||||
|
Shortcut.none()),
|
||||||
|
DEOBF(MENU_TOOLBAR, "menu.deobfuscation", "preferences.deobfuscation", "ui/helmChartLock",
|
||||||
|
Shortcut.keyboard(KeyEvent.VK_D, UiUtils.ctrlButton() | KeyEvent.ALT_DOWN_MASK)),
|
||||||
|
SHOW_LOG(MENU_TOOLBAR, "menu.log", "menu.log", "ui/logVerbose",
|
||||||
|
Shortcut.keyboard(KeyEvent.VK_L, UiUtils.ctrlButton() | KeyEvent.SHIFT_DOWN_MASK)),
|
||||||
|
BACK(MENU_TOOLBAR, "nav.back", "nav.back", "ui/left",
|
||||||
|
Shortcut.keyboard(KeyEvent.VK_ESCAPE)),
|
||||||
|
BACK_V(MENU_TOOLBAR, "nav.back", "nav.back", "ui/left",
|
||||||
|
Shortcut.none()),
|
||||||
|
FORWARD(MENU_TOOLBAR, "nav.forward", "nav.forward", "ui/right",
|
||||||
|
Shortcut.keyboard(KeyEvent.VK_RIGHT, KeyEvent.ALT_DOWN_MASK)),
|
||||||
|
FORWARD_V(MENU_TOOLBAR, "nav.forward", "nav.forward", "ui/right",
|
||||||
|
Shortcut.none()),
|
||||||
|
QUARK(MENU_TOOLBAR, "menu.quark", "menu.quark", "ui/quark",
|
||||||
|
Shortcut.none()),
|
||||||
|
OPEN_DEVICE(MENU_TOOLBAR, "debugger.process_selector", "debugger.process_selector", "ui/startDebugger",
|
||||||
|
Shortcut.none()),
|
||||||
|
|
||||||
|
FIND_USAGE(CODE_AREA, "popup.find_usage", "popup.find_usage", null,
|
||||||
|
Shortcut.keyboard(KeyEvent.VK_X)),
|
||||||
|
GOTO_DECLARATION(CODE_AREA, "popup.go_to_declaration", "popup.go_to_declaration", null,
|
||||||
|
Shortcut.keyboard(KeyEvent.VK_D)),
|
||||||
|
CODE_COMMENT(CODE_AREA, "popup.add_comment", "popup.add_comment", null,
|
||||||
|
Shortcut.keyboard(KeyEvent.VK_SEMICOLON)),
|
||||||
|
CODE_COMMENT_SEARCH(CODE_AREA, "popup.search_comment", "popup.search_comment", null,
|
||||||
|
Shortcut.keyboard(KeyEvent.VK_SEMICOLON, UiUtils.ctrlButton())),
|
||||||
|
CODE_RENAME(CODE_AREA, "popup.rename", "popup.rename", null,
|
||||||
|
Shortcut.keyboard(KeyEvent.VK_N)),
|
||||||
|
FRIDA_COPY(CODE_AREA, "popup.frida", "popup.frida", null,
|
||||||
|
Shortcut.keyboard(KeyEvent.VK_F)),
|
||||||
|
XPOSED_COPY(CODE_AREA, "popup.xposed", "popup.xposed", null,
|
||||||
|
Shortcut.keyboard(KeyEvent.VK_Y)),
|
||||||
|
JSON_PRETTIFY(CODE_AREA, "popup.json_prettify", "popup.json_prettify", null,
|
||||||
|
Shortcut.none()),
|
||||||
|
|
||||||
|
SCRIPT_RUN(PLUGIN_SCRIPT, "script.run", "script.run", "ui/run",
|
||||||
|
Shortcut.keyboard(KeyEvent.VK_F8)),
|
||||||
|
SCRIPT_SAVE(PLUGIN_SCRIPT, "script.save", "script.save", "ui/menu-saveall",
|
||||||
|
Shortcut.keyboard(KeyEvent.VK_S, UiUtils.ctrlButton())),
|
||||||
|
SCRIPT_AUTO_COMPLETE(PLUGIN_SCRIPT, "script.auto_complete", "script.auto_complete", null,
|
||||||
|
Shortcut.keyboard(KeyEvent.VK_SPACE, UiUtils.ctrlButton()));
|
||||||
|
|
||||||
|
private final ActionCategory category;
|
||||||
|
private final String nameRes;
|
||||||
|
private final String descRes;
|
||||||
|
private final String iconPath;
|
||||||
|
private final Shortcut defaultShortcut;
|
||||||
|
|
||||||
|
ActionModel(ActionCategory category, String nameRes, String descRes, String iconPath, Shortcut defaultShortcut) {
|
||||||
|
this.category = category;
|
||||||
|
this.nameRes = nameRes;
|
||||||
|
this.descRes = descRes;
|
||||||
|
this.iconPath = iconPath;
|
||||||
|
this.defaultShortcut = defaultShortcut;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<ActionModel> select(ActionCategory category) {
|
||||||
|
return Arrays.stream(values())
|
||||||
|
.filter(actionModel -> actionModel.category == category)
|
||||||
|
.collect(Collectors.toUnmodifiableList());
|
||||||
|
}
|
||||||
|
|
||||||
|
public ActionCategory getCategory() {
|
||||||
|
return category;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
if (nameRes != null) {
|
||||||
|
String name = NLS.str(nameRes);
|
||||||
|
if (name().endsWith("_V")) {
|
||||||
|
name = NLS.str("action.variant", name);
|
||||||
|
}
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDescription() {
|
||||||
|
if (descRes != null) {
|
||||||
|
return NLS.str(descRes);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ImageIcon getIcon() {
|
||||||
|
if (iconPath != null) {
|
||||||
|
return UiUtils.openSvgIcon(iconPath);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
public Shortcut getDefaultShortcut() {
|
||||||
|
return defaultShortcut;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,15 @@
|
|||||||
|
package jadx.gui.ui.action;
|
||||||
|
|
||||||
|
import javax.swing.JComponent;
|
||||||
|
|
||||||
|
import jadx.gui.utils.shortcut.Shortcut;
|
||||||
|
|
||||||
|
public interface IShortcutAction {
|
||||||
|
ActionModel getActionModel();
|
||||||
|
|
||||||
|
JComponent getShortcutComponent();
|
||||||
|
|
||||||
|
void performAction();
|
||||||
|
|
||||||
|
void setShortcut(Shortcut shortcut);
|
||||||
|
}
|
@ -0,0 +1,51 @@
|
|||||||
|
package jadx.gui.ui.action;
|
||||||
|
|
||||||
|
import java.awt.event.ActionEvent;
|
||||||
|
import java.awt.event.KeyEvent;
|
||||||
|
|
||||||
|
import javax.swing.JComponent;
|
||||||
|
import javax.swing.KeyStroke;
|
||||||
|
|
||||||
|
import org.fife.ui.autocomplete.AutoCompletion;
|
||||||
|
import org.fife.ui.autocomplete.CompletionProvider;
|
||||||
|
|
||||||
|
import jadx.gui.utils.shortcut.Shortcut;
|
||||||
|
|
||||||
|
public class JadxAutoCompletion extends AutoCompletion
|
||||||
|
implements IShortcutAction {
|
||||||
|
public static final String COMMAND = "JadxAutoCompletion.Command";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor.
|
||||||
|
*
|
||||||
|
* @param provider The completion provider. This cannot be <code>null</code>
|
||||||
|
*/
|
||||||
|
public JadxAutoCompletion(CompletionProvider provider) {
|
||||||
|
super(provider);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ActionModel getActionModel() {
|
||||||
|
return ActionModel.SCRIPT_AUTO_COMPLETE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JComponent getShortcutComponent() {
|
||||||
|
return getTextComponent();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void performAction() {
|
||||||
|
createAutoCompleteAction().actionPerformed(
|
||||||
|
new ActionEvent(this, ActionEvent.ACTION_PERFORMED, COMMAND));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setShortcut(Shortcut shortcut) {
|
||||||
|
if (shortcut != null && shortcut.isKeyboard()) {
|
||||||
|
setTriggerKey(shortcut.toKeyStroke());
|
||||||
|
} else {
|
||||||
|
setTriggerKey(KeyStroke.getKeyStroke(KeyEvent.VK_UNDEFINED, 0));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
141
jadx-gui/src/main/java/jadx/gui/ui/action/JadxGuiAction.java
Normal file
141
jadx-gui/src/main/java/jadx/gui/ui/action/JadxGuiAction.java
Normal file
@ -0,0 +1,141 @@
|
|||||||
|
package jadx.gui.ui.action;
|
||||||
|
|
||||||
|
import java.awt.event.ActionEvent;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
import javax.swing.ImageIcon;
|
||||||
|
import javax.swing.JComponent;
|
||||||
|
import javax.swing.KeyStroke;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import jadx.gui.utils.UiUtils;
|
||||||
|
import jadx.gui.utils.shortcut.Shortcut;
|
||||||
|
import jadx.gui.utils.ui.ActionHandler;
|
||||||
|
|
||||||
|
public class JadxGuiAction extends ActionHandler
|
||||||
|
implements IShortcutAction {
|
||||||
|
private static final String COMMAND = "JadxGuiAction.Command.%s";
|
||||||
|
|
||||||
|
private final ActionModel actionModel;
|
||||||
|
private final String id;
|
||||||
|
private JComponent shortcutComponent = null;
|
||||||
|
private KeyStroke addedKeyStroke = null;
|
||||||
|
private Shortcut shortcut;
|
||||||
|
|
||||||
|
public JadxGuiAction(ActionModel actionModel) {
|
||||||
|
super();
|
||||||
|
this.actionModel = actionModel;
|
||||||
|
this.id = actionModel.name();
|
||||||
|
|
||||||
|
updateProperties();
|
||||||
|
}
|
||||||
|
|
||||||
|
public JadxGuiAction(ActionModel actionModel, Runnable action) {
|
||||||
|
super(action);
|
||||||
|
this.actionModel = actionModel;
|
||||||
|
this.id = actionModel.name();
|
||||||
|
|
||||||
|
updateProperties();
|
||||||
|
}
|
||||||
|
|
||||||
|
public JadxGuiAction(ActionModel actionModel, Consumer<ActionEvent> consumer) {
|
||||||
|
super(consumer);
|
||||||
|
this.actionModel = actionModel;
|
||||||
|
this.id = actionModel.name();
|
||||||
|
|
||||||
|
updateProperties();
|
||||||
|
}
|
||||||
|
|
||||||
|
public JadxGuiAction(String id) {
|
||||||
|
super();
|
||||||
|
this.actionModel = null;
|
||||||
|
this.id = id;
|
||||||
|
|
||||||
|
updateProperties();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateProperties() {
|
||||||
|
if (actionModel == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
String name = actionModel.getName();
|
||||||
|
String description = actionModel.getDescription();
|
||||||
|
ImageIcon icon = actionModel.getIcon();
|
||||||
|
if (name != null) {
|
||||||
|
setName(name);
|
||||||
|
}
|
||||||
|
if (description != null) {
|
||||||
|
setShortDescription(description);
|
||||||
|
}
|
||||||
|
if (icon != null) {
|
||||||
|
setIcon(icon);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public ActionModel getActionModel() {
|
||||||
|
return actionModel;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setShortcut(Shortcut shortcut) {
|
||||||
|
this.shortcut = shortcut;
|
||||||
|
if (shortcut != null) {
|
||||||
|
setKeyBinding(shortcut.toKeyStroke());
|
||||||
|
} else {
|
||||||
|
setKeyBinding(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setShortcutComponent(JComponent component) {
|
||||||
|
this.shortcutComponent = component;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JComponent getShortcutComponent() {
|
||||||
|
return shortcutComponent;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void actionPerformed(ActionEvent e) {
|
||||||
|
super.actionPerformed(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void performAction() {
|
||||||
|
if (shortcutComponent != null && !shortcutComponent.isShowing()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
String shortcutType = "null";
|
||||||
|
if (shortcut != null) {
|
||||||
|
shortcutType = shortcut.getTypeString();
|
||||||
|
}
|
||||||
|
actionPerformed(new ActionEvent(this, ActionEvent.ACTION_PERFORMED,
|
||||||
|
String.format(COMMAND, shortcutType)));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isSource(ActionEvent event) {
|
||||||
|
return event.getActionCommand() != null
|
||||||
|
&& event.getActionCommand().startsWith(String.format(COMMAND, ""));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setKeyBinding(KeyStroke keyStroke) {
|
||||||
|
if (shortcutComponent == null) {
|
||||||
|
super.setKeyBinding(keyStroke);
|
||||||
|
} else {
|
||||||
|
// We just set the keyStroke for it to appear in the menu item
|
||||||
|
// (grayed out in the right)
|
||||||
|
super.setKeyBinding(keyStroke);
|
||||||
|
|
||||||
|
if (addedKeyStroke != null) {
|
||||||
|
UiUtils.removeKeyBinding(shortcutComponent, addedKeyStroke, id);
|
||||||
|
}
|
||||||
|
UiUtils.addKeyBinding(shortcutComponent, keyStroke, id, this::performAction);
|
||||||
|
addedKeyStroke = keyStroke;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -31,6 +31,7 @@ import jadx.gui.utils.DefaultPopupMenuListener;
|
|||||||
import jadx.gui.utils.JNodeCache;
|
import jadx.gui.utils.JNodeCache;
|
||||||
import jadx.gui.utils.JumpPosition;
|
import jadx.gui.utils.JumpPosition;
|
||||||
import jadx.gui.utils.UiUtils;
|
import jadx.gui.utils.UiUtils;
|
||||||
|
import jadx.gui.utils.shortcut.ShortcutsController;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The {@link AbstractCodeArea} implementation used for displaying Java code and text based
|
* The {@link AbstractCodeArea} implementation used for displaying Java code and text based
|
||||||
@ -42,9 +43,12 @@ public final class CodeArea extends AbstractCodeArea {
|
|||||||
private static final long serialVersionUID = 6312736869579635796L;
|
private static final long serialVersionUID = 6312736869579635796L;
|
||||||
|
|
||||||
private @Nullable ICodeInfo cachedCodeInfo;
|
private @Nullable ICodeInfo cachedCodeInfo;
|
||||||
|
private final ShortcutsController shortcutsController;
|
||||||
|
|
||||||
CodeArea(ContentPanel contentPanel, JNode node) {
|
CodeArea(ContentPanel contentPanel, JNode node) {
|
||||||
super(contentPanel, node);
|
super(contentPanel, node);
|
||||||
|
this.shortcutsController = getMainWindow().getShortcutsController();
|
||||||
|
|
||||||
setSyntaxEditingStyle(node.getSyntaxName());
|
setSyntaxEditingStyle(node.getSyntaxName());
|
||||||
boolean isJavaCode = node instanceof JClass;
|
boolean isJavaCode = node instanceof JClass;
|
||||||
if (isJavaCode) {
|
if (isJavaCode) {
|
||||||
@ -115,7 +119,8 @@ public final class CodeArea extends AbstractCodeArea {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void addMenuItems() {
|
private void addMenuItems() {
|
||||||
JNodePopupBuilder popup = new JNodePopupBuilder(this, getPopupMenu());
|
ShortcutsController shortcutsController = getMainWindow().getShortcutsController();
|
||||||
|
JNodePopupBuilder popup = new JNodePopupBuilder(this, getPopupMenu(), shortcutsController);
|
||||||
popup.addSeparator();
|
popup.addSeparator();
|
||||||
popup.add(new FindUsageAction(this));
|
popup.add(new FindUsageAction(this));
|
||||||
popup.add(new GoToDeclarationAction(this));
|
popup.add(new GoToDeclarationAction(this));
|
||||||
@ -143,7 +148,8 @@ public final class CodeArea extends AbstractCodeArea {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void addMenuForJsonFile() {
|
private void addMenuForJsonFile() {
|
||||||
JNodePopupBuilder popup = new JNodePopupBuilder(this, getPopupMenu());
|
ShortcutsController shortcutsController = getMainWindow().getShortcutsController();
|
||||||
|
JNodePopupBuilder popup = new JNodePopupBuilder(this, getPopupMenu(), shortcutsController);
|
||||||
popup.addSeparator();
|
popup.addSeparator();
|
||||||
popup.add(new JsonPrettifyAction(this));
|
popup.add(new JsonPrettifyAction(this));
|
||||||
}
|
}
|
||||||
@ -339,6 +345,8 @@ public final class CodeArea extends AbstractCodeArea {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void dispose() {
|
public void dispose() {
|
||||||
|
shortcutsController.unbindActionsForComponent(this);
|
||||||
|
|
||||||
super.dispose();
|
super.dispose();
|
||||||
cachedCodeInfo = null;
|
cachedCodeInfo = null;
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,24 @@
|
|||||||
|
package jadx.gui.ui.codearea;
|
||||||
|
|
||||||
|
import jadx.gui.ui.action.ActionModel;
|
||||||
|
import jadx.gui.ui.action.JadxGuiAction;
|
||||||
|
|
||||||
|
public class CodeAreaAction extends JadxGuiAction {
|
||||||
|
protected transient CodeArea codeArea;
|
||||||
|
|
||||||
|
public CodeAreaAction(ActionModel actionModel, CodeArea codeArea) {
|
||||||
|
super(actionModel);
|
||||||
|
this.codeArea = codeArea;
|
||||||
|
setShortcutComponent(codeArea);
|
||||||
|
}
|
||||||
|
|
||||||
|
public CodeAreaAction(String id, CodeArea codeArea) {
|
||||||
|
super(id);
|
||||||
|
this.codeArea = codeArea;
|
||||||
|
setShortcutComponent(codeArea);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void dispose() {
|
||||||
|
codeArea = null;
|
||||||
|
}
|
||||||
|
}
|
@ -1,9 +1,7 @@
|
|||||||
package jadx.gui.ui.codearea;
|
package jadx.gui.ui.codearea;
|
||||||
|
|
||||||
import java.awt.event.ActionEvent;
|
import java.awt.event.ActionEvent;
|
||||||
import java.awt.event.KeyEvent;
|
|
||||||
|
|
||||||
import javax.swing.AbstractAction;
|
|
||||||
import javax.swing.event.PopupMenuEvent;
|
import javax.swing.event.PopupMenuEvent;
|
||||||
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
@ -25,30 +23,24 @@ import jadx.api.metadata.annotations.InsnCodeOffset;
|
|||||||
import jadx.api.metadata.annotations.NodeDeclareRef;
|
import jadx.api.metadata.annotations.NodeDeclareRef;
|
||||||
import jadx.gui.JadxWrapper;
|
import jadx.gui.JadxWrapper;
|
||||||
import jadx.gui.treemodel.JClass;
|
import jadx.gui.treemodel.JClass;
|
||||||
|
import jadx.gui.ui.action.ActionModel;
|
||||||
|
import jadx.gui.ui.action.JadxGuiAction;
|
||||||
import jadx.gui.ui.dialog.CommentDialog;
|
import jadx.gui.ui.dialog.CommentDialog;
|
||||||
import jadx.gui.utils.DefaultPopupMenuListener;
|
import jadx.gui.utils.DefaultPopupMenuListener;
|
||||||
import jadx.gui.utils.NLS;
|
import jadx.gui.utils.NLS;
|
||||||
import jadx.gui.utils.UiUtils;
|
import jadx.gui.utils.UiUtils;
|
||||||
|
|
||||||
import static javax.swing.KeyStroke.getKeyStroke;
|
public class CommentAction extends CodeAreaAction implements DefaultPopupMenuListener {
|
||||||
|
|
||||||
public class CommentAction extends AbstractAction implements DefaultPopupMenuListener {
|
|
||||||
private static final long serialVersionUID = 4753838562204629112L;
|
private static final long serialVersionUID = 4753838562204629112L;
|
||||||
|
|
||||||
private static final Logger LOG = LoggerFactory.getLogger(CommentAction.class);
|
private static final Logger LOG = LoggerFactory.getLogger(CommentAction.class);
|
||||||
private final CodeArea codeArea;
|
|
||||||
private final boolean enabled;
|
private final boolean enabled;
|
||||||
|
|
||||||
private ICodeComment actionComment;
|
private ICodeComment actionComment;
|
||||||
|
|
||||||
public CommentAction(CodeArea codeArea) {
|
public CommentAction(CodeArea codeArea) {
|
||||||
super(NLS.str("popup.add_comment") + " (;)");
|
super(ActionModel.CODE_COMMENT, codeArea);
|
||||||
this.codeArea = codeArea;
|
|
||||||
this.enabled = codeArea.getNode() instanceof JClass;
|
this.enabled = codeArea.getNode() instanceof JClass;
|
||||||
if (enabled) {
|
|
||||||
UiUtils.addKeyBinding(codeArea, getKeyStroke(KeyEvent.VK_SEMICOLON, 0), "popup.add_comment",
|
|
||||||
() -> showCommentDialog(getCommentRef(codeArea.getCaretPosition())));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -64,7 +56,15 @@ public class CommentAction extends AbstractAction implements DefaultPopupMenuLis
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void actionPerformed(ActionEvent e) {
|
public void actionPerformed(ActionEvent e) {
|
||||||
showCommentDialog(this.actionComment);
|
if (!enabled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (JadxGuiAction.isSource(e)) {
|
||||||
|
showCommentDialog(getCommentRef(codeArea.getCaretPosition()));
|
||||||
|
} else {
|
||||||
|
showCommentDialog(this.actionComment);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void showCommentDialog(ICodeComment codeComment) {
|
private void showCommentDialog(ICodeComment codeComment) {
|
||||||
|
@ -1,28 +1,15 @@
|
|||||||
package jadx.gui.ui.codearea;
|
package jadx.gui.ui.codearea;
|
||||||
|
|
||||||
import java.awt.event.ActionEvent;
|
import java.awt.event.ActionEvent;
|
||||||
import java.awt.event.KeyEvent;
|
|
||||||
|
|
||||||
import javax.swing.AbstractAction;
|
|
||||||
import javax.swing.KeyStroke;
|
|
||||||
|
|
||||||
|
import jadx.gui.ui.action.ActionModel;
|
||||||
import jadx.gui.ui.dialog.SearchDialog;
|
import jadx.gui.ui.dialog.SearchDialog;
|
||||||
import jadx.gui.utils.NLS;
|
|
||||||
import jadx.gui.utils.UiUtils;
|
|
||||||
|
|
||||||
import static javax.swing.KeyStroke.getKeyStroke;
|
public class CommentSearchAction extends CodeAreaAction {
|
||||||
|
|
||||||
public class CommentSearchAction extends AbstractAction {
|
|
||||||
private static final long serialVersionUID = -3646341661734961590L;
|
private static final long serialVersionUID = -3646341661734961590L;
|
||||||
|
|
||||||
private final CodeArea codeArea;
|
|
||||||
|
|
||||||
public CommentSearchAction(CodeArea codeArea) {
|
public CommentSearchAction(CodeArea codeArea) {
|
||||||
super(NLS.str("popup.search_comment") + " (Ctrl + ;)");
|
super(ActionModel.CODE_COMMENT_SEARCH, codeArea);
|
||||||
this.codeArea = codeArea;
|
|
||||||
|
|
||||||
KeyStroke key = getKeyStroke(KeyEvent.VK_SEMICOLON, UiUtils.ctrlButton());
|
|
||||||
UiUtils.addKeyBinding(codeArea, key, "popup.search_comment", this::startSearch);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -1,20 +1,15 @@
|
|||||||
package jadx.gui.ui.codearea;
|
package jadx.gui.ui.codearea;
|
||||||
|
|
||||||
import java.awt.event.KeyEvent;
|
|
||||||
|
|
||||||
import jadx.gui.treemodel.JNode;
|
import jadx.gui.treemodel.JNode;
|
||||||
import jadx.gui.ui.MainWindow;
|
import jadx.gui.ui.MainWindow;
|
||||||
|
import jadx.gui.ui.action.ActionModel;
|
||||||
import jadx.gui.ui.dialog.UsageDialog;
|
import jadx.gui.ui.dialog.UsageDialog;
|
||||||
import jadx.gui.utils.NLS;
|
|
||||||
|
|
||||||
import static javax.swing.KeyStroke.getKeyStroke;
|
|
||||||
|
|
||||||
public final class FindUsageAction extends JNodeAction {
|
public final class FindUsageAction extends JNodeAction {
|
||||||
private static final long serialVersionUID = 4692546569977976384L;
|
private static final long serialVersionUID = 4692546569977976384L;
|
||||||
|
|
||||||
public FindUsageAction(CodeArea codeArea) {
|
public FindUsageAction(CodeArea codeArea) {
|
||||||
super(NLS.str("popup.find_usage") + " (x)", codeArea);
|
super(ActionModel.FIND_USAGE, codeArea);
|
||||||
addKeyBinding(getKeyStroke(KeyEvent.VK_X, 0), "trigger usage");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
package jadx.gui.ui.codearea;
|
package jadx.gui.ui.codearea;
|
||||||
|
|
||||||
import java.awt.event.KeyEvent;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
@ -23,18 +22,16 @@ import jadx.gui.treemodel.JClass;
|
|||||||
import jadx.gui.treemodel.JField;
|
import jadx.gui.treemodel.JField;
|
||||||
import jadx.gui.treemodel.JMethod;
|
import jadx.gui.treemodel.JMethod;
|
||||||
import jadx.gui.treemodel.JNode;
|
import jadx.gui.treemodel.JNode;
|
||||||
|
import jadx.gui.ui.action.ActionModel;
|
||||||
import jadx.gui.utils.NLS;
|
import jadx.gui.utils.NLS;
|
||||||
import jadx.gui.utils.UiUtils;
|
import jadx.gui.utils.UiUtils;
|
||||||
|
|
||||||
import static javax.swing.KeyStroke.getKeyStroke;
|
|
||||||
|
|
||||||
public final class FridaAction extends JNodeAction {
|
public final class FridaAction extends JNodeAction {
|
||||||
private static final Logger LOG = LoggerFactory.getLogger(FridaAction.class);
|
private static final Logger LOG = LoggerFactory.getLogger(FridaAction.class);
|
||||||
private static final long serialVersionUID = -3084073927621269039L;
|
private static final long serialVersionUID = -3084073927621269039L;
|
||||||
|
|
||||||
public FridaAction(CodeArea codeArea) {
|
public FridaAction(CodeArea codeArea) {
|
||||||
super(NLS.str("popup.frida") + " (f)", codeArea);
|
super(ActionModel.FRIDA_COPY, codeArea);
|
||||||
addKeyBinding(getKeyStroke(KeyEvent.VK_F, 0), "trigger frida");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -1,18 +1,13 @@
|
|||||||
package jadx.gui.ui.codearea;
|
package jadx.gui.ui.codearea;
|
||||||
|
|
||||||
import java.awt.event.KeyEvent;
|
|
||||||
|
|
||||||
import jadx.gui.treemodel.JNode;
|
import jadx.gui.treemodel.JNode;
|
||||||
import jadx.gui.utils.NLS;
|
import jadx.gui.ui.action.ActionModel;
|
||||||
|
|
||||||
import static javax.swing.KeyStroke.getKeyStroke;
|
|
||||||
|
|
||||||
public final class GoToDeclarationAction extends JNodeAction {
|
public final class GoToDeclarationAction extends JNodeAction {
|
||||||
private static final long serialVersionUID = -1186470538894941301L;
|
private static final long serialVersionUID = -1186470538894941301L;
|
||||||
|
|
||||||
public GoToDeclarationAction(CodeArea codeArea) {
|
public GoToDeclarationAction(CodeArea codeArea) {
|
||||||
super(NLS.str("popup.go_to_declaration") + " (d)", codeArea);
|
super(ActionModel.GOTO_DECLARATION, codeArea);
|
||||||
addKeyBinding(getKeyStroke(KeyEvent.VK_D, 0), "trigger goto decl");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -3,26 +3,26 @@ package jadx.gui.ui.codearea;
|
|||||||
import java.awt.event.ActionEvent;
|
import java.awt.event.ActionEvent;
|
||||||
import java.beans.PropertyChangeListener;
|
import java.beans.PropertyChangeListener;
|
||||||
|
|
||||||
import javax.swing.AbstractAction;
|
|
||||||
import javax.swing.KeyStroke;
|
|
||||||
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import jadx.gui.treemodel.JNode;
|
import jadx.gui.treemodel.JNode;
|
||||||
import jadx.gui.utils.UiUtils;
|
import jadx.gui.ui.action.ActionModel;
|
||||||
|
import jadx.gui.ui.action.JadxGuiAction;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add menu and key binding actions for JNode in code area
|
* Add menu and key binding actions for JNode in code area
|
||||||
*/
|
*/
|
||||||
public abstract class JNodeAction extends AbstractAction {
|
public abstract class JNodeAction extends CodeAreaAction {
|
||||||
private static final long serialVersionUID = -2600154727884853550L;
|
private static final long serialVersionUID = -2600154727884853550L;
|
||||||
|
|
||||||
private transient CodeArea codeArea;
|
|
||||||
private transient @Nullable JNode node;
|
private transient @Nullable JNode node;
|
||||||
|
|
||||||
public JNodeAction(String name, CodeArea codeArea) {
|
public JNodeAction(ActionModel actionModel, CodeArea codeArea) {
|
||||||
super(name);
|
super(actionModel, codeArea);
|
||||||
this.codeArea = codeArea;
|
}
|
||||||
|
|
||||||
|
public JNodeAction(String id, CodeArea codeArea) {
|
||||||
|
super(id, codeArea);
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract void runAction(JNode node);
|
public abstract void runAction(JNode node);
|
||||||
@ -31,21 +31,16 @@ public abstract class JNodeAction extends AbstractAction {
|
|||||||
return node != null;
|
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
|
@Override
|
||||||
public void actionPerformed(ActionEvent e) {
|
public void actionPerformed(ActionEvent e) {
|
||||||
runAction(node);
|
if (JadxGuiAction.isSource(e)) {
|
||||||
|
node = codeArea.getNodeUnderCaret();
|
||||||
|
if (isActionEnabled(node)) {
|
||||||
|
runAction(node);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
runAction(node);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void changeNode(@Nullable JNode node) {
|
public void changeNode(@Nullable JNode node) {
|
||||||
@ -57,9 +52,10 @@ public abstract class JNodeAction extends AbstractAction {
|
|||||||
return codeArea;
|
return codeArea;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void dispose() {
|
public void dispose() {
|
||||||
|
super.dispose();
|
||||||
node = null;
|
node = null;
|
||||||
codeArea = null;
|
|
||||||
for (PropertyChangeListener changeListener : getPropertyChangeListeners()) {
|
for (PropertyChangeListener changeListener : getPropertyChangeListeners()) {
|
||||||
removePropertyChangeListener(changeListener);
|
removePropertyChangeListener(changeListener);
|
||||||
}
|
}
|
||||||
|
@ -1,14 +1,18 @@
|
|||||||
package jadx.gui.ui.codearea;
|
package jadx.gui.ui.codearea;
|
||||||
|
|
||||||
import javax.swing.Action;
|
|
||||||
import javax.swing.JPopupMenu;
|
import javax.swing.JPopupMenu;
|
||||||
import javax.swing.event.PopupMenuListener;
|
import javax.swing.event.PopupMenuListener;
|
||||||
|
|
||||||
|
import jadx.gui.ui.action.JadxGuiAction;
|
||||||
|
import jadx.gui.utils.shortcut.ShortcutsController;
|
||||||
|
|
||||||
public class JNodePopupBuilder {
|
public class JNodePopupBuilder {
|
||||||
private final JPopupMenu menu;
|
private final JPopupMenu menu;
|
||||||
private final JNodePopupListener popupListener;
|
private final JNodePopupListener popupListener;
|
||||||
|
private final ShortcutsController shortcutsController;
|
||||||
|
|
||||||
public JNodePopupBuilder(CodeArea codeArea, JPopupMenu popupMenu) {
|
public JNodePopupBuilder(CodeArea codeArea, JPopupMenu popupMenu, ShortcutsController shortcutsController) {
|
||||||
|
this.shortcutsController = shortcutsController;
|
||||||
menu = popupMenu;
|
menu = popupMenu;
|
||||||
popupListener = new JNodePopupListener(codeArea);
|
popupListener = new JNodePopupListener(codeArea);
|
||||||
popupMenu.addPopupMenuListener(popupListener);
|
popupMenu.addPopupMenuListener(popupListener);
|
||||||
@ -19,11 +23,23 @@ public class JNodePopupBuilder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void add(JNodeAction nodeAction) {
|
public void add(JNodeAction nodeAction) {
|
||||||
|
// We set the shortcut immediately for two reasons
|
||||||
|
// - there might be multiple instances of this action with
|
||||||
|
// same ActionModel across different codeAreas, while
|
||||||
|
// ShortcutController only supports one instance
|
||||||
|
// - This action will be recreated when shortcuts are changed,
|
||||||
|
// so no need to bind it
|
||||||
|
if (nodeAction.getActionModel() != null) {
|
||||||
|
shortcutsController.bindImmediate(nodeAction);
|
||||||
|
}
|
||||||
menu.add(nodeAction);
|
menu.add(nodeAction);
|
||||||
popupListener.addActions(nodeAction);
|
popupListener.addActions(nodeAction);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void add(Action action) {
|
public void add(JadxGuiAction action) {
|
||||||
|
if (action.getActionModel() != null) {
|
||||||
|
shortcutsController.bindImmediate(action);
|
||||||
|
}
|
||||||
menu.add(action);
|
menu.add(action);
|
||||||
if (action instanceof PopupMenuListener) {
|
if (action instanceof PopupMenuListener) {
|
||||||
menu.addPopupMenuListener((PopupMenuListener) action);
|
menu.addPopupMenuListener((PopupMenuListener) action);
|
||||||
|
@ -6,7 +6,7 @@ import com.google.gson.JsonElement;
|
|||||||
import com.google.gson.JsonParser;
|
import com.google.gson.JsonParser;
|
||||||
|
|
||||||
import jadx.gui.treemodel.JNode;
|
import jadx.gui.treemodel.JNode;
|
||||||
import jadx.gui.utils.NLS;
|
import jadx.gui.ui.action.ActionModel;
|
||||||
|
|
||||||
public class JsonPrettifyAction extends JNodeAction {
|
public class JsonPrettifyAction extends JNodeAction {
|
||||||
|
|
||||||
@ -14,7 +14,7 @@ public class JsonPrettifyAction extends JNodeAction {
|
|||||||
private static final Gson GSON = new GsonBuilder().setPrettyPrinting().create();
|
private static final Gson GSON = new GsonBuilder().setPrettyPrinting().create();
|
||||||
|
|
||||||
public JsonPrettifyAction(CodeArea codeArea) {
|
public JsonPrettifyAction(CodeArea codeArea) {
|
||||||
super(NLS.str("popup.json_prettify"), codeArea);
|
super(ActionModel.JSON_PRETTIFY, codeArea);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -2,18 +2,14 @@ package jadx.gui.ui.codearea;
|
|||||||
|
|
||||||
import jadx.gui.treemodel.JNode;
|
import jadx.gui.treemodel.JNode;
|
||||||
import jadx.gui.treemodel.JRenameNode;
|
import jadx.gui.treemodel.JRenameNode;
|
||||||
|
import jadx.gui.ui.action.ActionModel;
|
||||||
import jadx.gui.ui.dialog.RenameDialog;
|
import jadx.gui.ui.dialog.RenameDialog;
|
||||||
import jadx.gui.utils.NLS;
|
|
||||||
|
|
||||||
import static java.awt.event.KeyEvent.VK_N;
|
|
||||||
import static javax.swing.KeyStroke.getKeyStroke;
|
|
||||||
|
|
||||||
public final class RenameAction extends JNodeAction {
|
public final class RenameAction extends JNodeAction {
|
||||||
private static final long serialVersionUID = -4680872086148463289L;
|
private static final long serialVersionUID = -4680872086148463289L;
|
||||||
|
|
||||||
public RenameAction(CodeArea codeArea) {
|
public RenameAction(CodeArea codeArea) {
|
||||||
super(NLS.str("popup.rename") + " (n)", codeArea);
|
super(ActionModel.CODE_RENAME, codeArea);
|
||||||
addKeyBinding(getKeyStroke(VK_N, 0), "trigger rename");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
package jadx.gui.ui.codearea;
|
package jadx.gui.ui.codearea;
|
||||||
|
|
||||||
import java.awt.event.KeyEvent;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
@ -17,18 +16,16 @@ import jadx.core.utils.exceptions.JadxRuntimeException;
|
|||||||
import jadx.gui.treemodel.JClass;
|
import jadx.gui.treemodel.JClass;
|
||||||
import jadx.gui.treemodel.JMethod;
|
import jadx.gui.treemodel.JMethod;
|
||||||
import jadx.gui.treemodel.JNode;
|
import jadx.gui.treemodel.JNode;
|
||||||
|
import jadx.gui.ui.action.ActionModel;
|
||||||
import jadx.gui.utils.NLS;
|
import jadx.gui.utils.NLS;
|
||||||
import jadx.gui.utils.UiUtils;
|
import jadx.gui.utils.UiUtils;
|
||||||
|
|
||||||
import static javax.swing.KeyStroke.getKeyStroke;
|
|
||||||
|
|
||||||
public class XposedAction extends JNodeAction {
|
public class XposedAction extends JNodeAction {
|
||||||
private static final Logger LOG = LoggerFactory.getLogger(XposedAction.class);
|
private static final Logger LOG = LoggerFactory.getLogger(XposedAction.class);
|
||||||
private static final long serialVersionUID = 2641585141624592578L;
|
private static final long serialVersionUID = 2641585141624592578L;
|
||||||
|
|
||||||
public XposedAction(CodeArea codeArea) {
|
public XposedAction(CodeArea codeArea) {
|
||||||
super(NLS.str("popup.xposed") + " (y)", codeArea);
|
super(ActionModel.XPOSED_COPY, codeArea);
|
||||||
addKeyBinding(getKeyStroke(KeyEvent.VK_Y, 0), "trigger xposed");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
22
jadx-gui/src/main/java/jadx/gui/ui/menu/HiddenMenuItem.java
Normal file
22
jadx-gui/src/main/java/jadx/gui/ui/menu/HiddenMenuItem.java
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
package jadx.gui.ui.menu;
|
||||||
|
|
||||||
|
import java.awt.Dimension;
|
||||||
|
import java.awt.Graphics;
|
||||||
|
|
||||||
|
import javax.swing.Action;
|
||||||
|
import javax.swing.JMenuItem;
|
||||||
|
|
||||||
|
public class HiddenMenuItem extends JMenuItem {
|
||||||
|
public HiddenMenuItem(Action a) {
|
||||||
|
super(a);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void paintComponent(Graphics g) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Dimension getPreferredSize() {
|
||||||
|
return new Dimension(0, 0);
|
||||||
|
}
|
||||||
|
}
|
64
jadx-gui/src/main/java/jadx/gui/ui/menu/JadxMenu.java
Normal file
64
jadx-gui/src/main/java/jadx/gui/ui/menu/JadxMenu.java
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
package jadx.gui.ui.menu;
|
||||||
|
|
||||||
|
import javax.swing.Action;
|
||||||
|
import javax.swing.JMenu;
|
||||||
|
import javax.swing.JMenuItem;
|
||||||
|
|
||||||
|
import jadx.gui.ui.action.ActionModel;
|
||||||
|
import jadx.gui.ui.action.JadxGuiAction;
|
||||||
|
import jadx.gui.utils.shortcut.Shortcut;
|
||||||
|
import jadx.gui.utils.shortcut.ShortcutsController;
|
||||||
|
|
||||||
|
public class JadxMenu extends JMenu {
|
||||||
|
private final ShortcutsController shortcutsController;
|
||||||
|
|
||||||
|
public JadxMenu(String name, ShortcutsController shortcutsController) {
|
||||||
|
super(name);
|
||||||
|
|
||||||
|
this.shortcutsController = shortcutsController;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JMenuItem add(JMenuItem menuItem) {
|
||||||
|
Action action = menuItem.getAction();
|
||||||
|
bindAction(action);
|
||||||
|
return super.add(menuItem);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JMenuItem add(Action action) {
|
||||||
|
bindAction(action);
|
||||||
|
return super.add(action);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void bindAction(Action action) {
|
||||||
|
if (action instanceof JadxGuiAction) {
|
||||||
|
shortcutsController.bind((JadxGuiAction) action);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void reloadShortcuts() {
|
||||||
|
for (int i = 0; i < getItemCount(); i++) {
|
||||||
|
// TODO only repaint the items whose shortcut changed
|
||||||
|
JMenuItem item = getItem(i);
|
||||||
|
if (item == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
Action action = item.getAction();
|
||||||
|
if (!(action instanceof JadxGuiAction) || ((JadxGuiAction) action).getActionModel() == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
ActionModel actionModel = ((JadxGuiAction) action).getActionModel();
|
||||||
|
Shortcut shortcut = shortcutsController.get(actionModel);
|
||||||
|
if (shortcut != null) {
|
||||||
|
item.setAccelerator(shortcut.toKeyStroke());
|
||||||
|
} else {
|
||||||
|
item.setAccelerator(null);
|
||||||
|
}
|
||||||
|
item.repaint();
|
||||||
|
item.revalidate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
15
jadx-gui/src/main/java/jadx/gui/ui/menu/JadxMenuBar.java
Normal file
15
jadx-gui/src/main/java/jadx/gui/ui/menu/JadxMenuBar.java
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
package jadx.gui.ui.menu;
|
||||||
|
|
||||||
|
import javax.swing.JMenu;
|
||||||
|
import javax.swing.JMenuBar;
|
||||||
|
|
||||||
|
public class JadxMenuBar extends JMenuBar {
|
||||||
|
public void reloadShortcuts() {
|
||||||
|
for (int i = 0; i < getMenuCount(); i++) {
|
||||||
|
JMenu menu = getMenu(i);
|
||||||
|
if (menu instanceof JadxMenu) {
|
||||||
|
((JadxMenu) menu).reloadShortcuts();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
150
jadx-gui/src/main/java/jadx/gui/utils/shortcut/Shortcut.java
Normal file
150
jadx-gui/src/main/java/jadx/gui/utils/shortcut/Shortcut.java
Normal file
@ -0,0 +1,150 @@
|
|||||||
|
package jadx.gui.utils.shortcut;
|
||||||
|
|
||||||
|
import java.awt.event.KeyEvent;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import javax.swing.KeyStroke;
|
||||||
|
|
||||||
|
import jadx.gui.utils.SystemInfo;
|
||||||
|
|
||||||
|
public class Shortcut {
|
||||||
|
private static final Set<Integer> FORBIDDEN_KEY_CODES = new HashSet<>(List.of(
|
||||||
|
KeyEvent.VK_UNDEFINED, KeyEvent.VK_SHIFT, KeyEvent.VK_ALT, KeyEvent.VK_META, KeyEvent.VK_ALT_GRAPH));
|
||||||
|
private static final Set<Integer> ALLOWED_MODIFIERS = new HashSet<>(List.of(
|
||||||
|
KeyEvent.CTRL_DOWN_MASK, KeyEvent.META_DOWN_MASK, KeyEvent.ALT_DOWN_MASK, KeyEvent.ALT_GRAPH_DOWN_MASK,
|
||||||
|
KeyEvent.SHIFT_DOWN_MASK));
|
||||||
|
|
||||||
|
private Integer keyCode = null;
|
||||||
|
private Integer modifiers = null;
|
||||||
|
private Integer mouseButton = null;
|
||||||
|
|
||||||
|
private Shortcut() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Shortcut keyboard(int keyCode) {
|
||||||
|
return keyboard(keyCode, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Shortcut keyboard(int keyCode, int modifiers) {
|
||||||
|
Shortcut shortcut = new Shortcut();
|
||||||
|
shortcut.keyCode = keyCode;
|
||||||
|
shortcut.modifiers = modifiers;
|
||||||
|
return shortcut;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Shortcut mouse(int mouseButton) {
|
||||||
|
Shortcut shortcut = new Shortcut();
|
||||||
|
shortcut.mouseButton = mouseButton;
|
||||||
|
return shortcut;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Shortcut none() {
|
||||||
|
Shortcut shortcut = new Shortcut();
|
||||||
|
// Must have at least one non-null attribute in order to be serialized
|
||||||
|
// otherwise will roll back to default Shortcut
|
||||||
|
shortcut.modifiers = 0;
|
||||||
|
return shortcut;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getKeyCode() {
|
||||||
|
return keyCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getModifiers() {
|
||||||
|
return modifiers;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getMouseButton() {
|
||||||
|
return mouseButton;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isKeyboard() {
|
||||||
|
return keyCode != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isMouse() {
|
||||||
|
return mouseButton != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isNone() {
|
||||||
|
return !isMouse() && !isKeyboard();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isValidKeyboard() {
|
||||||
|
return isKeyboard() && !FORBIDDEN_KEY_CODES.contains(keyCode) && isValidModifiers();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isValidModifiers() {
|
||||||
|
int modifiersTest = modifiers;
|
||||||
|
for (Integer modifier : ALLOWED_MODIFIERS) {
|
||||||
|
modifiersTest &= ~modifier;
|
||||||
|
}
|
||||||
|
return modifiersTest == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public KeyStroke toKeyStroke() {
|
||||||
|
return isKeyboard()
|
||||||
|
? KeyStroke.getKeyStroke(keyCode, modifiers,
|
||||||
|
modifiers != 0 && SystemInfo.IS_MAC)
|
||||||
|
: null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
if (isKeyboard()) {
|
||||||
|
return keyToString();
|
||||||
|
} else if (isMouse()) {
|
||||||
|
return mouseToString();
|
||||||
|
}
|
||||||
|
return "NONE";
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getTypeString() {
|
||||||
|
if (isKeyboard()) {
|
||||||
|
return "Keyboard";
|
||||||
|
} else if (isMouse()) {
|
||||||
|
return "Mouse";
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String mouseToString() {
|
||||||
|
return "MouseButton" + mouseButton;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String keyToString() {
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
if (modifiers != null && modifiers > 0) {
|
||||||
|
sb.append(KeyEvent.getModifiersExText(modifiers));
|
||||||
|
sb.append('+');
|
||||||
|
}
|
||||||
|
if (keyCode != null && keyCode != 0) {
|
||||||
|
sb.append(KeyEvent.getKeyText(keyCode));
|
||||||
|
} else {
|
||||||
|
sb.append("UNDEFINED");
|
||||||
|
}
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (this == o) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (o == null || getClass() != o.getClass()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
Shortcut shortcut = (Shortcut) o;
|
||||||
|
return Objects.equals(keyCode, shortcut.keyCode)
|
||||||
|
&& Objects.equals(modifiers, shortcut.modifiers)
|
||||||
|
&& Objects.equals(mouseButton, shortcut.mouseButton);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Objects.hash(keyCode, modifiers, mouseButton);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,143 @@
|
|||||||
|
package jadx.gui.utils.shortcut;
|
||||||
|
|
||||||
|
import java.awt.AWTEvent;
|
||||||
|
import java.awt.Toolkit;
|
||||||
|
import java.awt.event.MouseEvent;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import javax.swing.JComponent;
|
||||||
|
import javax.swing.KeyStroke;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import jadx.gui.settings.JadxSettings;
|
||||||
|
import jadx.gui.settings.data.ShortcutsWrapper;
|
||||||
|
import jadx.gui.ui.MainWindow;
|
||||||
|
import jadx.gui.ui.action.ActionModel;
|
||||||
|
import jadx.gui.ui.action.IShortcutAction;
|
||||||
|
import jadx.gui.utils.UiUtils;
|
||||||
|
|
||||||
|
public class ShortcutsController {
|
||||||
|
private ShortcutsWrapper shortcuts;
|
||||||
|
private final JadxSettings settings;
|
||||||
|
|
||||||
|
private final Map<ActionModel, Set<IShortcutAction>> boundActions = new HashMap<>();
|
||||||
|
|
||||||
|
private Set<ActionModel> mouseActions = null;
|
||||||
|
|
||||||
|
public ShortcutsController(JadxSettings settings) {
|
||||||
|
this.settings = settings;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void loadSettings() {
|
||||||
|
this.shortcuts = settings.getShortcuts();
|
||||||
|
|
||||||
|
indexMouseActions();
|
||||||
|
|
||||||
|
for (Map.Entry<ActionModel, Set<IShortcutAction>> actionsEntry : boundActions.entrySet()) {
|
||||||
|
ActionModel actionModel = actionsEntry.getKey();
|
||||||
|
Set<IShortcutAction> actions = actionsEntry.getValue();
|
||||||
|
Shortcut shortcut = get(actionModel);
|
||||||
|
if (actions != null) {
|
||||||
|
for (IShortcutAction action : actions) {
|
||||||
|
action.setShortcut(shortcut);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public Shortcut get(ActionModel actionModel) {
|
||||||
|
return shortcuts.get(actionModel);
|
||||||
|
}
|
||||||
|
|
||||||
|
public KeyStroke getKeyStroke(ActionModel actionModel) {
|
||||||
|
Shortcut shortcut = get(actionModel);
|
||||||
|
KeyStroke keyStroke = null;
|
||||||
|
if (shortcut != null && shortcut.isKeyboard()) {
|
||||||
|
keyStroke = shortcut.toKeyStroke();
|
||||||
|
}
|
||||||
|
return keyStroke;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Binds to an action and updates its shortcut every time loadSettings is called
|
||||||
|
*/
|
||||||
|
public void bind(IShortcutAction action) {
|
||||||
|
boundActions.computeIfAbsent(action.getActionModel(), k -> new HashSet<>());
|
||||||
|
boundActions.get(action.getActionModel()).add(action);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Immediately sets the shortcut for an action
|
||||||
|
*/
|
||||||
|
public void bindImmediate(IShortcutAction action) {
|
||||||
|
bind(action);
|
||||||
|
Shortcut shortcut = get(action.getActionModel());
|
||||||
|
action.setShortcut(shortcut);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Map<ActionModel, Shortcut> getDefault() {
|
||||||
|
Map<ActionModel, Shortcut> shortcuts = new HashMap<>();
|
||||||
|
for (ActionModel actionModel : ActionModel.values()) {
|
||||||
|
shortcuts.put(actionModel, actionModel.getDefaultShortcut());
|
||||||
|
}
|
||||||
|
return shortcuts;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void registerMouseEventListener(MainWindow mw) {
|
||||||
|
Toolkit.getDefaultToolkit().addAWTEventListener(event -> {
|
||||||
|
if (mw.isSettingsOpen()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(event instanceof MouseEvent)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
MouseEvent mouseEvent = (MouseEvent) event;
|
||||||
|
if (mouseEvent.getID() != MouseEvent.MOUSE_PRESSED) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int mouseButton = mouseEvent.getButton();
|
||||||
|
for (ActionModel actionModel : mouseActions) {
|
||||||
|
Shortcut shortcut = shortcuts.get(actionModel);
|
||||||
|
if (shortcut != null && shortcut.getMouseButton() == mouseButton) {
|
||||||
|
Set<IShortcutAction> actions = boundActions.get(actionModel);
|
||||||
|
if (actions != null) {
|
||||||
|
for (IShortcutAction action : actions) {
|
||||||
|
if (action != null) {
|
||||||
|
mouseEvent.consume();
|
||||||
|
UiUtils.uiRun(action::performAction);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, AWTEvent.MOUSE_EVENT_MASK);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void indexMouseActions() {
|
||||||
|
mouseActions = new HashSet<>();
|
||||||
|
for (ActionModel actionModel : ActionModel.values()) {
|
||||||
|
Shortcut shortcut = shortcuts.get(actionModel);
|
||||||
|
if (shortcut != null && shortcut.isMouse()) {
|
||||||
|
mouseActions.add(actionModel);
|
||||||
|
} else {
|
||||||
|
mouseActions.remove(actionModel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void unbindActionsForComponent(JComponent component) {
|
||||||
|
for (ActionModel actionModel : ActionModel.values()) {
|
||||||
|
Set<IShortcutAction> actions = boundActions.get(actionModel);
|
||||||
|
if (actions != null) {
|
||||||
|
actions.removeIf(action -> action != null && action.getShortcutComponent() == component);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,7 +1,6 @@
|
|||||||
package jadx.gui.utils.ui;
|
package jadx.gui.utils.ui;
|
||||||
|
|
||||||
import java.awt.event.ActionEvent;
|
import java.awt.event.ActionEvent;
|
||||||
import java.awt.event.KeyEvent;
|
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
import javax.swing.AbstractAction;
|
import javax.swing.AbstractAction;
|
||||||
@ -11,6 +10,7 @@ import javax.swing.JComponent;
|
|||||||
import javax.swing.KeyStroke;
|
import javax.swing.KeyStroke;
|
||||||
|
|
||||||
import jadx.gui.utils.UiUtils;
|
import jadx.gui.utils.UiUtils;
|
||||||
|
import jadx.gui.utils.shortcut.Shortcut;
|
||||||
|
|
||||||
public class ActionHandler extends AbstractAction {
|
public class ActionHandler extends AbstractAction {
|
||||||
|
|
||||||
@ -24,6 +24,11 @@ public class ActionHandler extends AbstractAction {
|
|||||||
this.consumer = consumer;
|
this.consumer = consumer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ActionHandler() {
|
||||||
|
this.consumer = ev -> {
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
public void setName(String name) {
|
public void setName(String name) {
|
||||||
putValue(NAME, name);
|
putValue(NAME, name);
|
||||||
}
|
}
|
||||||
@ -58,7 +63,7 @@ public class ActionHandler extends AbstractAction {
|
|||||||
public void addKeyBindToDescription() {
|
public void addKeyBindToDescription() {
|
||||||
KeyStroke keyStroke = (KeyStroke) getValue(ACCELERATOR_KEY);
|
KeyStroke keyStroke = (KeyStroke) getValue(ACCELERATOR_KEY);
|
||||||
if (keyStroke != null) {
|
if (keyStroke != null) {
|
||||||
String keyText = KeyEvent.getKeyText(keyStroke.getKeyCode());
|
String keyText = Shortcut.keyboard(keyStroke.getKeyCode(), keyStroke.getModifiers()).toString();
|
||||||
String desc = (String) getValue(SHORT_DESCRIPTION);
|
String desc = (String) getValue(SHORT_DESCRIPTION);
|
||||||
setShortDescription(desc + " (" + keyText + ")");
|
setShortDescription(desc + " (" + keyText + ")");
|
||||||
}
|
}
|
||||||
|
@ -23,6 +23,7 @@ menu.deobfuscation=Deobfuskierung
|
|||||||
menu.log=Log-Anzeige
|
menu.log=Log-Anzeige
|
||||||
menu.help=Hilfe
|
menu.help=Hilfe
|
||||||
menu.about=Über
|
menu.about=Über
|
||||||
|
#menu.quark=
|
||||||
menu.update_label=Neue Version %s verfügbar!
|
menu.update_label=Neue Version %s verfügbar!
|
||||||
|
|
||||||
file.open_action=Datei öffnen…
|
file.open_action=Datei öffnen…
|
||||||
@ -161,6 +162,8 @@ about_dialog.title=Über JADX
|
|||||||
preferences.title=Einstellungen
|
preferences.title=Einstellungen
|
||||||
preferences.deobfuscation=Deobfuskierung
|
preferences.deobfuscation=Deobfuskierung
|
||||||
preferences.appearance=Aussehen
|
preferences.appearance=Aussehen
|
||||||
|
#preferences.shortcuts=
|
||||||
|
#preferences.select_shortcuts=
|
||||||
preferences.decompile=Dekompilierung
|
preferences.decompile=Dekompilierung
|
||||||
preferences.plugins=Plugins
|
preferences.plugins=Plugins
|
||||||
preferences.project=Projekt
|
preferences.project=Projekt
|
||||||
@ -257,6 +260,9 @@ msg.open_file=Bitte Datei öffnen
|
|||||||
msg.saving_sources=Quelltexte speichern
|
msg.saving_sources=Quelltexte speichern
|
||||||
msg.language_changed_title=Sprache speichern
|
msg.language_changed_title=Sprache speichern
|
||||||
msg.language_changed=Die neue Sprache wird beim nächsten Start der Anwendung angezeigt.
|
msg.language_changed=Die neue Sprache wird beim nächsten Start der Anwendung angezeigt.
|
||||||
|
#msg.warning_title=
|
||||||
|
#msg.common_mouse_shortcut=
|
||||||
|
#msg.duplicate_shortcut=
|
||||||
msg.project_error_title=Fehler
|
msg.project_error_title=Fehler
|
||||||
msg.project_error=Projekt konnte nicht geladen werden
|
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.cmd_select_class_error=Klasse\n%s auswählen nicht möglich\nSie existiert nicht.
|
||||||
@ -290,6 +296,7 @@ popup.search_global=Globale Suche "%s"
|
|||||||
|
|
||||||
#script.run=Run
|
#script.run=Run
|
||||||
#script.save=Save
|
#script.save=Save
|
||||||
|
#script.auto_complete=
|
||||||
#script.check=Check
|
#script.check=Check
|
||||||
#script.format=Reformat
|
#script.format=Reformat
|
||||||
#script.log=Show log
|
#script.log=Show log
|
||||||
@ -405,3 +412,8 @@ adb_dialog.no_devices=Ich kann kein Gerät finden, um die App zu starten.
|
|||||||
adb_dialog.restart_while_debugging_title=Neustart während der Fehlersuche
|
adb_dialog.restart_while_debugging_title=Neustart während der Fehlersuche
|
||||||
adb_dialog.restart_while_debugging_msg=Sie debuggen eine App, sind Sie sicher, dass Sie eine Sitzung neu starten können?
|
adb_dialog.restart_while_debugging_msg=Sie debuggen eine App, sind Sie sicher, dass Sie eine Sitzung neu starten können?
|
||||||
adb_dialog.starting_debugger=Debugger starten…
|
adb_dialog.starting_debugger=Debugger starten…
|
||||||
|
|
||||||
|
#action.variant=
|
||||||
|
#action_category.menu_toolbar=
|
||||||
|
#action_category.code_area=
|
||||||
|
#action_category.plugin_script=
|
||||||
|
@ -23,6 +23,7 @@ menu.deobfuscation=Deobfuscation
|
|||||||
menu.log=Log Viewer
|
menu.log=Log Viewer
|
||||||
menu.help=Help
|
menu.help=Help
|
||||||
menu.about=About
|
menu.about=About
|
||||||
|
menu.quark=Quark Engine
|
||||||
menu.update_label=New version %s available!
|
menu.update_label=New version %s available!
|
||||||
|
|
||||||
file.open_action=Open files ...
|
file.open_action=Open files ...
|
||||||
@ -161,6 +162,8 @@ about_dialog.title=About JADX
|
|||||||
preferences.title=Preferences
|
preferences.title=Preferences
|
||||||
preferences.deobfuscation=Deobfuscation
|
preferences.deobfuscation=Deobfuscation
|
||||||
preferences.appearance=Appearance
|
preferences.appearance=Appearance
|
||||||
|
preferences.shortcuts=Shortcuts
|
||||||
|
preferences.select_shortcuts=Select a specific shortcuts group
|
||||||
preferences.decompile=Decompilation
|
preferences.decompile=Decompilation
|
||||||
preferences.plugins=Plugins
|
preferences.plugins=Plugins
|
||||||
preferences.project=Project
|
preferences.project=Project
|
||||||
@ -257,6 +260,9 @@ msg.open_file=Please open file
|
|||||||
msg.saving_sources=Saving sources
|
msg.saving_sources=Saving sources
|
||||||
msg.language_changed_title=Language changed
|
msg.language_changed_title=Language changed
|
||||||
msg.language_changed=New language will be displayed the next time application starts.
|
msg.language_changed=New language will be displayed the next time application starts.
|
||||||
|
msg.warning_title=Warning
|
||||||
|
msg.common_mouse_shortcut=This is a commonly used key, are you sure you would like to bind it to an action?
|
||||||
|
msg.duplicate_shortcut=The shortcut %s is already set in action "%s" from category "%s", continue ?
|
||||||
msg.project_error_title=Error
|
msg.project_error_title=Error
|
||||||
msg.project_error=Project could not be loaded
|
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.cmd_select_class_error=Failed to select the class\n%s\nThe class does not exist.
|
||||||
@ -290,6 +296,7 @@ popup.json_prettify=JSON Prettify
|
|||||||
|
|
||||||
script.run=Run
|
script.run=Run
|
||||||
script.save=Save
|
script.save=Save
|
||||||
|
script.auto_complete=Auto Complete
|
||||||
script.check=Check
|
script.check=Check
|
||||||
script.format=Reformat
|
script.format=Reformat
|
||||||
script.log=Show log
|
script.log=Show log
|
||||||
@ -405,3 +412,8 @@ adb_dialog.no_devices=Can't found any device to start app.
|
|||||||
adb_dialog.restart_while_debugging_title=Restart while debugging
|
adb_dialog.restart_while_debugging_title=Restart while debugging
|
||||||
adb_dialog.restart_while_debugging_msg=You're debugging an app, are you sure to restart a session?
|
adb_dialog.restart_while_debugging_msg=You're debugging an app, are you sure to restart a session?
|
||||||
adb_dialog.starting_debugger=Starting debugger...
|
adb_dialog.starting_debugger=Starting debugger...
|
||||||
|
|
||||||
|
action.variant=%s (variant)
|
||||||
|
action_category.menu_toolbar=Menu / Toolbar
|
||||||
|
action_category.code_area=Code Area
|
||||||
|
action_category.plugin_script=Plugin Script
|
||||||
|
@ -23,6 +23,7 @@ menu.deobfuscation=Desofuscación
|
|||||||
menu.log=Visor log
|
menu.log=Visor log
|
||||||
menu.help=Ayuda
|
menu.help=Ayuda
|
||||||
menu.about=Acerca de...
|
menu.about=Acerca de...
|
||||||
|
#menu.quark=
|
||||||
menu.update_label=¡Nueva versión %s disponible!
|
menu.update_label=¡Nueva versión %s disponible!
|
||||||
|
|
||||||
file.open_action=Abrir archivo...
|
file.open_action=Abrir archivo...
|
||||||
@ -161,6 +162,8 @@ about_dialog.title=Sobre JADX
|
|||||||
preferences.title=Preferencias
|
preferences.title=Preferencias
|
||||||
preferences.deobfuscation=Desofuscación
|
preferences.deobfuscation=Desofuscación
|
||||||
#preferences.appearance=Appearance
|
#preferences.appearance=Appearance
|
||||||
|
#preferences.shortcuts=
|
||||||
|
#preferences.select_shortcuts=
|
||||||
preferences.decompile=Descompilación
|
preferences.decompile=Descompilación
|
||||||
#preferences.plugins=Plugins
|
#preferences.plugins=Plugins
|
||||||
#preferences.project=
|
#preferences.project=
|
||||||
@ -257,6 +260,9 @@ msg.open_file=Por favor, abra un archivo
|
|||||||
msg.saving_sources=Guardando fuente
|
msg.saving_sources=Guardando fuente
|
||||||
msg.language_changed_title=Idioma cambiado
|
msg.language_changed_title=Idioma cambiado
|
||||||
msg.language_changed=El nuevo idioma se mostrará la próxima vez que la aplicación se inicie.
|
msg.language_changed=El nuevo idioma se mostrará la próxima vez que la aplicación se inicie.
|
||||||
|
#msg.warning_title=
|
||||||
|
#msg.common_mouse_shortcut=
|
||||||
|
#msg.duplicate_shortcut=
|
||||||
#msg.project_error_title=
|
#msg.project_error_title=
|
||||||
#msg.project_error=
|
#msg.project_error=
|
||||||
#msg.cmd_select_class_error=
|
#msg.cmd_select_class_error=
|
||||||
@ -290,6 +296,7 @@ popup.rename=Nimeta ümber
|
|||||||
|
|
||||||
#script.run=Run
|
#script.run=Run
|
||||||
#script.save=Save
|
#script.save=Save
|
||||||
|
#script.auto_complete=
|
||||||
#script.check=Check
|
#script.check=Check
|
||||||
#script.format=Reformat
|
#script.format=Reformat
|
||||||
#script.log=Show log
|
#script.log=Show log
|
||||||
@ -405,3 +412,8 @@ certificate.serialPubKeyY=Y
|
|||||||
#adb_dialog.restart_while_debugging_title=Restart while debugging
|
#adb_dialog.restart_while_debugging_title=Restart while debugging
|
||||||
#adb_dialog.restart_while_debugging_msg=You're debugging an app, are you sure to restart a session?
|
#adb_dialog.restart_while_debugging_msg=You're debugging an app, are you sure to restart a session?
|
||||||
#adb_dialog.starting_debugger=Starting debugger...
|
#adb_dialog.starting_debugger=Starting debugger...
|
||||||
|
|
||||||
|
#action.variant=
|
||||||
|
#action_category.menu_toolbar=
|
||||||
|
#action_category.code_area=
|
||||||
|
#action_category.plugin_script=
|
||||||
|
@ -23,6 +23,7 @@ menu.deobfuscation=난독화 해제
|
|||||||
menu.log=로그 뷰어
|
menu.log=로그 뷰어
|
||||||
menu.help=도움말
|
menu.help=도움말
|
||||||
menu.about=정보
|
menu.about=정보
|
||||||
|
#menu.quark=
|
||||||
menu.update_label=새 버전 %s 이(가) 존재합니다!
|
menu.update_label=새 버전 %s 이(가) 존재합니다!
|
||||||
|
|
||||||
file.open_action=파일 열기 ...
|
file.open_action=파일 열기 ...
|
||||||
@ -161,6 +162,8 @@ about_dialog.title=JADX 정보
|
|||||||
preferences.title=설정
|
preferences.title=설정
|
||||||
preferences.deobfuscation=난독화 해제
|
preferences.deobfuscation=난독화 해제
|
||||||
preferences.appearance=외관
|
preferences.appearance=외관
|
||||||
|
#preferences.shortcuts=
|
||||||
|
#preferences.select_shortcuts=
|
||||||
preferences.decompile=디컴파일
|
preferences.decompile=디컴파일
|
||||||
preferences.plugins=플러그인
|
preferences.plugins=플러그인
|
||||||
preferences.project=프로젝트
|
preferences.project=프로젝트
|
||||||
@ -257,6 +260,9 @@ msg.open_file=파일을 여십시오
|
|||||||
msg.saving_sources=소스 저장 중
|
msg.saving_sources=소스 저장 중
|
||||||
msg.language_changed_title=언어 변경됨
|
msg.language_changed_title=언어 변경됨
|
||||||
msg.language_changed=다음에 응용 프로그램이 시작되면 새 언어가 표시됩니다.
|
msg.language_changed=다음에 응용 프로그램이 시작되면 새 언어가 표시됩니다.
|
||||||
|
#msg.warning_title=
|
||||||
|
#msg.common_mouse_shortcut=
|
||||||
|
#msg.duplicate_shortcut=
|
||||||
msg.project_error_title=오류
|
msg.project_error_title=오류
|
||||||
msg.project_error=프로젝트를 로드 할 수 없습니다.
|
msg.project_error=프로젝트를 로드 할 수 없습니다.
|
||||||
msg.cmd_select_class_error=클래스를 선택하지 못했습니다.\n%s\n클래스가 없습니다.
|
msg.cmd_select_class_error=클래스를 선택하지 못했습니다.\n%s\n클래스가 없습니다.
|
||||||
@ -290,6 +296,7 @@ popup.search_global="%s" 전역 검색
|
|||||||
|
|
||||||
#script.run=Run
|
#script.run=Run
|
||||||
#script.save=Save
|
#script.save=Save
|
||||||
|
#script.auto_complete=
|
||||||
#script.check=Check
|
#script.check=Check
|
||||||
#script.format=Reformat
|
#script.format=Reformat
|
||||||
#script.log=Show log
|
#script.log=Show log
|
||||||
@ -405,3 +412,8 @@ adb_dialog.no_devices=앱을 실행할 기기를 찾을 수 없습니다.
|
|||||||
adb_dialog.restart_while_debugging_title=디버깅중 다시 시작
|
adb_dialog.restart_while_debugging_title=디버깅중 다시 시작
|
||||||
adb_dialog.restart_while_debugging_msg=앱을 디버깅하고 있습니다. 세션을 다시 시작 하시겠습니까?
|
adb_dialog.restart_while_debugging_msg=앱을 디버깅하고 있습니다. 세션을 다시 시작 하시겠습니까?
|
||||||
adb_dialog.starting_debugger=디버거 시작 중 ...
|
adb_dialog.starting_debugger=디버거 시작 중 ...
|
||||||
|
|
||||||
|
#action.variant=
|
||||||
|
#action_category.menu_toolbar=
|
||||||
|
#action_category.code_area=
|
||||||
|
#action_category.plugin_script=
|
||||||
|
@ -23,6 +23,7 @@ menu.deobfuscation=Desofuscar
|
|||||||
menu.log=Visualizador de log
|
menu.log=Visualizador de log
|
||||||
menu.help=Ajuda
|
menu.help=Ajuda
|
||||||
menu.about=Sobre
|
menu.about=Sobre
|
||||||
|
#menu.quark=
|
||||||
menu.update_label=Nova versão %s disponível!
|
menu.update_label=Nova versão %s disponível!
|
||||||
|
|
||||||
file.open_action=Abrir arquivos...
|
file.open_action=Abrir arquivos...
|
||||||
@ -161,6 +162,8 @@ about_dialog.title=Sobre o JADX
|
|||||||
preferences.title=Preferências
|
preferences.title=Preferências
|
||||||
preferences.deobfuscation=Desofuscar
|
preferences.deobfuscation=Desofuscar
|
||||||
preferences.appearance=Aparência
|
preferences.appearance=Aparência
|
||||||
|
#preferences.shortcuts=
|
||||||
|
#preferences.select_shortcuts=
|
||||||
preferences.decompile=Descompilação
|
preferences.decompile=Descompilação
|
||||||
preferences.plugins=Plugins
|
preferences.plugins=Plugins
|
||||||
preferences.project=Projeto
|
preferences.project=Projeto
|
||||||
@ -257,6 +260,9 @@ msg.open_file=Abra um arquivo
|
|||||||
msg.saving_sources=Salvando recursos
|
msg.saving_sources=Salvando recursos
|
||||||
msg.language_changed_title=Idioma alterado
|
msg.language_changed_title=Idioma alterado
|
||||||
msg.language_changed=Novo idioma será mostrado na próxima inicialização.
|
msg.language_changed=Novo idioma será mostrado na próxima inicialização.
|
||||||
|
#msg.warning_title=
|
||||||
|
#msg.common_mouse_shortcut=
|
||||||
|
#msg.duplicate_shortcut=
|
||||||
msg.project_error_title=Erro
|
msg.project_error_title=Erro
|
||||||
msg.project_error=Projeto não pôde ser carregado
|
msg.project_error=Projeto não pôde ser carregado
|
||||||
msg.cmd_select_class_error=Falha ao selecionar classe\n%s\nA classe não existe.
|
msg.cmd_select_class_error=Falha ao selecionar classe\n%s\nA classe não existe.
|
||||||
@ -290,6 +296,7 @@ popup.search_global=Busca global "%s"
|
|||||||
|
|
||||||
#script.run=Run
|
#script.run=Run
|
||||||
#script.save=Save
|
#script.save=Save
|
||||||
|
#script.auto_complete=
|
||||||
#script.check=Check
|
#script.check=Check
|
||||||
#script.format=Reformat
|
#script.format=Reformat
|
||||||
#script.log=Show log
|
#script.log=Show log
|
||||||
@ -405,3 +412,8 @@ adb_dialog.no_devices=Não foi possível encontrar nenhum dispositivo para inici
|
|||||||
adb_dialog.restart_while_debugging_title=Reiniciar enquanto depura
|
adb_dialog.restart_while_debugging_title=Reiniciar enquanto depura
|
||||||
adb_dialog.restart_while_debugging_msg=Você está depurando um aplicativo, você tem certeza que deseja reiniciar a sessão?
|
adb_dialog.restart_while_debugging_msg=Você está depurando um aplicativo, você tem certeza que deseja reiniciar a sessão?
|
||||||
adb_dialog.starting_debugger=Iniciando depurador...
|
adb_dialog.starting_debugger=Iniciando depurador...
|
||||||
|
|
||||||
|
#action.variant=
|
||||||
|
#action_category.menu_toolbar=
|
||||||
|
#action_category.code_area=
|
||||||
|
#action_category.plugin_script=
|
||||||
|
@ -23,6 +23,7 @@ menu.deobfuscation=Деобфускация
|
|||||||
menu.log=Просмотр логов
|
menu.log=Просмотр логов
|
||||||
menu.help=Помощь
|
menu.help=Помощь
|
||||||
menu.about=О программе
|
menu.about=О программе
|
||||||
|
#menu.quark=
|
||||||
menu.update_label=Версия %s уже доступна!
|
menu.update_label=Версия %s уже доступна!
|
||||||
|
|
||||||
file.open_action=Открыть файлы...
|
file.open_action=Открыть файлы...
|
||||||
@ -161,6 +162,8 @@ about_dialog.title=О программе JADX
|
|||||||
preferences.title=Параметры
|
preferences.title=Параметры
|
||||||
preferences.deobfuscation=Деобфускация
|
preferences.deobfuscation=Деобфускация
|
||||||
preferences.appearance=Внешний вид
|
preferences.appearance=Внешний вид
|
||||||
|
#preferences.shortcuts=
|
||||||
|
#preferences.select_shortcuts=
|
||||||
preferences.decompile=Декомпиляция
|
preferences.decompile=Декомпиляция
|
||||||
preferences.plugins=Плагины
|
preferences.plugins=Плагины
|
||||||
preferences.project=Проект
|
preferences.project=Проект
|
||||||
@ -257,6 +260,9 @@ msg.open_file=Пожалуйста, откройте файл
|
|||||||
msg.saving_sources=Сохранение ресурсов
|
msg.saving_sources=Сохранение ресурсов
|
||||||
msg.language_changed_title=Язык изменен
|
msg.language_changed_title=Язык изменен
|
||||||
msg.language_changed=Новый язык применится при следующем запуске программы
|
msg.language_changed=Новый язык применится при следующем запуске программы
|
||||||
|
#msg.warning_title=
|
||||||
|
#msg.common_mouse_shortcut=
|
||||||
|
#msg.duplicate_shortcut=
|
||||||
msg.project_error_title=Ошибка
|
msg.project_error_title=Ошибка
|
||||||
msg.project_error=Проект не может быть загружен
|
msg.project_error=Проект не может быть загружен
|
||||||
msg.cmd_select_class_error=Ошибка выбора класса\n%s\nЭтот класс не существует.
|
msg.cmd_select_class_error=Ошибка выбора класса\n%s\nЭтот класс не существует.
|
||||||
@ -290,6 +296,7 @@ popup.search_global=Глобальный поиск "%s"
|
|||||||
|
|
||||||
#script.run=Run
|
#script.run=Run
|
||||||
#script.save=Save
|
#script.save=Save
|
||||||
|
#script.auto_complete=
|
||||||
#script.check=Check
|
#script.check=Check
|
||||||
#script.format=Reformat
|
#script.format=Reformat
|
||||||
#script.log=Show log
|
#script.log=Show log
|
||||||
@ -405,3 +412,8 @@ adb_dialog.no_devices=Нет устройств для запуска прило
|
|||||||
adb_dialog.restart_while_debugging_title=Перезапустить отладчик
|
adb_dialog.restart_while_debugging_title=Перезапустить отладчик
|
||||||
adb_dialog.restart_while_debugging_msg=Запущен процесс отдадки приложения. Вы действительно хотите перезапустить сессию?
|
adb_dialog.restart_while_debugging_msg=Запущен процесс отдадки приложения. Вы действительно хотите перезапустить сессию?
|
||||||
adb_dialog.starting_debugger=Запуск отладки...
|
adb_dialog.starting_debugger=Запуск отладки...
|
||||||
|
|
||||||
|
#action.variant=
|
||||||
|
#action_category.menu_toolbar=
|
||||||
|
#action_category.code_area=
|
||||||
|
#action_category.plugin_script=
|
||||||
|
@ -23,6 +23,7 @@ menu.deobfuscation=反混淆
|
|||||||
menu.log=日志查看器
|
menu.log=日志查看器
|
||||||
menu.help=帮助
|
menu.help=帮助
|
||||||
menu.about=关于
|
menu.about=关于
|
||||||
|
#menu.quark=
|
||||||
menu.update_label=发现新版本 %s!
|
menu.update_label=发现新版本 %s!
|
||||||
|
|
||||||
file.open_action=打开文件…
|
file.open_action=打开文件…
|
||||||
@ -161,6 +162,8 @@ about_dialog.title=关于 JADX
|
|||||||
preferences.title=首选项
|
preferences.title=首选项
|
||||||
preferences.deobfuscation=反混淆
|
preferences.deobfuscation=反混淆
|
||||||
preferences.appearance=界面
|
preferences.appearance=界面
|
||||||
|
#preferences.shortcuts=
|
||||||
|
#preferences.select_shortcuts=
|
||||||
preferences.decompile=反编译
|
preferences.decompile=反编译
|
||||||
preferences.plugins=插件
|
preferences.plugins=插件
|
||||||
preferences.project=项目
|
preferences.project=项目
|
||||||
@ -257,6 +260,9 @@ msg.open_file=请打开文件
|
|||||||
msg.saving_sources=正在导出源代码
|
msg.saving_sources=正在导出源代码
|
||||||
msg.language_changed_title=语言已更改
|
msg.language_changed_title=语言已更改
|
||||||
msg.language_changed=新的语言将在下次应用程序启动时显示。
|
msg.language_changed=新的语言将在下次应用程序启动时显示。
|
||||||
|
#msg.warning_title=
|
||||||
|
#msg.common_mouse_shortcut=
|
||||||
|
#msg.duplicate_shortcut=
|
||||||
msg.project_error_title=错误
|
msg.project_error_title=错误
|
||||||
msg.project_error=项目无法加载
|
msg.project_error=项目无法加载
|
||||||
msg.cmd_select_class_error=无法选择类\n%s\n该类不存在。
|
msg.cmd_select_class_error=无法选择类\n%s\n该类不存在。
|
||||||
@ -290,6 +296,7 @@ popup.json_prettify=JSON 格式化
|
|||||||
|
|
||||||
script.run=运行
|
script.run=运行
|
||||||
script.save=保存
|
script.save=保存
|
||||||
|
#script.auto_complete=
|
||||||
script.check=检查
|
script.check=检查
|
||||||
script.format=重新格式化
|
script.format=重新格式化
|
||||||
script.log=显示日志
|
script.log=显示日志
|
||||||
@ -405,3 +412,8 @@ adb_dialog.no_devices=找不到任何设备用来启动APP。
|
|||||||
adb_dialog.restart_while_debugging_title=调试时重新启动
|
adb_dialog.restart_while_debugging_title=调试时重新启动
|
||||||
adb_dialog.restart_while_debugging_msg=你正在调试一个APP,确定要重新启动一个会话吗?
|
adb_dialog.restart_while_debugging_msg=你正在调试一个APP,确定要重新启动一个会话吗?
|
||||||
adb_dialog.starting_debugger=正在启动调试器…
|
adb_dialog.starting_debugger=正在启动调试器…
|
||||||
|
|
||||||
|
#action.variant=
|
||||||
|
#action_category.menu_toolbar=
|
||||||
|
#action_category.code_area=
|
||||||
|
#action_category.plugin_script=
|
||||||
|
@ -23,6 +23,7 @@ menu.deobfuscation=去模糊化
|
|||||||
menu.log=記錄檔檢視器
|
menu.log=記錄檔檢視器
|
||||||
menu.help=幫助
|
menu.help=幫助
|
||||||
menu.about=關於
|
menu.about=關於
|
||||||
|
#menu.quark=
|
||||||
menu.update_label=新版本 %s 可供下載!
|
menu.update_label=新版本 %s 可供下載!
|
||||||
|
|
||||||
file.open_action=開啟檔案...
|
file.open_action=開啟檔案...
|
||||||
@ -161,6 +162,8 @@ about_dialog.title=關於 JADX
|
|||||||
preferences.title=選項
|
preferences.title=選項
|
||||||
preferences.deobfuscation=去模糊化
|
preferences.deobfuscation=去模糊化
|
||||||
preferences.appearance=外觀
|
preferences.appearance=外觀
|
||||||
|
#preferences.shortcuts=
|
||||||
|
#preferences.select_shortcuts=
|
||||||
preferences.decompile=反編譯
|
preferences.decompile=反編譯
|
||||||
preferences.plugins=外掛程式
|
preferences.plugins=外掛程式
|
||||||
preferences.project=專案
|
preferences.project=專案
|
||||||
@ -257,6 +260,9 @@ msg.open_file=請開啟檔案
|
|||||||
msg.saving_sources=正在儲存原始碼
|
msg.saving_sources=正在儲存原始碼
|
||||||
msg.language_changed_title=已更改語言
|
msg.language_changed_title=已更改語言
|
||||||
msg.language_changed=新語言將於下次應用程式啟動時套用。
|
msg.language_changed=新語言將於下次應用程式啟動時套用。
|
||||||
|
#msg.warning_title=
|
||||||
|
#msg.common_mouse_shortcut=
|
||||||
|
#msg.duplicate_shortcut=
|
||||||
msg.project_error_title=錯誤
|
msg.project_error_title=錯誤
|
||||||
msg.project_error=無法載入專案
|
msg.project_error=無法載入專案
|
||||||
msg.cmd_select_class_error=無法選擇類別\n%s\n類別不存在。
|
msg.cmd_select_class_error=無法選擇類別\n%s\n類別不存在。
|
||||||
@ -290,6 +296,7 @@ popup.json_prettify=JSON 格式化
|
|||||||
|
|
||||||
script.run=執行
|
script.run=執行
|
||||||
script.save=儲存
|
script.save=儲存
|
||||||
|
#script.auto_complete=
|
||||||
script.check=檢查
|
script.check=檢查
|
||||||
script.format=重新格式化
|
script.format=重新格式化
|
||||||
script.log=顯示記錄檔
|
script.log=顯示記錄檔
|
||||||
@ -405,3 +412,8 @@ adb_dialog.no_devices=無法找到任何可以啟動應用程式的裝置。
|
|||||||
adb_dialog.restart_while_debugging_title=偵錯時重新啟動
|
adb_dialog.restart_while_debugging_title=偵錯時重新啟動
|
||||||
adb_dialog.restart_while_debugging_msg=您正在為應用程式偵錯,您確定要重新啟動工作階段嗎?
|
adb_dialog.restart_while_debugging_msg=您正在為應用程式偵錯,您確定要重新啟動工作階段嗎?
|
||||||
adb_dialog.starting_debugger=正在啟動偵錯工具...
|
adb_dialog.starting_debugger=正在啟動偵錯工具...
|
||||||
|
|
||||||
|
#action.variant=
|
||||||
|
#action_category.menu_toolbar=
|
||||||
|
#action_category.code_area=
|
||||||
|
#action_category.plugin_script=
|
||||||
|
Loading…
Reference in New Issue
Block a user