feat(gui): split settings into pages

This commit is contained in:
Skylot
2023-05-27 15:40:38 +01:00
parent c28320e1b2
commit 3bf101d7ac
5 changed files with 349 additions and 228 deletions
@@ -0,0 +1,51 @@
package jadx.gui.settings.ui;
import java.awt.GraphicsEnvironment;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import say.swing.JFontChooser;
public class JPreferredFontChooser extends JFontChooser {
private static final Logger LOG = LoggerFactory.getLogger(JPreferredFontChooser.class);
private static final String[] PREFERRED_FONTS = new String[] {
"Monospaced", "Consolas", "Courier", "Courier New",
"Lucida Sans Typewriter", "Lucida Console",
"SimSun", "SimHei",
};
private String[] filteredFonts;
@Override
protected String[] getFontFamilies() {
if (filteredFonts == null) {
GraphicsEnvironment env = GraphicsEnvironment.getLocalGraphicsEnvironment();
Set<String> fontSet = new HashSet<>();
Collections.addAll(fontSet, env.getAvailableFontFamilyNames());
ArrayList<String> found = new ArrayList<>(PREFERRED_FONTS.length);
for (String font : PREFERRED_FONTS) {
if (fontSet.contains(font)) {
found.add(font);
}
}
if (found.size() == PREFERRED_FONTS.length) {
filteredFonts = PREFERRED_FONTS;
} else if (found.size() > 0) {
filteredFonts = new String[found.size()];
for (int i = 0; i < found.size(); i++) {
filteredFonts[i] = found.get(i);
}
} else {
LOG.warn("Can't found any preferred fonts for smali, use all available.");
filteredFonts = env.getAvailableFontFamilyNames();
}
}
return filteredFonts;
}
}
@@ -1,33 +1,24 @@
package jadx.gui.settings;
package jadx.gui.settings.ui;
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.GraphicsEnvironment;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.Toolkit;
import java.awt.datatransfer.Clipboard;
import java.awt.datatransfer.StringSelection;
import java.awt.event.ActionEvent;
import java.awt.event.ItemEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.function.Consumer;
import javax.swing.AbstractAction;
import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.BoxLayout;
@@ -42,11 +33,11 @@ import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSpinner;
import javax.swing.JSplitPane;
import javax.swing.JTextField;
import javax.swing.KeyStroke;
import javax.swing.ScrollPaneConstants;
import javax.swing.SpinnerNumberModel;
import javax.swing.SwingConstants;
import javax.swing.SwingUtilities;
import javax.swing.WindowConstants;
@@ -71,6 +62,10 @@ import jadx.api.plugins.options.OptionDescription.OptionFlag;
import jadx.core.plugins.PluginContext;
import jadx.gui.cache.code.CodeCacheMode;
import jadx.gui.cache.usage.UsageCacheMode;
import jadx.gui.settings.JadxProject;
import jadx.gui.settings.JadxSettings;
import jadx.gui.settings.JadxSettingsAdapter;
import jadx.gui.settings.LineNumbersMode;
import jadx.gui.ui.MainWindow;
import jadx.gui.ui.codearea.EditorTheme;
import jadx.gui.utils.FontUtils;
@@ -79,6 +74,7 @@ import jadx.gui.utils.LangLocale;
import jadx.gui.utils.NLS;
import jadx.gui.utils.UiUtils;
import jadx.gui.utils.plugins.CollectPluginOptions;
import jadx.gui.utils.ui.ActionHandler;
import jadx.gui.utils.ui.DocumentUpdateListener;
public class JadxSettingsWindow extends JDialog {
@@ -102,98 +98,71 @@ public class JadxSettingsWindow extends JDialog {
initUI();
setTitle(NLS.str("preferences.title"));
setSize(400, 550);
setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
setModalityType(ModalityType.APPLICATION_MODAL);
pack();
UiUtils.setWindowIcons(this);
setLocationRelativeTo(null);
if (!mainWindow.getSettings().loadWindowPos(this)) {
setSize(700, 800);
}
}
private void initUI() {
JPanel panel = new JPanel();
panel.setLayout(new BoxLayout(panel, BoxLayout.LINE_AXIS));
panel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
JPanel groupPanel = new JPanel();
groupPanel.setLayout(new BoxLayout(groupPanel, BoxLayout.LINE_AXIS));
groupPanel.setBorder(BorderFactory.createEmptyBorder(10, 3, 3, 10));
JPanel leftPanel = new JPanel();
JPanel rightPanel = new JPanel();
leftPanel.setLayout(new BoxLayout(leftPanel, BoxLayout.PAGE_AXIS));
rightPanel.setLayout(new BoxLayout(rightPanel, BoxLayout.PAGE_AXIS));
panel.add(leftPanel);
panel.add(rightPanel);
List<SettingsGroupPanel> groups = new ArrayList<>();
groups.add(makeDecompilationGroup());
groups.add(makeDeobfuscationGroup());
groups.add(makeRenameGroup());
groups.add(makeAppearanceGroup());
groups.add(makeSearchResGroup());
groups.add(makeProjectGroup());
groups.add(makePluginOptionsGroup());
groups.add(makeOtherGroup());
leftPanel.add(makeDeobfuscationGroup());
leftPanel.add(makeRenameGroup());
leftPanel.add(makeProjectGroup());
leftPanel.add(makeAppearanceGroup());
leftPanel.add(makeOtherGroup());
leftPanel.add(makeSearchResGroup());
leftPanel.add(Box.createVerticalGlue());
SettingsTree tree = new SettingsTree();
tree.init(groupPanel, groups);
JScrollPane leftPane = new JScrollPane(tree);
leftPane.setBorder(BorderFactory.createEmptyBorder(10, 10, 3, 3));
rightPanel.add(makeDecompilationGroup());
rightPanel.add(makePluginOptionsGroup());
rightPanel.add(Box.createVerticalGlue());
JPanel wrapGroupPanel = new JPanel(new BorderLayout());
wrapGroupPanel.add(groupPanel, BorderLayout.PAGE_START);
JScrollPane rightPane = new JScrollPane(wrapGroupPanel);
rightPane.getVerticalScrollBar().setUnitIncrement(16);
rightPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED);
rightPane.setBorder(BorderFactory.createEmptyBorder());
JSplitPane splitPane = new JSplitPane();
splitPane.setResizeWeight(0.2);
splitPane.setLeftComponent(leftPane);
splitPane.setRightComponent(rightPane);
Container contentPane = getContentPane();
contentPane.add(splitPane, BorderLayout.CENTER);
contentPane.add(buildButtonsPane(), BorderLayout.PAGE_END);
KeyStroke strokeEsc = KeyStroke.getKeyStroke("ESCAPE");
InputMap inputMap = getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
inputMap.put(strokeEsc, "ESCAPE");
getRootPane().getActionMap().put("ESCAPE", new ActionHandler(this::cancel));
}
private JPanel buildButtonsPane() {
JButton saveBtn = new JButton(NLS.str("preferences.save"));
saveBtn.addActionListener(event -> {
settings.sync();
enableComponents(this, false);
saveBtn.addActionListener(event -> save());
SwingUtilities.invokeLater(() -> {
if (needReload) {
mainWindow.reopen();
}
if (!settings.getLangLocale().equals(prevLang)) {
JOptionPane.showMessageDialog(
this,
NLS.str("msg.language_changed", settings.getLangLocale()),
NLS.str("msg.language_changed_title", settings.getLangLocale()),
JOptionPane.INFORMATION_MESSAGE);
}
dispose();
});
});
JButton cancelButton = new JButton(NLS.str("preferences.cancel"));
cancelButton.addActionListener(event -> cancel());
JButton resetBtn = new JButton(NLS.str("preferences.reset"));
resetBtn.addActionListener(event -> {
int res = JOptionPane.showConfirmDialog(
JadxSettingsWindow.this,
NLS.str("preferences.reset_message"),
NLS.str("preferences.reset_title"),
JOptionPane.YES_NO_OPTION);
if (res == JOptionPane.YES_OPTION) {
String defaults = JadxSettingsAdapter.makeString(JadxSettings.makeDefault());
JadxSettingsAdapter.fill(settings, defaults);
mainWindow.loadSettings();
needReload();
getContentPane().removeAll();
initUI();
pack();
repaint();
}
});
resetBtn.addActionListener(event -> reset());
JButton copyBtn = new JButton(NLS.str("preferences.copy"));
copyBtn.addActionListener(event -> {
JsonObject settingsJson = JadxSettingsAdapter.makeJsonObject(this.settings);
// remove irrelevant preferences
settingsJson.remove("windowPos");
settingsJson.remove("mainWindowExtendedState");
settingsJson.remove("lastSaveProjectPath");
settingsJson.remove("lastOpenFilePath");
settingsJson.remove("lastSaveFilePath");
settingsJson.remove("recentProjects");
String settingsText = new GsonBuilder().setPrettyPrinting().create().toJson(settingsJson);
Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
StringSelection selection = new StringSelection(settingsText);
clipboard.setContents(selection, selection);
JOptionPane.showMessageDialog(
JadxSettingsWindow.this,
NLS.str("preferences.copy_message"));
});
copyBtn.addActionListener(event -> copySettings());
JPanel buttonPane = new JPanel();
buttonPane.setLayout(new BoxLayout(buttonPane, BoxLayout.LINE_AXIS));
@@ -205,29 +174,8 @@ public class JadxSettingsWindow extends JDialog {
buttonPane.add(Box.createRigidArea(new Dimension(10, 0)));
buttonPane.add(cancelButton);
Container contentPane = getContentPane();
JScrollPane scrollPane = new JScrollPane(panel);
scrollPane.getVerticalScrollBar().setUnitIncrement(16);
scrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED);
contentPane.add(scrollPane, BorderLayout.CENTER);
contentPane.add(buttonPane, BorderLayout.PAGE_END);
getRootPane().setDefaultButton(saveBtn);
KeyStroke strokeEsc = KeyStroke.getKeyStroke("ESCAPE");
InputMap inputMap = getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
inputMap.put(strokeEsc, "ESCAPE");
getRootPane().getActionMap().put("ESCAPE", new AbstractAction() {
@Override
public void actionPerformed(ActionEvent e) {
cancel();
}
});
}
private void cancel() {
JadxSettingsAdapter.fill(settings, startSettings);
mainWindow.loadSettings();
dispose();
return buttonPane;
}
private static void enableComponents(Container container, boolean enable) {
@@ -239,7 +187,7 @@ public class JadxSettingsWindow extends JDialog {
}
}
private SettingsGroup makeDeobfuscationGroup() {
private SettingsGroupPanel makeDeobfuscationGroup() {
JCheckBox deobfOn = new JCheckBox();
deobfOn.setSelected(settings.isDeobfuscationOn());
deobfOn.addItemListener(e -> {
@@ -280,7 +228,7 @@ public class JadxSettingsWindow extends JDialog {
}
});
SettingsGroup deobfGroup = new SettingsGroup(NLS.str("preferences.deobfuscation"));
SettingsGroupPanel deobfGroup = new SettingsGroupPanel(NLS.str("preferences.deobfuscation"));
deobfGroup.addRow(NLS.str("preferences.deobfuscation_on"), deobfOn);
deobfGroup.addRow(NLS.str("preferences.deobfuscation_min_len"), minLenSpinner);
deobfGroup.addRow(NLS.str("preferences.deobfuscation_max_len"), maxLenSpinner);
@@ -294,7 +242,7 @@ public class JadxSettingsWindow extends JDialog {
return deobfGroup;
}
private SettingsGroup makeRenameGroup() {
private SettingsGroupPanel makeRenameGroup() {
JCheckBox renameCaseSensitive = new JCheckBox();
renameCaseSensitive.setSelected(settings.isRenameCaseSensitive());
renameCaseSensitive.addItemListener(e -> {
@@ -323,7 +271,7 @@ public class JadxSettingsWindow extends JDialog {
needReload();
});
SettingsGroup group = new SettingsGroup(NLS.str("preferences.rename"));
SettingsGroupPanel group = new SettingsGroupPanel(NLS.str("preferences.rename"));
group.addRow(NLS.str("preferences.rename_case"), renameCaseSensitive);
group.addRow(NLS.str("preferences.rename_valid"), renameValid);
group.addRow(NLS.str("preferences.rename_printable"), renamePrintable);
@@ -335,18 +283,27 @@ public class JadxSettingsWindow extends JDialog {
connectedComponents.forEach(comp -> comp.setEnabled(enabled));
}
private SettingsGroup makeProjectGroup() {
private SettingsGroupPanel makeProjectGroup() {
JCheckBox autoSave = new JCheckBox();
autoSave.setSelected(settings.isAutoSaveProject());
autoSave.addItemListener(e -> settings.setAutoSaveProject(e.getStateChange() == ItemEvent.SELECTED));
SettingsGroup group = new SettingsGroup(NLS.str("preferences.project"));
SettingsGroupPanel group = new SettingsGroupPanel(NLS.str("preferences.project"));
group.addRow(NLS.str("preferences.autoSave"), autoSave);
return group;
}
private SettingsGroup makeAppearanceGroup() {
private SettingsGroupPanel makeAppearanceGroup() {
JComboBox<LangLocale> languageCbx = new JComboBox<>(NLS.getLangLocales());
for (LangLocale locale : NLS.getLangLocales()) {
if (locale.equals(settings.getLangLocale())) {
languageCbx.setSelectedItem(locale);
break;
}
}
languageCbx.addActionListener(e -> settings.setLangLocale((LangLocale) languageCbx.getSelectedItem()));
JButton fontBtn = new JButton(NLS.str("preferences.select_font"));
JButton smaliFontBtn = new JButton(NLS.str("preferences.select_smali_font"));
@@ -372,7 +329,8 @@ public class JadxSettingsWindow extends JDialog {
mainWindow.loadSettings();
});
SettingsGroup group = new SettingsGroup(NLS.str("preferences.appearance"));
SettingsGroupPanel group = new SettingsGroupPanel(NLS.str("preferences.appearance"));
group.addRow(NLS.str("preferences.language"), languageCbx);
group.addRow(NLS.str("preferences.laf_theme"), lafCbx);
group.addRow(NLS.str("preferences.theme"), themesCbx);
JLabel fontLabel = group.addRow(getFontLabelStr(), fontBtn);
@@ -424,7 +382,7 @@ public class JadxSettingsWindow extends JDialog {
return NLS.str("preferences.smali_font") + ": " + font.getFontName() + ' ' + fontStyleName + ' ' + font.getSize();
}
private SettingsGroup makeDecompilationGroup() {
private SettingsGroupPanel makeDecompilationGroup() {
JCheckBox useDx = new JCheckBox();
useDx.setSelected(settings.isUseDx());
useDx.addItemListener(e -> {
@@ -587,7 +545,14 @@ public class JadxSettingsWindow extends JDialog {
needReload();
});
SettingsGroup other = new SettingsGroup(NLS.str("preferences.decompile"));
JComboBox<IntegerFormat> integerFormat = new JComboBox<>(IntegerFormat.values());
integerFormat.setSelectedItem(settings.getIntegerFormat());
integerFormat.addActionListener(e -> {
settings.setIntegerFormat((IntegerFormat) integerFormat.getSelectedItem());
needReload();
});
SettingsGroupPanel other = new SettingsGroupPanel(NLS.str("preferences.decompile"));
other.addRow(NLS.str("preferences.threads"), threadsCount);
other.addRow(NLS.str("preferences.excludedPackages"),
NLS.str("preferences.excludedPackages.tooltip"), editExcludedPackages);
@@ -611,11 +576,12 @@ public class JadxSettingsWindow extends JDialog {
other.addRow(NLS.str("preferences.skipResourcesDecode"), resourceDecode);
other.addRow(NLS.str("preferences.useKotlinMethodsForVarNames"), kotlinRenameVars);
other.addRow(NLS.str("preferences.commentsLevel"), commentsLevel);
other.addRow(NLS.str("preferences.integerFormat"), integerFormat);
return other;
}
private SettingsGroup makePluginOptionsGroup() {
SettingsGroup pluginsGroup = new SettingsGroup(NLS.str("preferences.plugins"));
private SettingsGroupPanel makePluginOptionsGroup() {
SettingsGroupPanel pluginsGroup = new SettingsGroupPanel(NLS.str("preferences.plugins"));
List<PluginContext> list = new CollectPluginOptions(mainWindow.getWrapper()).build();
for (PluginContext context : list) {
addPluginOptions(pluginsGroup, context);
@@ -623,7 +589,7 @@ public class JadxSettingsWindow extends JDialog {
return pluginsGroup;
}
private void addPluginOptions(SettingsGroup pluginsGroup, PluginContext context) {
private void addPluginOptions(SettingsGroupPanel pluginsGroup, PluginContext context) {
JadxPluginOptions options = context.getOptions();
if (options == null) {
return;
@@ -716,16 +682,7 @@ public class JadxSettingsWindow extends JDialog {
}
}
private SettingsGroup makeOtherGroup() {
JComboBox<LangLocale> languageCbx = new JComboBox<>(NLS.getLangLocales());
for (LangLocale locale : NLS.getLangLocales()) {
if (locale.equals(settings.getLangLocale())) {
languageCbx.setSelectedItem(locale);
break;
}
}
languageCbx.addActionListener(e -> settings.setLangLocale((LangLocale) languageCbx.getSelectedItem()));
private SettingsGroupPanel makeOtherGroup() {
JComboBox<LineNumbersMode> lineNumbersMode = new JComboBox<>(LineNumbersMode.values());
lineNumbersMode.setSelectedItem(settings.getLineNumbersMode());
lineNumbersMode.addActionListener(e -> {
@@ -759,26 +716,17 @@ public class JadxSettingsWindow extends JDialog {
needReload();
});
JComboBox<IntegerFormat> integerFormat = new JComboBox<>(IntegerFormat.values());
integerFormat.setSelectedItem(settings.getIntegerFormat());
integerFormat.addActionListener(e -> {
settings.setIntegerFormat((IntegerFormat) integerFormat.getSelectedItem());
needReload();
});
SettingsGroup group = new SettingsGroup(NLS.str("preferences.other"));
group.addRow(NLS.str("preferences.language"), languageCbx);
SettingsGroupPanel group = new SettingsGroupPanel(NLS.str("preferences.other"));
group.addRow(NLS.str("preferences.lineNumbersMode"), lineNumbersMode);
group.addRow(NLS.str("preferences.jumpOnDoubleClick"), jumpOnDoubleClick);
group.addRow(NLS.str("preferences.useAlternativeFileDialog"), useAltFileDialog);
group.addRow(NLS.str("preferences.check_for_updates"), update);
group.addRow(NLS.str("preferences.cfg"), cfg);
group.addRow(NLS.str("preferences.raw_cfg"), rawCfg);
group.addRow(NLS.str("preferences.integerFormat"), integerFormat);
return group;
}
private SettingsGroup makeSearchResGroup() {
private SettingsGroupPanel makeSearchResGroup() {
JSpinner resultsPerPage = new JSpinner(
new SpinnerNumberModel(settings.getSearchResultsPerPage(), 0, Integer.MAX_VALUE, 1));
resultsPerPage.addChangeListener(ev -> settings.setSearchResultsPerPage((Integer) resultsPerPage.getValue()));
@@ -794,103 +742,80 @@ public class JadxSettingsWindow extends JDialog {
}));
fileExtField.setText(settings.getSrhResourceFileExt());
SettingsGroup searchGroup = new SettingsGroup(NLS.str("preferences.search_group_title"));
SettingsGroupPanel searchGroup = new SettingsGroupPanel(NLS.str("preferences.search_group_title"));
searchGroup.addRow(NLS.str("preferences.search_results_per_page"), resultsPerPage);
searchGroup.addRow(NLS.str("preferences.res_skip_file"), sizeLimit);
searchGroup.addRow(NLS.str("preferences.res_file_ext"), fileExtField);
return searchGroup;
}
private void save() {
settings.sync();
enableComponents(this, false);
SwingUtilities.invokeLater(() -> {
if (needReload) {
mainWindow.reopen();
}
if (!settings.getLangLocale().equals(prevLang)) {
JOptionPane.showMessageDialog(
this,
NLS.str("msg.language_changed", settings.getLangLocale()),
NLS.str("msg.language_changed_title", settings.getLangLocale()),
JOptionPane.INFORMATION_MESSAGE);
}
dispose();
});
}
private void cancel() {
JadxSettingsAdapter.fill(settings, startSettings);
mainWindow.loadSettings();
dispose();
}
private void reset() {
int res = JOptionPane.showConfirmDialog(
JadxSettingsWindow.this,
NLS.str("preferences.reset_message"),
NLS.str("preferences.reset_title"),
JOptionPane.YES_NO_OPTION);
if (res == JOptionPane.YES_OPTION) {
String defaults = JadxSettingsAdapter.makeString(JadxSettings.makeDefault());
JadxSettingsAdapter.fill(settings, defaults);
mainWindow.loadSettings();
needReload();
getContentPane().removeAll();
initUI();
pack();
repaint();
}
}
private void copySettings() {
JsonObject settingsJson = JadxSettingsAdapter.makeJsonObject(this.settings);
// remove irrelevant preferences
settingsJson.remove("windowPos");
settingsJson.remove("mainWindowExtendedState");
settingsJson.remove("lastSaveProjectPath");
settingsJson.remove("lastOpenFilePath");
settingsJson.remove("lastSaveFilePath");
settingsJson.remove("recentProjects");
String settingsText = new GsonBuilder().setPrettyPrinting().create().toJson(settingsJson);
Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
StringSelection selection = new StringSelection(settingsText);
clipboard.setContents(selection, selection);
JOptionPane.showMessageDialog(
JadxSettingsWindow.this,
NLS.str("preferences.copy_message"));
}
private void needReload() {
needReload = true;
}
private static class SettingsGroup extends JPanel {
private static final long serialVersionUID = -6487309975896192544L;
private final GridBagConstraints c;
private int row;
public SettingsGroup(String title) {
setBorder(BorderFactory.createTitledBorder(title));
setLayout(new GridBagLayout());
c = new GridBagConstraints();
c.insets = new Insets(5, 5, 5, 5);
c.weighty = 1.0;
}
public JLabel addRow(String label, JComponent comp) {
return addRow(label, null, comp);
}
public JLabel addRow(String label, String tooltip, JComponent comp) {
c.gridy = row++;
JLabel jLabel = new JLabel(label);
jLabel.setLabelFor(comp);
jLabel.setHorizontalAlignment(SwingConstants.LEFT);
c.gridx = 0;
c.gridwidth = 1;
c.anchor = GridBagConstraints.LINE_START;
c.weightx = 0.8;
c.fill = GridBagConstraints.NONE;
add(jLabel, c);
c.gridx = 1;
c.gridwidth = GridBagConstraints.REMAINDER;
c.anchor = GridBagConstraints.CENTER;
c.weightx = 0.2;
c.fill = GridBagConstraints.HORIZONTAL;
if (tooltip != null) {
jLabel.setToolTipText(tooltip);
comp.setToolTipText(tooltip);
}
add(comp, c);
comp.addPropertyChangeListener("enabled", evt -> jLabel.setEnabled((boolean) evt.getNewValue()));
return jLabel;
}
public void end() {
add(Box.createVerticalGlue());
}
}
private static class JPreferredFontChooser extends JFontChooser {
private static final String[] PREFERRED_FONTS = new String[] {
"Monospaced", "Consolas", "Courier", "Courier New",
"Lucida Sans Typewriter", "Lucida Console",
"SimSun", "SimHei",
};
private String[] filteredFonts;
@Override
protected String[] getFontFamilies() {
if (filteredFonts == null) {
GraphicsEnvironment env = GraphicsEnvironment.getLocalGraphicsEnvironment();
Set<String> fontSet = new HashSet<>();
Collections.addAll(fontSet, env.getAvailableFontFamilyNames());
ArrayList<String> found = new ArrayList<>(PREFERRED_FONTS.length);
for (String font : PREFERRED_FONTS) {
if (fontSet.contains(font)) {
found.add(font);
}
}
if (found.size() == PREFERRED_FONTS.length) {
filteredFonts = PREFERRED_FONTS;
} else if (found.size() > 0) {
filteredFonts = new String[found.size()];
for (int i = 0; i < found.size(); i++) {
filteredFonts[i] = found.get(i);
}
} else {
// this machine is crazy.
LOG.warn("Can't found any preferred fonts for smali, use all available.");
filteredFonts = env.getAvailableFontFamilyNames();
}
}
return filteredFonts;
}
@Override
public void dispose() {
settings.saveWindowPos(this);
super.dispose();
}
}
@@ -0,0 +1,74 @@
package jadx.gui.settings.ui;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingConstants;
public class SettingsGroupPanel extends JPanel {
private static final long serialVersionUID = -6487309975896192544L;
private final String title;
private final GridBagConstraints c;
private int row;
public SettingsGroupPanel(String title) {
this.title = title;
setBorder(BorderFactory.createTitledBorder(title));
setLayout(new GridBagLayout());
c = new GridBagConstraints();
c.insets = new Insets(5, 5, 5, 5);
c.weighty = 1.0;
}
public JLabel addRow(String label, JComponent comp) {
return addRow(label, null, comp);
}
public JLabel addRow(String label, String tooltip, JComponent comp) {
c.gridy = row++;
JLabel jLabel = new JLabel(label);
jLabel.setLabelFor(comp);
jLabel.setHorizontalAlignment(SwingConstants.LEFT);
c.gridx = 0;
c.gridwidth = 1;
c.anchor = GridBagConstraints.LINE_START;
c.weightx = 0.8;
c.fill = GridBagConstraints.NONE;
add(jLabel, c);
c.gridx = 1;
c.gridwidth = GridBagConstraints.REMAINDER;
c.anchor = GridBagConstraints.CENTER;
c.weightx = 0.2;
c.fill = GridBagConstraints.HORIZONTAL;
if (tooltip != null) {
jLabel.setToolTipText(tooltip);
comp.setToolTipText(tooltip);
}
add(comp, c);
comp.addPropertyChangeListener("enabled", evt -> jLabel.setEnabled((boolean) evt.getNewValue()));
return jLabel;
}
public void end() {
add(Box.createVerticalGlue());
}
public String getTitle() {
return title;
}
@Override
public String toString() {
return title;
}
}
@@ -0,0 +1,71 @@
package jadx.gui.settings.ui;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import javax.swing.JPanel;
import javax.swing.JTree;
import javax.swing.event.TreeExpansionEvent;
import javax.swing.event.TreeWillExpandListener;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.ExpandVetoException;
import javax.swing.tree.TreeNode;
import javax.swing.tree.TreePath;
import javax.swing.tree.TreeSelectionModel;
import jadx.gui.utils.NLS;
public class SettingsTree extends JTree {
public void init(JPanel groupPanel, List<SettingsGroupPanel> groups) {
DefaultMutableTreeNode treeRoot = new DefaultMutableTreeNode(NLS.str("preferences.title"));
for (SettingsGroupPanel group : groups) {
treeRoot.add(new DefaultMutableTreeNode(group));
}
setModel(new DefaultTreeModel(treeRoot));
getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);
setFocusable(false);
addTreeSelectionListener(e -> {
DefaultMutableTreeNode node = (DefaultMutableTreeNode) getLastSelectedPathComponent();
Object obj = node.getUserObject();
groupPanel.removeAll();
if (obj instanceof SettingsGroupPanel) {
SettingsGroupPanel panel = (SettingsGroupPanel) obj;
groupPanel.add(panel);
}
groupPanel.updateUI();
});
// expand all nodes and disallow collapsing
setNodeExpandedState(this, treeRoot, true);
addTreeWillExpandListener(new TreeWillExpandListener() {
@Override
public void treeWillExpand(TreeExpansionEvent event) {
}
@Override
public void treeWillCollapse(TreeExpansionEvent event) throws ExpandVetoException {
throw new ExpandVetoException(event, "Collapsing tree not allowed");
}
});
addSelectionRow(1);
}
private static void setNodeExpandedState(JTree tree, TreeNode node, boolean expanded) {
ArrayList<? extends TreeNode> list = Collections.list(node.children());
for (TreeNode treeNode : list) {
setNodeExpandedState(tree, treeNode, expanded);
}
DefaultMutableTreeNode mutableTreeNode = (DefaultMutableTreeNode) node;
if (!expanded && mutableTreeNode.isRoot()) {
return;
}
TreePath path = new TreePath(mutableTreeNode.getPath());
if (expanded) {
tree.expandPath(path);
} else {
tree.collapsePath(path);
}
}
}
@@ -100,7 +100,7 @@ import jadx.gui.plugins.mappings.RenameMappingsGui;
import jadx.gui.plugins.quark.QuarkDialog;
import jadx.gui.settings.JadxProject;
import jadx.gui.settings.JadxSettings;
import jadx.gui.settings.JadxSettingsWindow;
import jadx.gui.settings.ui.JadxSettingsWindow;
import jadx.gui.treemodel.ApkSignature;
import jadx.gui.treemodel.JClass;
import jadx.gui.treemodel.JLoadableNode;