diff --git a/jadx-gui/src/main/java/jadx/gui/settings/ui/JPreferredFontChooser.java b/jadx-gui/src/main/java/jadx/gui/settings/ui/JPreferredFontChooser.java new file mode 100644 index 000000000..fcaad01e8 --- /dev/null +++ b/jadx-gui/src/main/java/jadx/gui/settings/ui/JPreferredFontChooser.java @@ -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 fontSet = new HashSet<>(); + Collections.addAll(fontSet, env.getAvailableFontFamilyNames()); + ArrayList 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; + } +} diff --git a/jadx-gui/src/main/java/jadx/gui/settings/JadxSettingsWindow.java b/jadx-gui/src/main/java/jadx/gui/settings/ui/JadxSettingsWindow.java similarity index 78% rename from jadx-gui/src/main/java/jadx/gui/settings/JadxSettingsWindow.java rename to jadx-gui/src/main/java/jadx/gui/settings/ui/JadxSettingsWindow.java index c40c1cd0c..b1d63e9f9 100644 --- a/jadx-gui/src/main/java/jadx/gui/settings/JadxSettingsWindow.java +++ b/jadx-gui/src/main/java/jadx/gui/settings/ui/JadxSettingsWindow.java @@ -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 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 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 = 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 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 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 = new JComboBox<>(LineNumbersMode.values()); lineNumbersMode.setSelectedItem(settings.getLineNumbersMode()); lineNumbersMode.addActionListener(e -> { @@ -759,26 +716,17 @@ public class JadxSettingsWindow extends JDialog { needReload(); }); - JComboBox 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 fontSet = new HashSet<>(); - Collections.addAll(fontSet, env.getAvailableFontFamilyNames()); - ArrayList 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(); } } diff --git a/jadx-gui/src/main/java/jadx/gui/settings/ui/SettingsGroupPanel.java b/jadx-gui/src/main/java/jadx/gui/settings/ui/SettingsGroupPanel.java new file mode 100644 index 000000000..12b1e660f --- /dev/null +++ b/jadx-gui/src/main/java/jadx/gui/settings/ui/SettingsGroupPanel.java @@ -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; + } +} diff --git a/jadx-gui/src/main/java/jadx/gui/settings/ui/SettingsTree.java b/jadx-gui/src/main/java/jadx/gui/settings/ui/SettingsTree.java new file mode 100644 index 000000000..238561daf --- /dev/null +++ b/jadx-gui/src/main/java/jadx/gui/settings/ui/SettingsTree.java @@ -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 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 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); + } + } +} diff --git a/jadx-gui/src/main/java/jadx/gui/ui/MainWindow.java b/jadx-gui/src/main/java/jadx/gui/ui/MainWindow.java index 5ff6ff270..31e261e0c 100644 --- a/jadx-gui/src/main/java/jadx/gui/ui/MainWindow.java +++ b/jadx-gui/src/main/java/jadx/gui/ui/MainWindow.java @@ -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;