diff options
| author | 2025-08-20 17:15:23 +0100 | |
|---|---|---|
| committer | 2025-09-13 09:14:23 +0100 | |
| commit | d0a52bb6de19c81705094b26132931a57dc40866 (patch) | |
| tree | 1ed5009b5c84acef7f1d67e564899a77c327eded /enigma-swing/src/main/java | |
| parent | Add I18n service (diff) | |
| download | enigma-d0a52bb6de19c81705094b26132931a57dc40866.tar.gz enigma-d0a52bb6de19c81705094b26132931a57dc40866.tar.xz enigma-d0a52bb6de19c81705094b26132931a57dc40866.zip | |
Add GuiService for plugins to make additions to the GUI. Also add enough context to do something useful.
Diffstat (limited to 'enigma-swing/src/main/java')
4 files changed, 105 insertions, 36 deletions
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 5881900c..12877fed 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/Gui.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/Gui.java | |||
| @@ -45,6 +45,7 @@ import org.jetbrains.annotations.Nullable; | |||
| 45 | 45 | ||
| 46 | import cuchaz.enigma.Enigma; | 46 | import cuchaz.enigma.Enigma; |
| 47 | import cuchaz.enigma.analysis.EntryReference; | 47 | import cuchaz.enigma.analysis.EntryReference; |
| 48 | import cuchaz.enigma.api.service.GuiService; | ||
| 48 | import cuchaz.enigma.gui.config.Themes; | 49 | import cuchaz.enigma.gui.config.Themes; |
| 49 | import cuchaz.enigma.gui.config.UiConfig; | 50 | import cuchaz.enigma.gui.config.UiConfig; |
| 50 | import cuchaz.enigma.gui.dialog.JavadocDialog; | 51 | import cuchaz.enigma.gui.dialog.JavadocDialog; |
| @@ -142,6 +143,10 @@ public class Gui { | |||
| 142 | LanguageUtil.addListener(this::retranslateUi); | 143 | LanguageUtil.addListener(this::retranslateUi); |
| 143 | Themes.addListener((lookAndFeel, boxHighlightPainters) -> SwingUtilities.updateComponentTreeUI(this.getFrame())); | 144 | Themes.addListener((lookAndFeel, boxHighlightPainters) -> SwingUtilities.updateComponentTreeUI(this.getFrame())); |
| 144 | 145 | ||
| 146 | for (GuiService guiService : enigma.getServices().get(GuiService.TYPE)) { | ||
| 147 | guiService.onStart(controller); | ||
| 148 | } | ||
| 149 | |||
| 145 | this.mainWindow.setVisible(true); | 150 | this.mainWindow.setVisible(true); |
| 146 | } | 151 | } |
| 147 | 152 | ||
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 e1e8521a..3609427d 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/GuiController.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/GuiController.java | |||
| @@ -31,6 +31,7 @@ import javax.swing.JOptionPane; | |||
| 31 | import javax.swing.SwingUtilities; | 31 | import javax.swing.SwingUtilities; |
| 32 | 32 | ||
| 33 | import org.jetbrains.annotations.ApiStatus; | 33 | import org.jetbrains.annotations.ApiStatus; |
| 34 | import org.jetbrains.annotations.Nullable; | ||
| 34 | 35 | ||
| 35 | import cuchaz.enigma.Enigma; | 36 | import cuchaz.enigma.Enigma; |
| 36 | import cuchaz.enigma.EnigmaProject; | 37 | import cuchaz.enigma.EnigmaProject; |
| @@ -46,6 +47,8 @@ import cuchaz.enigma.analysis.MethodReferenceTreeNode; | |||
| 46 | import cuchaz.enigma.analysis.StructureTreeNode; | 47 | import cuchaz.enigma.analysis.StructureTreeNode; |
| 47 | import cuchaz.enigma.analysis.StructureTreeOptions; | 48 | import cuchaz.enigma.analysis.StructureTreeOptions; |
| 48 | import cuchaz.enigma.api.service.ObfuscationTestService; | 49 | import cuchaz.enigma.api.service.ObfuscationTestService; |
| 50 | import cuchaz.enigma.api.view.GuiView; | ||
| 51 | import cuchaz.enigma.api.view.entry.EntryReferenceView; | ||
| 49 | import cuchaz.enigma.classhandle.ClassHandle; | 52 | import cuchaz.enigma.classhandle.ClassHandle; |
| 50 | import cuchaz.enigma.classhandle.ClassHandleProvider; | 53 | import cuchaz.enigma.classhandle.ClassHandleProvider; |
| 51 | import cuchaz.enigma.classprovider.ClasspathClassProvider; | 54 | import cuchaz.enigma.classprovider.ClasspathClassProvider; |
| @@ -91,7 +94,7 @@ import cuchaz.enigma.utils.Utils; | |||
| 91 | import cuchaz.enigma.utils.validation.PrintValidatable; | 94 | import cuchaz.enigma.utils.validation.PrintValidatable; |
| 92 | import cuchaz.enigma.utils.validation.ValidationContext; | 95 | import cuchaz.enigma.utils.validation.ValidationContext; |
| 93 | 96 | ||
| 94 | public class GuiController implements ClientPacketHandler { | 97 | public class GuiController implements ClientPacketHandler, GuiView { |
| 95 | private final Gui gui; | 98 | private final Gui gui; |
| 96 | public final Enigma enigma; | 99 | public final Enigma enigma; |
| 97 | 100 | ||
| @@ -115,6 +118,11 @@ public class GuiController implements ClientPacketHandler { | |||
| 115 | this.enigma = enigma; | 118 | this.enigma = enigma; |
| 116 | } | 119 | } |
| 117 | 120 | ||
| 121 | @Override | ||
| 122 | public EnigmaProject getProject() { | ||
| 123 | return project; | ||
| 124 | } | ||
| 125 | |||
| 118 | public boolean isDirty() { | 126 | public boolean isDirty() { |
| 119 | return project != null && project.getMapper().isDirty(); | 127 | return project != null && project.getMapper().isDirty(); |
| 120 | } | 128 | } |
| @@ -330,6 +338,12 @@ public class GuiController implements ClientPacketHandler { | |||
| 330 | } | 338 | } |
| 331 | } | 339 | } |
| 332 | 340 | ||
| 341 | @Override | ||
| 342 | @Nullable | ||
| 343 | public EntryReferenceView getCursorReference() { | ||
| 344 | return gui.getCursorReference(); | ||
| 345 | } | ||
| 346 | |||
| 333 | /** | 347 | /** |
| 334 | * Navigates to the declaration with respect to navigation history. | 348 | * Navigates to the declaration with respect to navigation history. |
| 335 | * | 349 | * |
diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/CustomMenuItem.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/CustomMenuItem.java new file mode 100644 index 00000000..055a00f7 --- /dev/null +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/CustomMenuItem.java | |||
| @@ -0,0 +1,49 @@ | |||
| 1 | package cuchaz.enigma.gui.elements; | ||
| 2 | |||
| 3 | import java.util.function.BooleanSupplier; | ||
| 4 | import java.util.function.Supplier; | ||
| 5 | |||
| 6 | import javax.swing.JMenuItem; | ||
| 7 | import javax.swing.KeyStroke; | ||
| 8 | |||
| 9 | import cuchaz.enigma.api.service.GuiService; | ||
| 10 | import cuchaz.enigma.utils.I18n; | ||
| 11 | |||
| 12 | public class CustomMenuItem implements GuiService.MenuItemBuilder { | ||
| 13 | private final JMenuItem menuItem; | ||
| 14 | private final Supplier<String> translationKey; | ||
| 15 | private BooleanSupplier enabledCondition = () -> true; | ||
| 16 | |||
| 17 | public CustomMenuItem(JMenuItem menuItem, Supplier<String> translationKey) { | ||
| 18 | this.menuItem = menuItem; | ||
| 19 | this.translationKey = translationKey; | ||
| 20 | menuItem.setText(translationKey.get()); | ||
| 21 | } | ||
| 22 | |||
| 23 | @Override | ||
| 24 | public GuiService.MenuItemBuilder setAccelerator(KeyStroke accelerator) { | ||
| 25 | menuItem.setAccelerator(accelerator); | ||
| 26 | return this; | ||
| 27 | } | ||
| 28 | |||
| 29 | @Override | ||
| 30 | public GuiService.MenuItemBuilder setEnabledWhen(BooleanSupplier condition) { | ||
| 31 | this.enabledCondition = condition; | ||
| 32 | return this; | ||
| 33 | } | ||
| 34 | |||
| 35 | @Override | ||
| 36 | public GuiService.MenuItemBuilder setAction(Runnable action) { | ||
| 37 | menuItem.addActionListener(e -> action.run()); | ||
| 38 | return this; | ||
| 39 | } | ||
| 40 | |||
| 41 | public void updateUiState() { | ||
| 42 | menuItem.setEnabled(enabledCondition.getAsBoolean()); | ||
| 43 | menuItem.setText(I18n.translate(translationKey.get())); | ||
| 44 | } | ||
| 45 | |||
| 46 | public void retranslateUi() { | ||
| 47 | menuItem.setText(I18n.translate(translationKey.get())); | ||
| 48 | } | ||
| 49 | } | ||
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 dd37798a..8f968f38 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 | |||
| @@ -1,13 +1,18 @@ | |||
| 1 | package cuchaz.enigma.gui.elements; | 1 | package cuchaz.enigma.gui.elements; |
| 2 | 2 | ||
| 3 | import java.awt.Component; | ||
| 3 | import java.awt.event.InputEvent; | 4 | import java.awt.event.InputEvent; |
| 4 | import java.awt.event.KeyEvent; | 5 | import java.awt.event.KeyEvent; |
| 6 | import java.util.ArrayList; | ||
| 7 | import java.util.List; | ||
| 8 | import java.util.function.Supplier; | ||
| 5 | 9 | ||
| 6 | import javax.swing.JMenuItem; | 10 | import javax.swing.JMenuItem; |
| 7 | import javax.swing.JPopupMenu; | 11 | import javax.swing.JPopupMenu; |
| 8 | import javax.swing.KeyStroke; | 12 | import javax.swing.KeyStroke; |
| 9 | 13 | ||
| 10 | import cuchaz.enigma.analysis.EntryReference; | 14 | import cuchaz.enigma.analysis.EntryReference; |
| 15 | import cuchaz.enigma.api.service.GuiService; | ||
| 11 | import cuchaz.enigma.gui.EditableType; | 16 | import cuchaz.enigma.gui.EditableType; |
| 12 | import cuchaz.enigma.gui.Gui; | 17 | import cuchaz.enigma.gui.Gui; |
| 13 | import cuchaz.enigma.gui.GuiController; | 18 | import cuchaz.enigma.gui.GuiController; |
| @@ -36,6 +41,7 @@ public class EditorPopupMenu { | |||
| 36 | private final JMenuItem zoomInItem = new JMenuItem(); | 41 | private final JMenuItem zoomInItem = new JMenuItem(); |
| 37 | private final JMenuItem zoomOutMenu = new JMenuItem(); | 42 | private final JMenuItem zoomOutMenu = new JMenuItem(); |
| 38 | private final JMenuItem resetZoomItem = new JMenuItem(); | 43 | private final JMenuItem resetZoomItem = new JMenuItem(); |
| 44 | private final List<CustomMenuItem> customItems = new ArrayList<>(); | ||
| 39 | 45 | ||
| 40 | private final EditorPanel editor; | 46 | private final EditorPanel editor; |
| 41 | private final Gui gui; | 47 | private final Gui gui; |
| @@ -102,48 +108,35 @@ public class EditorPopupMenu { | |||
| 102 | this.zoomInItem.addActionListener(event -> editor.offsetEditorZoom(2)); | 108 | this.zoomInItem.addActionListener(event -> editor.offsetEditorZoom(2)); |
| 103 | this.zoomOutMenu.addActionListener(event -> editor.offsetEditorZoom(-2)); | 109 | this.zoomOutMenu.addActionListener(event -> editor.offsetEditorZoom(-2)); |
| 104 | this.resetZoomItem.addActionListener(event -> editor.resetEditorZoom()); | 110 | this.resetZoomItem.addActionListener(event -> editor.resetEditorZoom()); |
| 111 | |||
| 112 | for (GuiService guiService : gui.getController().enigma.getServices().get(GuiService.TYPE)) { | ||
| 113 | guiService.addToEditorContextMenu(gui.getController(), new GuiService.MenuRegistrar() { | ||
| 114 | @Override | ||
| 115 | public void addSeparator() { | ||
| 116 | ui.addSeparator(); | ||
| 117 | } | ||
| 118 | |||
| 119 | @Override | ||
| 120 | public GuiService.MenuItemBuilder add(Supplier<String> translationKey) { | ||
| 121 | JMenuItem menuItem = new JMenuItem(); | ||
| 122 | ui.add(menuItem); | ||
| 123 | CustomMenuItem item = new CustomMenuItem(menuItem, translationKey); | ||
| 124 | customItems.add(item); | ||
| 125 | return item; | ||
| 126 | } | ||
| 127 | }); | ||
| 128 | } | ||
| 105 | } | 129 | } |
| 106 | 130 | ||
| 107 | // TODO have editor redirect key event to menu so that the actions get | 131 | // TODO have editor redirect key event to menu so that the actions get |
| 108 | // triggered without having to hardcode them here, because this | 132 | // triggered without having to hardcode them here, because this |
| 109 | // is a hack | 133 | // is a hack |
| 110 | public boolean handleKeyEvent(KeyEvent event) { | 134 | public boolean handleKeyEvent(KeyEvent event) { |
| 111 | if (event.isControlDown()) { | 135 | KeyStroke keyStroke = KeyStroke.getKeyStrokeForEvent(event); |
| 112 | switch (event.getKeyCode()) { | ||
| 113 | case KeyEvent.VK_I: | ||
| 114 | this.showInheritanceItem.doClick(); | ||
| 115 | return true; | ||
| 116 | case KeyEvent.VK_M: | ||
| 117 | this.showImplementationsItem.doClick(); | ||
| 118 | return true; | ||
| 119 | case KeyEvent.VK_N: | ||
| 120 | this.openEntryItem.doClick(); | ||
| 121 | return true; | ||
| 122 | case KeyEvent.VK_P: | ||
| 123 | this.openPreviousItem.doClick(); | ||
| 124 | return true; | ||
| 125 | case KeyEvent.VK_E: | ||
| 126 | this.openNextItem.doClick(); | ||
| 127 | return true; | ||
| 128 | case KeyEvent.VK_C: | ||
| 129 | if (event.isShiftDown()) { | ||
| 130 | this.showCallsSpecificItem.doClick(); | ||
| 131 | } else { | ||
| 132 | this.showCallsItem.doClick(); | ||
| 133 | } | ||
| 134 | 136 | ||
| 135 | return true; | 137 | for (Component component : ui.getComponents()) { |
| 136 | case KeyEvent.VK_O: | 138 | if (component instanceof JMenuItem menuItem && keyStroke.equals(menuItem.getAccelerator())) { |
| 137 | this.toggleMappingItem.doClick(); | 139 | menuItem.doClick(); |
| 138 | return true; | ||
| 139 | case KeyEvent.VK_R: | ||
| 140 | this.renameItem.doClick(); | ||
| 141 | return true; | ||
| 142 | case KeyEvent.VK_D: | ||
| 143 | this.editJavadocItem.doClick(); | ||
| 144 | return true; | ||
| 145 | case KeyEvent.VK_V: | ||
| 146 | this.pasteItem.doClick(); | ||
| 147 | return true; | 140 | return true; |
| 148 | } | 141 | } |
| 149 | } | 142 | } |
| @@ -184,6 +177,10 @@ public class EditorPopupMenu { | |||
| 184 | } | 177 | } |
| 185 | 178 | ||
| 186 | this.toggleMappingItem.setText(I18n.translate("popup_menu." + (isDeobfuscated ? "reset_obfuscated" : "mark_deobfuscated"))); | 179 | this.toggleMappingItem.setText(I18n.translate("popup_menu." + (isDeobfuscated ? "reset_obfuscated" : "mark_deobfuscated"))); |
| 180 | |||
| 181 | for (CustomMenuItem customItem : customItems) { | ||
| 182 | customItem.updateUiState(); | ||
| 183 | } | ||
| 187 | } | 184 | } |
| 188 | 185 | ||
| 189 | public void retranslateUi() { | 186 | public void retranslateUi() { |
| @@ -201,6 +198,10 @@ public class EditorPopupMenu { | |||
| 201 | this.zoomInItem.setText(I18n.translate("popup_menu.zoom.in")); | 198 | this.zoomInItem.setText(I18n.translate("popup_menu.zoom.in")); |
| 202 | this.zoomOutMenu.setText(I18n.translate("popup_menu.zoom.out")); | 199 | this.zoomOutMenu.setText(I18n.translate("popup_menu.zoom.out")); |
| 203 | this.resetZoomItem.setText(I18n.translate("popup_menu.zoom.reset")); | 200 | this.resetZoomItem.setText(I18n.translate("popup_menu.zoom.reset")); |
| 201 | |||
| 202 | for (CustomMenuItem customItem : customItems) { | ||
| 203 | customItem.retranslateUi(); | ||
| 204 | } | ||
| 204 | } | 205 | } |
| 205 | 206 | ||
| 206 | public JPopupMenu getUi() { | 207 | public JPopupMenu getUi() { |