From 045a44a9ae2a70087e0d960a7196d1564c4c7ab1 Mon Sep 17 00:00:00 2001 From: Marco Rebhan Date: Sun, 20 Jun 2021 11:48:37 +0200 Subject: Allow disabling editing part of the mappings For now, this is done over CLI options, it cannot be changed through the GUI. The new CLI options are: -C, --no-edit-classes Disable editing class names -D, --no-edit-javadocs Disable editing Javadocs -E, --no-edit-all Disable editing everything -F, --no-edit-fields Disable editing field names -M, --no-edit-methods Disable editing method names -P, --no-edit-parameters Disable editing parameter names -c, --edit-classes Enable editing class names -d, --edit-javadocs Enable editing Javadocs -e, --edit-all Enable editing everything --edit-locals Enable editing local variable names -f, --edit-fields Enable editing field names -m, --edit-methods Enable editing method names --no-edit-locals Disable editing local variable names -p, --edit-parameters Enable editing parameter names They can be combined together and are applied in order of appearance in the argument list from left to right, and everything is editable by default, as before. For example, --no-edit-all --edit-javadocs --edit-parameters (or -Edp for short) will allow you to edit only Javadocs and parameter names. --(no-)edit-locals is only provided for completeness and has no effect right now because Enigma does not allow for editing locals other than parameters. --- .../main/java/cuchaz/enigma/gui/EditableType.java | 10 +++ .../src/main/java/cuchaz/enigma/gui/Gui.java | 11 ++- .../src/main/java/cuchaz/enigma/gui/Main.java | 49 ++++++++++++- .../enigma/gui/elements/ConvertingTextField.java | 33 ++++++--- .../enigma/gui/elements/EditorPopupMenu.java | 39 ++++++++--- .../cuchaz/enigma/gui/panels/IdentifierPanel.java | 81 +++++++++++++++------- 6 files changed, 176 insertions(+), 47 deletions(-) create mode 100644 enigma-swing/src/main/java/cuchaz/enigma/gui/EditableType.java (limited to 'enigma-swing/src/main/java/cuchaz') diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/EditableType.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/EditableType.java new file mode 100644 index 00000000..3dfa35ce --- /dev/null +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/EditableType.java @@ -0,0 +1,10 @@ +package cuchaz.enigma.gui; + +public enum EditableType { + CLASS, + METHOD, + FIELD, + PARAMETER, + LOCAL_VARIABLE, + JAVADOC, +} 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 52bd93ee..c46bda2e 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/Gui.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/Gui.java @@ -73,6 +73,7 @@ public class Gui implements LanguageChangeListener { public History, Entry>> referenceHistory; private ConnectionState connectionState; private boolean isJarOpen; + private final Set editableTypes; public JFileChooser jarFileChooser; public JFileChooser tinyMappingsFileChooser; @@ -112,7 +113,9 @@ public class Gui implements LanguageChangeListener { private final JTabbedPane openFiles; private final HashBiMap editors = HashBiMap.create(); - public Gui(EnigmaProfile profile) { + public Gui(EnigmaProfile profile, Set editableTypes) { + this.editableTypes = editableTypes; + // init frame this.frame = new JFrame(Enigma.NAME); final Container pane = this.frame.getContentPane(); @@ -596,7 +599,7 @@ public class Gui implements LanguageChangeListener { public void startDocChange(EditorPanel editor) { EntryReference, Entry> cursorReference = editor.getCursorReference(); - if (cursorReference == null) return; + if (cursorReference == null || !this.isEditable(EditableType.JAVADOC)) return; JavadocDialog.show(frame, getController(), cursorReference); } @@ -987,4 +990,8 @@ public class Gui implements LanguageChangeListener { return vc.canProceed(); } + public boolean isEditable(EditableType t) { + return this.editableTypes.contains(t); + } + } diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/Main.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/Main.java index b655bd4e..cd968fce 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/Main.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/Main.java @@ -15,6 +15,9 @@ import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; +import java.util.EnumSet; +import java.util.List; +import java.util.Set; import com.google.common.io.MoreFiles; import joptsimple.*; @@ -42,6 +45,21 @@ public class Main { .withRequiredArg() .withValuesConvertedBy(PathConverter.INSTANCE); + parser.acceptsAll(List.of("edit-all", "e"), "Enable editing everything"); + parser.acceptsAll(List.of("no-edit-all", "E"), "Disable editing everything"); + parser.acceptsAll(List.of("edit-classes", "c"), "Enable editing class names"); + parser.acceptsAll(List.of("no-edit-classes", "C"), "Disable editing class names"); + parser.acceptsAll(List.of("edit-methods", "m"), "Enable editing method names"); + parser.acceptsAll(List.of("no-edit-methods", "M"), "Disable editing method names"); + parser.acceptsAll(List.of("edit-fields", "f"), "Enable editing field names"); + parser.acceptsAll(List.of("no-edit-fields", "F"), "Disable editing field names"); + parser.acceptsAll(List.of("edit-parameters", "p"), "Enable editing parameter names"); + parser.acceptsAll(List.of("no-edit-parameters", "P"), "Disable editing parameter names"); + parser.acceptsAll(List.of("edit-locals"), "Enable editing local variable names"); + parser.acceptsAll(List.of("no-edit-locals"), "Disable editing local variable names"); + parser.acceptsAll(List.of("edit-javadocs", "d"), "Enable editing Javadocs"); + parser.acceptsAll(List.of("no-edit-javadocs", "D"), "Disable editing Javadocs"); + parser.accepts("help", "Displays help information"); try { @@ -52,13 +70,42 @@ public class Main { return; } + Set editables = EnumSet.allOf(EditableType.class); + + for (OptionSpec spec : options.specs()) { + for (String s : spec.options()) { + switch (s) { + case "edit-all" -> editables.addAll(List.of(EditableType.values())); + case "no-edit-all" -> editables.clear(); + case "edit-classes" -> editables.add(EditableType.CLASS); + case "no-edit-classes" -> editables.remove(EditableType.CLASS); + case "edit-methods" -> editables.add(EditableType.METHOD); + case "no-edit-methods" -> editables.remove(EditableType.METHOD); + case "edit-fields" -> editables.add(EditableType.FIELD); + case "no-edit-fields" -> editables.remove(EditableType.FIELD); + case "edit-parameters" -> editables.add(EditableType.PARAMETER); + case "no-edit-parameters" -> editables.remove(EditableType.PARAMETER); + case "edit-locals" -> { + editables.add(EditableType.LOCAL_VARIABLE); + System.err.println("warning: --edit-locals has no effect as local variables are currently not editable"); + } + case "no-edit-locals" -> { + editables.remove(EditableType.LOCAL_VARIABLE); + System.err.println("warning: --no-edit-locals has no effect as local variables are currently not editable"); + } + case "edit-javadocs" -> editables.add(EditableType.JAVADOC); + case "no-edit-javadocs" -> editables.remove(EditableType.JAVADOC); + } + } + } + EnigmaProfile parsedProfile = EnigmaProfile.read(options.valueOf(profile)); I18n.setLanguage(UiConfig.getLanguage()); System.setProperty("apple.laf.useScreenMenuBar", "true"); Themes.setupTheme(); - Gui gui = new Gui(parsedProfile); + Gui gui = new Gui(parsedProfile, editables); GuiController controller = gui.getController(); if (options.has(jar)) { diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/ConvertingTextField.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/ConvertingTextField.java index 7eb1edba..9a6ea098 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/ConvertingTextField.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/ConvertingTextField.java @@ -25,7 +25,8 @@ public class ConvertingTextField implements Validatable { private final JPanel ui; private final ValidatableTextField textField; private final JLabel label; - private boolean isEditing = false; + private boolean editing = false; + private boolean editable = true; private final Set listeners = new HashSet<>(); @@ -68,10 +69,11 @@ public class ConvertingTextField implements Validatable { } public void startEditing() { - if (isEditing) return; + if (this.editing || !this.editable) return; + this.ui.removeAll(); this.ui.add(this.textField); - this.isEditing = true; + this.editing = true; this.ui.validate(); this.ui.repaint(); this.textField.requestFocusInWindow(); @@ -80,7 +82,7 @@ public class ConvertingTextField implements Validatable { } public void stopEditing(boolean abort) { - if (!isEditing) return; + if (!editing) return; if (!listeners.stream().allMatch(l -> l.tryStopEditing(this, abort))) return; @@ -92,7 +94,7 @@ public class ConvertingTextField implements Validatable { this.ui.removeAll(); this.ui.add(this.label); - this.isEditing = false; + this.editing = false; this.ui.validate(); this.ui.repaint(); this.listeners.forEach(l -> l.onStopEditing(this, abort)); @@ -105,19 +107,28 @@ public class ConvertingTextField implements Validatable { } public void setEditText(String text) { - if (!isEditing) return; + if (!editing) return; this.textField.setText(text); } + public void setEditable(boolean editable) { + if (!editable) { + this.stopEditing(true); + } + + this.label.setEnabled(editable); + this.editable = editable; + } + public void selectAll() { - if (!isEditing) return; + if (!editing) return; this.textField.selectAll(); } public void selectSubstring(int startIndex) { - if (!isEditing) return; + if (!editing) return; Document doc = this.textField.getDocument(); if (doc != null) { @@ -126,13 +137,13 @@ public class ConvertingTextField implements Validatable { } public void selectSubstring(int startIndex, int endIndex) { - if (!isEditing) return; + if (!editing) return; this.textField.select(startIndex, endIndex); } public String getText() { - if (isEditing) { + if (editing) { return this.textField.getText(); } else { return this.label.getText(); @@ -144,7 +155,7 @@ public class ConvertingTextField implements Validatable { } public boolean hasChanges() { - if (!isEditing) return false; + if (!editing) return false; return !this.textField.getText().equals(this.label.getText()); } diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/EditorPopupMenu.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/EditorPopupMenu.java index dbe8948c..637f6a48 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/EditorPopupMenu.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/EditorPopupMenu.java @@ -8,13 +8,11 @@ import javax.swing.JPopupMenu; import javax.swing.KeyStroke; import cuchaz.enigma.analysis.EntryReference; +import cuchaz.enigma.gui.EditableType; import cuchaz.enigma.gui.Gui; import cuchaz.enigma.gui.GuiController; import cuchaz.enigma.gui.panels.EditorPanel; -import cuchaz.enigma.translation.representation.entry.ClassEntry; -import cuchaz.enigma.translation.representation.entry.Entry; -import cuchaz.enigma.translation.representation.entry.FieldEntry; -import cuchaz.enigma.translation.representation.entry.MethodEntry; +import cuchaz.enigma.translation.representation.entry.*; import cuchaz.enigma.utils.I18n; public class EditorPopupMenu { @@ -147,12 +145,35 @@ public class EditorPopupMenu { boolean isClassEntry = referenceEntry instanceof ClassEntry; boolean isFieldEntry = referenceEntry instanceof FieldEntry; - boolean isMethodEntry = referenceEntry instanceof MethodEntry && !((MethodEntry) referenceEntry).isConstructor(); - boolean isConstructorEntry = referenceEntry instanceof MethodEntry && ((MethodEntry) referenceEntry).isConstructor(); + boolean isMethodEntry = referenceEntry instanceof MethodEntry me && !me.isConstructor(); + boolean isConstructorEntry = referenceEntry instanceof MethodEntry me && me.isConstructor(); boolean isRenamable = ref != null && controller.project.isRenamable(ref); - this.renameItem.setEnabled(isRenamable); - this.editJavadocItem.setEnabled(isRenamable); + // TODO get rid of this with Entry rework + EditableType type = null; + + if (referenceEntry instanceof ClassEntry) { + type = EditableType.CLASS; + } else if (referenceEntry instanceof MethodEntry me) { + if (me.isConstructor()) { + // treat constructors as classes because renaming one renames + // the class + type = EditableType.CLASS; + } else { + type = EditableType.METHOD; + } + } else if (referenceEntry instanceof FieldEntry) { + type = EditableType.FIELD; + } else if (referenceEntry instanceof LocalVariableEntry lve) { + if (lve.isArgument()) { + type = EditableType.PARAMETER; + } else { + type = EditableType.LOCAL_VARIABLE; + } + } + + this.renameItem.setEnabled(isRenamable && (type != null && this.gui.isEditable(type))); + this.editJavadocItem.setEnabled(isRenamable && this.gui.isEditable(EditableType.JAVADOC)); this.showInheritanceItem.setEnabled(isClassEntry || isMethodEntry || isConstructorEntry); this.showImplementationsItem.setEnabled(isClassEntry || isMethodEntry); this.showCallsItem.setEnabled(isRenamable && (isClassEntry || isFieldEntry || isMethodEntry || isConstructorEntry)); @@ -160,7 +181,7 @@ public class EditorPopupMenu { this.openEntryItem.setEnabled(isRenamable && (isClassEntry || isFieldEntry || isMethodEntry || isConstructorEntry)); this.openPreviousItem.setEnabled(controller.hasPreviousReference()); this.openNextItem.setEnabled(controller.hasNextReference()); - this.toggleMappingItem.setEnabled(isRenamable); + this.toggleMappingItem.setEnabled(isRenamable && (type != null && this.gui.isEditable(type))); if (referenceEntry != null && this.gui.getController().project.getMapper().extendedDeobfuscate(referenceEntry).isDeobfuscated()) { this.toggleMappingItem.setText(I18n.translate("popup_menu.reset_obfuscated")); diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/panels/IdentifierPanel.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/panels/IdentifierPanel.java index a4914678..3bae94c3 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/panels/IdentifierPanel.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/panels/IdentifierPanel.java @@ -16,6 +16,7 @@ import javax.swing.JPanel; import cuchaz.enigma.EnigmaProject; import cuchaz.enigma.analysis.EntryReference; +import cuchaz.enigma.gui.EditableType; import cuchaz.enigma.gui.Gui; import cuchaz.enigma.gui.elements.ConvertingTextField; import cuchaz.enigma.gui.events.ConvertingTextFieldListener; @@ -83,7 +84,7 @@ public class IdentifierPanel { this.nameField = null; - TableHelper th = new TableHelper(this.ui, this.entry, this.gui.getController().project); + TableHelper th = new TableHelper(this.ui, this.entry, this.gui); th.begin(); if (this.entry == null) { this.ui.setEnabled(false); @@ -93,27 +94,39 @@ public class IdentifierPanel { if (deobfEntry instanceof ClassEntry) { ClassEntry ce = (ClassEntry) deobfEntry; String name = ce.isInnerClass() ? ce.getName() : ce.getFullName(); - this.nameField = th.addRenameTextField(I18n.translate("info_panel.identifier.class"), name); - th.addModifierRow(I18n.translate("info_panel.identifier.modifier"), this::onModifierChanged); + this.nameField = th.addRenameTextField(EditableType.CLASS, name); + th.addModifierRow(I18n.translate("info_panel.identifier.modifier"), EditableType.CLASS, this::onModifierChanged); } else if (deobfEntry instanceof FieldEntry) { FieldEntry fe = (FieldEntry) deobfEntry; - this.nameField = th.addRenameTextField(I18n.translate("info_panel.identifier.field"), fe.getName()); + this.nameField = th.addRenameTextField(EditableType.FIELD, fe.getName()); th.addStringRow(I18n.translate("info_panel.identifier.class"), fe.getParent().getFullName()); th.addCopiableStringRow(I18n.translate("info_panel.identifier.type_descriptor"), fe.getDesc().toString()); - th.addModifierRow(I18n.translate("info_panel.identifier.modifier"), this::onModifierChanged); + th.addModifierRow(I18n.translate("info_panel.identifier.modifier"), EditableType.FIELD, this::onModifierChanged); } else if (deobfEntry instanceof MethodEntry) { MethodEntry me = (MethodEntry) deobfEntry; if (me.isConstructor()) { - th.addStringRow(I18n.translate("info_panel.identifier.constructor"), me.getParent().getFullName()); + ClassEntry ce = me.getParent(); + if (ce != null) { + String name = ce.isInnerClass() ? ce.getName() : ce.getFullName(); + this.nameField = th.addRenameTextField(EditableType.CLASS, name); + } } else { - this.nameField = th.addRenameTextField(I18n.translate("info_panel.identifier.method"), me.getName()); + this.nameField = th.addRenameTextField(EditableType.METHOD, me.getName()); th.addStringRow(I18n.translate("info_panel.identifier.class"), me.getParent().getFullName()); } th.addCopiableStringRow(I18n.translate("info_panel.identifier.method_descriptor"), me.getDesc().toString()); - th.addModifierRow(I18n.translate("info_panel.identifier.modifier"), this::onModifierChanged); + th.addModifierRow(I18n.translate("info_panel.identifier.modifier"), EditableType.METHOD, this::onModifierChanged); } else if (deobfEntry instanceof LocalVariableEntry) { LocalVariableEntry lve = (LocalVariableEntry) deobfEntry; - this.nameField = th.addRenameTextField(I18n.translate("info_panel.identifier.variable"), lve.getName()); + EditableType type; + + if (lve.isArgument()) { + type = EditableType.PARAMETER; + } else { + type = EditableType.LOCAL_VARIABLE; + } + + this.nameField = th.addRenameTextField(type, lve.getName()); th.addStringRow(I18n.translate("info_panel.identifier.class"), lve.getContainingClass().getFullName()); th.addStringRow(I18n.translate("info_panel.identifier.method"), lve.getParent().getName()); th.addStringRow(I18n.translate("info_panel.identifier.index"), Integer.toString(lve.getIndex())); @@ -185,13 +198,13 @@ public class IdentifierPanel { private final Container c; private final Entry e; - private final EnigmaProject project; + private final Gui gui; private int row; - public TableHelper(Container c, Entry e, EnigmaProject project) { + public TableHelper(Container c, Entry e, Gui gui) { this.c = c; this.e = e; - this.project = project; + this.gui = gui; } public void begin() { @@ -228,11 +241,21 @@ public class IdentifierPanel { return textField; } - public ConvertingTextField addRenameTextField(String c1, String c2) { - if (project.isRenamable(e)) { - return addConvertingTextField(c1, c2); + public ConvertingTextField addRenameTextField(EditableType type, String c2) { + String description = switch(type) { + case CLASS -> I18n.translate("info_panel.identifier.class"); + case METHOD -> I18n.translate("info_panel.identifier.method"); + case FIELD -> I18n.translate("info_panel.identifier.field"); + case PARAMETER, LOCAL_VARIABLE -> I18n.translate("info_panel.identifier.variable"); + default -> throw new IllegalStateException("Unexpected value: " + type); + }; + + if (this.gui.getController().project.isRenamable(e)) { + ConvertingTextField field = addConvertingTextField(description, c2); + field.setEditable(this.gui.isEditable(type)); + return field; } else { - addStringRow(c1, c2); + addStringRow(description, c2); return null; } } @@ -245,22 +268,32 @@ public class IdentifierPanel { addCopiableRow(new JLabel(c1), GuiUtil.unboldLabel(new JLabel(c2))); } - public JComboBox addModifierRow(String c1, Consumer changeListener) { - if (!project.isRenamable(e)) + public JComboBox addModifierRow(String c1, EditableType type, Consumer changeListener) { + EnigmaProject project = this.gui.getController().project; + + if (!project.isRenamable(e)) { return null; + } + JComboBox combo = new JComboBox<>(AccessModifier.values()); EntryMapping mapping = project.getMapper().getDeobfMapping(e); + if (mapping != null) { combo.setSelectedIndex(mapping.getAccessModifier().ordinal()); } else { combo.setSelectedIndex(AccessModifier.UNCHANGED.ordinal()); } - combo.addItemListener(event -> { - if (event.getStateChange() == ItemEvent.SELECTED) { - AccessModifier modifier = (AccessModifier) event.getItem(); - changeListener.accept(modifier); - } - }); + + if (this.gui.isEditable(type)) { + combo.addItemListener(event -> { + if (event.getStateChange() == ItemEvent.SELECTED) { + AccessModifier modifier = (AccessModifier) event.getItem(); + changeListener.accept(modifier); + } + }); + } else { + combo.setEnabled(false); + } addRow(new JLabel(c1), combo); -- cgit v1.2.3