From 1ec1ece9efbcc668b9c79de58d79b3176da1b7ca Mon Sep 17 00:00:00 2001 From: YanisBft Date: Tue, 15 Jun 2021 23:30:03 +0200 Subject: Structure panel options (#400) * Structure panel options * changes * always show inner classes in the tree * workaround for toString() and similar * show constructor methods depending on the class obfuscation * use ListCellRenderer instead of toString * list cell renderer--- .../src/main/java/cuchaz/enigma/gui/Gui.java | 2 +- .../main/java/cuchaz/enigma/gui/GuiController.java | 4 +- .../cuchaz/enigma/gui/panels/StructurePanel.java | 63 +++++++++++++++++----- .../renderer/StructureOptionListCellRenderer.java | 21 ++++++++ .../cuchaz/enigma/analysis/StructureTreeNode.java | 54 +++++++++++++------ .../enigma/analysis/StructureTreeOptions.java | 59 ++++++++++++++++++++ enigma/src/main/resources/lang/en_us.json | 14 ++++- enigma/src/main/resources/lang/fr_fr.json | 14 ++++- enigma/src/main/resources/lang/ja_jp.json | 1 - 9 files changed, 197 insertions(+), 35 deletions(-) create mode 100644 enigma-swing/src/main/java/cuchaz/enigma/gui/renderer/StructureOptionListCellRenderer.java create mode 100644 enigma/src/main/java/cuchaz/enigma/analysis/StructureTreeOptions.java diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/Gui.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/Gui.java index 8d4fb5be..60c535f5 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/Gui.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/Gui.java @@ -622,7 +622,7 @@ public class Gui implements LanguageChangeListener { this.structurePanel.getSortingPanel().setVisible(true); // get the class structure - StructureTreeNode node = this.controller.getClassStructure(classEntry, this.structurePanel.shouldHideDeobfuscated()); + StructureTreeNode node = this.controller.getClassStructure(classEntry, this.structurePanel.getOptions()); // show the tree at the root TreePath path = getPathToRoot(node); diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/GuiController.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/GuiController.java index aeee242f..e6f7b832 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/GuiController.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/GuiController.java @@ -396,9 +396,9 @@ public class GuiController implements ClientPacketHandler { chp.invalidateMapped(); } - public StructureTreeNode getClassStructure(ClassEntry entry, boolean hideDeobfuscated) { + public StructureTreeNode getClassStructure(ClassEntry entry, StructureTreeOptions options) { StructureTreeNode rootNode = new StructureTreeNode(this.project, entry, entry); - rootNode.load(this.project, hideDeobfuscated); + rootNode.load(this.project, options); return rootNode; } diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/panels/StructurePanel.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/panels/StructurePanel.java index d6f7e5a7..1bff9a98 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/panels/StructurePanel.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/panels/StructurePanel.java @@ -1,7 +1,10 @@ package cuchaz.enigma.gui.panels; +import cuchaz.enigma.analysis.StructureTreeOptions; import cuchaz.enigma.analysis.StructureTreeNode; import cuchaz.enigma.gui.Gui; +import cuchaz.enigma.gui.renderer.StructureOptionListCellRenderer; +import cuchaz.enigma.gui.util.GridBagConstraintsBuilder; import cuchaz.enigma.gui.util.GuiUtil; import cuchaz.enigma.gui.util.SingleTreeSelectionModel; import cuchaz.enigma.translation.representation.entry.ClassEntry; @@ -18,17 +21,41 @@ import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; public class StructurePanel extends JPanel { - private JPanel sortingPanel; - private JCheckBox hideDeobfuscated; + private final JPanel optionsPanel; - private JTree structureTree; + private final JLabel obfuscationVisibilityLabel = new JLabel(); + private final JLabel documentationVisibilityLabel = new JLabel(); + private final JLabel sortingOrderLabel = new JLabel(); + + private final JComboBox obfuscationVisibility; + private final JComboBox documentationVisibility; + private final JComboBox sortingOrder; + + private final JTree structureTree; public StructurePanel(Gui gui) { - this.sortingPanel = new JPanel(); - this.hideDeobfuscated = new JCheckBox(I18n.translate("info_panel.tree.structure.hide_deobfuscated")); - this.hideDeobfuscated.addActionListener(event -> gui.showStructure(gui.getActiveEditor())); - this.sortingPanel.add(this.hideDeobfuscated); - this.sortingPanel.setVisible(false); + this.optionsPanel = new JPanel(new GridBagLayout()); + this.optionsPanel.setVisible(false); + + GridBagConstraintsBuilder cb = GridBagConstraintsBuilder.create().insets(5).fill(GridBagConstraints.HORIZONTAL); + + this.optionsPanel.add(this.obfuscationVisibilityLabel, cb.pos(0, 0).build()); + this.obfuscationVisibility = new JComboBox<>(StructureTreeOptions.ObfuscationVisibility.values()); + this.obfuscationVisibility.setRenderer(new StructureOptionListCellRenderer()); + this.obfuscationVisibility.addActionListener(event -> gui.showStructure(gui.getActiveEditor())); + this.optionsPanel.add(this.obfuscationVisibility, cb.pos(1, 0).build()); + + this.optionsPanel.add(this.documentationVisibilityLabel, cb.pos(0, 1).build()); + this.documentationVisibility = new JComboBox<>(StructureTreeOptions.DocumentationVisibility.values()); + this.documentationVisibility.setRenderer(new StructureOptionListCellRenderer()); + this.documentationVisibility.addActionListener(event -> gui.showStructure(gui.getActiveEditor())); + this.optionsPanel.add(this.documentationVisibility, cb.pos(1, 1).build()); + + this.optionsPanel.add(this.sortingOrderLabel, cb.pos(0, 2).build()); + this.sortingOrder = new JComboBox<>(StructureTreeOptions.SortingOrder.values()); + this.sortingOrder.setRenderer(new StructureOptionListCellRenderer()); + this.sortingOrder.addActionListener(event -> gui.showStructure(gui.getActiveEditor())); + this.optionsPanel.add(this.sortingOrder, cb.pos(1, 2).build()); this.structureTree = new JTree(); this.structureTree.setModel(null); @@ -53,20 +80,26 @@ public class StructurePanel extends JPanel { } }); + this.retranslateUi(); + this.setLayout(new BorderLayout()); - this.add(this.sortingPanel, BorderLayout.NORTH); + this.add(this.optionsPanel, BorderLayout.NORTH); this.add(new JScrollPane(this.structureTree)); } public JPanel getSortingPanel() { - return this.sortingPanel; + return this.optionsPanel; } /** - * Returns whether the "Hide Deobfuscated" option of this structure panel is selected. + * Creates and returns the options of this structure panel. */ - public boolean shouldHideDeobfuscated() { - return this.hideDeobfuscated.isSelected(); + public StructureTreeOptions getOptions() { + return new StructureTreeOptions( + (StructureTreeOptions.ObfuscationVisibility) this.obfuscationVisibility.getSelectedItem(), + (StructureTreeOptions.DocumentationVisibility) this.documentationVisibility.getSelectedItem(), + (StructureTreeOptions.SortingOrder) this.sortingOrder.getSelectedItem() + ); } public JTree getStructureTree() { @@ -74,7 +107,9 @@ public class StructurePanel extends JPanel { } public void retranslateUi() { - this.hideDeobfuscated.setText(I18n.translate("info_panel.tree.structure.hide_deobfuscated")); + this.obfuscationVisibilityLabel.setText(I18n.translate("structure.options.obfuscation")); + this.documentationVisibilityLabel.setText(I18n.translate("structure.options.documentation")); + this.sortingOrderLabel.setText(I18n.translate("structure.options.sorting")); } class StructureTreeCellRenderer extends DefaultTreeCellRenderer { diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/renderer/StructureOptionListCellRenderer.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/renderer/StructureOptionListCellRenderer.java new file mode 100644 index 00000000..f9a1cae6 --- /dev/null +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/renderer/StructureOptionListCellRenderer.java @@ -0,0 +1,21 @@ +package cuchaz.enigma.gui.renderer; + +import cuchaz.enigma.analysis.StructureTreeOptions; +import cuchaz.enigma.utils.I18n; + +import javax.swing.*; +import java.awt.*; + +public class StructureOptionListCellRenderer extends DefaultListCellRenderer { + + @Override + public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { + Component c = super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus); + + if (value instanceof StructureTreeOptions.Option option) { + this.setText(I18n.translate(option.getTranslationKey())); + } + + return c; + } +} diff --git a/enigma/src/main/java/cuchaz/enigma/analysis/StructureTreeNode.java b/enigma/src/main/java/cuchaz/enigma/analysis/StructureTreeNode.java index b4343c10..aea76180 100644 --- a/enigma/src/main/java/cuchaz/enigma/analysis/StructureTreeNode.java +++ b/enigma/src/main/java/cuchaz/enigma/analysis/StructureTreeNode.java @@ -10,7 +10,9 @@ import cuchaz.enigma.translation.representation.entry.*; import javax.swing.tree.DefaultMutableTreeNode; import java.util.ArrayList; +import java.util.Comparator; import java.util.List; +import java.util.stream.Stream; public class StructureTreeNode extends DefaultMutableTreeNode { private final List nameProposalServices; @@ -32,25 +34,47 @@ public class StructureTreeNode extends DefaultMutableTreeNode { return this.entry; } - public void load(EnigmaProject project, boolean hideDeobfuscated) { - List children = project.getJarIndex().getChildrenByClass().get(this.parentEntry); - - for (ParentedEntry child : children) { + public void load(EnigmaProject project, StructureTreeOptions options) { + Stream children = project.getJarIndex().getChildrenByClass().get(this.parentEntry).stream(); + + children = switch (options.obfuscationVisibility()) { + case ALL -> children; + case OBFUSCATED -> children + // remove deobfuscated members if only obfuscated, unless it's an inner class + .filter(e -> (e instanceof ClassEntry) || (project.isObfuscated(e) && project.isRenamable(e))) + // keep constructor methods if the class is obfuscated + .filter(e -> !(e instanceof MethodEntry m && m.isConstructor()) || project.isObfuscated(e.getParent())); + case DEOBFUSCATED -> children.filter(e -> (e instanceof ClassEntry) + || (!project.isObfuscated(e) && project.isRenamable(e)) + // keep constructor methods if the class is deobfuscated + || (e instanceof MethodEntry m && m.isConstructor()) && !project.isObfuscated(e.getParent())); + }; + + children = switch (options.documentationVisibility()) { + case ALL -> children; + // TODO remove EntryRemapper.deobfuscate() calls when javadocs will no longer be tied to deobfuscation + case DOCUMENTED -> children.filter(e -> (e instanceof ClassEntry) || (project.getMapper().deobfuscate(e).getJavadocs() != null && !project.getMapper().deobfuscate(e).getJavadocs().isBlank())); + case NON_DOCUMENTED -> children.filter(e -> (e instanceof ClassEntry) || (project.getMapper().deobfuscate(e).getJavadocs() == null || project.getMapper().deobfuscate(e).getJavadocs().isBlank())); + }; + + children = switch (options.sortingOrder()) { + case DEFAULT -> children; + case A_Z -> children.sorted(Comparator.comparing(e -> (e instanceof MethodEntry m && m.isConstructor()) + // compare the class name when the entry is a constructor + ? project.getMapper().deobfuscate(e.getParent()).getSimpleName().toLowerCase() + : project.getMapper().deobfuscate(e).getSimpleName().toLowerCase())); + case Z_A -> children.sorted(Comparator.comparing(e -> (e instanceof MethodEntry m && m.isConstructor()) + ? project.getMapper().deobfuscate(((ParentedEntry) e).getParent()).getSimpleName().toLowerCase() + : project.getMapper().deobfuscate((ParentedEntry) e).getSimpleName().toLowerCase()) + .reversed()); + }; + + for (ParentedEntry child : children.toList()) { StructureTreeNode childNode = new StructureTreeNode(project, this.parentEntry, child); if (child instanceof ClassEntry) { childNode = new StructureTreeNode(project, (ClassEntry) child, child); - childNode.load(project, hideDeobfuscated); - } - - // don't add deobfuscated members if hideDeobfuscated is true, unless it's an inner class - if (hideDeobfuscated && !project.isObfuscated(child) && !(child instanceof ClassEntry)) { - continue; - } - - // don't add constructor methods if hideDeobfuscated is true - if (hideDeobfuscated && (child instanceof MethodEntry method) && method.isConstructor()) { - continue; + childNode.load(project, options); } this.add(childNode); diff --git a/enigma/src/main/java/cuchaz/enigma/analysis/StructureTreeOptions.java b/enigma/src/main/java/cuchaz/enigma/analysis/StructureTreeOptions.java new file mode 100644 index 00000000..cfc80b4a --- /dev/null +++ b/enigma/src/main/java/cuchaz/enigma/analysis/StructureTreeOptions.java @@ -0,0 +1,59 @@ +package cuchaz.enigma.analysis; + +public record StructureTreeOptions( + ObfuscationVisibility obfuscationVisibility, + DocumentationVisibility documentationVisibility, + SortingOrder sortingOrder) { + + public enum ObfuscationVisibility implements Option { + ALL("structure.options.obfuscation.all"), + OBFUSCATED("structure.options.obfuscation.obfuscated"), + DEOBFUSCATED("structure.options.obfuscation.deobfuscated"); + + private final String translationKey; + + ObfuscationVisibility(String translationKey) { + this.translationKey = translationKey; + } + + public String getTranslationKey() { + return this.translationKey; + } + } + + public enum DocumentationVisibility implements Option { + ALL("structure.options.documentation.all"), + DOCUMENTED("structure.options.documentation.documented"), + NON_DOCUMENTED("structure.options.documentation.non_documented"); + + private final String translationKey; + + DocumentationVisibility(String translationKey) { + this.translationKey = translationKey; + } + + public String getTranslationKey() { + return this.translationKey; + } + } + + public enum SortingOrder implements Option { + DEFAULT("structure.options.sorting.default"), + A_Z("structure.options.sorting.a_z"), + Z_A("structure.options.sorting.z_a"); + + private final String translationKey; + + SortingOrder(String translationKey) { + this.translationKey = translationKey; + } + + public String getTranslationKey() { + return this.translationKey; + } + } + + public interface Option { + String getTranslationKey(); + } +} diff --git a/enigma/src/main/resources/lang/en_us.json b/enigma/src/main/resources/lang/en_us.json index 57b2ca8f..35a4d93e 100644 --- a/enigma/src/main/resources/lang/en_us.json +++ b/enigma/src/main/resources/lang/en_us.json @@ -106,13 +106,25 @@ "info_panel.editor.class.decompiling": "(decompiling...)", "info_panel.editor.class.not_found": "Unable to find class:", "info_panel.tree.structure": "Structure", - "info_panel.tree.structure.hide_deobfuscated": "Hide deobfuscated members", "info_panel.tree.inheritance": "Inheritance", "info_panel.tree.implementations": "Implementations", "info_panel.tree.calls": "Call Graph", "popup.copied": "Copied!", + "structure.options.obfuscation": "Obfuscation Visibility", + "structure.options.obfuscation.all": "All", + "structure.options.obfuscation.obfuscated": "Only obfuscated", + "structure.options.obfuscation.deobfuscated": "Only deobfuscated", + "structure.options.documentation": "Documentation Visibility", + "structure.options.documentation.all": "All", + "structure.options.documentation.documented": "Only documented", + "structure.options.documentation.non_documented": "Only non documented", + "structure.options.sorting": "Sorting Order", + "structure.options.sorting.default": "Default", + "structure.options.sorting.a_z": "A to Z", + "structure.options.sorting.z_a": "Z to A", + "log_panel.messages": "Messages", "log_panel.users": "Users", diff --git a/enigma/src/main/resources/lang/fr_fr.json b/enigma/src/main/resources/lang/fr_fr.json index 5c46c87a..ea1a20f6 100644 --- a/enigma/src/main/resources/lang/fr_fr.json +++ b/enigma/src/main/resources/lang/fr_fr.json @@ -106,13 +106,25 @@ "info_panel.editor.class.decompiling": "(décompilation...)", "info_panel.editor.class.not_found": "Impossible de trouver la classe :", "info_panel.tree.structure": "Structure", - "info_panel.tree.structure.hide_deobfuscated": "Masquer les membres déobfusqués", "info_panel.tree.inheritance": "Héritage", "info_panel.tree.implementations": "Implémentations", "info_panel.tree.calls": "Graphique des appels", "popup.copied": "Copié !", + "structure.options.obfuscation": "Visibilité de l'obfuscation", + "structure.options.obfuscation.all": "Tous", + "structure.options.obfuscation.obfuscated": "Seulement obfusqués", + "structure.options.obfuscation.deobfuscated": "Seulement déobfusqués", + "structure.options.documentation": "Visibilité de la documentation", + "structure.options.documentation.all": "Tous", + "structure.options.documentation.documented": "Seulement documentés", + "structure.options.documentation.non_documented": "Seulement non documentés", + "structure.options.sorting": "Ordre de tri", + "structure.options.sorting.default": "Par défaut", + "structure.options.sorting.a_z": "A à Z", + "structure.options.sorting.z_a": "Z à A", + "log_panel.messages": "Messages", "log_panel.users": "Utilisateurs", diff --git a/enigma/src/main/resources/lang/ja_jp.json b/enigma/src/main/resources/lang/ja_jp.json index 0ff162a1..9399e172 100644 --- a/enigma/src/main/resources/lang/ja_jp.json +++ b/enigma/src/main/resources/lang/ja_jp.json @@ -102,7 +102,6 @@ "info_panel.editor.class.decompiling": "(デコンパイル中...)", "info_panel.editor.class.not_found": "クラスが見つけられません:", "info_panel.tree.structure": "構造", - "info_panel.tree.structure.hide_deobfuscated": "難読化解除されたメンバを隠す", "info_panel.tree.inheritance": "継承", "info_panel.tree.implementations": "実装", "info_panel.tree.calls": "呼び出し関係", -- cgit v1.2.3