From 9c736848fb7aa82d295b3aa2946e6cd132ee998f Mon Sep 17 00:00:00 2001 From: modmuss50 Date: Wed, 14 Sep 2022 13:12:55 +0100 Subject: Add checkstyle (#460) --- enigma-swing/build.gradle | 30 +- .../main/java/cuchaz/enigma/gui/BrowserCaret.java | 20 +- .../main/java/cuchaz/enigma/gui/ClassSelector.java | 69 +++-- .../main/java/cuchaz/enigma/gui/EditableType.java | 9 +- .../cuchaz/enigma/gui/EnigmaQuickFindDialog.java | 26 +- .../java/cuchaz/enigma/gui/EnigmaSyntaxKit.java | 55 ++-- .../java/cuchaz/enigma/gui/ExceptionIgnorer.java | 24 +- .../src/main/java/cuchaz/enigma/gui/Gui.java | 127 +++++--- .../main/java/cuchaz/enigma/gui/GuiController.java | 149 +++++---- .../src/main/java/cuchaz/enigma/gui/Main.java | 106 ++++--- .../java/cuchaz/enigma/gui/NestedPackages.java | 37 +-- .../java/cuchaz/enigma/gui/QuickFindAction.java | 9 +- .../main/java/cuchaz/enigma/gui/ReadableToken.java | 19 +- .../cuchaz/enigma/gui/TokenListCellRenderer.java | 30 +- .../java/cuchaz/enigma/gui/config/LookAndFeel.java | 11 +- .../java/cuchaz/enigma/gui/config/NetConfig.java | 2 - .../enigma/gui/config/OldConfigImporter.java | 4 +- .../main/java/cuchaz/enigma/gui/config/Themes.java | 9 +- .../java/cuchaz/enigma/gui/config/UiConfig.java | 15 +- .../cuchaz/enigma/gui/config/legacy/Config.java | 20 +- .../java/cuchaz/enigma/gui/dialog/AboutDialog.java | 31 +- .../cuchaz/enigma/gui/dialog/AbstractDialog.java | 12 +- .../cuchaz/enigma/gui/dialog/ChangeDialog.java | 2 - .../enigma/gui/dialog/ConnectToServerDialog.java | 27 +- .../java/cuchaz/enigma/gui/dialog/CrashDialog.java | 50 +-- .../enigma/gui/dialog/CreateServerDialog.java | 24 +- .../java/cuchaz/enigma/gui/dialog/FontDialog.java | 30 +- .../cuchaz/enigma/gui/dialog/JavadocDialog.java | 74 +++-- .../cuchaz/enigma/gui/dialog/ProgressDialog.java | 33 +- .../cuchaz/enigma/gui/dialog/SearchDialog.java | 92 +++--- .../java/cuchaz/enigma/gui/dialog/StatsDialog.java | 23 +- .../gui/elements/AbstractInheritanceTree.java | 9 +- .../java/cuchaz/enigma/gui/elements/CallsTree.java | 16 +- .../enigma/gui/elements/CollapsibleTabbedPane.java | 11 +- .../enigma/gui/elements/ConvertingTextField.java | 43 ++- .../enigma/gui/elements/DeobfPanelPopupMenu.java | 101 +++---- .../enigma/gui/elements/EditorPopupMenu.java | 66 ++-- .../enigma/gui/elements/EditorTabPopupMenu.java | 1 - .../enigma/gui/elements/EditorTabbedPane.java | 11 +- .../enigma/gui/elements/JMultiLineToolTip.java | 19 +- .../java/cuchaz/enigma/gui/elements/MenuBar.java | 99 ++++-- .../java/cuchaz/enigma/gui/elements/StatusBar.java | 2 +- .../gui/elements/ValidatablePasswordField.java | 2 - .../enigma/gui/elements/ValidatableTextArea.java | 2 - .../enigma/gui/elements/ValidatableTextField.java | 2 - .../cuchaz/enigma/gui/elements/ValidatableUi.java | 43 +-- .../gui/events/ConvertingTextFieldListener.java | 2 - .../enigma/gui/events/EditorActionListener.java | 2 - .../enigma/gui/events/ThemeChangeListener.java | 2 - .../enigma/gui/highlight/BoxHighlightPainter.java | 19 +- .../gui/highlight/SelectionHighlightPainter.java | 26 +- .../enigma/gui/newabstraction/EntryValidation.java | 3 +- .../enigma/gui/node/ClassSelectorClassNode.java | 34 ++- .../enigma/gui/node/ClassSelectorPackageNode.java | 27 +- .../enigma/gui/panels/ClosableTabTitlePane.java | 28 +- .../java/cuchaz/enigma/gui/panels/DeobfPanel.java | 3 +- .../java/cuchaz/enigma/gui/panels/EditorPanel.java | 223 +++++++++----- .../cuchaz/enigma/gui/panels/IdentifierPanel.java | 48 +-- .../java/cuchaz/enigma/gui/panels/ObfPanel.java | 4 +- .../cuchaz/enigma/gui/panels/StructurePanel.java | 291 +++++++++--------- .../enigma/gui/renderer/CallsTreeCellRenderer.java | 78 ++--- .../renderer/ImplementationsTreeCellRenderer.java | 39 +-- .../gui/renderer/InheritanceTreeCellRenderer.java | 19 +- .../gui/renderer/MessageListCellRenderer.java | 4 +- .../renderer/StructureOptionListCellRenderer.java | 25 +- .../java/cuchaz/enigma/gui/search/SearchEntry.java | 2 - .../java/cuchaz/enigma/gui/search/SearchUtil.java | 71 +++-- .../cuchaz/enigma/gui/stats/StatsGenerator.java | 212 ++++++------- .../java/cuchaz/enigma/gui/stats/StatsMember.java | 8 +- .../java/cuchaz/enigma/gui/stats/StatsResult.java | 6 +- .../enigma/gui/util/AbstractListCellRenderer.java | 17 +- .../enigma/gui/util/GridBagConstraintsBuilder.java | 2 - .../main/java/cuchaz/enigma/gui/util/GuiUtil.java | 334 +++++++++++---------- .../main/java/cuchaz/enigma/gui/util/History.java | 4 +- .../enigma/gui/util/LanguageChangeListener.java | 2 - .../java/cuchaz/enigma/gui/util/LanguageUtil.java | 2 - .../enigma/gui/util/ScaleChangeListener.java | 2 - .../java/cuchaz/enigma/gui/util/ScaleUtil.java | 5 +- .../enigma/gui/util/SingleTreeSelectionModel.java | 7 +- 79 files changed, 1757 insertions(+), 1390 deletions(-) (limited to 'enigma-swing') diff --git a/enigma-swing/build.gradle b/enigma-swing/build.gradle index d783e950..a4a13d88 100644 --- a/enigma-swing/build.gradle +++ b/enigma-swing/build.gradle @@ -1,18 +1,18 @@ plugins { - id 'application' - id 'com.github.johnrengelman.shadow' version '7.0.0' + id 'application' + id 'com.github.johnrengelman.shadow' version '7.0.0' } dependencies { - implementation project(':enigma') - implementation project(':enigma-server') + implementation project(':enigma') + implementation project(':enigma-server') - implementation 'net.sf.jopt-simple:jopt-simple:6.0-alpha-3' - implementation 'com.formdev:flatlaf:1.6.1' - implementation 'com.formdev:flatlaf-extras:1.6.1' // for SVG icons - implementation 'de.sciss:syntaxpane:1.2.0' - implementation 'com.github.lukeu:swing-dpi:0.9' - implementation 'org.drjekyll:fontchooser:2.5.2' + implementation 'net.sf.jopt-simple:jopt-simple:6.0-alpha-3' + implementation 'com.formdev:flatlaf:1.6.1' + implementation 'com.formdev:flatlaf-extras:1.6.1' // for SVG icons + implementation 'de.sciss:syntaxpane:1.2.0' + implementation 'com.github.lukeu:swing-dpi:0.9' + implementation 'org.drjekyll:fontchooser:2.5.2' } mainClassName = 'cuchaz.enigma.gui.Main' @@ -20,9 +20,9 @@ mainClassName = 'cuchaz.enigma.gui.Main' jar.manifest.attributes 'Main-Class': mainClassName publishing { - publications { - shadow(MavenPublication) { publication -> - publication.from components.java - } - } + publications { + shadow(MavenPublication) { publication -> + publication.from components.java + } + } } diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/BrowserCaret.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/BrowserCaret.java index af105dbd..a213a592 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/BrowserCaret.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/BrowserCaret.java @@ -1,20 +1,19 @@ /******************************************************************************* - * Copyright (c) 2015 Jeff Martin. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html - *

- * Contributors: - * Jeff Martin - initial API and implementation - ******************************************************************************/ +* Copyright (c) 2015 Jeff Martin. +* All rights reserved. This program and the accompanying materials +* are made available under the terms of the GNU Lesser General Public +* License v3.0 which accompanies this distribution, and is available at +* http://www.gnu.org/licenses/lgpl.html +* +*

Contributors: +* Jeff Martin - initial API and implementation +******************************************************************************/ package cuchaz.enigma.gui; import javax.swing.text.DefaultCaret; public class BrowserCaret extends DefaultCaret { - @Override public boolean isSelectionVisible() { return true; @@ -24,5 +23,4 @@ public class BrowserCaret extends DefaultCaret { public boolean isVisible() { return true; } - } diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/ClassSelector.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/ClassSelector.java index b7d3c4ef..4b9fa595 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/ClassSelector.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/ClassSelector.java @@ -1,35 +1,43 @@ /******************************************************************************* - * Copyright (c) 2015 Jeff Martin. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html - *

- * Contributors: - * Jeff Martin - initial API and implementation - ******************************************************************************/ +* Copyright (c) 2015 Jeff Martin. +* All rights reserved. This program and the accompanying materials +* are made available under the terms of the GNU Lesser General Public +* License v3.0 which accompanies this distribution, and is available at +* http://www.gnu.org/licenses/lgpl.html +* +*

Contributors: +* Jeff Martin - initial API and implementation +******************************************************************************/ package cuchaz.enigma.gui; -import cuchaz.enigma.gui.node.ClassSelectorClassNode; -import cuchaz.enigma.gui.util.GuiUtil; -import cuchaz.enigma.translation.representation.entry.ClassEntry; -import cuchaz.enigma.utils.validation.ValidationContext; - -import javax.swing.*; -import javax.swing.event.CellEditorListener; -import javax.swing.event.ChangeEvent; -import javax.swing.tree.*; -import java.awt.*; +import java.awt.Component; import java.awt.event.KeyAdapter; import java.awt.event.KeyEvent; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Comparator; +import java.util.EventObject; import java.util.List; -import java.util.*; -public class ClassSelector extends JTree { +import javax.swing.JTree; +import javax.swing.event.CellEditorListener; +import javax.swing.event.ChangeEvent; +import javax.swing.tree.DefaultMutableTreeNode; +import javax.swing.tree.DefaultTreeCellEditor; +import javax.swing.tree.DefaultTreeCellRenderer; +import javax.swing.tree.DefaultTreeModel; +import javax.swing.tree.TreeNode; +import javax.swing.tree.TreePath; + +import cuchaz.enigma.gui.node.ClassSelectorClassNode; +import cuchaz.enigma.gui.util.GuiUtil; +import cuchaz.enigma.translation.representation.entry.ClassEntry; +import cuchaz.enigma.utils.validation.ValidationContext; +public class ClassSelector extends JTree { public static final Comparator DEOBF_CLASS_COMPARATOR = Comparator.comparing(ClassEntry::getFullName); private final Comparator comparator; @@ -56,6 +64,7 @@ public class ClassSelector extends JTree { if (selectionListener != null && event.getClickCount() == 2) { // get the selected node TreePath path = getSelectionPath(); + if (path != null && path.getLastPathComponent() instanceof ClassSelectorClassNode node) { selectionListener.onSelectClass(node.getObfEntry()); } @@ -121,23 +130,31 @@ public class ClassSelector extends JTree { TreePath path = getSelectionPath(); Object realPath = path.getLastPathComponent(); + if (realPath instanceof DefaultMutableTreeNode node && data != null) { TreeNode parentNode = node.getParent(); - if (parentNode == null) + + if (parentNode == null) { return; + } + boolean allowEdit = true; + for (int i = 0; i < parentNode.getChildCount(); i++) { TreeNode childNode = parentNode.getChildAt(i); + if (childNode != null && childNode.toString().equals(data) && childNode != node) { allowEdit = false; break; } } + if (allowEdit && renameSelectionListener != null) { Object prevData = node.getUserObject(); Object objectData = node.getUserObject() instanceof ClassEntry ? new ClassEntry(((ClassEntry) prevData).getPackageName() + "/" + data) : data; gui.validateImmediateAction(vc -> { renameSelectionListener.onSelectionRename(vc, node.getUserObject(), objectData, node); + if (vc.canProceed()) { node.setUserObject(objectData); // Make sure that it's modified } else { @@ -206,15 +223,19 @@ public class ClassSelector extends JTree { public List getExpansionState() { List state = new ArrayList<>(); int rowCount = getRowCount(); + for (int i = 0; i < rowCount; i++) { TreePath path = getPathForRow(i); + if (isPathSelected(path)) { state.add(new StateEntry(State.SELECTED, path)); } + if (isExpanded(path)) { state.add(new StateEntry(State.EXPANDED, path)); } } + return state; } @@ -223,8 +244,8 @@ public class ClassSelector extends JTree { for (StateEntry entry : expansionState) { switch (entry.state) { - case SELECTED -> addSelectionPath(entry.path); - case EXPANDED -> expandPath(entry.path); + case SELECTED -> addSelectionPath(entry.path); + case EXPANDED -> expandPath(entry.path); } } } diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/EditableType.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/EditableType.java index 1e8d6cf0..6028609f 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/EditableType.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/EditableType.java @@ -2,7 +2,11 @@ package cuchaz.enigma.gui; import javax.annotation.Nullable; -import cuchaz.enigma.translation.representation.entry.*; +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.LocalVariableEntry; +import cuchaz.enigma.translation.representation.entry.MethodEntry; public enum EditableType { CLASS, @@ -10,8 +14,7 @@ public enum EditableType { FIELD, PARAMETER, LOCAL_VARIABLE, - JAVADOC, - ; + JAVADOC; @Nullable public static EditableType fromEntry(Entry entry) { diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/EnigmaQuickFindDialog.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/EnigmaQuickFindDialog.java index c912be3a..0af8f9c1 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/EnigmaQuickFindDialog.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/EnigmaQuickFindDialog.java @@ -1,16 +1,22 @@ package cuchaz.enigma.gui; -import de.sciss.syntaxpane.actions.DocumentSearchData; -import de.sciss.syntaxpane.actions.gui.QuickFindDialog; - -import javax.swing.*; -import javax.swing.text.JTextComponent; -import java.awt.*; +import java.awt.Component; +import java.awt.Container; +import java.awt.Point; import java.awt.event.KeyAdapter; import java.awt.event.KeyEvent; import java.util.stream.IntStream; import java.util.stream.Stream; +import javax.swing.JButton; +import javax.swing.JTextField; +import javax.swing.JToolBar; +import javax.swing.SwingUtilities; +import javax.swing.text.JTextComponent; + +import de.sciss.syntaxpane.actions.DocumentSearchData; +import de.sciss.syntaxpane.actions.gui.QuickFindDialog; + public class EnigmaQuickFindDialog extends QuickFindDialog { public EnigmaQuickFindDialog(JTextComponent target) { super(target, DocumentSearchData.getFromEditor(target)); @@ -22,6 +28,7 @@ public class EnigmaQuickFindDialog extends QuickFindDialog { @Override public void keyPressed(KeyEvent e) { super.keyPressed(e); + if (e.getKeyCode() == KeyEvent.VK_ENTER) { JToolBar toolBar = getToolBar(); boolean next = !e.isShiftDown(); @@ -78,13 +85,10 @@ public class EnigmaQuickFindDialog extends QuickFindDialog { } private static Stream components(Container container) { - return IntStream.range(0, container.getComponentCount()) - .mapToObj(container::getComponent); + return IntStream.range(0, container.getComponentCount()).mapToObj(container::getComponent); } private static Stream components(Container container, Class type) { - return components(container) - .filter(type::isInstance) - .map(type::cast); + return components(container).filter(type::isInstance).map(type::cast); } } diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/EnigmaSyntaxKit.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/EnigmaSyntaxKit.java index 43745dd9..b81782eb 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/EnigmaSyntaxKit.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/EnigmaSyntaxKit.java @@ -10,7 +10,6 @@ import de.sciss.syntaxpane.util.Configuration; import cuchaz.enigma.gui.config.UiConfig; public class EnigmaSyntaxKit extends JavaSyntaxKit { - private static Configuration configuration = null; @Override @@ -18,23 +17,20 @@ public class EnigmaSyntaxKit extends JavaSyntaxKit { if (configuration == null) { initConfig(DefaultSyntaxKit.getConfig(JavaSyntaxKit.class)); } + return configuration; } public void initConfig(Configuration baseConfig) { configuration = flattenConfiguration(baseConfig, EnigmaSyntaxKit.class); - // Remove all actions except a select few because they disregard the - // editable state of the editor, or at least are useless anyway because - // they would try editing the file. - // Also includes the Action.insert-date action which is written in - // Javascript and causes the editor to freeze on first load for a short - // time. - configuration.keySet().removeIf(s -> s.startsWith("Action.") && - !(s.startsWith("Action.find") || - s.startsWith("Action.goto-line") || - s.startsWith("Action.jump-to-pair") || - s.startsWith("Action.quick-find"))); + // Remove all actions except a select few because they disregard the + // editable state of the editor, or at least are useless anyway because + // they would try editing the file. + // Also includes the Action.insert-date action which is written in + // Javascript and causes the editor to freeze on first load for a short + // time. + configuration.keySet().removeIf(s -> s.startsWith("Action.") && !(s.startsWith("Action.find") || s.startsWith("Action.goto-line") || s.startsWith("Action.jump-to-pair") || s.startsWith("Action.quick-find"))); // See de.sciss.syntaxpane.TokenType configuration.put("Style.KEYWORD", String.format("%d, 0", UiConfig.getHighlightColor().getRGB())); @@ -59,27 +55,28 @@ public class EnigmaSyntaxKit extends JavaSyntaxKit { Font editorFont = UiConfig.activeUseCustomFonts() ? UiConfig.getEditorFont() : UiConfig.getFallbackEditorFont(); configuration.put("DefaultFont", UiConfig.encodeFont(editorFont)); - } + } + + /** + * Creates a new configuration from the passed configuration so that it has + * no parents and all its values are on the same level. This is needed since + * there is no way to remove map entries from parent configurations. + * + * @param source the configuration to flatten + * @param configClass the class for the new configuration + * @return a new configuration + */ + private static Configuration flattenConfiguration(Configuration source, Class configClass) { + Configuration config = new Configuration(configClass, null); - /** - * Creates a new configuration from the passed configuration so that it has - * no parents and all its values are on the same level. This is needed since - * there is no way to remove map entries from parent configurations. - * - * @param source the configuration to flatten - * @param configClass the class for the new configuration - * @return a new configuration - */ - private static Configuration flattenConfiguration(Configuration source, Class configClass) { - Configuration config = new Configuration(configClass, null); - for (String p : source.stringPropertyNames()) { - config.put(p, source.getString(p)); - } - return config; + for (String p : source.stringPropertyNames()) { + config.put(p, source.getString(p)); + } + + return config; } public static void invalidate() { configuration = null; } - } diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/ExceptionIgnorer.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/ExceptionIgnorer.java index 6246192c..76d18598 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/ExceptionIgnorer.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/ExceptionIgnorer.java @@ -1,28 +1,27 @@ /******************************************************************************* - * Copyright (c) 2015 Jeff Martin. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html - *

- * Contributors: - * Jeff Martin - initial API and implementation - ******************************************************************************/ +* Copyright (c) 2015 Jeff Martin. +* All rights reserved. This program and the accompanying materials +* are made available under the terms of the GNU Lesser General Public +* License v3.0 which accompanies this distribution, and is available at +* http://www.gnu.org/licenses/lgpl.html +* +*

Contributors: +* Jeff Martin - initial API and implementation +******************************************************************************/ package cuchaz.enigma.gui; public class ExceptionIgnorer { - public static boolean shouldIgnore(Throwable t) { - // is this that pesky concurrent access bug in the highlight painter system? // (ancient ui code is ancient) if (t instanceof ArrayIndexOutOfBoundsException) { StackTraceElement[] stackTrace = t.getStackTrace(); - if (stackTrace.length > 1) { + if (stackTrace.length > 1) { // does this stack frame match javax.swing.text.DefaultHighlighter.paint*() ? StackTraceElement frame = stackTrace[1]; + if (frame.getClassName().equals("javax.swing.text.DefaultHighlighter") && frame.getMethodName().startsWith("paint")) { return true; } @@ -31,5 +30,4 @@ public class ExceptionIgnorer { return false; } - } 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 5a1e3d8b..b3117ce2 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/Gui.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/Gui.java @@ -1,13 +1,13 @@ /******************************************************************************* - * Copyright (c) 2015 Jeff Martin. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html - *

- * Contributors: - * Jeff Martin - initial API and implementation - ******************************************************************************/ +* Copyright (c) 2015 Jeff Martin. +* All rights reserved. This program and the accompanying materials +* are made available under the terms of the GNU Lesser General Public +* License v3.0 which accompanies this distribution, and is available at +* http://www.gnu.org/licenses/lgpl.html +* +*

Contributors: +* Jeff Martin - initial API and implementation +******************************************************************************/ package cuchaz.enigma.gui; @@ -17,7 +17,6 @@ import java.awt.Point; import java.awt.event.ActionEvent; import java.nio.file.Path; import java.util.Collection; -import java.util.Collections; import java.util.List; import java.util.Set; import java.util.concurrent.CompletableFuture; @@ -25,12 +24,23 @@ import java.util.function.Consumer; import java.util.function.Function; import javax.annotation.Nullable; -import javax.swing.*; +import javax.swing.AbstractAction; +import javax.swing.DefaultListModel; +import javax.swing.JButton; +import javax.swing.JFileChooser; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JList; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JScrollBar; +import javax.swing.JScrollPane; +import javax.swing.JSplitPane; +import javax.swing.JTabbedPane; +import javax.swing.JTextField; +import javax.swing.SwingUtilities; +import javax.swing.WindowConstants; import javax.swing.tree.DefaultMutableTreeNode; -import javax.swing.tree.TreeNode; -import javax.swing.tree.TreePath; - -import com.google.common.collect.Lists; import cuchaz.enigma.Enigma; import cuchaz.enigma.EnigmaProfile; @@ -39,8 +49,19 @@ import cuchaz.enigma.gui.config.Themes; import cuchaz.enigma.gui.config.UiConfig; import cuchaz.enigma.gui.dialog.JavadocDialog; import cuchaz.enigma.gui.dialog.SearchDialog; -import cuchaz.enigma.gui.elements.*; -import cuchaz.enigma.gui.panels.*; +import cuchaz.enigma.gui.elements.CallsTree; +import cuchaz.enigma.gui.elements.CollapsibleTabbedPane; +import cuchaz.enigma.gui.elements.EditorTabbedPane; +import cuchaz.enigma.gui.elements.ImplementationsTree; +import cuchaz.enigma.gui.elements.InheritanceTree; +import cuchaz.enigma.gui.elements.MainWindow; +import cuchaz.enigma.gui.elements.MenuBar; +import cuchaz.enigma.gui.elements.ValidatableUi; +import cuchaz.enigma.gui.panels.DeobfPanel; +import cuchaz.enigma.gui.panels.EditorPanel; +import cuchaz.enigma.gui.panels.IdentifierPanel; +import cuchaz.enigma.gui.panels.ObfPanel; +import cuchaz.enigma.gui.panels.StructurePanel; import cuchaz.enigma.gui.renderer.MessageListCellRenderer; import cuchaz.enigma.gui.util.GuiUtil; import cuchaz.enigma.gui.util.LanguageUtil; @@ -57,7 +78,6 @@ import cuchaz.enigma.utils.validation.ParameterizedMessage; import cuchaz.enigma.utils.validation.ValidationContext; public class Gui { - private final MainWindow mainWindow = new MainWindow(Enigma.NAME); private final GuiController controller; @@ -179,6 +199,7 @@ public class Gui { // restore state int[] layout = UiConfig.getLayout(); + if (layout.length >= 4) { this.splitClasses.setDividerLocation(layout[0]); this.splitCenter.setDividerLocation(layout[1]); @@ -200,6 +221,7 @@ public class Gui { frame.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE); Point windowPos = UiConfig.getWindowPos("Main Window", null); + if (windowPos != null) { frame.setLocation(windowPos); } else { @@ -327,18 +349,26 @@ public class Gui { public void startDocChange(EditorPanel editor) { EntryReference, Entry> cursorReference = editor.getCursorReference(); - if (cursorReference == null || !this.isEditable(EditableType.JAVADOC)) return; + + if (cursorReference == null || !this.isEditable(EditableType.JAVADOC)) { + return; + } + JavadocDialog.show(mainWindow.frame(), getController(), cursorReference); } public void startRename(EditorPanel editor, String text) { - if (editor != this.editorTabbedPane.getActiveEditor()) return; + if (editor != this.editorTabbedPane.getActiveEditor()) { + return; + } infoPanel.startRenaming(text); } public void startRename(EditorPanel editor) { - if (editor != this.editorTabbedPane.getActiveEditor()) return; + if (editor != this.editorTabbedPane.getActiveEditor()) { + return; + } infoPanel.startRenaming(); } @@ -349,7 +379,10 @@ public class Gui { public void showInheritance(EditorPanel editor) { EntryReference, Entry> cursorReference = editor.getCursorReference(); - if (cursorReference == null) return; + + if (cursorReference == null) { + return; + } this.inheritanceTree.display(cursorReference.entry); tabs.setSelectedIndex(1); @@ -357,7 +390,10 @@ public class Gui { public void showImplementations(EditorPanel editor) { EntryReference, Entry> cursorReference = editor.getCursorReference(); - if (cursorReference == null) return; + + if (cursorReference == null) { + return; + } this.implementationsTree.display(cursorReference.entry); tabs.setSelectedIndex(2); @@ -365,7 +401,10 @@ public class Gui { public void showCalls(EditorPanel editor, boolean recurse) { EntryReference, Entry> cursorReference = editor.getCursorReference(); - if (cursorReference == null) return; + + if (cursorReference == null) { + return; + } this.callsTree.showCalls(cursorReference.entry, recurse); tabs.setSelectedIndex(3); @@ -373,7 +412,10 @@ public class Gui { public void toggleMapping(EditorPanel editor) { EntryReference, Entry> cursorReference = editor.getCursorReference(); - if (cursorReference == null) return; + + if (cursorReference == null) { + return; + } Entry obfEntry = cursorReference.entry; toggleMappingFromEntry(obfEntry); @@ -388,14 +430,15 @@ public class Gui { } public void showDiscardDiag(Function callback, String... options) { - int response = JOptionPane.showOptionDialog(this.mainWindow.frame(), I18n.translate("prompt.close.summary"), I18n.translate("prompt.close.title"), JOptionPane.YES_NO_CANCEL_OPTION, - JOptionPane.QUESTION_MESSAGE, null, options, options[2]); + int response = JOptionPane.showOptionDialog(this.mainWindow.frame(), I18n.translate("prompt.close.summary"), I18n.translate("prompt.close.title"), JOptionPane.YES_NO_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE, null, options, options[2]); callback.apply(response); } public CompletableFuture saveMapping() { - if (this.enigmaMappingsFileChooser.getSelectedFile() != null || this.enigmaMappingsFileChooser.showSaveDialog(this.mainWindow.frame()) == JFileChooser.APPROVE_OPTION) + if (this.enigmaMappingsFileChooser.getSelectedFile() != null || this.enigmaMappingsFileChooser.showSaveDialog(this.mainWindow.frame()) == JFileChooser.APPROVE_OPTION) { return this.controller.saveMappings(this.enigmaMappingsFileChooser.getSelectedFile().toPath()); + } + return CompletableFuture.completedFuture(null); } @@ -421,16 +464,13 @@ public class Gui { private void exit() { UiConfig.setWindowPos("Main Window", this.mainWindow.frame().getLocationOnScreen()); UiConfig.setWindowSize("Main Window", this.mainWindow.frame().getSize()); - UiConfig.setLayout( - this.splitClasses.getDividerLocation(), - this.splitCenter.getDividerLocation(), - this.splitRight.getDividerLocation(), - this.logSplit.getDividerLocation()); + UiConfig.setLayout(this.splitClasses.getDividerLocation(), this.splitCenter.getDividerLocation(), this.splitRight.getDividerLocation(), this.logSplit.getDividerLocation()); UiConfig.save(); if (searchDialog != null) { searchDialog.dispose(); } + this.mainWindow.frame().dispose(); System.exit(0); } @@ -452,6 +492,7 @@ public class Gui { onRenameFromClassTree(vc, prevDataChild, dataChild, node); } + node.setUserObject(data); // Ob package will never be modified, just reload deob view this.deobfPanel.deobfClasses.reload(); @@ -462,11 +503,7 @@ public class Gui { // fast enough for now EntryRemapper mapper = this.controller.project.getMapper(); ClassEntry deobf = (ClassEntry) prevData; - ClassEntry obf = mapper.getObfToDeobf().getAllEntries() - .filter(e -> e instanceof ClassEntry) - .map(e -> (ClassEntry) e) - .filter(e -> mapper.deobfuscate(e).equals(deobf)) - .findAny().orElse(deobf); + ClassEntry obf = mapper.getObfToDeobf().getAllEntries().filter(e -> e instanceof ClassEntry).map(e -> (ClassEntry) e).filter(e -> mapper.deobfuscate(e).equals(deobf)).findAny().orElse(deobf); this.controller.applyChange(vc, EntryChange.modify(obf).withDeobfName(((ClassEntry) data).getFullName())); } else { @@ -493,16 +530,12 @@ public class Gui { this.obfPanel.obfClasses.removeEntry(classEntry); this.deobfPanel.deobfClasses.reload(); this.obfPanel.obfClasses.reload(); - } - // Deob -> ob - else if (!isOldOb) { + } else if (!isOldOb) { // Deob -> ob this.obfPanel.obfClasses.moveClassIn(classEntry); this.deobfPanel.deobfClasses.removeEntry(classEntry); this.deobfPanel.deobfClasses.reload(); this.obfPanel.obfClasses.reload(); - } - // Local move - else if (isOldOb) { + } else if (isOldOb) { // Local move this.obfPanel.obfClasses.moveClassIn(classEntry); this.obfPanel.obfClasses.reload(); } else { @@ -526,6 +559,7 @@ public class Gui { if (searchDialog == null) { searchDialog = new SearchDialog(this); } + return searchDialog; } @@ -549,9 +583,11 @@ public class Gui { private void sendMessage() { String text = chatBox.getText().trim(); + if (!text.isEmpty()) { getController().sendPacket(new MessageC2SPacket(text)); } + chatBox.setText(""); } @@ -617,16 +653,17 @@ public class Gui { public boolean validateImmediateAction(Consumer op) { ValidationContext vc = new ValidationContext(); op.accept(vc); + if (!vc.canProceed()) { List messages = vc.getMessages(); String text = ValidatableUi.formatMessages(messages); JOptionPane.showMessageDialog(this.getFrame(), text, String.format("%d message(s)", messages.size()), JOptionPane.ERROR_MESSAGE); } + return vc.canProceed(); } public boolean isEditable(EditableType t) { return this.editableTypes.contains(t); } - } 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 47a854f9..0eb9a167 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/GuiController.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/GuiController.java @@ -1,13 +1,13 @@ /******************************************************************************* - * Copyright (c) 2015 Jeff Martin. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html - *

- * Contributors: - * Jeff Martin - initial API and implementation - ******************************************************************************/ +* Copyright (c) 2015 Jeff Martin. +* All rights reserved. This program and the accompanying materials +* are made available under the terms of the GNU Lesser General Public +* License v3.0 which accompanies this distribution, and is available at +* http://www.gnu.org/licenses/lgpl.html +* +*

Contributors: +* Jeff Martin - initial API and implementation +******************************************************************************/ package cuchaz.enigma.gui; @@ -32,7 +32,17 @@ import com.google.common.collect.Lists; import cuchaz.enigma.Enigma; import cuchaz.enigma.EnigmaProfile; import cuchaz.enigma.EnigmaProject; -import cuchaz.enigma.analysis.*; +import cuchaz.enigma.analysis.ClassImplementationsTreeNode; +import cuchaz.enigma.analysis.ClassInheritanceTreeNode; +import cuchaz.enigma.analysis.ClassReferenceTreeNode; +import cuchaz.enigma.analysis.EntryReference; +import cuchaz.enigma.analysis.FieldReferenceTreeNode; +import cuchaz.enigma.analysis.IndexTreeBuilder; +import cuchaz.enigma.analysis.MethodImplementationsTreeNode; +import cuchaz.enigma.analysis.MethodInheritanceTreeNode; +import cuchaz.enigma.analysis.MethodReferenceTreeNode; +import cuchaz.enigma.analysis.StructureTreeNode; +import cuchaz.enigma.analysis.StructureTreeOptions; import cuchaz.enigma.api.service.ObfuscationTestService; import cuchaz.enigma.classhandle.ClassHandle; import cuchaz.enigma.classhandle.ClassHandleProvider; @@ -44,7 +54,12 @@ import cuchaz.enigma.gui.newabstraction.EntryValidation; import cuchaz.enigma.gui.stats.StatsGenerator; import cuchaz.enigma.gui.stats.StatsMember; import cuchaz.enigma.gui.util.History; -import cuchaz.enigma.network.*; +import cuchaz.enigma.network.ClientPacketHandler; +import cuchaz.enigma.network.EnigmaClient; +import cuchaz.enigma.network.EnigmaServer; +import cuchaz.enigma.network.IntegratedEnigmaServer; +import cuchaz.enigma.network.Message; +import cuchaz.enigma.network.ServerPacketHandler; import cuchaz.enigma.network.packet.EntryChangeC2SPacket; import cuchaz.enigma.network.packet.LoginC2SPacket; import cuchaz.enigma.network.packet.Packet; @@ -54,7 +69,12 @@ import cuchaz.enigma.source.SourceIndex; import cuchaz.enigma.source.Token; import cuchaz.enigma.translation.TranslateResult; import cuchaz.enigma.translation.Translator; -import cuchaz.enigma.translation.mapping.*; +import cuchaz.enigma.translation.mapping.EntryChange; +import cuchaz.enigma.translation.mapping.EntryMapping; +import cuchaz.enigma.translation.mapping.EntryRemapper; +import cuchaz.enigma.translation.mapping.EntryUtil; +import cuchaz.enigma.translation.mapping.MappingDelta; +import cuchaz.enigma.translation.mapping.ResolutionStrategy; import cuchaz.enigma.translation.mapping.serde.MappingFormat; import cuchaz.enigma.translation.mapping.serde.MappingParseException; import cuchaz.enigma.translation.mapping.serde.MappingSaveParameters; @@ -90,9 +110,7 @@ public class GuiController implements ClientPacketHandler { public GuiController(Gui gui, EnigmaProfile profile) { this.gui = gui; - this.enigma = Enigma.builder() - .setProfile(profile) - .build(); + this.enigma = Enigma.builder().setProfile(profile).build(); } public boolean isDirty() { @@ -121,7 +139,9 @@ public class GuiController implements ClientPacketHandler { } public CompletableFuture openMappings(MappingFormat format, Path path) { - if (project == null) return CompletableFuture.completedFuture(null); + if (project == null) { + return CompletableFuture.completedFuture(null); + } gui.setMappingsFile(path); @@ -145,7 +165,9 @@ public class GuiController implements ClientPacketHandler { @Override public void openMappings(EntryTree mappings) { - if (project == null) return; + if (project == null) { + return; + } project.setMappings(mappings); refreshClasses(); @@ -168,7 +190,9 @@ public class GuiController implements ClientPacketHandler { * @return the future of saving */ public CompletableFuture saveMappings(Path path, MappingFormat format) { - if (project == null) return CompletableFuture.completedFuture(null); + if (project == null) { + return CompletableFuture.completedFuture(null); + } return ProgressDialog.runOffThread(this.gui.getFrame(), progress -> { EntryRemapper mapper = project.getMapper(); @@ -189,7 +213,9 @@ public class GuiController implements ClientPacketHandler { } public void closeMappings() { - if (project == null) return; + if (project == null) { + return; + } project.setMappings(null); @@ -202,9 +228,11 @@ public class GuiController implements ClientPacketHandler { Path jarPath = this.project.getJarPath(); MappingFormat loadedMappingFormat = this.loadedMappingFormat; Path loadedMappingPath = this.loadedMappingPath; + if (jarPath != null) { this.closeJar(); CompletableFuture f = this.openJar(jarPath); + if (loadedMappingFormat != null && loadedMappingPath != null) { f.whenComplete((v, t) -> this.openMappings(loadedMappingFormat, loadedMappingPath)); } @@ -214,6 +242,7 @@ public class GuiController implements ClientPacketHandler { public void reloadMappings() { MappingFormat loadedMappingFormat = this.loadedMappingFormat; Path loadedMappingPath = this.loadedMappingPath; + if (loadedMappingFormat != null && loadedMappingPath != null) { this.closeMappings(); this.openMappings(loadedMappingFormat, loadedMappingPath); @@ -221,29 +250,34 @@ public class GuiController implements ClientPacketHandler { } public CompletableFuture dropMappings() { - if (project == null) return CompletableFuture.completedFuture(null); + if (project == null) { + return CompletableFuture.completedFuture(null); + } return ProgressDialog.runOffThread(this.gui.getFrame(), progress -> project.dropMappings(progress)); } public CompletableFuture exportSource(final Path path) { - if (project == null) return CompletableFuture.completedFuture(null); + if (project == null) { + return CompletableFuture.completedFuture(null); + } return ProgressDialog.runOffThread(this.gui.getFrame(), progress -> { EnigmaProject.JarExport jar = project.exportRemappedJar(progress); - jar.decompileStream(progress, chp.getDecompilerService(), EnigmaProject.DecompileErrorStrategy.TRACE_AS_SOURCE) - .forEach(source -> { - try { - source.writeTo(source.resolvePath(path)); - } catch (IOException e) { - e.printStackTrace(); - } - }); + jar.decompileStream(progress, chp.getDecompilerService(), EnigmaProject.DecompileErrorStrategy.TRACE_AS_SOURCE).forEach(source -> { + try { + source.writeTo(source.resolvePath(path)); + } catch (IOException e) { + e.printStackTrace(); + } + }); }); } public CompletableFuture exportJar(final Path path) { - if (project == null) return CompletableFuture.completedFuture(null); + if (project == null) { + return CompletableFuture.completedFuture(null); + } return ProgressDialog.runOffThread(this.gui.getFrame(), progress -> { EnigmaProject.JarExport jar = project.exportRemappedJar(progress); @@ -269,20 +303,14 @@ public class GuiController implements ClientPacketHandler { } try { - return tokenHandle.getSource().get() - .map(DecompiledClassSource::getIndex) - .map(index -> new ReadableToken( - index.getLineNumber(token.start), - index.getColumnNumber(token.start), - index.getColumnNumber(token.end))) - .unwrapOr(null); + return tokenHandle.getSource().get().map(DecompiledClassSource::getIndex).map(index -> new ReadableToken(index.getLineNumber(token.start), index.getColumnNumber(token.start), index.getColumnNumber(token.end))).unwrapOr(null); } catch (InterruptedException | ExecutionException e) { throw new RuntimeException(e); } } /** - * Navigates to the declaration with respect to navigation history + * Navigates to the declaration with respect to navigation history. * * @param entry the entry whose declaration will be navigated to */ @@ -290,11 +318,12 @@ public class GuiController implements ClientPacketHandler { if (entry == null) { throw new IllegalArgumentException("Entry cannot be null!"); } + openReference(EntryReference.declaration(entry, entry.getName())); } /** - * Navigates to the reference with respect to navigation history + * Navigates to the reference with respect to navigation history. * * @param reference the reference */ @@ -302,6 +331,7 @@ public class GuiController implements ClientPacketHandler { if (reference == null) { throw new IllegalArgumentException("Reference cannot be null!"); } + if (this.referenceHistory == null) { this.referenceHistory = new History<>(reference); } else { @@ -317,11 +347,7 @@ public class GuiController implements ClientPacketHandler { EntryRemapper mapper = this.project.getMapper(); SourceIndex index = source.getIndex(); - return mapper.getObfResolver().resolveReference(reference, ResolutionStrategy.RESOLVE_CLOSEST) - .stream() - .flatMap(r -> index.getReferenceTokens(r).stream()) - .sorted() - .toList(); + return mapper.getObfResolver().resolveReference(reference, ResolutionStrategy.RESOLVE_CLOSEST).stream().flatMap(r -> index.getReferenceTokens(r).stream()).sorted().toList(); } public void openPreviousReference() { @@ -349,6 +375,7 @@ public class GuiController implements ClientPacketHandler { // entry is not in the jar. Ignore it return; } + openDeclaration(entry); } @@ -356,12 +383,15 @@ public class GuiController implements ClientPacketHandler { if (!project.isRenamable(reference.getLocationClassEntry())) { return; } + openReference(reference); } public void refreshClasses() { - if (project == null) return; - + if (project == null) { + return; + } + List obfClasses = Lists.newArrayList(); List deobfClasses = Lists.newArrayList(); this.addSeparatedClasses(obfClasses, deobfClasses); @@ -373,8 +403,7 @@ public class GuiController implements ClientPacketHandler { EntryRemapper mapper = project.getMapper(); Collection classes = project.getJarIndex().getEntryIndex().getClasses(); - Stream visibleClasses = classes.stream() - .filter(entry -> !entry.isInnerClass()); + Stream visibleClasses = classes.stream().filter(entry -> !entry.isInnerClass()); visibleClasses.forEach(entry -> { if (gui.isSingleClassTree()) { @@ -428,12 +457,15 @@ public class GuiController implements ClientPacketHandler { public MethodImplementationsTreeNode getMethodImplementations(MethodEntry entry) { Translator translator = project.getMapper().getDeobfuscator(); List rootNodes = indexTreeBuilder.buildMethodImplementations(translator, entry); + if (rootNodes.isEmpty()) { return null; } + if (rootNodes.size() > 1) { System.err.println("WARNING: Method " + entry + " implements multiple interfaces. Only showing first one."); } + return MethodImplementationsTreeNode.findNode(rootNodes.get(0), entry); } @@ -481,13 +513,20 @@ public class GuiController implements ClientPacketHandler { public void applyChange(ValidationContext vc, EntryChange change) { this.applyChange0(vc, change); gui.showStructure(gui.getActiveEditor()); - if (!vc.canProceed()) return; + + if (!vc.canProceed()) { + return; + } + this.sendPacket(new EntryChangeC2SPacket(change)); } private void applyChange0(ValidationContext vc, EntryChange change) { validateChange(vc, change); - if (!vc.canProceed()) return; + + if (!vc.canProceed()) { + return; + } Entry target = change.getTarget(); EntryMapping prev = this.project.getMapper().getDeobfMapping(target); @@ -506,6 +545,7 @@ public class GuiController implements ClientPacketHandler { if (!Objects.equals(prev.javadoc(), mapping.javadoc())) { this.chp.invalidateJavadoc(target.getTopLevelClass()); } + gui.showStructure(gui.getActiveEditor()); } @@ -517,10 +557,7 @@ public class GuiController implements ClientPacketHandler { File statsFile = File.createTempFile("stats", ".html"); try (FileWriter w = new FileWriter(statsFile)) { - w.write( - Utils.readResourceToString("/stats.html") - .replace("/*data*/", data) - ); + w.write(Utils.readResourceToString("/stats.html").replace("/*data*/", data)); } Desktop.getDesktop().open(statsFile); @@ -573,15 +610,18 @@ public class GuiController implements ClientPacketHandler { if (client != null) { client.disconnect(); } + if (server != null) { server.stop(); } + client = null; server = null; SwingUtilities.invokeLater(() -> { if (reason != null) { JOptionPane.showMessageDialog(gui.getFrame(), I18n.translate(reason), I18n.translate("disconnect.disconnected"), JOptionPane.INFORMATION_MESSAGE); } + gui.setConnectionState(ConnectionState.NOT_CONNECTED); }); } @@ -602,5 +642,4 @@ public class GuiController implements ClientPacketHandler { public void updateUserList(List users) { gui.setUserList(users); } - } 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 1172a393..56f43859 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/Main.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/Main.java @@ -1,13 +1,13 @@ /******************************************************************************* - * Copyright (c) 2015 Jeff Martin. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html - *

- * Contributors: - * Jeff Martin - initial API and implementation - ******************************************************************************/ +* Copyright (c) 2015 Jeff Martin. +* All rights reserved. This program and the accompanying materials +* are made available under the terms of the GNU Lesser General Public +* License v3.0 which accompanies this distribution, and is available at +* http://www.gnu.org/licenses/lgpl.html +* +*

Contributors: +* Jeff Martin - initial API and implementation +******************************************************************************/ package cuchaz.enigma.gui; @@ -20,7 +20,11 @@ import java.util.List; import java.util.Set; import com.google.common.io.MoreFiles; -import joptsimple.*; +import joptsimple.OptionException; +import joptsimple.OptionParser; +import joptsimple.OptionSet; +import joptsimple.OptionSpec; +import joptsimple.ValueConverter; import cuchaz.enigma.EnigmaProfile; import cuchaz.enigma.gui.config.Themes; @@ -30,21 +34,14 @@ import cuchaz.enigma.translation.mapping.serde.MappingFormat; import cuchaz.enigma.utils.I18n; public class Main { - public static void main(String[] args) throws IOException { OptionParser parser = new OptionParser(); - OptionSpec jar = parser.accepts("jar", "Jar file to open at startup") - .withRequiredArg() - .withValuesConvertedBy(PathConverter.INSTANCE); + OptionSpec jar = parser.accepts("jar", "Jar file to open at startup").withRequiredArg().withValuesConvertedBy(PathConverter.INSTANCE); - OptionSpec mappings = parser.accepts("mappings", "Mappings file to open at startup") - .withRequiredArg() - .withValuesConvertedBy(PathConverter.INSTANCE); + OptionSpec mappings = parser.accepts("mappings", "Mappings file to open at startup").withRequiredArg().withValuesConvertedBy(PathConverter.INSTANCE); - OptionSpec profile = parser.accepts("profile", "Profile json to apply at startup") - .withRequiredArg() - .withValuesConvertedBy(PathConverter.INSTANCE); + OptionSpec profile = parser.accepts("profile", "Profile json to apply at startup").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"); @@ -78,26 +75,26 @@ public class Main { 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); + 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); } } } @@ -110,7 +107,7 @@ public class Main { Gui gui = new Gui(parsedProfile, editables); GuiController controller = gui.getController(); - + if (options.has("single-class-tree")) { gui.setSingleClassTree(true); } @@ -120,6 +117,7 @@ public class Main { CrashDialog.init(gui.getFrame()); Thread.setDefaultUncaughtExceptionHandler((thread, t) -> { t.printStackTrace(System.err); + if (!ExceptionIgnorer.shouldIgnore(t)) { CrashDialog.show(t); } @@ -128,19 +126,19 @@ public class Main { if (options.has(jar)) { Path jarPath = options.valueOf(jar); - controller.openJar(jarPath) - .whenComplete((v, t) -> { - if (options.has(mappings)) { - Path mappingsPath = options.valueOf(mappings); - if (Files.isDirectory(mappingsPath)) { - controller.openMappings(MappingFormat.ENIGMA_DIRECTORY, mappingsPath); - } else if ("zip".equalsIgnoreCase(MoreFiles.getFileExtension(mappingsPath))) { - controller.openMappings(MappingFormat.ENIGMA_ZIP, mappingsPath); - } else { - controller.openMappings(MappingFormat.ENIGMA_FILE, mappingsPath); - } - } - }); + controller.openJar(jarPath).whenComplete((v, t) -> { + if (options.has(mappings)) { + Path mappingsPath = options.valueOf(mappings); + + if (Files.isDirectory(mappingsPath)) { + controller.openMappings(MappingFormat.ENIGMA_DIRECTORY, mappingsPath); + } else if ("zip".equalsIgnoreCase(MoreFiles.getFileExtension(mappingsPath))) { + controller.openMappings(MappingFormat.ENIGMA_ZIP, mappingsPath); + } else { + controller.openMappings(MappingFormat.ENIGMA_FILE, mappingsPath); + } + } + }); } } catch (OptionException e) { System.out.println("Invalid arguments: " + e.getMessage()); diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/NestedPackages.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/NestedPackages.java index 309f9106..c4541fc6 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/NestedPackages.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/NestedPackages.java @@ -1,21 +1,22 @@ package cuchaz.enigma.gui; -import cuchaz.enigma.gui.node.ClassSelectorClassNode; -import cuchaz.enigma.gui.node.ClassSelectorPackageNode; -import cuchaz.enigma.translation.mapping.EntryRemapper; -import cuchaz.enigma.translation.representation.entry.ClassEntry; +import java.util.Collection; +import java.util.Comparator; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.Map; import javax.swing.tree.DefaultMutableTreeNode; import javax.swing.tree.MutableTreeNode; import javax.swing.tree.TreeNode; import javax.swing.tree.TreePath; -import java.util.Collection; -import java.util.Comparator; -import java.util.HashMap; -import java.util.Map; -public class NestedPackages { +import cuchaz.enigma.gui.node.ClassSelectorClassNode; +import cuchaz.enigma.gui.node.ClassSelectorPackageNode; +import cuchaz.enigma.translation.mapping.EntryRemapper; +import cuchaz.enigma.translation.representation.entry.ClassEntry; +public class NestedPackages { private final DefaultMutableTreeNode root = new DefaultMutableTreeNode(); private final Map packageToNode = new HashMap<>(); private final Map classToNode = new HashMap<>(); @@ -42,20 +43,20 @@ public class NestedPackages { return 0; }; - for (var entry : entries) { + for (ClassEntry entry : entries) { addEntry(entry); } } public void addEntry(ClassEntry entry) { - var translated = remapper.deobfuscate(entry); + ClassEntry translated = remapper.deobfuscate(entry); var me = new ClassSelectorClassNode(entry, translated); classToNode.put(entry, me); insert(getPackage(translated.getPackageName()), me); } public DefaultMutableTreeNode getPackage(String packageName) { - var node = packageToNode.get(packageName); + DefaultMutableTreeNode node = packageToNode.get(packageName); if (packageName == null) { return root; @@ -75,7 +76,7 @@ public class NestedPackages { } public TreePath getPackagePath(String packageName) { - var node = packageToNode.getOrDefault(packageName, root); + DefaultMutableTreeNode node = packageToNode.getOrDefault(packageName, root); return new TreePath(node.getPath()); } @@ -84,15 +85,15 @@ public class NestedPackages { } public void removeClassNode(ClassEntry entry) { - var node = classToNode.remove(entry); + ClassSelectorClassNode node = classToNode.remove(entry); if (node != null) { node.removeFromParent(); // remove dangling packages - var packageNode = packageToNode.get(entry.getPackageName()); + DefaultMutableTreeNode packageNode = packageToNode.get(entry.getPackageName()); while (packageNode != null && packageNode.getChildCount() == 0) { - var theNode = packageNode; + DefaultMutableTreeNode theNode = packageNode; packageNode = (DefaultMutableTreeNode) packageNode.getParent(); theNode.removeFromParent(); @@ -108,8 +109,8 @@ public class NestedPackages { } private void insert(DefaultMutableTreeNode parent, MutableTreeNode child) { - var index = 0; - var children = parent.children(); + int index = 0; + Enumeration children = parent.children(); while (children.hasMoreElements()) { if (comparator.compare(children.nextElement(), child) < 0) { diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/QuickFindAction.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/QuickFindAction.java index b7fa2eba..ff80e17f 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/QuickFindAction.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/QuickFindAction.java @@ -1,10 +1,11 @@ package cuchaz.enigma.gui; -import de.sciss.syntaxpane.SyntaxDocument; -import de.sciss.syntaxpane.actions.DefaultSyntaxAction; +import java.awt.event.ActionEvent; import javax.swing.text.JTextComponent; -import java.awt.event.ActionEvent; + +import de.sciss.syntaxpane.SyntaxDocument; +import de.sciss.syntaxpane.actions.DefaultSyntaxAction; public final class QuickFindAction extends DefaultSyntaxAction { public QuickFindAction() { @@ -26,6 +27,7 @@ public final class QuickFindAction extends DefaultSyntaxAction { public static Data get(JTextComponent target) { Object o = target.getDocument().getProperty(KEY); + if (o instanceof Data) { return (Data) o; } @@ -39,6 +41,7 @@ public final class QuickFindAction extends DefaultSyntaxAction { if (findDialog == null) { findDialog = new EnigmaQuickFindDialog(target); } + findDialog.showFor(target); } } diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/ReadableToken.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/ReadableToken.java index 3e4b30cd..eac11ed4 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/ReadableToken.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/ReadableToken.java @@ -1,18 +1,17 @@ /******************************************************************************* - * Copyright (c) 2015 Jeff Martin. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html - *

- * Contributors: - * Jeff Martin - initial API and implementation - ******************************************************************************/ +* Copyright (c) 2015 Jeff Martin. +* All rights reserved. This program and the accompanying materials +* are made available under the terms of the GNU Lesser General Public +* License v3.0 which accompanies this distribution, and is available at +* http://www.gnu.org/licenses/lgpl.html +* +*

Contributors: +* Jeff Martin - initial API and implementation +******************************************************************************/ package cuchaz.enigma.gui; public class ReadableToken { - public int line; public int startColumn; public int endColumn; diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/TokenListCellRenderer.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/TokenListCellRenderer.java index 4ef04428..000793e6 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/TokenListCellRenderer.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/TokenListCellRenderer.java @@ -1,23 +1,26 @@ /******************************************************************************* - * Copyright (c) 2015 Jeff Martin. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html - *

- * Contributors: - * Jeff Martin - initial API and implementation - ******************************************************************************/ +* Copyright (c) 2015 Jeff Martin. +* All rights reserved. This program and the accompanying materials +* are made available under the terms of the GNU Lesser General Public +* License v3.0 which accompanies this distribution, and is available at +* http://www.gnu.org/licenses/lgpl.html +* +*

Contributors: +* Jeff Martin - initial API and implementation +******************************************************************************/ package cuchaz.enigma.gui; -import cuchaz.enigma.source.Token; +import java.awt.Component; -import javax.swing.*; -import java.awt.*; +import javax.swing.DefaultListCellRenderer; +import javax.swing.JLabel; +import javax.swing.JList; +import javax.swing.ListCellRenderer; -public class TokenListCellRenderer implements ListCellRenderer { +import cuchaz.enigma.source.Token; +public class TokenListCellRenderer implements ListCellRenderer { private GuiController controller; private DefaultListCellRenderer defaultRenderer; @@ -32,5 +35,4 @@ public class TokenListCellRenderer implements ListCellRenderer { label.setText(this.controller.getReadableToken(token).toString()); return label; } - } diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/config/LookAndFeel.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/config/LookAndFeel.java index cec3fa1e..2088aac2 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/config/LookAndFeel.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/config/LookAndFeel.java @@ -40,11 +40,11 @@ public enum LookAndFeel { try { switch (this) { - case NONE -> UIManager.setLookAndFeel(NONE_LAF); - case DEFAULT -> UIManager.setLookAndFeel(new FlatLightLaf()); - case METAL -> UIManager.setLookAndFeel(new MetalLookAndFeel()); - case DARCULA -> UIManager.setLookAndFeel(new FlatDarkLaf()); - case SYSTEM -> UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); + case NONE -> UIManager.setLookAndFeel(NONE_LAF); + case DEFAULT -> UIManager.setLookAndFeel(new FlatLightLaf()); + case METAL -> UIManager.setLookAndFeel(new MetalLookAndFeel()); + case DARCULA -> UIManager.setLookAndFeel(new FlatDarkLaf()); + case SYSTEM -> UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); } } catch (Exception e) { throw new Error("Failed to set global look and feel", e); @@ -66,5 +66,4 @@ public enum LookAndFeel { int b = (int) (0.3 * c.getRed() + 0.59 * c.getGreen() + 0.11 * c.getBlue()); return b < 85; } - } diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/config/NetConfig.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/config/NetConfig.java index 4439cb8a..eaf20e7f 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/config/NetConfig.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/config/NetConfig.java @@ -4,7 +4,6 @@ import cuchaz.enigma.config.ConfigContainer; import cuchaz.enigma.network.EnigmaServer; public final class NetConfig { - private NetConfig() { } @@ -53,5 +52,4 @@ public final class NetConfig { public static void setServerPort(int port) { cfg.data().section("Server").setInt("Port", port); } - } diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/config/OldConfigImporter.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/config/OldConfigImporter.java index 660d2313..2e84991b 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/config/OldConfigImporter.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/config/OldConfigImporter.java @@ -5,7 +5,6 @@ import java.awt.Font; import cuchaz.enigma.gui.config.legacy.Config; public final class OldConfigImporter { - private OldConfigImporter() { } @@ -13,14 +12,15 @@ public final class OldConfigImporter { public static void doImport() { if (Config.CONFIG_FILE.exists()) { Config config = new Config(); + if (config.editorFont != null) { UiConfig.setEditorFont(Font.decode(config.editorFont)); } + UiConfig.setLanguage(config.language); UiConfig.setLookAndFeel(config.lookAndFeel); UiConfig.setScaleFactor(config.scaleFactor); UiConfig.setDecompiler(config.decompiler); } } - } diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/config/Themes.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/config/Themes.java index 839a5cbc..e2db9682 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/config/Themes.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/config/Themes.java @@ -16,7 +16,6 @@ import cuchaz.enigma.gui.util.ScaleUtil; import cuchaz.enigma.source.RenamableTokenType; public class Themes { - private static final Set listeners = new HashSet<>(); // Calling this after the UI is initialized (e.g. when the user changes @@ -87,11 +86,8 @@ public class Themes { } public static ImmutableMap getBoxHighlightPainters() { - return ImmutableMap.of( - RenamableTokenType.OBFUSCATED, BoxHighlightPainter.create(UiConfig.getObfuscatedColor(), UiConfig.getObfuscatedOutlineColor()), - RenamableTokenType.PROPOSED, BoxHighlightPainter.create(UiConfig.getProposedColor(), UiConfig.getProposedOutlineColor()), - RenamableTokenType.DEOBFUSCATED, BoxHighlightPainter.create(UiConfig.getDeobfuscatedColor(), UiConfig.getDeobfuscatedOutlineColor()) - ); + return ImmutableMap.of(RenamableTokenType.OBFUSCATED, BoxHighlightPainter.create(UiConfig.getObfuscatedColor(), UiConfig.getObfuscatedOutlineColor()), RenamableTokenType.PROPOSED, BoxHighlightPainter.create(UiConfig.getProposedColor(), UiConfig.getProposedOutlineColor()), + RenamableTokenType.DEOBFUSCATED, BoxHighlightPainter.create(UiConfig.getDeobfuscatedColor(), UiConfig.getDeobfuscatedOutlineColor())); } public static void addListener(ThemeChangeListener listener) { @@ -101,5 +97,4 @@ public class Themes { public static void removeListener(ThemeChangeListener listener) { listeners.remove(listener); } - } diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/config/UiConfig.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/config/UiConfig.java index 8a10acec..cdf27cac 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/config/UiConfig.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/config/UiConfig.java @@ -1,6 +1,10 @@ package cuchaz.enigma.gui.config; -import java.awt.*; +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Font; +import java.awt.Point; +import java.awt.Toolkit; import java.util.Optional; import java.util.OptionalInt; @@ -10,7 +14,6 @@ import cuchaz.enigma.gui.util.ScaleUtil; import cuchaz.enigma.utils.I18n; public final class UiConfig { - private UiConfig() { } @@ -82,11 +85,11 @@ public final class UiConfig { * @return an integer array composed of these 4 dimensions */ public static int[] getLayout() { - return swing.data().section("Main Window").getIntArray("Layout").orElseGet(() -> new int[] { -1, -1, -1, -1 }); + return swing.data().section("Main Window").getIntArray("Layout").orElseGet(() -> new int[]{-1, -1, -1, -1}); } public static void setLayout(int leftV, int left, int right, int rightH) { - swing.data().section("Main Window").setIntArray("Layout", new int[] { leftV, left, right, rightH }); + swing.data().section("Main Window").setIntArray("Layout", new int[]{leftV, left, right, rightH}); } public static LookAndFeel getLookAndFeel() { @@ -287,6 +290,7 @@ public final class UiConfig { ConfigSection section = swing.data().section(window); OptionalInt width = section.getInt(String.format("Width %s", screenSize.width)); OptionalInt height = section.getInt(String.format("Height %s", screenSize.height)); + if (width.isPresent() && height.isPresent()) { return new Dimension(width.getAsInt(), height.getAsInt()); } else { @@ -306,6 +310,7 @@ public final class UiConfig { ConfigSection section = swing.data().section(window); OptionalInt x = section.getInt(String.format("X %s", screenSize.width)); OptionalInt y = section.getInt(String.format("Y %s", screenSize.height)); + if (x.isPresent() && y.isPresent()) { int ix = x.getAsInt(); int iy = y.getAsInt(); @@ -354,6 +359,7 @@ public final class UiConfig { public static void setLookAndFeelDefaults(LookAndFeel laf, boolean isDark) { ConfigSection s = swing.data().section("Themes").section(laf.name()).section("Colors"); + if (!isDark) { // Defaults found here: https://github.com/Sciss/SyntaxPane/blob/122da367ff7a5d31627a70c62a48a9f0f4f85a0a/src/main/resources/de/sciss/syntaxpane/defaultsyntaxkit/config.properties#L139 s.setIfAbsentRgbColor("Line Numbers Foreground", 0x333300); @@ -412,5 +418,4 @@ public final class UiConfig { s.setIfAbsentRgbColor("Text", 0xF8F8F2); } } - } diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/config/legacy/Config.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/config/legacy/Config.java index 1265750f..0e8f7da2 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/config/legacy/Config.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/config/legacy/Config.java @@ -6,7 +6,15 @@ import java.lang.reflect.Type; import java.nio.charset.Charset; import com.google.common.io.Files; -import com.google.gson.*; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.InstanceCreator; +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonDeserializer; +import com.google.gson.JsonElement; +import com.google.gson.JsonPrimitive; +import com.google.gson.JsonSerializationContext; +import com.google.gson.JsonSerializer; import cuchaz.enigma.gui.config.Decompiler; import cuchaz.enigma.utils.I18n; @@ -28,7 +36,7 @@ public class Config { } Color baseColor = new Color(rgb); - return new Color(baseColor.getRed(), baseColor.getGreen(), baseColor.getBlue(), (int)(255 * alpha)); + return new Color(baseColor.getRed(), baseColor.getGreen(), baseColor.getBlue(), (int) (255 * alpha)); } } @@ -73,12 +81,7 @@ public class Config { public Decompiler decompiler = Decompiler.CFR; public Config() { - gson = new GsonBuilder() - .registerTypeAdapter(Integer.class, new IntSerializer()) - .registerTypeAdapter(Integer.class, new IntDeserializer()) - .registerTypeAdapter(Config.class, (InstanceCreator) type -> this) - .setPrettyPrinting() - .create(); + gson = new GsonBuilder().registerTypeAdapter(Integer.class, new IntSerializer()).registerTypeAdapter(Integer.class, new IntDeserializer()).registerTypeAdapter(Config.class, (InstanceCreator) type -> this).setPrettyPrinting().create(); this.loadConfig(); } @@ -105,5 +108,4 @@ public class Config { return (int) Long.parseLong(json.getAsString().replace("#", ""), 16); } } - } diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/dialog/AboutDialog.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/dialog/AboutDialog.java index f8922e64..c2211209 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/dialog/AboutDialog.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/dialog/AboutDialog.java @@ -1,13 +1,13 @@ /******************************************************************************* - * Copyright (c) 2015 Jeff Martin. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html - *

- * Contributors: - * Jeff Martin - initial API and implementation - ******************************************************************************/ +* Copyright (c) 2015 Jeff Martin. +* All rights reserved. This program and the accompanying materials +* are made available under the terms of the GNU Lesser General Public +* License v3.0 which accompanies this distribution, and is available at +* http://www.gnu.org/licenses/lgpl.html +* +*

Contributors: +* Jeff Martin - initial API and implementation +******************************************************************************/ package cuchaz.enigma.gui.dialog; @@ -15,7 +15,11 @@ import java.awt.Container; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; -import javax.swing.*; +import javax.swing.JButton; +import javax.swing.JDialog; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.WindowConstants; import cuchaz.enigma.Enigma; import cuchaz.enigma.gui.util.GridBagConstraintsBuilder; @@ -23,16 +27,12 @@ import cuchaz.enigma.gui.util.GuiUtil; import cuchaz.enigma.utils.I18n; public class AboutDialog { - public static void show(JFrame parent) { JDialog frame = new JDialog(parent, String.format(I18n.translate("menu.help.about.title"), Enigma.NAME), true); Container pane = frame.getContentPane(); pane.setLayout(new GridBagLayout()); - GridBagConstraintsBuilder cb = GridBagConstraintsBuilder.create() - .insets(2) - .weight(1.0, 0.0) - .anchor(GridBagConstraints.WEST); + GridBagConstraintsBuilder cb = GridBagConstraintsBuilder.create().insets(2).weight(1.0, 0.0).anchor(GridBagConstraints.WEST); JLabel title = new JLabel(Enigma.NAME); title.setFont(title.getFont().deriveFont(title.getFont().getSize2D() * 1.5f)); @@ -52,5 +52,4 @@ public class AboutDialog { frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE); frame.setVisible(true); } - } diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/dialog/AbstractDialog.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/dialog/AbstractDialog.java index c9ca8090..76232c44 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/dialog/AbstractDialog.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/dialog/AbstractDialog.java @@ -1,6 +1,12 @@ package cuchaz.enigma.gui.dialog; -import java.awt.*; +import java.awt.BorderLayout; +import java.awt.Component; +import java.awt.Container; +import java.awt.FlowLayout; +import java.awt.Frame; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; import java.util.List; import javax.swing.JButton; @@ -15,7 +21,6 @@ import cuchaz.enigma.utils.Pair; import cuchaz.enigma.utils.validation.ValidationContext; public abstract class AbstractDialog extends JDialog { - protected final ValidationContext vc = new ValidationContext(); private boolean actionConfirm = false; @@ -38,6 +43,7 @@ public abstract class AbstractDialog extends JDialog { inputContainer.add(label, cb.pos(0, i).weightX(0.0).anchor(GridBagConstraints.LINE_END).fill(GridBagConstraints.NONE).build()); inputContainer.add(component, cb.pos(1, i).weightX(1.0).anchor(GridBagConstraints.LINE_END).fill(GridBagConstraints.HORIZONTAL).build()); } + contentPane.add(inputContainer, BorderLayout.CENTER); Container buttonContainer = new JPanel(new FlowLayout(FlowLayout.RIGHT, ScaleUtil.scale(4), ScaleUtil.scale(4))); JButton connectButton = new JButton(I18n.translate(confirmAction)); @@ -57,6 +63,7 @@ public abstract class AbstractDialog extends JDialog { protected void confirm() { vc.reset(); validateInputs(); + if (vc.canProceed()) { actionConfirm = true; setVisible(false); @@ -74,5 +81,4 @@ public abstract class AbstractDialog extends JDialog { public void validateInputs() { } - } diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/dialog/ChangeDialog.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/dialog/ChangeDialog.java index df65473c..51948b56 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/dialog/ChangeDialog.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/dialog/ChangeDialog.java @@ -14,7 +14,6 @@ import javax.swing.JPanel; import cuchaz.enigma.utils.I18n; public class ChangeDialog { - public static void show(Window parent) { // init frame JDialog frame = new JDialog(parent, I18n.translate("menu.view.change.title"), Dialog.DEFAULT_MODALITY_TYPE); @@ -48,5 +47,4 @@ public class ChangeDialog { frame.setLocationRelativeTo(parent); frame.setVisible(true); } - } diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/dialog/ConnectToServerDialog.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/dialog/ConnectToServerDialog.java index 2486dfe1..1c2bd4c1 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/dialog/ConnectToServerDialog.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/dialog/ConnectToServerDialog.java @@ -20,7 +20,6 @@ import cuchaz.enigma.utils.validation.Message; import cuchaz.enigma.utils.validation.StandardValidation; public class ConnectToServerDialog extends AbstractDialog { - private JTextField usernameField; private ValidatableTextField ipField; private JPasswordField passwordField; @@ -45,16 +44,13 @@ public class ConnectToServerDialog extends AbstractDialog { ipField.addActionListener(event -> confirm()); passwordField.addActionListener(event -> confirm()); - return Arrays.asList( - new Pair<>("prompt.connect.username", usernameField), - new Pair<>("prompt.connect.address", ipField), - new Pair<>("prompt.password", passwordField) - ); + return Arrays.asList(new Pair<>("prompt.connect.username", usernameField), new Pair<>("prompt.connect.address", ipField), new Pair<>("prompt.password", passwordField)); } @Override public void validateInputs() { vc.setActiveElement(ipField); + if (StandardValidation.notBlank(vc, ipField.getText())) { if (ServerAddress.from(ipField.getText(), EnigmaServer.DEFAULT_PORT) == null) { vc.raise(Message.INVALID_IP); @@ -63,16 +59,18 @@ public class ConnectToServerDialog extends AbstractDialog { } public Result getResult() { - if (!isActionConfirm()) return null; + if (!isActionConfirm()) { + return null; + } + vc.reset(); validateInputs(); - if (!vc.canProceed()) return null; - return new Result( - usernameField.getText(), - ipField.getText(), - Objects.requireNonNull(ServerAddress.from(ipField.getText(), EnigmaServer.DEFAULT_PORT)), - passwordField.getPassword() - ); + + if (!vc.canProceed()) { + return null; + } + + return new Result(usernameField.getText(), ipField.getText(), Objects.requireNonNull(ServerAddress.from(ipField.getText(), EnigmaServer.DEFAULT_PORT)), passwordField.getPassword()); } public static Result show(Frame parent) { @@ -114,5 +112,4 @@ public class ConnectToServerDialog extends AbstractDialog { return password; } } - } diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/dialog/CrashDialog.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/dialog/CrashDialog.java index c2a93fa5..a84e9775 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/dialog/CrashDialog.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/dialog/CrashDialog.java @@ -1,31 +1,42 @@ /******************************************************************************* - * Copyright (c) 2015 Jeff Martin. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html - *

- * Contributors: - * Jeff Martin - initial API and implementation - ******************************************************************************/ +* Copyright (c) 2015 Jeff Martin. +* All rights reserved. This program and the accompanying materials +* are made available under the terms of the GNU Lesser General Public +* License v3.0 which accompanies this distribution, and is available at +* http://www.gnu.org/licenses/lgpl.html +* +*

Contributors: +* Jeff Martin - initial API and implementation +******************************************************************************/ package cuchaz.enigma.gui.dialog; +import java.awt.BorderLayout; +import java.awt.Container; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.io.PrintWriter; +import java.io.StringWriter; + +import javax.swing.BorderFactory; +import javax.swing.Box; +import javax.swing.BoxLayout; +import javax.swing.JButton; +import javax.swing.JFileChooser; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JTextArea; +import javax.swing.WindowConstants; + import cuchaz.enigma.Enigma; import cuchaz.enigma.gui.util.GuiUtil; -import cuchaz.enigma.utils.I18n; import cuchaz.enigma.gui.util.ScaleUtil; - -import javax.swing.*; -import java.awt.*; -import java.io.PrintWriter; -import java.io.StringWriter; -import java.io.FileWriter; -import java.io.File; -import java.io.IOException; +import cuchaz.enigma.utils.I18n; public class CrashDialog { - private static CrashDialog instance = null; private JFrame frame; @@ -53,6 +64,7 @@ public class CrashDialog { exportButton.addActionListener(event -> { JFileChooser chooser = new JFileChooser(); chooser.setSelectedFile(new File("enigma_crash.log")); + if (chooser.showSaveDialog(null) == JFileChooser.APPROVE_OPTION) { try { File file = chooser.getSelectedFile(); diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/dialog/CreateServerDialog.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/dialog/CreateServerDialog.java index 07daf6dc..35999e28 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/dialog/CreateServerDialog.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/dialog/CreateServerDialog.java @@ -16,7 +16,6 @@ import cuchaz.enigma.utils.validation.Message; import cuchaz.enigma.utils.validation.StandardValidation; public class CreateServerDialog extends AbstractDialog { - private ValidatableTextField portField; private ValidatablePasswordField passwordField; @@ -38,10 +37,7 @@ public class CreateServerDialog extends AbstractDialog { portField.addActionListener(event -> confirm()); passwordField.addActionListener(event -> confirm()); - return Arrays.asList( - new Pair<>("prompt.create_server.port", portField), - new Pair<>("prompt.password", passwordField) - ); + return Arrays.asList(new Pair<>("prompt.create_server.port", portField), new Pair<>("prompt.password", passwordField)); } @Override @@ -49,20 +45,25 @@ public class CreateServerDialog extends AbstractDialog { vc.setActiveElement(portField); StandardValidation.isIntInRange(vc, portField.getText(), 0, 65535); vc.setActiveElement(passwordField); + if (passwordField.getPassword().length > EnigmaServer.MAX_PASSWORD_LENGTH) { vc.raise(Message.FIELD_LENGTH_OUT_OF_RANGE, EnigmaServer.MAX_PASSWORD_LENGTH); } } public Result getResult() { - if (!isActionConfirm()) return null; + if (!isActionConfirm()) { + return null; + } + vc.reset(); validateInputs(); - if (!vc.canProceed()) return null; - return new Result( - Integer.parseInt(portField.getText()), - passwordField.getPassword() - ); + + if (!vc.canProceed()) { + return null; + } + + return new Result(Integer.parseInt(portField.getText()), passwordField.getPassword()); } public static Result show(Frame parent) { @@ -92,5 +93,4 @@ public class CreateServerDialog extends AbstractDialog { return password; } } - } diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/dialog/FontDialog.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/dialog/FontDialog.java index 4e02a666..f0bae17b 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/dialog/FontDialog.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/dialog/FontDialog.java @@ -1,6 +1,11 @@ package cuchaz.enigma.gui.dialog; -import java.awt.*; +import java.awt.Component; +import java.awt.Container; +import java.awt.Font; +import java.awt.Frame; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; import java.util.List; import javax.swing.JButton; @@ -16,20 +21,9 @@ import cuchaz.enigma.gui.util.ScaleUtil; import cuchaz.enigma.utils.I18n; public class FontDialog extends JDialog { + private static final List CATEGORIES = List.of("Default", "Default 2", "Small", "Editor"); - private static final List CATEGORIES = List.of( - "Default", - "Default 2", - "Small", - "Editor" - ); - - private static final List CATEGORY_TEXTS = List.of( - "fonts.cat.default", - "fonts.cat.default2", - "fonts.cat.small", - "fonts.cat.editor" - ); + private static final List CATEGORY_TEXTS = List.of("fonts.cat.default", "fonts.cat.default2", "fonts.cat.small", "fonts.cat.editor"); private final JList entries = new JList<>(CATEGORY_TEXTS.stream().map(I18n::translate).toArray(String[]::new)); private final FontChooser chooser = new FontChooser(Font.decode(Font.DIALOG)); @@ -55,8 +49,7 @@ public class FontDialog extends JDialog { Container contentPane = this.getContentPane(); contentPane.setLayout(new GridBagLayout()); - GridBagConstraintsBuilder cb = GridBagConstraintsBuilder.create() - .insets(2); + GridBagConstraintsBuilder cb = GridBagConstraintsBuilder.create().insets(2); contentPane.add(this.entries, cb.pos(0, 0).weight(0.0, 1.0).fill(GridBagConstraints.BOTH).build()); contentPane.add(this.chooser, cb.pos(1, 0).weight(1.0, 1.0).fill(GridBagConstraints.BOTH).size(2, 1).build()); @@ -77,6 +70,7 @@ public class FontDialog extends JDialog { private void categoryChanged() { this.updateUiState(); int selectedIndex = this.entries.getSelectedIndex(); + if (selectedIndex != -1) { this.chooser.setSelectedFont(this.fonts[selectedIndex]); } @@ -84,6 +78,7 @@ public class FontDialog extends JDialog { private void selectedFontChanged() { int selectedIndex = this.entries.getSelectedIndex(); + if (selectedIndex != -1) { this.fonts[selectedIndex] = this.chooser.getSelectedFont(); } @@ -98,6 +93,7 @@ public class FontDialog extends JDialog { for (int i = 0; i < CATEGORIES.size(); i++) { UiConfig.setFont(CATEGORIES.get(i), this.fonts[i]); } + UiConfig.setUseCustomFonts(this.customCheckBox.isSelected()); UiConfig.save(); ChangeDialog.show(this); @@ -118,8 +114,8 @@ public class FontDialog extends JDialog { for (Component component : ((Container) self).getComponents()) { recursiveSetEnabled(component, enabled); } + self.setEnabled(enabled); } } - } diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/dialog/JavadocDialog.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/dialog/JavadocDialog.java index 9470e11c..d6e544d0 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/dialog/JavadocDialog.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/dialog/JavadocDialog.java @@ -1,13 +1,13 @@ /******************************************************************************* - * Copyright (c) 2015 Jeff Martin. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html - *

- * Contributors: - * Jeff Martin - initial API and implementation - ******************************************************************************/ +* Copyright (c) 2015 Jeff Martin. +* All rights reserved. This program and the accompanying materials +* are made available under the terms of the GNU Lesser General Public +* License v3.0 which accompanies this distribution, and is available at +* http://www.gnu.org/licenses/lgpl.html +* +*

Contributors: +* Jeff Martin - initial API and implementation +******************************************************************************/ package cuchaz.enigma.gui.dialog; @@ -18,7 +18,15 @@ import java.awt.FlowLayout; import java.awt.event.KeyAdapter; import java.awt.event.KeyEvent; -import javax.swing.*; +import javax.swing.JButton; +import javax.swing.JComboBox; +import javax.swing.JDialog; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JMenuBar; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.WindowConstants; import javax.swing.text.html.HTML; import com.google.common.base.Strings; @@ -35,7 +43,6 @@ import cuchaz.enigma.utils.I18n; import cuchaz.enigma.utils.validation.ValidationContext; public class JavadocDialog { - private final JDialog ui; private final GuiController controller; private final Entry entry; @@ -62,19 +69,21 @@ public class JavadocDialog { @Override public void keyPressed(KeyEvent event) { switch (event.getKeyCode()) { - case KeyEvent.VK_ENTER: - if (event.isControlDown()) { - doSave(); - if (vc.canProceed()) { - close(); - } + case KeyEvent.VK_ENTER: + if (event.isControlDown()) { + doSave(); + + if (vc.canProceed()) { + close(); } - break; - case KeyEvent.VK_ESCAPE: - close(); - break; - default: - break; + } + + break; + case KeyEvent.VK_ESCAPE: + close(); + break; + default: + break; } } }); @@ -108,6 +117,7 @@ public class JavadocDialog { } else { tagText = tag.getText() + " " + text.getSelectedText(); } + text.replaceSelection(tagText); } else { text.insert(tagText, text.getCaretPosition()); @@ -116,6 +126,7 @@ public class JavadocDialog { if (tag.isInline()) { text.setCaretPosition(text.getCaretPosition() - 1); } + text.grabFocus(); }); tagsMenu.add(tagButton); @@ -124,9 +135,11 @@ public class JavadocDialog { // add html tags JComboBox htmlList = new JComboBox(); htmlList.setPreferredSize(new Dimension()); + for (HTML.Tag htmlTag : HTML.getAllTags()) { htmlList.addItem(htmlTag.toString()); } + htmlList.addActionListener(action -> { String tagText = "<" + htmlList.getSelectedItem().toString() + ">"; text.insert(tagText, text.getCaretPosition()); @@ -146,9 +159,17 @@ public class JavadocDialog { public void doSave() { vc.reset(); validate(); - if (!vc.canProceed()) return; + + if (!vc.canProceed()) { + return; + } + save(); - if (!vc.canProceed()) return; + + if (!vc.canProceed()) { + return; + } + close(); } @@ -189,7 +210,7 @@ public class JavadocDialog { private boolean inline; - private JavadocTag(boolean inline) { + JavadocTag(boolean inline) { this.inline = inline; } @@ -201,5 +222,4 @@ public class JavadocDialog { return this.inline; } } - } diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/dialog/ProgressDialog.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/dialog/ProgressDialog.java index d76ddea9..3beae21c 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/dialog/ProgressDialog.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/dialog/ProgressDialog.java @@ -1,13 +1,13 @@ /******************************************************************************* - * Copyright (c) 2015 Jeff Martin. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html - *

- * Contributors: - * Jeff Martin - initial API and implementation - ******************************************************************************/ +* Copyright (c) 2015 Jeff Martin. +* All rights reserved. This program and the accompanying materials +* are made available under the terms of the GNU Lesser General Public +* License v3.0 which accompanies this distribution, and is available at +* http://www.gnu.org/licenses/lgpl.html +* +*

Contributors: +* Jeff Martin - initial API and implementation +******************************************************************************/ package cuchaz.enigma.gui.dialog; @@ -17,7 +17,12 @@ import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.util.concurrent.CompletableFuture; -import javax.swing.*; +import javax.swing.JDialog; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JProgressBar; +import javax.swing.SwingUtilities; +import javax.swing.WindowConstants; import cuchaz.enigma.Enigma; import cuchaz.enigma.ProgressListener; @@ -27,7 +32,6 @@ import cuchaz.enigma.gui.util.ScaleUtil; import cuchaz.enigma.utils.I18n; public class ProgressDialog implements ProgressListener, AutoCloseable { - private final JDialog dialog; private final JLabel labelTitle = new JLabel(); private final JLabel labelText = GuiUtil.unboldLabel(new JLabel()); @@ -39,11 +43,7 @@ public class ProgressDialog implements ProgressListener, AutoCloseable { Container pane = this.dialog.getContentPane(); pane.setLayout(new GridBagLayout()); - GridBagConstraintsBuilder cb = GridBagConstraintsBuilder.create() - .insets(2) - .anchor(GridBagConstraints.WEST) - .fill(GridBagConstraints.BOTH) - .weight(1.0, 0.0); + GridBagConstraintsBuilder cb = GridBagConstraintsBuilder.create().insets(2).anchor(GridBagConstraints.WEST).fill(GridBagConstraints.BOTH).weight(1.0, 0.0); pane.add(this.labelTitle, cb.pos(0, 0).build()); pane.add(this.labelText, cb.pos(0, 1).build()); @@ -121,6 +121,7 @@ public class ProgressDialog implements ProgressListener, AutoCloseable { public void step(int numDone, String message) { SwingUtilities.invokeLater(() -> { this.labelText.setText(message); + if (numDone != -1) { this.progress.setValue(numDone); this.progress.setIndeterminate(false); diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/dialog/SearchDialog.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/dialog/SearchDialog.java index e65b661b..7814dd81 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/dialog/SearchDialog.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/dialog/SearchDialog.java @@ -1,13 +1,13 @@ /******************************************************************************* - * Copyright (c) 2015 Jeff Martin. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html - *

- * Contributors: - * Jeff Martin - initial API and implementation - ******************************************************************************/ +* Copyright (c) 2015 Jeff Martin. +* All rights reserved. This program and the accompanying materials +* are made available under the terms of the GNU Lesser General Public +* License v3.0 which accompanies this distribution, and is available at +* http://www.gnu.org/licenses/lgpl.html +* +*

Contributors: +* Jeff Martin - initial API and implementation +******************************************************************************/ package cuchaz.enigma.gui.dialog; @@ -15,17 +15,36 @@ import java.awt.BorderLayout; import java.awt.Color; import java.awt.FlowLayout; import java.awt.Font; -import java.awt.event.*; -import java.util.*; +import java.awt.event.ComponentAdapter; +import java.awt.event.ComponentEvent; +import java.awt.event.KeyAdapter; +import java.awt.event.KeyEvent; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Queue; import java.util.concurrent.ConcurrentLinkedQueue; -import javax.swing.*; +import javax.swing.DefaultListModel; +import javax.swing.JButton; +import javax.swing.JDialog; +import javax.swing.JLabel; +import javax.swing.JList; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JTextField; +import javax.swing.ListSelectionModel; +import javax.swing.SwingUtilities; import javax.swing.event.DocumentEvent; import javax.swing.event.DocumentListener; import cuchaz.enigma.analysis.index.EntryIndex; import cuchaz.enigma.gui.Gui; import cuchaz.enigma.gui.GuiController; +import cuchaz.enigma.gui.search.SearchEntry; +import cuchaz.enigma.gui.search.SearchUtil; import cuchaz.enigma.gui.util.AbstractListCellRenderer; import cuchaz.enigma.gui.util.GuiUtil; import cuchaz.enigma.gui.util.ScaleUtil; @@ -34,11 +53,8 @@ import cuchaz.enigma.translation.representation.entry.FieldEntry; import cuchaz.enigma.translation.representation.entry.MethodEntry; import cuchaz.enigma.translation.representation.entry.ParentedEntry; import cuchaz.enigma.utils.I18n; -import cuchaz.enigma.gui.search.SearchEntry; -import cuchaz.enigma.gui.search.SearchUtil; public class SearchDialog { - private final JTextField searchField; private DefaultListModel classListModel; private final JList classList; @@ -60,7 +76,6 @@ public class SearchDialog { searchField = new JTextField(); searchField.getDocument().addDocumentListener(new DocumentListener() { - @Override public void insertUpdate(DocumentEvent e) { updateList(); @@ -75,7 +90,6 @@ public class SearchDialog { public void changedUpdate(DocumentEvent e) { updateList(); } - }); searchField.addKeyListener(new KeyAdapter() { @Override @@ -143,23 +157,9 @@ public class SearchDialog { final EntryIndex entryIndex = parent.getController().project.getJarIndex().getEntryIndex(); switch (type) { - case CLASS -> entryIndex.getClasses().parallelStream() - .filter(e -> !e.isInnerClass()) - .map(e -> SearchEntryImpl.from(e, parent.getController())) - .map(SearchUtil.Entry::from) - .sequential() - .forEach(su::add); - case METHOD -> entryIndex.getMethods().parallelStream() - .filter(e -> !e.isConstructor() && !entryIndex.getMethodAccess(e).isSynthetic()) - .map(e -> SearchEntryImpl.from(e, parent.getController())) - .map(SearchUtil.Entry::from) - .sequential() - .forEach(su::add); - case FIELD -> entryIndex.getFields().parallelStream() - .map(e -> SearchEntryImpl.from(e, parent.getController())) - .map(SearchUtil.Entry::from) - .sequential() - .forEach(su::add); + case CLASS -> entryIndex.getClasses().parallelStream().filter(e -> !e.isInnerClass()).map(e -> SearchEntryImpl.from(e, parent.getController())).map(SearchUtil.Entry::from).sequential().forEach(su::add); + case METHOD -> entryIndex.getMethods().parallelStream().filter(e -> !e.isConstructor() && !entryIndex.getMethodAccess(e).isSynthetic()).map(e -> SearchEntryImpl.from(e, parent.getController())).map(SearchUtil.Entry::from).sequential().forEach(su::add); + case FIELD -> entryIndex.getFields().parallelStream().map(e -> SearchEntryImpl.from(e, parent.getController())).map(SearchUtil.Entry::from).sequential().forEach(su::add); } updateList(); @@ -172,6 +172,7 @@ public class SearchDialog { private void openSelected() { SearchEntryImpl selectedValue = classList.getSelectedValue(); + if (selectedValue != null) { openEntry(selectedValue); } @@ -181,6 +182,7 @@ public class SearchDialog { close(); su.hit(e); parent.getController().navigateTo(e.obf); + if (e.obf instanceof ClassEntry) { if (e.deobf != null) { parent.getDeobfPanel().deobfClasses.setSelectionClass((ClassEntry) e.deobf); @@ -202,7 +204,9 @@ public class SearchDialog { // Updates the list of class names private void updateList() { - if (currentSearch != null) currentSearch.stop(); + if (currentSearch != null) { + currentSearch.stop(); + } DefaultListModel classListModel = new DefaultListModel<>(); this.classListModel = classListModel; @@ -210,7 +214,9 @@ public class SearchDialog { // handle these search result like minecraft scheduled tasks to prevent // flooding swing buttons inputs etc with tons of (possibly outdated) invocations - record Order(int idx, SearchEntryImpl e) {} + record Order(int idx, SearchEntryImpl e) { + } + Queue queue = new ConcurrentLinkedQueue<>(); Runnable updater = new Runnable() { @Override @@ -221,8 +227,9 @@ public class SearchDialog { // too large count may increase delay for key and input handling, etc. int count = 100; + while (count > 0 && !queue.isEmpty()) { - var o = queue.remove(); + Order o = queue.remove(); classListModel.insertElementAt(o.e, o.idx); count--; } @@ -240,7 +247,6 @@ public class SearchDialog { } private static final class SearchEntryImpl implements SearchEntry { - public final ParentedEntry obf; public final ParentedEntry deobf; @@ -270,10 +276,13 @@ public class SearchDialog { public static SearchEntryImpl from(ParentedEntry e, GuiController controller) { ParentedEntry deobf = controller.project.getMapper().deobfuscate(e); - if (deobf.equals(e)) deobf = null; + + if (deobf.equals(e)) { + deobf = null; + } + return new SearchEntryImpl(e, deobf); } - } private static final class ListCellRendererImpl extends AbstractListCellRenderer { @@ -281,7 +290,7 @@ public class SearchDialog { private final JLabel mainName; private final JLabel secondaryName; - public ListCellRendererImpl(Gui gui) { + ListCellRendererImpl(Gui gui) { this.setLayout(new BorderLayout()); this.gui = gui; @@ -316,7 +325,6 @@ public class SearchDialog { mainName.setIcon(GuiUtil.FIELD_ICON); } } - } public enum Type { diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/dialog/StatsDialog.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/dialog/StatsDialog.java index 0398093b..1ab66ef2 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/dialog/StatsDialog.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/dialog/StatsDialog.java @@ -4,10 +4,19 @@ import java.awt.Container; import java.awt.Dimension; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; -import java.util.*; +import java.util.Collections; +import java.util.HashMap; +import java.util.Locale; +import java.util.Map; +import java.util.Set; import java.util.stream.Collectors; -import javax.swing.*; +import javax.swing.JButton; +import javax.swing.JCheckBox; +import javax.swing.JDialog; +import javax.swing.JLabel; +import javax.swing.JTextField; +import javax.swing.SwingUtilities; import cuchaz.enigma.gui.Gui; import cuchaz.enigma.gui.config.UiConfig; @@ -19,14 +28,15 @@ import cuchaz.enigma.gui.util.ScaleUtil; import cuchaz.enigma.utils.I18n; public class StatsDialog { - public static void show(Gui gui) { ProgressDialog.runOffThread(gui.getFrame(), listener -> { final StatsGenerator statsGenerator = new StatsGenerator(gui.getController().project); final Map results = new HashMap<>(); + for (StatsMember member : StatsMember.values()) { results.put(member, statsGenerator.generate(listener, Collections.singleton(member), "", false)); } + SwingUtilities.invokeLater(() -> show(gui, results)); }); } @@ -111,12 +121,7 @@ public class StatsDialog { private static void generateStats(Gui gui, Map checkboxes, String topLevelPackage, boolean includeSynthetic) { // get members from selected checkboxes - Set includedMembers = checkboxes - .entrySet() - .stream() - .filter(entry -> entry.getValue().isSelected()) - .map(Map.Entry::getKey) - .collect(Collectors.toSet()); + Set includedMembers = checkboxes.entrySet().stream().filter(entry -> entry.getValue().isSelected()).map(Map.Entry::getKey).collect(Collectors.toSet()); // checks if a project is open if (gui.getController().project != null) { diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/AbstractInheritanceTree.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/AbstractInheritanceTree.java index 39aa212f..3f1625de 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/AbstractInheritanceTree.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/AbstractInheritanceTree.java @@ -7,7 +7,11 @@ import javax.annotation.Nullable; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JTree; -import javax.swing.tree.*; +import javax.swing.tree.DefaultMutableTreeNode; +import javax.swing.tree.DefaultTreeModel; +import javax.swing.tree.TreeCellRenderer; +import javax.swing.tree.TreeNode; +import javax.swing.tree.TreePath; import cuchaz.enigma.analysis.ClassInheritanceTreeNode; import cuchaz.enigma.analysis.MethodInheritanceTreeNode; @@ -40,11 +44,13 @@ public abstract class AbstractInheritanceTree { if (event.getClickCount() >= 2 && event.getButton() == MouseEvent.BUTTON1) { // get the selected node TreePath path = tree.getSelectionPath(); + if (path == null) { return; } Object node = path.getLastPathComponent(); + if (node instanceof ClassInheritanceTreeNode classNode) { gui.getController().navigateTo(new ClassEntry(classNode.getObfClassName())); } else if (node instanceof MethodInheritanceTreeNode methodNode) { @@ -72,7 +78,6 @@ public abstract class AbstractInheritanceTree { } public void retranslateUi() { - } @Nullable diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/CallsTree.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/CallsTree.java index c92534f0..56711880 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/CallsTree.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/CallsTree.java @@ -5,7 +5,12 @@ import java.awt.event.MouseEvent; import java.util.Collection; import java.util.Vector; -import javax.swing.*; +import javax.swing.JList; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JSplitPane; +import javax.swing.JTree; +import javax.swing.ListSelectionModel; import javax.swing.tree.DefaultTreeModel; import javax.swing.tree.TreeNode; import javax.swing.tree.TreePath; @@ -47,12 +52,7 @@ public class CallsTree { this.tokens.setPreferredSize(ScaleUtil.getDimension(0, 200)); this.tokens.setMinimumSize(ScaleUtil.getDimension(0, 200)); - JSplitPane contentPane = new JSplitPane( - JSplitPane.VERTICAL_SPLIT, - true, - new JScrollPane(this.callsTree), - new JScrollPane(this.tokens) - ); + JSplitPane contentPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT, true, new JScrollPane(this.callsTree), new JScrollPane(this.tokens)); contentPane.setResizeWeight(1); // let the top side take all the slack contentPane.resetToPreferredSizes(); @@ -109,6 +109,7 @@ public class CallsTree { private void onTokenClicked(MouseEvent event) { if (event.getClickCount() == 2) { Token selected = this.tokens.getSelectedValue(); + if (selected != null) { this.gui.openClass(this.gui.getController().getTokenHandle().getRef()).navigateToToken(selected); } @@ -116,7 +117,6 @@ public class CallsTree { } public void retranslateUi() { - } public JPanel getPanel() { diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/CollapsibleTabbedPane.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/CollapsibleTabbedPane.java index fb497b11..e05ab458 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/CollapsibleTabbedPane.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/CollapsibleTabbedPane.java @@ -5,7 +5,6 @@ import java.awt.event.MouseEvent; import javax.swing.JTabbedPane; public class CollapsibleTabbedPane extends JTabbedPane { - public CollapsibleTabbedPane() { } @@ -20,9 +19,14 @@ public class CollapsibleTabbedPane extends JTabbedPane { @Override protected void processMouseEvent(MouseEvent e) { int id = e.getID(); + if (id == MouseEvent.MOUSE_PRESSED) { - if (!isEnabled()) return; + if (!isEnabled()) { + return; + } + int tabIndex = getUI().tabForCoordinate(this, e.getX(), e.getY()); + if (tabIndex >= 0 && isEnabledAt(tabIndex)) { if (tabIndex == getSelectedIndex()) { if (isFocusOwner() && isRequestFocusEnabled()) { @@ -30,11 +34,12 @@ public class CollapsibleTabbedPane extends JTabbedPane { } else { setSelectedIndex(-1); } + return; } } } + super.processMouseEvent(e); } - } 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 9a6ea098..301ae7f1 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 @@ -1,7 +1,12 @@ package cuchaz.enigma.gui.elements; import java.awt.GridLayout; -import java.awt.event.*; +import java.awt.event.FocusAdapter; +import java.awt.event.FocusEvent; +import java.awt.event.KeyAdapter; +import java.awt.event.KeyEvent; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; import java.util.HashSet; import java.util.Set; @@ -21,7 +26,6 @@ import cuchaz.enigma.utils.validation.Validatable; * A label that converts into an editable text field when you click it. */ public class ConvertingTextField implements Validatable { - private final JPanel ui; private final ValidatableTextField textField; private final JLabel label; @@ -69,7 +73,9 @@ public class ConvertingTextField implements Validatable { } public void startEditing() { - if (this.editing || !this.editable) return; + if (this.editing || !this.editable) { + return; + } this.ui.removeAll(); this.ui.add(this.textField); @@ -82,9 +88,13 @@ public class ConvertingTextField implements Validatable { } public void stopEditing(boolean abort) { - if (!editing) return; + if (!editing) { + return; + } - if (!listeners.stream().allMatch(l -> l.tryStopEditing(this, abort))) return; + if (!listeners.stream().allMatch(l -> l.tryStopEditing(this, abort))) { + return; + } if (abort) { this.textField.setText(this.label.getText()); @@ -107,7 +117,9 @@ public class ConvertingTextField implements Validatable { } public void setEditText(String text) { - if (!editing) return; + if (!editing) { + return; + } this.textField.setText(text); } @@ -122,22 +134,29 @@ public class ConvertingTextField implements Validatable { } public void selectAll() { - if (!editing) return; + if (!editing) { + return; + } this.textField.selectAll(); } public void selectSubstring(int startIndex) { - if (!editing) return; + if (!editing) { + return; + } Document doc = this.textField.getDocument(); + if (doc != null) { this.selectSubstring(startIndex, doc.getLength()); } } public void selectSubstring(int startIndex, int endIndex) { - if (!editing) return; + if (!editing) { + return; + } this.textField.select(startIndex, endIndex); } @@ -155,7 +174,10 @@ public class ConvertingTextField implements Validatable { } public boolean hasChanges() { - if (!editing) return false; + if (!editing) { + return false; + } + return !this.textField.getText().equals(this.label.getText()); } @@ -180,5 +202,4 @@ public class ConvertingTextField implements Validatable { public JPanel getUi() { return ui; } - } diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/DeobfPanelPopupMenu.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/DeobfPanelPopupMenu.java index 0b44881f..bcc6dc6f 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/DeobfPanelPopupMenu.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/DeobfPanelPopupMenu.java @@ -9,55 +9,54 @@ import cuchaz.enigma.gui.panels.DeobfPanel; import cuchaz.enigma.utils.I18n; public class DeobfPanelPopupMenu { - - private final JPopupMenu ui; - private final JMenuItem renamePackage = new JMenuItem(); - private final JMenuItem renameClass = new JMenuItem(); - private final JMenuItem expandAll = new JMenuItem(); - private final JMenuItem collapseAll = new JMenuItem(); - - public DeobfPanelPopupMenu(DeobfPanel panel) { - this.ui = new JPopupMenu(); - - this.ui.add(this.renamePackage); - this.ui.add(this.renameClass); - this.ui.addSeparator(); - this.ui.add(this.expandAll); - this.ui.add(this.collapseAll); - - ClassSelector deobfClasses = panel.deobfClasses; - - this.renamePackage.addActionListener(a -> { - TreePath path; - - if (deobfClasses.getSelectedClass() != null) { - // Rename parent package if selected path is a class - path = deobfClasses.getSelectionPath().getParentPath(); - } else { - // Rename selected path if it's already a package - path = deobfClasses.getSelectionPath(); - } - - deobfClasses.getUI().startEditingAtPath(deobfClasses, path); - }); - this.renameClass.addActionListener(a -> deobfClasses.getUI().startEditingAtPath(deobfClasses, deobfClasses.getSelectionPath())); - this.expandAll.addActionListener(a -> deobfClasses.expandAll()); - this.collapseAll.addActionListener(a -> deobfClasses.collapseAll()); - - this.retranslateUi(); - } - - public void show(ClassSelector deobfClasses, int x, int y) { - // Only enable rename class if selected path is a class - this.renameClass.setEnabled(deobfClasses.getSelectedClass() != null); - - this.ui.show(deobfClasses, x, y); - } - - public void retranslateUi() { - this.renamePackage.setText(I18n.translate("popup_menu.deobf_panel.rename_package")); - this.renameClass.setText(I18n.translate("popup_menu.deobf_panel.rename_class")); - this.expandAll.setText(I18n.translate("popup_menu.deobf_panel.expand_all")); - this.collapseAll.setText(I18n.translate("popup_menu.deobf_panel.collapse_all")); - } + private final JPopupMenu ui; + private final JMenuItem renamePackage = new JMenuItem(); + private final JMenuItem renameClass = new JMenuItem(); + private final JMenuItem expandAll = new JMenuItem(); + private final JMenuItem collapseAll = new JMenuItem(); + + public DeobfPanelPopupMenu(DeobfPanel panel) { + this.ui = new JPopupMenu(); + + this.ui.add(this.renamePackage); + this.ui.add(this.renameClass); + this.ui.addSeparator(); + this.ui.add(this.expandAll); + this.ui.add(this.collapseAll); + + ClassSelector deobfClasses = panel.deobfClasses; + + this.renamePackage.addActionListener(a -> { + TreePath path; + + if (deobfClasses.getSelectedClass() != null) { + // Rename parent package if selected path is a class + path = deobfClasses.getSelectionPath().getParentPath(); + } else { + // Rename selected path if it's already a package + path = deobfClasses.getSelectionPath(); + } + + deobfClasses.getUI().startEditingAtPath(deobfClasses, path); + }); + this.renameClass.addActionListener(a -> deobfClasses.getUI().startEditingAtPath(deobfClasses, deobfClasses.getSelectionPath())); + this.expandAll.addActionListener(a -> deobfClasses.expandAll()); + this.collapseAll.addActionListener(a -> deobfClasses.collapseAll()); + + this.retranslateUi(); + } + + public void show(ClassSelector deobfClasses, int x, int y) { + // Only enable rename class if selected path is a class + this.renameClass.setEnabled(deobfClasses.getSelectedClass() != null); + + this.ui.show(deobfClasses, x, y); + } + + public void retranslateUi() { + this.renamePackage.setText(I18n.translate("popup_menu.deobf_panel.rename_package")); + this.renameClass.setText(I18n.translate("popup_menu.deobf_panel.rename_class")); + this.expandAll.setText(I18n.translate("popup_menu.deobf_panel.expand_all")); + this.collapseAll.setText(I18n.translate("popup_menu.deobf_panel.collapse_all")); + } } 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 2ce6ed93..d128bf5c 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 @@ -19,7 +19,6 @@ import cuchaz.enigma.translation.representation.entry.MethodEntry; import cuchaz.enigma.utils.I18n; public class EditorPopupMenu { - private final JPopupMenu ui = new JPopupMenu(); private final JMenuItem renameItem = new JMenuItem(); @@ -105,39 +104,41 @@ public class EditorPopupMenu { public boolean handleKeyEvent(KeyEvent event) { if (event.isControlDown()) { switch (event.getKeyCode()) { - case KeyEvent.VK_I: - this.showInheritanceItem.doClick(); - return true; - case KeyEvent.VK_M: - this.showImplementationsItem.doClick(); - return true; - case KeyEvent.VK_N: - this.openEntryItem.doClick(); - return true; - case KeyEvent.VK_P: - this.openPreviousItem.doClick(); - return true; - case KeyEvent.VK_E: - this.openNextItem.doClick(); - return true; - case KeyEvent.VK_C: - if (event.isShiftDown()) { - this.showCallsSpecificItem.doClick(); - } else { - this.showCallsItem.doClick(); - } - return true; - case KeyEvent.VK_O: - this.toggleMappingItem.doClick(); - return true; - case KeyEvent.VK_R: - this.renameItem.doClick(); - return true; - case KeyEvent.VK_D: - this.editJavadocItem.doClick(); - return true; + case KeyEvent.VK_I: + this.showInheritanceItem.doClick(); + return true; + case KeyEvent.VK_M: + this.showImplementationsItem.doClick(); + return true; + case KeyEvent.VK_N: + this.openEntryItem.doClick(); + return true; + case KeyEvent.VK_P: + this.openPreviousItem.doClick(); + return true; + case KeyEvent.VK_E: + this.openNextItem.doClick(); + return true; + case KeyEvent.VK_C: + if (event.isShiftDown()) { + this.showCallsSpecificItem.doClick(); + } else { + this.showCallsItem.doClick(); + } + + return true; + case KeyEvent.VK_O: + this.toggleMappingItem.doClick(); + return true; + case KeyEvent.VK_R: + this.renameItem.doClick(); + return true; + case KeyEvent.VK_D: + this.editJavadocItem.doClick(); + return true; } } + return false; } @@ -191,5 +192,4 @@ public class EditorPopupMenu { public JPopupMenu getUi() { return ui; } - } diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/EditorTabPopupMenu.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/EditorTabPopupMenu.java index 0b4926eb..93854818 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/EditorTabPopupMenu.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/EditorTabPopupMenu.java @@ -11,7 +11,6 @@ import cuchaz.enigma.gui.panels.EditorPanel; import cuchaz.enigma.utils.I18n; public class EditorTabPopupMenu { - private final JPopupMenu ui; private final JMenuItem close; private final JMenuItem closeAll; diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/EditorTabbedPane.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/EditorTabbedPane.java index ff0bba3f..7a6290ea 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/EditorTabbedPane.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/EditorTabbedPane.java @@ -39,7 +39,11 @@ public class EditorTabbedPane { public EditorPanel openClass(ClassEntry entry) { EditorPanel editorPanel = this.editors.computeIfAbsent(entry, e -> { ClassHandle ch = this.gui.getController().getClassHandleProvider().openClass(entry); - if (ch == null) return null; + + if (ch == null) { + return null; + } + EditorPanel ed = new EditorPanel(this.gui); ed.setup(); ed.setClassHandle(ch); @@ -125,7 +129,10 @@ public class EditorTabbedPane { int index = this.openFiles.indexOfComponent(ed.getUi()); for (int i = this.openFiles.getTabCount() - 1; i >= 0; i--) { - if (i == index) continue; + if (i == index) { + continue; + } + closeEditor(EditorPanel.byUi(this.openFiles.getComponentAt(i))); } } diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/JMultiLineToolTip.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/JMultiLineToolTip.java index 533d1b30..9e632d9d 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/JMultiLineToolTip.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/JMultiLineToolTip.java @@ -12,13 +12,12 @@ import javax.swing.plaf.ComponentUI; import javax.swing.plaf.basic.BasicToolTipUI; /** - * Implements a multi line tooltip for GUI components + * Implements a multi line tooltip for GUI components. * Copied from http://www.codeguru.com/java/articles/122.shtml * * @author Zafir Anjum */ public class JMultiLineToolTip extends JToolTip { - private static final long serialVersionUID = 7813662474312183098L; public JMultiLineToolTip() { @@ -52,10 +51,9 @@ public class JMultiLineToolTip extends JToolTip { } /** - * UI for multi line tool tip + * UI for multi line tool tip. */ class MultiLineToolTipUI extends BasicToolTipUI { - static MultiLineToolTipUI sharedInstance = new MultiLineToolTipUI(); Font smallFont; static JToolTip tip; @@ -67,7 +65,7 @@ class MultiLineToolTipUI extends BasicToolTipUI { return sharedInstance; } - public MultiLineToolTipUI() { + MultiLineToolTipUI() { super(); } @@ -93,7 +91,11 @@ class MultiLineToolTipUI extends BasicToolTipUI { public Dimension getPreferredSize(JComponent c) { String tipText = ((JToolTip) c).getTipText(); - if (tipText == null) return new Dimension(0, 0); + + if (tipText == null) { + return new Dimension(0, 0); + } + textArea = new JTextArea(tipText); rendererPane.removeAll(); rendererPane.add(textArea); @@ -112,8 +114,9 @@ class MultiLineToolTipUI extends BasicToolTipUI { d.width = width; d.height++; textArea.setSize(d); - } else + } else { textArea.setLineWrap(false); + } Dimension dim = textArea.getPreferredSize(); @@ -129,4 +132,4 @@ class MultiLineToolTipUI extends BasicToolTipUI { public Dimension getMaximumSize(JComponent c) { return getPreferredSize(c); } -} \ No newline at end of file +} diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/MenuBar.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/MenuBar.java index eeb52ccf..24a69b65 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/MenuBar.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/MenuBar.java @@ -12,7 +12,14 @@ import java.util.Map; import java.util.stream.Collectors; import java.util.stream.IntStream; -import javax.swing.*; +import javax.swing.ButtonGroup; +import javax.swing.JFileChooser; +import javax.swing.JMenu; +import javax.swing.JMenuBar; +import javax.swing.JMenuItem; +import javax.swing.JOptionPane; +import javax.swing.JRadioButtonMenuItem; +import javax.swing.KeyStroke; import cuchaz.enigma.gui.ConnectionState; import cuchaz.enigma.gui.Gui; @@ -20,7 +27,13 @@ import cuchaz.enigma.gui.config.Decompiler; import cuchaz.enigma.gui.config.LookAndFeel; import cuchaz.enigma.gui.config.NetConfig; import cuchaz.enigma.gui.config.UiConfig; -import cuchaz.enigma.gui.dialog.*; +import cuchaz.enigma.gui.dialog.AboutDialog; +import cuchaz.enigma.gui.dialog.ChangeDialog; +import cuchaz.enigma.gui.dialog.ConnectToServerDialog; +import cuchaz.enigma.gui.dialog.CreateServerDialog; +import cuchaz.enigma.gui.dialog.FontDialog; +import cuchaz.enigma.gui.dialog.SearchDialog; +import cuchaz.enigma.gui.dialog.StatsDialog; import cuchaz.enigma.gui.util.GuiUtil; import cuchaz.enigma.gui.util.LanguageUtil; import cuchaz.enigma.gui.util.ScaleUtil; @@ -29,7 +42,6 @@ import cuchaz.enigma.utils.I18n; import cuchaz.enigma.utils.Pair; public class MenuBar { - private final JMenu fileMenu = new JMenu(); private final JMenuItem jarOpenItem = new JMenuItem(); private final JMenuItem jarCloseItem = new JMenuItem(); @@ -221,13 +233,16 @@ public class MenuBar { } File file = d.getSelectedFile(); + // checks if the file name is not empty if (file != null) { Path path = file.toPath(); + // checks if the file name corresponds to an existing file if (Files.exists(path)) { this.gui.getController().openJar(path); } + UiConfig.setLastSelectedDir(d.getCurrentDirectory().getAbsolutePath()); } } @@ -241,8 +256,10 @@ public class MenuBar { this.gui.showDiscardDiag((response -> { if (response == JOptionPane.YES_OPTION) { this.gui.saveMapping().thenRun(then); - } else if (response == JOptionPane.NO_OPTION) + } else if (response == JOptionPane.NO_OPTION) { then.run(); + } + return null; }), I18n.translate("prompt.close.save"), I18n.translate("prompt.close.discard"), I18n.translate("prompt.cancel")); } else { @@ -264,6 +281,7 @@ public class MenuBar { private void onExportSourceClicked() { this.gui.exportSourceFileChooser.setCurrentDirectory(new File(UiConfig.getLastSelectedDir())); + if (this.gui.exportSourceFileChooser.showSaveDialog(this.gui.getFrame()) == JFileChooser.APPROVE_OPTION) { UiConfig.setLastSelectedDir(this.gui.exportSourceFileChooser.getCurrentDirectory().toString()); this.gui.getController().exportSource(this.gui.exportSourceFileChooser.getSelectedFile().toPath()); @@ -287,29 +305,35 @@ public class MenuBar { } private void onCustomScaleClicked() { - String answer = (String) JOptionPane.showInputDialog(this.gui.getFrame(), I18n.translate("menu.view.scale.custom.title"), I18n.translate("menu.view.scale.custom.title"), - JOptionPane.QUESTION_MESSAGE, null, null, Float.toString(UiConfig.getScaleFactor() * 100)); - if (answer == null) return; + String answer = (String) JOptionPane.showInputDialog(this.gui.getFrame(), I18n.translate("menu.view.scale.custom.title"), I18n.translate("menu.view.scale.custom.title"), JOptionPane.QUESTION_MESSAGE, null, null, Float.toString(UiConfig.getScaleFactor() * 100)); + + if (answer == null) { + return; + } + float newScale = 1.0f; + try { newScale = Float.parseFloat(answer) / 100f; } catch (NumberFormatException ignored) { + // ignored } + ScaleUtil.setScaleFactor(newScale); ChangeDialog.show(this.gui.getFrame()); } private void onFontClicked(Gui gui) { -// FontDialog fd = new FontDialog(gui.getFrame(), "Choose Font", true); -// fd.setLocationRelativeTo(gui.getFrame()); -// fd.setSelectedFont(UiConfig.getEditorFont()); -// fd.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE); -// fd.setVisible(true); -// -// if (!fd.isCancelSelected()) { -// UiConfig.setEditorFont(fd.getSelectedFont()); -// UiConfig.save(); -// } + // FontDialog fd = new FontDialog(gui.getFrame(), "Choose Font", true); + // fd.setLocationRelativeTo(gui.getFrame()); + // fd.setSelectedFont(UiConfig.getEditorFont()); + // fd.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE); + // fd.setVisible(true); + // + // if (!fd.isCancelSelected()) { + // UiConfig.setEditorFont(fd.getSelectedFont()); + // UiConfig.save(); + // } FontDialog.display(gui.getFrame()); } @@ -324,11 +348,15 @@ public class MenuBar { this.gui.getController().disconnectIfConnected(null); return; } + ConnectToServerDialog.Result result = ConnectToServerDialog.show(this.gui.getFrame()); + if (result == null) { return; } + this.gui.getController().disconnectIfConnected(null); + try { this.gui.getController().createClient(result.getUsername(), result.getAddress().address, result.getAddress().port, result.getPassword()); NetConfig.setUsername(result.getUsername()); @@ -339,6 +367,7 @@ public class MenuBar { JOptionPane.showMessageDialog(this.gui.getFrame(), e.toString(), I18n.translate("menu.collab.connect.error"), JOptionPane.ERROR_MESSAGE); this.gui.getController().disconnectIfConnected(null); } + Arrays.fill(result.getPassword(), (char) 0); } @@ -347,11 +376,15 @@ public class MenuBar { this.gui.getController().disconnectIfConnected(null); return; } + CreateServerDialog.Result result = CreateServerDialog.show(this.gui.getFrame()); + if (result == null) { return; } + this.gui.getController().disconnectIfConnected(null); + try { this.gui.getController().createServer(result.getPort(), result.getPassword()); NetConfig.setServerPort(result.getPort()); @@ -373,6 +406,7 @@ public class MenuBar { JMenuItem item = new JMenuItem(I18n.translate("mapping_format." + format.name().toLowerCase(Locale.ROOT))); item.addActionListener(event -> { gui.enigmaMappingsFileChooser.setCurrentDirectory(new File(UiConfig.getLastSelectedDir())); + if (gui.enigmaMappingsFileChooser.showOpenDialog(gui.getFrame()) == JFileChooser.APPROVE_OPTION) { File selectedFile = gui.enigmaMappingsFileChooser.getSelectedFile(); gui.getController().openMappings(format, selectedFile.toPath()); @@ -411,9 +445,11 @@ public class MenuBar { for (Decompiler decompiler : Decompiler.values()) { JRadioButtonMenuItem decompilerButton = new JRadioButtonMenuItem(decompiler.name); decompilerGroup.add(decompilerButton); + if (decompiler.equals(UiConfig.getDecompiler())) { decompilerButton.setSelected(true); } + decompilerButton.addActionListener(event -> { gui.getController().setDecompiler(decompiler.service); @@ -426,12 +462,15 @@ public class MenuBar { private static void prepareThemesMenu(JMenu themesMenu, Gui gui) { ButtonGroup themeGroup = new ButtonGroup(); + for (LookAndFeel lookAndFeel : LookAndFeel.values()) { JRadioButtonMenuItem themeButton = new JRadioButtonMenuItem(I18n.translate("menu.view.themes." + lookAndFeel.name().toLowerCase(Locale.ROOT))); themeGroup.add(themeButton); + if (lookAndFeel.equals(UiConfig.getLookAndFeel())) { themeButton.setSelected(true); } + themeButton.addActionListener(_e -> { UiConfig.setLookAndFeel(lookAndFeel); UiConfig.save(); @@ -443,12 +482,15 @@ public class MenuBar { private static void prepareLanguagesMenu(JMenu languagesMenu) { ButtonGroup languageGroup = new ButtonGroup(); + for (String lang : I18n.getAvailableLanguages()) { JRadioButtonMenuItem languageButton = new JRadioButtonMenuItem(I18n.getLanguageName(lang)); languageGroup.add(languageButton); + if (lang.equals(UiConfig.getLanguage())) { languageButton.setSelected(true); } + languageButton.addActionListener(event -> { UiConfig.setLanguage(lang); I18n.setLanguage(lang); @@ -461,25 +503,25 @@ public class MenuBar { private static void prepareScaleMenu(JMenu scaleMenu, Gui gui) { ButtonGroup scaleGroup = new ButtonGroup(); - Map scaleButtons = IntStream.of(100, 125, 150, 175, 200) - .mapToObj(scaleFactor -> { - float realScaleFactor = scaleFactor / 100f; - JRadioButtonMenuItem menuItem = new JRadioButtonMenuItem(String.format("%d%%", scaleFactor)); - menuItem.addActionListener(event -> ScaleUtil.setScaleFactor(realScaleFactor)); - menuItem.addActionListener(event -> ChangeDialog.show(gui.getFrame())); - scaleGroup.add(menuItem); - scaleMenu.add(menuItem); - return new Pair<>(realScaleFactor, menuItem); - }) - .collect(Collectors.toMap(x -> x.a, x -> x.b)); + Map scaleButtons = IntStream.of(100, 125, 150, 175, 200).mapToObj(scaleFactor -> { + float realScaleFactor = scaleFactor / 100f; + JRadioButtonMenuItem menuItem = new JRadioButtonMenuItem(String.format("%d%%", scaleFactor)); + menuItem.addActionListener(event -> ScaleUtil.setScaleFactor(realScaleFactor)); + menuItem.addActionListener(event -> ChangeDialog.show(gui.getFrame())); + scaleGroup.add(menuItem); + scaleMenu.add(menuItem); + return new Pair<>(realScaleFactor, menuItem); + }).collect(Collectors.toMap(x -> x.a, x -> x.b)); JRadioButtonMenuItem currentScaleButton = scaleButtons.get(UiConfig.getScaleFactor()); + if (currentScaleButton != null) { currentScaleButton.setSelected(true); } ScaleUtil.addListener((newScale, _oldScale) -> { JRadioButtonMenuItem mi = scaleButtons.get(newScale); + if (mi != null) { mi.setSelected(true); } else { @@ -487,5 +529,4 @@ public class MenuBar { } }); } - } diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/StatusBar.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/StatusBar.java index 0c667c00..48404a1c 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/StatusBar.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/StatusBar.java @@ -54,7 +54,7 @@ public class StatusBar { * * @param message the message to display * @param timeout the timeout in milliseconds to wait until clearing the - * message; if 0, the message is not automatically cleared + * message; if 0, the message is not automatically cleared */ public void showMessage(String message, int timeout) { this.timer.stop(); diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/ValidatablePasswordField.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/ValidatablePasswordField.java index 02e1bc39..4329cae0 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/ValidatablePasswordField.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/ValidatablePasswordField.java @@ -14,7 +14,6 @@ import cuchaz.enigma.utils.validation.ParameterizedMessage; import cuchaz.enigma.utils.validation.Validatable; public class ValidatablePasswordField extends JPasswordField implements Validatable { - private List messages = new ArrayList<>(); private String tooltipText = null; @@ -92,5 +91,4 @@ public class ValidatablePasswordField extends JPasswordField implements Validata super.paint(g); ValidatableUi.drawMarker(this, g, messages); } - } diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/ValidatableTextArea.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/ValidatableTextArea.java index 7d1f8665..2d5e2295 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/ValidatableTextArea.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/ValidatableTextArea.java @@ -14,7 +14,6 @@ import cuchaz.enigma.utils.validation.ParameterizedMessage; import cuchaz.enigma.utils.validation.Validatable; public class ValidatableTextArea extends JTextArea implements Validatable { - private List messages = new ArrayList<>(); private String tooltipText = null; @@ -96,5 +95,4 @@ public class ValidatableTextArea extends JTextArea implements Validatable { super.paint(g); ValidatableUi.drawMarker(this, g, messages); } - } diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/ValidatableTextField.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/ValidatableTextField.java index c114dc17..be658d59 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/ValidatableTextField.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/ValidatableTextField.java @@ -14,7 +14,6 @@ import cuchaz.enigma.utils.validation.ParameterizedMessage; import cuchaz.enigma.utils.validation.Validatable; public class ValidatableTextField extends JTextField implements Validatable { - private List messages = new ArrayList<>(); private String tooltipText = null; @@ -92,5 +91,4 @@ public class ValidatableTextField extends JTextField implements Validatable { super.paint(g); ValidatableUi.drawMarker(this, g, messages); } - } diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/ValidatableUi.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/ValidatableUi.java index 5df63486..b8b8431b 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/ValidatableUi.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/ValidatableUi.java @@ -13,26 +13,29 @@ import cuchaz.enigma.gui.util.ScaleUtil; import cuchaz.enigma.utils.validation.ParameterizedMessage; public final class ValidatableUi { - private ValidatableUi() { } public static String getTooltipText(String tooltipText, List messages) { List strings = new ArrayList<>(); + if (tooltipText != null) { strings.add(tooltipText); } + if (!messages.isEmpty()) { strings.add("Error(s): "); messages.forEach(msg -> { strings.add(String.format(" - %s", msg.getText())); String longDesc = msg.getLongText(); + if (!longDesc.isEmpty()) { Arrays.stream(longDesc.split("\n")).map(s -> String.format(" %s", s)).forEach(strings::add); } }); } + if (strings.isEmpty()) { return null; } else { @@ -49,11 +52,13 @@ public final class ValidatableUi { messages.forEach(msg -> { strings.add(String.format(" - %s", msg.getText())); String longDesc = msg.getLongText(); + if (!longDesc.isEmpty()) { Arrays.stream(longDesc.split("\n")).map(s -> String.format(" %s", s)).forEach(strings::add); } }); } + if (strings.isEmpty()) { return null; } else { @@ -63,6 +68,7 @@ public final class ValidatableUi { public static void drawMarker(Component self, Graphics g, List messages) { Color color = ValidatableUi.getMarkerColor(messages); + if (color != null) { g.setColor(color); int x1 = self.getWidth() - ScaleUtil.scale(8) - 1; @@ -75,33 +81,32 @@ public final class ValidatableUi { @Nullable public static Color getMarkerColor(List messages) { - int level = messages.stream() - .mapToInt(ValidatableUi::getMessageLevel) - .max().orElse(0); + int level = messages.stream().mapToInt(ValidatableUi::getMessageLevel).max().orElse(0); switch (level) { - case 0: - return null; - case 1: - return Color.BLUE; - case 2: - return Color.ORANGE; - case 3: - return Color.RED; + case 0: + return null; + case 1: + return Color.BLUE; + case 2: + return Color.ORANGE; + case 3: + return Color.RED; } + throw new IllegalStateException("unreachable"); } private static int getMessageLevel(ParameterizedMessage message) { switch (message.message.type) { - case INFO: - return 1; - case WARNING: - return 2; - case ERROR: - return 3; + case INFO: + return 1; + case WARNING: + return 2; + case ERROR: + return 3; } + throw new IllegalStateException("unreachable"); } - } diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/events/ConvertingTextFieldListener.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/events/ConvertingTextFieldListener.java index 6e17fec1..d9ec95c2 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/events/ConvertingTextFieldListener.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/events/ConvertingTextFieldListener.java @@ -3,7 +3,6 @@ package cuchaz.enigma.gui.events; import cuchaz.enigma.gui.elements.ConvertingTextField; public interface ConvertingTextFieldListener { - default void onStartEditing(ConvertingTextField field) { } @@ -13,5 +12,4 @@ public interface ConvertingTextFieldListener { default void onStopEditing(ConvertingTextField field, boolean abort) { } - } diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/events/EditorActionListener.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/events/EditorActionListener.java index 48c9ec4b..1651abf0 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/events/EditorActionListener.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/events/EditorActionListener.java @@ -7,7 +7,6 @@ import cuchaz.enigma.translation.representation.entry.ClassEntry; import cuchaz.enigma.translation.representation.entry.Entry; public interface EditorActionListener { - default void onCursorReferenceChanged(EditorPanel editor, EntryReference, Entry> ref) { } @@ -16,5 +15,4 @@ public interface EditorActionListener { default void onTitleChanged(EditorPanel editor, String title) { } - } diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/events/ThemeChangeListener.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/events/ThemeChangeListener.java index 10d7ce1c..e2054b29 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/events/ThemeChangeListener.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/events/ThemeChangeListener.java @@ -7,7 +7,5 @@ import cuchaz.enigma.gui.highlight.BoxHighlightPainter; import cuchaz.enigma.source.RenamableTokenType; public interface ThemeChangeListener { - void onThemeChanged(LookAndFeel lookAndFeel, Map boxHighlightPainters); - } diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/highlight/BoxHighlightPainter.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/highlight/BoxHighlightPainter.java index 2d8d76a7..a97b3779 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/highlight/BoxHighlightPainter.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/highlight/BoxHighlightPainter.java @@ -1,13 +1,13 @@ /******************************************************************************* - * Copyright (c) 2015 Jeff Martin. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html - *

- * Contributors: - * Jeff Martin - initial API and implementation - ******************************************************************************/ +* Copyright (c) 2015 Jeff Martin. +* All rights reserved. This program and the accompanying materials +* are made available under the terms of the GNU Lesser General Public +* License v3.0 which accompanies this distribution, and is available at +* http://www.gnu.org/licenses/lgpl.html +* +*

Contributors: +* Jeff Martin - initial API and implementation +******************************************************************************/ package cuchaz.enigma.gui.highlight; @@ -67,5 +67,4 @@ public class BoxHighlightPainter implements Highlighter.HighlightPainter { g.setColor(this.borderColor); g.drawRoundRect(bounds.x, bounds.y, bounds.width, bounds.height, 4, 4); } - } diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/highlight/SelectionHighlightPainter.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/highlight/SelectionHighlightPainter.java index 22d64201..a807802d 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/highlight/SelectionHighlightPainter.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/highlight/SelectionHighlightPainter.java @@ -1,17 +1,21 @@ /******************************************************************************* - * Copyright (c) 2015 Jeff Martin. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html - *

- * Contributors: - * Jeff Martin - initial API and implementation - ******************************************************************************/ +* Copyright (c) 2015 Jeff Martin. +* All rights reserved. This program and the accompanying materials +* are made available under the terms of the GNU Lesser General Public +* License v3.0 which accompanies this distribution, and is available at +* http://www.gnu.org/licenses/lgpl.html +* +*

Contributors: +* Jeff Martin - initial API and implementation +******************************************************************************/ package cuchaz.enigma.gui.highlight; -import java.awt.*; +import java.awt.BasicStroke; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.Rectangle; +import java.awt.Shape; import javax.swing.text.Highlighter; import javax.swing.text.JTextComponent; @@ -19,7 +23,6 @@ import javax.swing.text.JTextComponent; import cuchaz.enigma.gui.config.UiConfig; public class SelectionHighlightPainter implements Highlighter.HighlightPainter { - public static final SelectionHighlightPainter INSTANCE = new SelectionHighlightPainter(); @Override @@ -31,5 +34,4 @@ public class SelectionHighlightPainter implements Highlighter.HighlightPainter { g2d.setStroke(new BasicStroke(2.0f)); g2d.drawRoundRect(bounds.x, bounds.y, bounds.width, bounds.height, 4, 4); } - } diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/newabstraction/EntryValidation.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/newabstraction/EntryValidation.java index 898529a4..1dcdeabf 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/newabstraction/EntryValidation.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/newabstraction/EntryValidation.java @@ -6,17 +6,16 @@ import cuchaz.enigma.utils.validation.Message; import cuchaz.enigma.utils.validation.ValidationContext; public class EntryValidation { - public static boolean validateJavadoc(ValidationContext vc, String javadoc) { if (javadoc.contains("*/")) { vc.raise(Message.ILLEGAL_DOC_COMMENT_END); return false; } + return true; } public static boolean validateRename(ValidationContext vc, EnigmaProject p, Entry entry, String newName) { return p.getMapper().getValidator().validateRename(vc, entry, newName); } - } diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/node/ClassSelectorClassNode.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/node/ClassSelectorClassNode.java index 922f8f24..f931a93f 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/node/ClassSelectorClassNode.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/node/ClassSelectorClassNode.java @@ -1,22 +1,21 @@ /******************************************************************************* - * Copyright (c) 2015 Jeff Martin. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html - *

- * Contributors: - * Jeff Martin - initial API and implementation - ******************************************************************************/ +* Copyright (c) 2015 Jeff Martin. +* All rights reserved. This program and the accompanying materials +* are made available under the terms of the GNU Lesser General Public +* License v3.0 which accompanies this distribution, and is available at +* http://www.gnu.org/licenses/lgpl.html +* +*

Contributors: +* Jeff Martin - initial API and implementation +******************************************************************************/ package cuchaz.enigma.gui.node; -import cuchaz.enigma.translation.representation.entry.ClassEntry; - import javax.swing.tree.DefaultMutableTreeNode; -public class ClassSelectorClassNode extends DefaultMutableTreeNode { +import cuchaz.enigma.translation.representation.entry.ClassEntry; +public class ClassSelectorClassNode extends DefaultMutableTreeNode { private final ClassEntry obfEntry; private ClassEntry classEntry; @@ -57,12 +56,17 @@ public class ClassSelectorClassNode extends DefaultMutableTreeNode { @Override public void setUserObject(Object userObject) { String packageName = ""; - if (classEntry.getPackageName() != null) + + if (classEntry.getPackageName() != null) { packageName = classEntry.getPackageName() + "/"; - if (userObject instanceof String) + } + + if (userObject instanceof String) { this.classEntry = new ClassEntry(packageName + userObject); - else if (userObject instanceof ClassEntry) + } else if (userObject instanceof ClassEntry) { this.classEntry = (ClassEntry) userObject; + } + super.setUserObject(classEntry); } diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/node/ClassSelectorPackageNode.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/node/ClassSelectorPackageNode.java index c1c7d387..dfcbd8c1 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/node/ClassSelectorPackageNode.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/node/ClassSelectorPackageNode.java @@ -1,22 +1,21 @@ /******************************************************************************* - * Copyright (c) 2015 Jeff Martin. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html - *

- * Contributors: - * Jeff Martin - initial API and implementation - ******************************************************************************/ +* Copyright (c) 2015 Jeff Martin. +* All rights reserved. This program and the accompanying materials +* are made available under the terms of the GNU Lesser General Public +* License v3.0 which accompanies this distribution, and is available at +* http://www.gnu.org/licenses/lgpl.html +* +*

Contributors: +* Jeff Martin - initial API and implementation +******************************************************************************/ package cuchaz.enigma.gui.node; -import cuchaz.enigma.translation.representation.entry.ClassEntry; - import javax.swing.tree.DefaultMutableTreeNode; -public class ClassSelectorPackageNode extends DefaultMutableTreeNode { +import cuchaz.enigma.translation.representation.entry.ClassEntry; +public class ClassSelectorPackageNode extends DefaultMutableTreeNode { private String packageName; public ClassSelectorPackageNode(String packageName) { @@ -34,8 +33,10 @@ public class ClassSelectorPackageNode extends DefaultMutableTreeNode { @Override public void setUserObject(Object userObject) { - if (userObject instanceof String) + if (userObject instanceof String) { this.packageName = (String) userObject; + } + super.setUserObject(userObject); } diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/panels/ClosableTabTitlePane.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/panels/ClosableTabTitlePane.java index fe5c8578..dca714dd 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/panels/ClosableTabTitlePane.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/panels/ClosableTabTitlePane.java @@ -9,12 +9,17 @@ import java.awt.event.MouseEvent; import javax.accessibility.AccessibleContext; import javax.annotation.Nullable; -import javax.swing.*; +import javax.swing.JButton; +import javax.swing.JComponent; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JTabbedPane; +import javax.swing.SwingUtilities; +import javax.swing.UIManager; import javax.swing.border.EmptyBorder; import javax.swing.event.ChangeListener; public class ClosableTabTitlePane { - private final JPanel ui; private final JButton closeButton; private final JLabel label; @@ -66,19 +71,7 @@ public class ClosableTabTitlePane { if (parent != null) { Point pt = new Point(e.getXOnScreen(), e.getYOnScreen()); SwingUtilities.convertPointFromScreen(pt, parent); - MouseEvent e1 = new MouseEvent( - parent, - e.getID(), - e.getWhen(), - e.getModifiersEx(), - (int) pt.getX(), - (int) pt.getY(), - e.getXOnScreen(), - e.getYOnScreen(), - e.getClickCount(), - e.isPopupTrigger(), - e.getButton() - ); + MouseEvent e1 = new MouseEvent(parent, e.getID(), e.getWhen(), e.getModifiersEx(), (int) pt.getX(), (int) pt.getY(), e.getXOnScreen(), e.getYOnScreen(), e.getClickCount(), e.isPopupTrigger(), e.getButton()); parent.dispatchEvent(e1); } } @@ -91,11 +84,13 @@ public class ClosableTabTitlePane { if (this.parent != null) { pane.removeChangeListener(cachedChangeListener); } + if (pane != null) { updateState(pane); cachedChangeListener = e -> updateState(pane); pane.addChangeListener(cachedChangeListener); } + this.parent = pane; } @@ -123,11 +118,12 @@ public class ClosableTabTitlePane { public static ClosableTabTitlePane byUi(Component c) { if (c instanceof JComponent) { Object prop = ((JComponent) c).getClientProperty(ClosableTabTitlePane.class); + if (prop instanceof ClosableTabTitlePane) { return (ClosableTabTitlePane) prop; } } + return null; } - } diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/panels/DeobfPanel.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/panels/DeobfPanel.java index 10fc5e1a..5d1d0f2b 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/panels/DeobfPanel.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/panels/DeobfPanel.java @@ -15,7 +15,6 @@ import cuchaz.enigma.gui.util.GuiUtil; import cuchaz.enigma.utils.I18n; public class DeobfPanel extends JPanel { - public final ClassSelector deobfClasses; private final JLabel title = new JLabel(); @@ -44,6 +43,7 @@ public class DeobfPanel extends JPanel { if (SwingUtilities.isRightMouseButton(e)) { deobfClasses.setSelectionRow(deobfClasses.getClosestRowForLocation(e.getX(), e.getY())); int i = deobfClasses.getRowForPath(deobfClasses.getSelectionPath()); + if (i != -1) { deobfPanelPopupMenu.show(deobfClasses, e.getX(), e.getY()); } @@ -54,5 +54,4 @@ public class DeobfPanel extends JPanel { this.title.setText(I18n.translate(gui.isSingleClassTree() ? "info_panel.classes" : "info_panel.classes.deobfuscated")); this.deobfPanelPopupMenu.retranslateUi(); } - } diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/panels/EditorPanel.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/panels/EditorPanel.java index f4b190bc..cb74ceca 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/panels/EditorPanel.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/panels/EditorPanel.java @@ -1,14 +1,35 @@ package cuchaz.enigma.gui.panels; -import java.awt.*; -import java.awt.event.*; +import java.awt.Color; +import java.awt.Component; +import java.awt.Font; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.GridLayout; +import java.awt.Rectangle; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.KeyAdapter; +import java.awt.event.KeyEvent; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.Map; import javax.annotation.Nullable; -import javax.swing.*; +import javax.swing.JButton; +import javax.swing.JComponent; +import javax.swing.JEditorPane; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JProgressBar; +import javax.swing.JScrollPane; +import javax.swing.JSeparator; +import javax.swing.JTextArea; +import javax.swing.SwingUtilities; +import javax.swing.Timer; import javax.swing.text.BadLocationException; import javax.swing.text.Document; import javax.swing.text.Highlighter.HighlightPainter; @@ -46,7 +67,6 @@ import cuchaz.enigma.utils.I18n; import cuchaz.enigma.utils.Result; public class EditorPanel { - private final JPanel ui = new JPanel(); private final JEditorPane editor = new JEditorPane(); private final JScrollPane editorScrollPane = new JScrollPane(this.editor); @@ -123,18 +143,19 @@ public class EditorPanel { @Override public void mouseReleased(MouseEvent e) { switch (e.getButton()) { - case MouseEvent.BUTTON3: // Right click - EditorPanel.this.editor.setCaretPosition(EditorPanel.this.editor.viewToModel(e.getPoint())); - break; + case MouseEvent.BUTTON3: // Right click + EditorPanel.this.editor.setCaretPosition(EditorPanel.this.editor.viewToModel(e.getPoint())); + break; - case 4: // Back navigation - gui.getController().openPreviousReference(); - break; + case 4: // Back navigation + gui.getController().openPreviousReference(); + break; - case 5: // Forward navigation - gui.getController().openNextReference(); - break; + case 5: // Forward navigation + gui.getController().openNextReference(); + break; } + EditorPanel.this.mouseIsPressed = false; } }); @@ -143,31 +164,36 @@ public class EditorPanel { public void keyPressed(KeyEvent event) { if (event.isControlDown()) { EditorPanel.this.shouldNavigateOnClick = false; - if (EditorPanel.this.popupMenu.handleKeyEvent(event)) return; + + if (EditorPanel.this.popupMenu.handleKeyEvent(event)) { + return; + } + switch (event.getKeyCode()) { - case KeyEvent.VK_F5: - if (EditorPanel.this.classHandle != null) { - EditorPanel.this.classHandle.invalidate(); - } - break; - - case KeyEvent.VK_F: - // prevent navigating on click when quick find activated - break; - - case KeyEvent.VK_ADD: - case KeyEvent.VK_EQUALS: - case KeyEvent.VK_PLUS: - offsetEditorZoom(2); - break; - case KeyEvent.VK_SUBTRACT: - case KeyEvent.VK_MINUS: - offsetEditorZoom(-2); - break; - - default: - EditorPanel.this.shouldNavigateOnClick = true; // CTRL - break; + case KeyEvent.VK_F5: + if (EditorPanel.this.classHandle != null) { + EditorPanel.this.classHandle.invalidate(); + } + + break; + + case KeyEvent.VK_F: + // prevent navigating on click when quick find activated + break; + + case KeyEvent.VK_ADD: + case KeyEvent.VK_EQUALS: + case KeyEvent.VK_PLUS: + offsetEditorZoom(2); + break; + case KeyEvent.VK_SUBTRACT: + case KeyEvent.VK_MINUS: + offsetEditorZoom(-2); + break; + + default: + EditorPanel.this.shouldNavigateOnClick = true; // CTRL + break; } } } @@ -175,8 +201,14 @@ public class EditorPanel { @Override public void keyTyped(KeyEvent event) { EntryReference, Entry> ref = EditorPanel.this.getCursorReference(); - if (ref == null) return; - if (!EditorPanel.this.controller.project.isRenamable(ref)) return; + + if (ref == null) { + return; + } + + if (!EditorPanel.this.controller.project.isRenamable(ref)) { + return; + } if (!event.isControlDown() && !event.isAltDown() && Character.isJavaIdentifierPart(event.getKeyChar())) { EnigmaProject project = gui.getController().project; @@ -184,8 +216,10 @@ public class EditorPanel { Entry entry = reference.getNameableEntry(); String name = String.valueOf(event.getKeyChar()); + if (entry instanceof ClassEntry && ((ClassEntry) entry).getParent() == null) { String packageName = ((ClassEntry) entry).getPackageName(); + if (packageName != null) { name = packageName + "/" + name; } @@ -207,12 +241,14 @@ public class EditorPanel { if ((this.editorLaf == null || this.editorLaf != laf)) { this.editor.updateUI(); this.editor.setBackground(UiConfig.getEditorBackgroundColor()); + if (this.editorLaf != null) { this.classHandle.invalidateMapped(); } this.editorLaf = laf; } + this.boxHighlightPainters = boxHighlightPainters; }; @@ -223,19 +259,23 @@ public class EditorPanel { public static EditorPanel byUi(Component ui) { if (ui instanceof JComponent) { Object prop = ((JComponent) ui).getClientProperty(EditorPanel.class); + if (prop instanceof EditorPanel) { return (EditorPanel) prop; } } + return null; } public void setClassHandle(ClassHandle handle) { ClassEntry old = null; + if (this.classHandle != null) { old = this.classHandle.getRef(); this.classHandle.close(); } + setClassHandle0(old, handle); } @@ -299,53 +339,61 @@ public class EditorPanel { } else { this.displayError(res.unwrapErr()); } + this.nextReference = null; }); } public void displayError(ClassHandleError t) { this.setDisplayMode(DisplayMode.ERRORED); + String str = switch (t.type) { - case DECOMPILE -> "editor.decompile_error"; - case REMAP -> "editor.remap_error"; + case DECOMPILE -> "editor.decompile_error"; + case REMAP -> "editor.remap_error"; }; + this.errorLabel.setText(I18n.translate(str)); this.errorTextArea.setText(t.getStackTrace()); this.errorTextArea.setCaretPosition(0); } public void setDisplayMode(DisplayMode mode) { - if (this.mode == mode) return; + if (this.mode == mode) { + return; + } + this.ui.removeAll(); + switch (mode) { - case INACTIVE: - break; - case IN_PROGRESS: { - // make progress bar start from the left every time - this.decompilingProgressBar.setIndeterminate(false); - this.decompilingProgressBar.setIndeterminate(true); - - this.ui.setLayout(new GridBagLayout()); - GridBagConstraintsBuilder cb = GridBagConstraintsBuilder.create().insets(2); - this.ui.add(this.decompilingLabel, cb.pos(0, 0).anchor(GridBagConstraints.SOUTH).build()); - this.ui.add(this.decompilingProgressBar, cb.pos(0, 1).anchor(GridBagConstraints.NORTH).build()); - break; - } - case SUCCESS: { - this.ui.setLayout(new GridLayout(1, 1, 0, 0)); - this.ui.add(this.editorScrollPane); - break; - } - case ERRORED: { - this.ui.setLayout(new GridBagLayout()); - GridBagConstraintsBuilder cb = GridBagConstraintsBuilder.create().insets(2).weight(1.0, 0.0).anchor(GridBagConstraints.WEST); - this.ui.add(this.errorLabel, cb.pos(0, 0).build()); - this.ui.add(new JSeparator(JSeparator.HORIZONTAL), cb.pos(0, 1).fill(GridBagConstraints.HORIZONTAL).build()); - this.ui.add(this.errorScrollPane, cb.pos(0, 2).weight(1.0, 1.0).fill(GridBagConstraints.BOTH).build()); - this.ui.add(this.retryButton, cb.pos(0, 3).weight(0.0, 0.0).anchor(GridBagConstraints.EAST).build()); - break; - } + case INACTIVE: + break; + case IN_PROGRESS: { + // make progress bar start from the left every time + this.decompilingProgressBar.setIndeterminate(false); + this.decompilingProgressBar.setIndeterminate(true); + + this.ui.setLayout(new GridBagLayout()); + GridBagConstraintsBuilder cb = GridBagConstraintsBuilder.create().insets(2); + this.ui.add(this.decompilingLabel, cb.pos(0, 0).anchor(GridBagConstraints.SOUTH).build()); + this.ui.add(this.decompilingProgressBar, cb.pos(0, 1).anchor(GridBagConstraints.NORTH).build()); + break; + } + case SUCCESS: { + this.ui.setLayout(new GridLayout(1, 1, 0, 0)); + this.ui.add(this.editorScrollPane); + break; + } + case ERRORED: { + this.ui.setLayout(new GridBagLayout()); + GridBagConstraintsBuilder cb = GridBagConstraintsBuilder.create().insets(2).weight(1.0, 0.0).anchor(GridBagConstraints.WEST); + this.ui.add(this.errorLabel, cb.pos(0, 0).build()); + this.ui.add(new JSeparator(JSeparator.HORIZONTAL), cb.pos(0, 1).fill(GridBagConstraints.HORIZONTAL).build()); + this.ui.add(this.errorScrollPane, cb.pos(0, 2).weight(1.0, 1.0).fill(GridBagConstraints.BOTH).build()); + this.ui.add(this.retryButton, cb.pos(0, 3).weight(0.0, 0.0).anchor(GridBagConstraints.EAST).build()); + break; + } } + this.ui.validate(); this.ui.repaint(); this.mode = mode; @@ -353,6 +401,7 @@ public class EditorPanel { public void offsetEditorZoom(int zoomAmount) { int newResult = this.fontSize + zoomAmount; + if (newResult > 8 && newResult < 72) { this.fontSize = newResult; this.editor.setFont(ScaleUtil.getFont(this.editor.getFont().getFontName(), Font.PLAIN, this.fontSize)); @@ -365,8 +414,13 @@ public class EditorPanel { } public void onCaretMove(int pos, boolean fromClick) { - if (this.settingSource) return; - if (this.controller.project == null) return; + if (this.settingSource) { + return; + } + + if (this.controller.project == null) { + return; + } EntryRemapper mapper = this.controller.project.getMapper(); Token token = getToken(pos); @@ -378,10 +432,12 @@ public class EditorPanel { if (referenceEntry != null && this.shouldNavigateOnClick && fromClick) { this.shouldNavigateOnClick = false; Entry navigationEntry = referenceEntry; + if (this.cursorReference.context == null) { EntryResolver resolver = mapper.getObfResolver(); navigationEntry = resolver.resolveFirstEntry(referenceEntry, ResolutionStrategy.RESOLVE_ROOT); } + this.controller.navigateTo(navigationEntry); } } @@ -398,6 +454,7 @@ public class EditorPanel { if (this.source == null) { return null; } + return this.source.getIndex().getReferenceToken(pos); } @@ -406,16 +463,22 @@ public class EditorPanel { if (this.source == null) { return null; } + return this.source.getIndex().getReference(token); } public void setSource(DecompiledClassSource source) { this.setDisplayMode(DisplayMode.SUCCESS); - if (source == null) return; + + if (source == null) { + return; + } + try { this.settingSource = true; int newCaretPos = 0; + if (this.source != null && this.source.getEntry().equals(source.getEntry())) { int caretPos = this.editor.getCaretPosition(); @@ -441,9 +504,11 @@ public class EditorPanel { this.source = source; this.editor.getHighlighter().removeAllHighlights(); this.editor.setText(source.toString()); + if (this.source != null) { this.editor.setCaretPosition(newCaretPos); } + setHighlightedTokens(source.getHighlightedTokens()); setCursorReference(getReference(getToken(this.editor.getCaretPosition()))); } finally { @@ -515,10 +580,16 @@ public class EditorPanel { * @param reference */ private void showReference0(EntryReference, Entry> reference) { - if (this.source == null) return; - if (reference == null) return; + if (this.source == null) { + return; + } + + if (reference == null) { + return; + } List tokens = this.controller.getTokensForReference(this.source, reference); + if (tokens.isEmpty()) { // DEBUG System.err.println(String.format("WARNING: no tokens found for %s in %s", reference, this.classHandle.getRef())); @@ -531,6 +602,7 @@ public class EditorPanel { if (token == null) { throw new IllegalArgumentException("Token cannot be null!"); } + navigateToToken(token, SelectionHighlightPainter.INSTANCE); } @@ -546,9 +618,11 @@ public class EditorPanel { // make sure the token is visible in the scroll window Rectangle start = this.editor.modelToView(token.start); Rectangle end = this.editor.modelToView(token.end); + if (start == null || end == null) { return; } + Rectangle show = start.union(end); show.grow(start.width * 10, start.height * 6); SwingUtilities.invokeLater(() -> this.editor.scrollRectToVisible(show)); @@ -625,5 +699,4 @@ public class EditorPanel { SUCCESS, ERRORED, } - } 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 e71894db..7b75f1a8 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 @@ -25,12 +25,15 @@ import cuchaz.enigma.gui.util.ScaleUtil; import cuchaz.enigma.translation.mapping.AccessModifier; import cuchaz.enigma.translation.mapping.EntryChange; import cuchaz.enigma.translation.mapping.EntryMapping; -import cuchaz.enigma.translation.representation.entry.*; +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.LocalVariableEntry; +import cuchaz.enigma.translation.representation.entry.MethodEntry; import cuchaz.enigma.utils.I18n; import cuchaz.enigma.utils.validation.ValidationContext; public class IdentifierPanel { - private final Gui gui; private final JPanel ui = new JPanel(); @@ -57,7 +60,9 @@ public class IdentifierPanel { } public boolean startRenaming() { - if (this.nameField == null) return false; + if (this.nameField == null) { + return false; + } this.nameField.startEditing(); @@ -65,7 +70,9 @@ public class IdentifierPanel { } public boolean startRenaming(String text) { - if (this.nameField == null) return false; + if (this.nameField == null) { + return false; + } this.nameField.startEditing(); this.nameField.setEditText(text); @@ -84,6 +91,7 @@ public class IdentifierPanel { TableHelper th = new TableHelper(this.ui, this.entry, this.gui); th.begin(); + if (this.entry == null) { this.ui.setEnabled(false); } else { @@ -102,8 +110,10 @@ public class IdentifierPanel { th.addModifierRow(I18n.translate("info_panel.identifier.modifier"), EditableType.FIELD, this::onModifierChanged); } else if (deobfEntry instanceof MethodEntry) { MethodEntry me = (MethodEntry) deobfEntry; + if (me.isConstructor()) { ClassEntry ce = me.getParent(); + if (ce != null) { String name = ce.isInnerClass() ? ce.getName() : ce.getFullName(); this.nameField = th.addRenameTextField(EditableType.CLASS, name); @@ -112,6 +122,7 @@ public class IdentifierPanel { 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"), EditableType.METHOD, this::onModifierChanged); } else if (deobfEntry instanceof LocalVariableEntry) { @@ -132,6 +143,7 @@ public class IdentifierPanel { throw new IllegalStateException("unreachable"); } } + th.end(); if (this.nameField != null) { @@ -139,6 +151,7 @@ public class IdentifierPanel { @Override public void onStartEditing(ConvertingTextField field) { int i = field.getText().lastIndexOf('/'); + if (i != -1) { field.selectSubstring(i + 1); } @@ -146,7 +159,10 @@ public class IdentifierPanel { @Override public boolean tryStopEditing(ConvertingTextField field, boolean abort) { - if (abort) return true; + if (abort) { + return true; + } + vc.reset(); vc.setActiveElement(field); validateRename(field.getText()); @@ -162,6 +178,7 @@ public class IdentifierPanel { } EditorPanel e = gui.getActiveEditor(); + if (e != null) { e.getEditor().requestFocusInWindow(); } @@ -192,13 +209,12 @@ public class IdentifierPanel { } private static final class TableHelper { - private final Container c; private final Entry e; private final Gui gui; private int row; - public TableHelper(Container c, Entry e, Gui gui) { + TableHelper(Container c, Entry e, Gui gui) { this.c = c; this.e = e; this.gui = gui; @@ -210,9 +226,7 @@ public class IdentifierPanel { } public void addRow(Component c1, Component c2) { - GridBagConstraintsBuilder cb = GridBagConstraintsBuilder.create() - .insets(2) - .anchor(GridBagConstraints.WEST); + GridBagConstraintsBuilder cb = GridBagConstraintsBuilder.create().insets(2).anchor(GridBagConstraints.WEST); c.add(c1, cb.pos(0, this.row).build()); c.add(c2, cb.pos(1, this.row).weightX(1.0).fill(GridBagConstraints.HORIZONTAL).build()); @@ -239,12 +253,12 @@ public class IdentifierPanel { } 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); + 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)) { @@ -296,7 +310,5 @@ public class IdentifierPanel { // Add an empty panel with y-weight=1 so that all the other elements get placed at the top edge c.add(new JPanel(), GridBagConstraintsBuilder.create().pos(0, row).weight(0.0, 1.0).build()); } - } - } diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/panels/ObfPanel.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/panels/ObfPanel.java index 7783843d..f82e6663 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/panels/ObfPanel.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/panels/ObfPanel.java @@ -13,7 +13,6 @@ import cuchaz.enigma.translation.representation.entry.ClassEntry; import cuchaz.enigma.utils.I18n; public class ObfPanel extends JPanel { - public final ClassSelector obfClasses; private final JLabel title = new JLabel(); @@ -25,9 +24,11 @@ public class ObfPanel extends JPanel { Comparator obfClassComparator = (a, b) -> { String aname = a.getFullName(); String bname = b.getFullName(); + if (aname.length() != bname.length()) { return aname.length() - bname.length(); } + return aname.compareTo(bname); }; @@ -45,5 +46,4 @@ public class ObfPanel extends JPanel { public void retranslateUi() { this.title.setText(I18n.translate("info_panel.classes.obfuscated")); } - } 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 ccded45c..571c638f 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,9 +1,16 @@ package cuchaz.enigma.gui.panels; -import java.awt.*; +import java.awt.BorderLayout; +import java.awt.Component; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; import java.awt.event.MouseEvent; -import javax.swing.*; +import javax.swing.JComboBox; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JTree; import javax.swing.tree.DefaultTreeCellRenderer; import javax.swing.tree.DefaultTreeModel; import javax.swing.tree.TreeNode; @@ -23,144 +30,144 @@ import cuchaz.enigma.translation.representation.entry.ParentedEntry; import cuchaz.enigma.utils.I18n; public class StructurePanel { - private final Gui gui; - - private final JPanel panel = new JPanel(new BorderLayout()); - - private final JPanel optionsPanel; - - 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.gui = gui; - - 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 -> this.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 -> this.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 -> this.showStructure(gui.getActiveEditor())); - this.optionsPanel.add(this.sortingOrder, cb.pos(1, 2).build()); - - this.structureTree = new JTree(); - this.structureTree.setModel(null); - this.structureTree.setCellRenderer(new StructureTreeCellRenderer(gui)); - this.structureTree.setSelectionModel(new SingleTreeSelectionModel()); - this.structureTree.setShowsRootHandles(true); - this.structureTree.addMouseListener(GuiUtil.onMouseClick(this::onClick)); - - this.retranslateUi(); - - this.panel.add(this.optionsPanel, BorderLayout.NORTH); - this.panel.add(new JScrollPane(this.structureTree)); - } - - public void showStructure(EditorPanel editor) { - structureTree.setModel(null); - - if (editor == null) { - this.optionsPanel.setVisible(false); - return; - } - - ClassEntry classEntry = editor.getClassHandle().getRef(); - if (classEntry == null) return; - - this.optionsPanel.setVisible(true); - - // get the class structure - StructureTreeNode node = this.gui.getController().getClassStructure(classEntry, this.getOptions()); - - // show the tree at the root - TreePath path = GuiUtil.getPathToRoot(node); - structureTree.setModel(new DefaultTreeModel((TreeNode) path.getPathComponent(0))); - structureTree.expandPath(path); - structureTree.setSelectionRow(structureTree.getRowForPath(path)); - } - - private void onClick(MouseEvent event) { - if (event.getClickCount() >= 2 && event.getButton() == MouseEvent.BUTTON1) { - // get the selected node - TreePath path = structureTree.getSelectionPath(); - if (path == null) { - return; - } - - Object node = path.getLastPathComponent(); - - if (node instanceof StructureTreeNode) { - this.gui.getController().navigateTo(((StructureTreeNode) node).getEntry()); - } - } - } - - /** - * Creates and returns the options of this structure panel. - */ - private StructureTreeOptions getOptions() { - return new StructureTreeOptions( - (StructureTreeOptions.ObfuscationVisibility) this.obfuscationVisibility.getSelectedItem(), - (StructureTreeOptions.DocumentationVisibility) this.documentationVisibility.getSelectedItem(), - (StructureTreeOptions.SortingOrder) this.sortingOrder.getSelectedItem() - ); - } - - public void retranslateUi() { - this.obfuscationVisibilityLabel.setText(I18n.translate("structure.options.obfuscation")); - this.documentationVisibilityLabel.setText(I18n.translate("structure.options.documentation")); - this.sortingOrderLabel.setText(I18n.translate("structure.options.sorting")); - } - - public JPanel getPanel() { - return this.panel; - } - - private static class StructureTreeCellRenderer extends DefaultTreeCellRenderer { - private final Gui gui; - - StructureTreeCellRenderer(Gui gui) { - this.gui = gui; - } - - @Override - public Component getTreeCellRendererComponent(JTree tree, Object value, boolean selected, boolean expanded, boolean leaf, int row, boolean hasFocus) { - Component c = super.getTreeCellRendererComponent(tree, value, selected, expanded, leaf, row, hasFocus); - ParentedEntry entry = ((StructureTreeNode) value).getEntry(); - - if (entry instanceof ClassEntry classEntry) { - this.setIcon(GuiUtil.getClassIcon(gui, classEntry)); - } else if (entry instanceof MethodEntry methodEntry) { - this.setIcon(GuiUtil.getMethodIcon(methodEntry)); - } else if (entry instanceof FieldEntry) { - this.setIcon(GuiUtil.FIELD_ICON); - } - - this.setText("" + ((StructureTreeNode) value).toHtml()); - - return c; - } - } + private final Gui gui; + + private final JPanel panel = new JPanel(new BorderLayout()); + + private final JPanel optionsPanel; + + 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.gui = gui; + + 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 -> this.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 -> this.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 -> this.showStructure(gui.getActiveEditor())); + this.optionsPanel.add(this.sortingOrder, cb.pos(1, 2).build()); + + this.structureTree = new JTree(); + this.structureTree.setModel(null); + this.structureTree.setCellRenderer(new StructureTreeCellRenderer(gui)); + this.structureTree.setSelectionModel(new SingleTreeSelectionModel()); + this.structureTree.setShowsRootHandles(true); + this.structureTree.addMouseListener(GuiUtil.onMouseClick(this::onClick)); + + this.retranslateUi(); + + this.panel.add(this.optionsPanel, BorderLayout.NORTH); + this.panel.add(new JScrollPane(this.structureTree)); + } + + public void showStructure(EditorPanel editor) { + structureTree.setModel(null); + + if (editor == null) { + this.optionsPanel.setVisible(false); + return; + } + + ClassEntry classEntry = editor.getClassHandle().getRef(); + + if (classEntry == null) { + return; + } + + this.optionsPanel.setVisible(true); + + // get the class structure + StructureTreeNode node = this.gui.getController().getClassStructure(classEntry, this.getOptions()); + + // show the tree at the root + TreePath path = GuiUtil.getPathToRoot(node); + structureTree.setModel(new DefaultTreeModel((TreeNode) path.getPathComponent(0))); + structureTree.expandPath(path); + structureTree.setSelectionRow(structureTree.getRowForPath(path)); + } + + private void onClick(MouseEvent event) { + if (event.getClickCount() >= 2 && event.getButton() == MouseEvent.BUTTON1) { + // get the selected node + TreePath path = structureTree.getSelectionPath(); + + if (path == null) { + return; + } + + Object node = path.getLastPathComponent(); + + if (node instanceof StructureTreeNode) { + this.gui.getController().navigateTo(((StructureTreeNode) node).getEntry()); + } + } + } + + /** + * Creates and returns the options of this structure panel. + */ + private StructureTreeOptions getOptions() { + return new StructureTreeOptions((StructureTreeOptions.ObfuscationVisibility) this.obfuscationVisibility.getSelectedItem(), (StructureTreeOptions.DocumentationVisibility) this.documentationVisibility.getSelectedItem(), (StructureTreeOptions.SortingOrder) this.sortingOrder.getSelectedItem()); + } + + public void retranslateUi() { + this.obfuscationVisibilityLabel.setText(I18n.translate("structure.options.obfuscation")); + this.documentationVisibilityLabel.setText(I18n.translate("structure.options.documentation")); + this.sortingOrderLabel.setText(I18n.translate("structure.options.sorting")); + } + + public JPanel getPanel() { + return this.panel; + } + + private static class StructureTreeCellRenderer extends DefaultTreeCellRenderer { + private final Gui gui; + + StructureTreeCellRenderer(Gui gui) { + this.gui = gui; + } + + @Override + public Component getTreeCellRendererComponent(JTree tree, Object value, boolean selected, boolean expanded, boolean leaf, int row, boolean hasFocus) { + Component c = super.getTreeCellRendererComponent(tree, value, selected, expanded, leaf, row, hasFocus); + ParentedEntry entry = ((StructureTreeNode) value).getEntry(); + + if (entry instanceof ClassEntry classEntry) { + this.setIcon(GuiUtil.getClassIcon(gui, classEntry)); + } else if (entry instanceof MethodEntry methodEntry) { + this.setIcon(GuiUtil.getMethodIcon(methodEntry)); + } else if (entry instanceof FieldEntry) { + this.setIcon(GuiUtil.FIELD_ICON); + } + + this.setText("" + ((StructureTreeNode) value).toHtml()); + + return c; + } + } } diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/renderer/CallsTreeCellRenderer.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/renderer/CallsTreeCellRenderer.java index 0aa6510c..3791a1ec 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/renderer/CallsTreeCellRenderer.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/renderer/CallsTreeCellRenderer.java @@ -1,45 +1,51 @@ package cuchaz.enigma.gui.renderer; -import cuchaz.enigma.analysis.*; +import java.awt.Component; + +import javax.swing.JTree; +import javax.swing.tree.DefaultTreeCellRenderer; + +import cuchaz.enigma.analysis.ClassReferenceTreeNode; +import cuchaz.enigma.analysis.EntryReference; +import cuchaz.enigma.analysis.FieldReferenceTreeNode; +import cuchaz.enigma.analysis.MethodReferenceTreeNode; +import cuchaz.enigma.analysis.ReferenceTreeNode; import cuchaz.enigma.gui.Gui; import cuchaz.enigma.gui.config.UiConfig; import cuchaz.enigma.gui.util.GuiUtil; import cuchaz.enigma.translation.representation.entry.MethodEntry; -import javax.swing.*; -import javax.swing.tree.DefaultTreeCellRenderer; -import java.awt.*; - public class CallsTreeCellRenderer extends DefaultTreeCellRenderer { - private final Gui gui; - - public CallsTreeCellRenderer(Gui gui) { - this.gui = gui; - } - - @Override - public Component getTreeCellRendererComponent(JTree tree, Object value, boolean sel, boolean expanded, boolean leaf, int row, boolean hasFocus) { - Component c = super.getTreeCellRendererComponent(tree, value, sel, expanded, leaf, row, hasFocus); - EntryReference reference = ((ReferenceTreeNode) value).getReference(); - - this.setForeground(UiConfig.getTextColor()); - - // if the node represents the method calling the entry - if (reference != null) { - if (reference.context instanceof MethodEntry) { - this.setIcon(GuiUtil.getMethodIcon((MethodEntry) reference.context)); - } - // if the node represents the called entry - } else { - if (value instanceof ClassReferenceTreeNode node) { - this.setIcon(GuiUtil.getClassIcon(this.gui, node.getEntry())); - } else if (value instanceof MethodReferenceTreeNode node) { - this.setIcon(GuiUtil.getMethodIcon(node.getEntry())); - } else if (value instanceof FieldReferenceTreeNode) { - this.setIcon(GuiUtil.FIELD_ICON); - } - } - - return c; - } + private final Gui gui; + + public CallsTreeCellRenderer(Gui gui) { + this.gui = gui; + } + + @Override + public Component getTreeCellRendererComponent(JTree tree, Object value, boolean sel, boolean expanded, boolean leaf, int row, boolean hasFocus) { + Component c = super.getTreeCellRendererComponent(tree, value, sel, expanded, leaf, row, hasFocus); + EntryReference reference = ((ReferenceTreeNode) value).getReference(); + + this.setForeground(UiConfig.getTextColor()); + + // if the node represents the method calling the entry + if (reference != null) { + if (reference.context instanceof MethodEntry) { + this.setIcon(GuiUtil.getMethodIcon((MethodEntry) reference.context)); + } + + // if the node represents the called entry + } else { + if (value instanceof ClassReferenceTreeNode node) { + this.setIcon(GuiUtil.getClassIcon(this.gui, node.getEntry())); + } else if (value instanceof MethodReferenceTreeNode node) { + this.setIcon(GuiUtil.getMethodIcon(node.getEntry())); + } else if (value instanceof FieldReferenceTreeNode) { + this.setIcon(GuiUtil.FIELD_ICON); + } + } + + return c; + } } diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/renderer/ImplementationsTreeCellRenderer.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/renderer/ImplementationsTreeCellRenderer.java index 7bf39005..b4126c02 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/renderer/ImplementationsTreeCellRenderer.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/renderer/ImplementationsTreeCellRenderer.java @@ -1,34 +1,35 @@ package cuchaz.enigma.gui.renderer; +import java.awt.Component; + +import javax.swing.JTree; +import javax.swing.tree.DefaultTreeCellRenderer; + import cuchaz.enigma.analysis.ClassImplementationsTreeNode; import cuchaz.enigma.analysis.MethodImplementationsTreeNode; import cuchaz.enigma.gui.Gui; import cuchaz.enigma.gui.config.UiConfig; import cuchaz.enigma.gui.util.GuiUtil; -import javax.swing.*; -import javax.swing.tree.DefaultTreeCellRenderer; -import java.awt.*; - public class ImplementationsTreeCellRenderer extends DefaultTreeCellRenderer { - private final Gui gui; + private final Gui gui; - public ImplementationsTreeCellRenderer(Gui gui) { - this.gui = gui; - } + public ImplementationsTreeCellRenderer(Gui gui) { + this.gui = gui; + } - @Override - public Component getTreeCellRendererComponent(JTree tree, Object value, boolean sel, boolean expanded, boolean leaf, int row, boolean hasFocus) { - Component c = super.getTreeCellRendererComponent(tree, value, sel, expanded, leaf, row, hasFocus); + @Override + public Component getTreeCellRendererComponent(JTree tree, Object value, boolean sel, boolean expanded, boolean leaf, int row, boolean hasFocus) { + Component c = super.getTreeCellRendererComponent(tree, value, sel, expanded, leaf, row, hasFocus); - this.setForeground(UiConfig.getTextColor()); + this.setForeground(UiConfig.getTextColor()); - if (value instanceof ClassImplementationsTreeNode node) { - this.setIcon(GuiUtil.getClassIcon(this.gui, node.getClassEntry())); - } else if (value instanceof MethodImplementationsTreeNode node) { - this.setIcon(GuiUtil.getMethodIcon(node.getMethodEntry())); - } + if (value instanceof ClassImplementationsTreeNode node) { + this.setIcon(GuiUtil.getClassIcon(this.gui, node.getClassEntry())); + } else if (value instanceof MethodImplementationsTreeNode node) { + this.setIcon(GuiUtil.getMethodIcon(node.getMethodEntry())); + } - return c; - } + return c; + } } diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/renderer/InheritanceTreeCellRenderer.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/renderer/InheritanceTreeCellRenderer.java index a1025531..04bf0f93 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/renderer/InheritanceTreeCellRenderer.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/renderer/InheritanceTreeCellRenderer.java @@ -1,13 +1,13 @@ /******************************************************************************* - * Copyright (c) 2015 Jeff Martin. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html - *

- * Contributors: - * Jeff Martin - initial API and implementation - ******************************************************************************/ +* Copyright (c) 2015 Jeff Martin. +* All rights reserved. This program and the accompanying materials +* are made available under the terms of the GNU Lesser General Public +* License v3.0 which accompanies this distribution, and is available at +* http://www.gnu.org/licenses/lgpl.html +* +*

Contributors: +* Jeff Martin - initial API and implementation +******************************************************************************/ package cuchaz.enigma.gui.renderer; @@ -37,6 +37,7 @@ public class InheritanceTreeCellRenderer extends DefaultTreeCellRenderer { if (!(value instanceof MethodInheritanceTreeNode node) || node.isImplemented()) { ret.setForeground(UiConfig.getTextColor()); ret.setFont(ret.getFont().deriveFont(Font.PLAIN)); + if (value instanceof ClassInheritanceTreeNode) { this.setIcon(GuiUtil.getClassIcon(this.gui, ((ClassInheritanceTreeNode) value).getClassEntry())); } else if (value instanceof MethodInheritanceTreeNode) { diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/renderer/MessageListCellRenderer.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/renderer/MessageListCellRenderer.java index b6ae0c50..123990e4 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/renderer/MessageListCellRenderer.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/renderer/MessageListCellRenderer.java @@ -10,15 +10,15 @@ import cuchaz.enigma.network.Message; // For now, just render the translated text. // TODO: Icons or something later? public class MessageListCellRenderer extends DefaultListCellRenderer { - @Override public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus); Message message = (Message) value; + if (message != null) { setText(message.translate()); } + return this; } - } 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 index f9a1cae6..09cdc9bd 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/renderer/StructureOptionListCellRenderer.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/renderer/StructureOptionListCellRenderer.java @@ -1,21 +1,22 @@ package cuchaz.enigma.gui.renderer; +import java.awt.Component; + +import javax.swing.DefaultListCellRenderer; +import javax.swing.JList; + 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); - @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())); - } + if (value instanceof StructureTreeOptions.Option option) { + this.setText(I18n.translate(option.getTranslationKey())); + } - return c; - } + return c; + } } diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/search/SearchEntry.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/search/SearchEntry.java index 91727c38..93507bcd 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/search/SearchEntry.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/search/SearchEntry.java @@ -3,7 +3,6 @@ package cuchaz.enigma.gui.search; import java.util.List; public interface SearchEntry { - List getSearchableNames(); /** @@ -13,5 +12,4 @@ public interface SearchEntry { * @return a unique identifier for this search entry */ String getIdentifier(); - } diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/search/SearchUtil.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/search/SearchUtil.java index a3b35faa..c8212ce5 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/search/SearchUtil.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/search/SearchUtil.java @@ -1,6 +1,14 @@ package cuchaz.enigma.gui.search; -import java.util.*; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; import java.util.concurrent.Executor; import java.util.concurrent.Executors; import java.util.concurrent.atomic.AtomicBoolean; @@ -14,7 +22,6 @@ import java.util.stream.Stream; import cuchaz.enigma.utils.Pair; public class SearchUtil { - private final Map> entries = new HashMap<>(); private final Map hitCount = new HashMap<>(); private final Executor searchExecutor = Executors.newWorkStealingPool(); @@ -45,12 +52,7 @@ public class SearchUtil { } public Stream search(String term) { - return entries.values().parallelStream() - .map(e -> new Pair<>(e, e.getScore(term, hitCount.getOrDefault(e.searchEntry.getIdentifier(), 0)))) - .filter(e -> e.b > 0) - .sorted(Comparator.comparingDouble(o -> -o.b)) - .map(e -> e.a.searchEntry) - .sequential(); + return entries.values().parallelStream().map(e -> new Pair<>(e, e.getScore(term, hitCount.getOrDefault(e.searchEntry.getIdentifier(), 0)))).filter(e -> e.b > 0).sorted(Comparator.comparingDouble(o -> -o.b)).map(e -> e.a.searchEntry).sequential(); } public SearchControl asyncSearch(String term, SearchResultConsumer consumer) { @@ -61,21 +63,36 @@ public class SearchUtil { AtomicInteger size = new AtomicInteger(); AtomicBoolean control = new AtomicBoolean(false); AtomicInteger elapsed = new AtomicInteger(); + for (Entry value : entries.values()) { searchExecutor.execute(() -> { try { - if (control.get()) return; + if (control.get()) { + return; + } + float score = value.getScore(term, hitCount.getOrDefault(value.searchEntry.getIdentifier(), 0)); - if (score <= 0) return; + + if (score <= 0) { + return; + } + score = -score; // sort descending + try { scoresLock.lock(); - if (control.get()) return; + + if (control.get()) { + return; + } + int dataSize = size.getAndIncrement(); int index = Arrays.binarySearch(scores, 0, dataSize, score); + if (index < 0) { index = ~index; } + System.arraycopy(scores, index, scores, index + 1, dataSize - index); scores[index] = score; consumer.add(index, value.searchEntry); @@ -113,7 +130,6 @@ public class SearchUtil { } public static final class Entry { - public final T searchEntry; private final String[][] components; @@ -124,9 +140,7 @@ public class SearchUtil { public float getScore(String term, int hits) { String ucTerm = term.toUpperCase(Locale.ROOT); - float maxScore = (float) Arrays.stream(components) - .mapToDouble(name -> getScoreFor(ucTerm, name)) - .max().orElse(0.0); + float maxScore = (float) Arrays.stream(components).mapToDouble(name -> getScoreFor(ucTerm, name)).max().orElse(0.0); return maxScore * (hits + 1); } @@ -156,17 +170,20 @@ public class SearchUtil { String component = name[componentIndex]; float posMultiplier = (name.length - componentIndex) * 0.3f; Map newSnapshots = new HashMap<>(); + for (Map.Entry snapshot : snapshots.entrySet()) { String remaining = snapshot.getKey(); float score = snapshot.getValue(); component = component.toUpperCase(Locale.ROOT); int l = compareEqualLength(remaining, component); + for (int i = 1; i <= l; i++) { float baseScore = scorePerChar * i; float chainBonus = (i - 1) * 0.5f; merge(newSnapshots, Collections.singletonMap(remaining.substring(i), score + baseScore * posMultiplier + chainBonus), Math::max); } } + merge(snapshots, newSnapshots, Math::max); } @@ -180,24 +197,24 @@ public class SearchUtil { } public static Entry from(T e) { - String[][] components = e.getSearchableNames().parallelStream() - .map(Entry::wordwiseSplit) - .toArray(String[][]::new); + String[][] components = e.getSearchableNames().parallelStream().map(Entry::wordwiseSplit).toArray(String[][]::new); return new Entry<>(e, components); } private static int compareEqualLength(String s1, String s2) { int len = 0; + while (len < s1.length() && len < s2.length() && s1.charAt(len) == s2.charAt(len)) { len += 1; } + return len; } /** * Splits the given input into components, trying to detect word parts. - *

- * Example of how words get split (using | as seperator): + * + *

Example of how words get split (using | as seperator): *

MinecraftClientGame -> Minecraft|Client|Game

*

HTTPInputStream -> HTTP|Input|Stream

*

class_932 -> class|_|932

@@ -210,46 +227,57 @@ public class SearchUtil { */ private static String[] wordwiseSplit(String input) { List list = new ArrayList<>(); + while (!input.isEmpty()) { int take; + if (Character.isLetter(input.charAt(0))) { if (input.length() == 1) { take = 1; } else { boolean nextSegmentIsUppercase = Character.isUpperCase(input.charAt(0)) && Character.isUpperCase(input.charAt(1)); + if (nextSegmentIsUppercase) { int nextLowercase = 1; + while (Character.isUpperCase(input.charAt(nextLowercase))) { nextLowercase += 1; + if (nextLowercase == input.length()) { nextLowercase += 1; break; } } + take = nextLowercase - 1; } else { int nextUppercase = 1; + while (nextUppercase < input.length() && Character.isLowerCase(input.charAt(nextUppercase))) { nextUppercase += 1; } + take = nextUppercase; } } } else if (Character.isDigit(input.charAt(0))) { int nextNonNum = 1; + while (nextNonNum < input.length() && Character.isLetter(input.charAt(nextNonNum)) && !Character.isLowerCase(input.charAt(nextNonNum))) { nextNonNum += 1; } + take = nextNonNum; } else { take = 1; } + list.add(input.substring(0, take)); input = input.substring(take); } + return list.toArray(new String[0]); } - } @FunctionalInterface @@ -264,5 +292,4 @@ public class SearchUtil { float getProgress(); } - } diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/stats/StatsGenerator.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/stats/StatsGenerator.java index 20d6a0eb..99b5572b 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/stats/StatsGenerator.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/stats/StatsGenerator.java @@ -1,5 +1,10 @@ package cuchaz.enigma.gui.stats; +import java.util.EnumSet; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + import cuchaz.enigma.EnigmaProject; import cuchaz.enigma.ProgressListener; import cuchaz.enigma.analysis.index.EntryIndex; @@ -7,109 +12,112 @@ import cuchaz.enigma.translation.mapping.EntryRemapper; import cuchaz.enigma.translation.mapping.EntryResolver; import cuchaz.enigma.translation.mapping.ResolutionStrategy; import cuchaz.enigma.translation.representation.TypeDescriptor; -import cuchaz.enigma.translation.representation.entry.*; +import cuchaz.enigma.translation.representation.entry.ClassEntry; +import cuchaz.enigma.translation.representation.entry.Entry; +import cuchaz.enigma.translation.representation.entry.FieldDefEntry; +import cuchaz.enigma.translation.representation.entry.FieldEntry; +import cuchaz.enigma.translation.representation.entry.LocalVariableEntry; +import cuchaz.enigma.translation.representation.entry.MethodDefEntry; +import cuchaz.enigma.translation.representation.entry.MethodEntry; import cuchaz.enigma.utils.I18n; -import java.util.*; - public class StatsGenerator { - private final EnigmaProject project; - private final EntryIndex entryIndex; - private final EntryRemapper mapper; - private final EntryResolver entryResolver; - - public StatsGenerator(EnigmaProject project) { - this.project = project; - this.entryIndex = project.getJarIndex().getEntryIndex(); - this.mapper = project.getMapper(); - this.entryResolver = project.getJarIndex().getEntryResolver(); - } - - public StatsResult generate(ProgressListener progress, Set includedMembers, String topLevelPackage, boolean includeSynthetic) { - includedMembers = EnumSet.copyOf(includedMembers); - int totalWork = 0; - int totalMappable = 0; - - if (includedMembers.contains(StatsMember.METHODS) || includedMembers.contains(StatsMember.PARAMETERS)) { - totalWork += entryIndex.getMethods().size(); - } - - if (includedMembers.contains(StatsMember.FIELDS)) { - totalWork += entryIndex.getFields().size(); - } - - if (includedMembers.contains(StatsMember.CLASSES)) { - totalWork += entryIndex.getClasses().size(); - } - - progress.init(totalWork, I18n.translate("progress.stats")); - - Map counts = new HashMap<>(); - - int numDone = 0; - if (includedMembers.contains(StatsMember.METHODS) || includedMembers.contains(StatsMember.PARAMETERS)) { - for (MethodEntry method : entryIndex.getMethods()) { - progress.step(numDone++, I18n.translate("type.methods")); - MethodEntry root = entryResolver - .resolveEntry(method, ResolutionStrategy.RESOLVE_ROOT) - .stream() - .findFirst() - .orElseThrow(AssertionError::new); - - if (root == method) { - if (includedMembers.contains(StatsMember.METHODS) && !((MethodDefEntry) method).getAccess().isSynthetic()) { - update(counts, method); - totalMappable ++; - } - - if (includedMembers.contains(StatsMember.PARAMETERS) && (!((MethodDefEntry) method).getAccess().isSynthetic() || includeSynthetic)) { - int index = ((MethodDefEntry) method).getAccess().isStatic() ? 0 : 1; - for (TypeDescriptor argument : method.getDesc().getArgumentDescs()) { - update(counts, new LocalVariableEntry(method, index, "", true,null)); - index += argument.getSize(); - totalMappable ++; - } - } - } - } - } - - if (includedMembers.contains(StatsMember.FIELDS)) { - for (FieldEntry field : entryIndex.getFields()) { - progress.step(numDone++, I18n.translate("type.fields")); - if (!((FieldDefEntry)field).getAccess().isSynthetic()) { - update(counts, field); - totalMappable ++; - } - } - } - - if (includedMembers.contains(StatsMember.CLASSES)) { - for (ClassEntry clazz : entryIndex.getClasses()) { - progress.step(numDone++, I18n.translate("type.classes")); - update(counts, clazz); - totalMappable ++; - } - } - - progress.step(-1, I18n.translate("progress.stats.data")); - - StatsResult.Tree tree = new StatsResult.Tree<>(); - - for (Map.Entry entry : counts.entrySet()) { - if (entry.getKey().startsWith(topLevelPackage)) { - tree.getNode(entry.getKey()).value = entry.getValue(); - } - } - - tree.collapse(tree.root); - return new StatsResult(totalMappable, counts.values().stream().mapToInt(i -> i).sum(), tree); - } - - private void update(Map counts, Entry entry) { - if (project.isObfuscated(entry)) { - String parent = mapper.deobfuscate(entry.getAncestry().get(0)).getName().replace('/', '.'); - counts.put(parent, counts.getOrDefault(parent, 0) + 1); - } - } + private final EnigmaProject project; + private final EntryIndex entryIndex; + private final EntryRemapper mapper; + private final EntryResolver entryResolver; + + public StatsGenerator(EnigmaProject project) { + this.project = project; + this.entryIndex = project.getJarIndex().getEntryIndex(); + this.mapper = project.getMapper(); + this.entryResolver = project.getJarIndex().getEntryResolver(); + } + + public StatsResult generate(ProgressListener progress, Set includedMembers, String topLevelPackage, boolean includeSynthetic) { + includedMembers = EnumSet.copyOf(includedMembers); + int totalWork = 0; + int totalMappable = 0; + + if (includedMembers.contains(StatsMember.METHODS) || includedMembers.contains(StatsMember.PARAMETERS)) { + totalWork += entryIndex.getMethods().size(); + } + + if (includedMembers.contains(StatsMember.FIELDS)) { + totalWork += entryIndex.getFields().size(); + } + + if (includedMembers.contains(StatsMember.CLASSES)) { + totalWork += entryIndex.getClasses().size(); + } + + progress.init(totalWork, I18n.translate("progress.stats")); + + Map counts = new HashMap<>(); + + int numDone = 0; + + if (includedMembers.contains(StatsMember.METHODS) || includedMembers.contains(StatsMember.PARAMETERS)) { + for (MethodEntry method : entryIndex.getMethods()) { + progress.step(numDone++, I18n.translate("type.methods")); + MethodEntry root = entryResolver.resolveEntry(method, ResolutionStrategy.RESOLVE_ROOT).stream().findFirst().orElseThrow(AssertionError::new); + + if (root == method) { + if (includedMembers.contains(StatsMember.METHODS) && !((MethodDefEntry) method).getAccess().isSynthetic()) { + update(counts, method); + totalMappable++; + } + + if (includedMembers.contains(StatsMember.PARAMETERS) && (!((MethodDefEntry) method).getAccess().isSynthetic() || includeSynthetic)) { + int index = ((MethodDefEntry) method).getAccess().isStatic() ? 0 : 1; + + for (TypeDescriptor argument : method.getDesc().getArgumentDescs()) { + update(counts, new LocalVariableEntry(method, index, "", true, null)); + index += argument.getSize(); + totalMappable++; + } + } + } + } + } + + if (includedMembers.contains(StatsMember.FIELDS)) { + for (FieldEntry field : entryIndex.getFields()) { + progress.step(numDone++, I18n.translate("type.fields")); + + if (!((FieldDefEntry) field).getAccess().isSynthetic()) { + update(counts, field); + totalMappable++; + } + } + } + + if (includedMembers.contains(StatsMember.CLASSES)) { + for (ClassEntry clazz : entryIndex.getClasses()) { + progress.step(numDone++, I18n.translate("type.classes")); + update(counts, clazz); + totalMappable++; + } + } + + progress.step(-1, I18n.translate("progress.stats.data")); + + StatsResult.Tree tree = new StatsResult.Tree<>(); + + for (Map.Entry entry : counts.entrySet()) { + if (entry.getKey().startsWith(topLevelPackage)) { + tree.getNode(entry.getKey()).value = entry.getValue(); + } + } + + tree.collapse(tree.root); + return new StatsResult(totalMappable, counts.values().stream().mapToInt(i -> i).sum(), tree); + } + + private void update(Map counts, Entry entry) { + if (project.isObfuscated(entry)) { + String parent = mapper.deobfuscate(entry.getAncestry().get(0)).getName().replace('/', '.'); + counts.put(parent, counts.getOrDefault(parent, 0) + 1); + } + } } diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/stats/StatsMember.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/stats/StatsMember.java index 0e2452fa..0037ab5b 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/stats/StatsMember.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/stats/StatsMember.java @@ -1,8 +1,8 @@ package cuchaz.enigma.gui.stats; public enum StatsMember { - CLASSES, - METHODS, - FIELDS, - PARAMETERS + CLASSES, + METHODS, + FIELDS, + PARAMETERS } diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/stats/StatsResult.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/stats/StatsResult.java index 0a71a647..12726c0b 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/stats/StatsResult.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/stats/StatsResult.java @@ -1,14 +1,13 @@ package cuchaz.enigma.gui.stats; -import com.google.gson.GsonBuilder; - import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; -public final class StatsResult { +import com.google.gson.GsonBuilder; +public final class StatsResult { private final int total; private final int unmapped; private final Tree tree; @@ -101,5 +100,4 @@ public final class StatsResult { } } } - } diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/util/AbstractListCellRenderer.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/util/AbstractListCellRenderer.java index 612e3e92..f8ce36db 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/util/AbstractListCellRenderer.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/util/AbstractListCellRenderer.java @@ -3,11 +3,16 @@ package cuchaz.enigma.gui.util; import java.awt.Component; import java.awt.event.MouseEvent; -import javax.swing.*; +import javax.swing.BorderFactory; +import javax.swing.JComponent; +import javax.swing.JList; +import javax.swing.JPanel; +import javax.swing.ListCellRenderer; +import javax.swing.UIDefaults; +import javax.swing.UIManager; import javax.swing.border.Border; public abstract class AbstractListCellRenderer extends JPanel implements ListCellRenderer { - private static final Border NO_FOCUS_BORDER = BorderFactory.createEmptyBorder(1, 1, 1, 1); private Border noFocusBorder; @@ -21,22 +26,27 @@ public abstract class AbstractListCellRenderer extends JPanel implements List Border border = UIManager.getLookAndFeel().getDefaults().getBorder("List.List.cellNoFocusBorder"); noFocusBorder = border != null ? border : NO_FOCUS_BORDER; } + return noFocusBorder; } protected Border getBorder(boolean isSelected, boolean cellHasFocus) { Border b = null; + if (cellHasFocus) { UIDefaults defaults = UIManager.getLookAndFeel().getDefaults(); + if (isSelected) { b = defaults.getBorder("List.focusSelectedCellHighlightBorder"); } + if (b == null) { b = defaults.getBorder("List.focusCellHighlightBorder"); } } else { b = getNoFocusBorder(); } + return b; } @@ -68,10 +78,11 @@ public abstract class AbstractListCellRenderer extends JPanel implements List @Override public String getToolTipText(MouseEvent event) { Component c = getComponentAt(event.getPoint()); + if (c instanceof JComponent) { return ((JComponent) c).getToolTipText(); } + return getToolTipText(); } - } diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/util/GridBagConstraintsBuilder.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/util/GridBagConstraintsBuilder.java index 6a756867..2d8aa73e 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/util/GridBagConstraintsBuilder.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/util/GridBagConstraintsBuilder.java @@ -3,7 +3,6 @@ package cuchaz.enigma.gui.util; import java.awt.GridBagConstraints; public final class GridBagConstraintsBuilder { - private final GridBagConstraints inner; private GridBagConstraintsBuilder(GridBagConstraints inner) { @@ -136,5 +135,4 @@ public final class GridBagConstraintsBuilder { public GridBagConstraints build() { return (GridBagConstraints) this.inner.clone(); } - } diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/util/GuiUtil.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/util/GuiUtil.java index 3b8ecbc5..d0784243 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/util/GuiUtil.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/util/GuiUtil.java @@ -1,8 +1,17 @@ package cuchaz.enigma.gui.util; -import java.awt.*; +import java.awt.Color; +import java.awt.Cursor; +import java.awt.Desktop; +import java.awt.Font; +import java.awt.Toolkit; import java.awt.datatransfer.StringSelection; -import java.awt.event.*; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.awt.event.MouseListener; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; +import java.awt.event.WindowListener; import java.awt.font.TextAttribute; import java.io.IOException; import java.net.URI; @@ -13,7 +22,14 @@ import java.util.Map; import java.util.NoSuchElementException; import java.util.function.Consumer; -import javax.swing.*; +import javax.swing.Icon; +import javax.swing.JComponent; +import javax.swing.JLabel; +import javax.swing.JToolTip; +import javax.swing.Popup; +import javax.swing.PopupFactory; +import javax.swing.Timer; +import javax.swing.ToolTipManager; import javax.swing.tree.TreeNode; import javax.swing.tree.TreePath; @@ -28,160 +44,160 @@ import cuchaz.enigma.translation.representation.entry.MethodEntry; import cuchaz.enigma.utils.Os; public class GuiUtil { - public static final Icon CLASS_ICON = loadIcon("class"); - public static final Icon INTERFACE_ICON = loadIcon("interface"); - public static final Icon ENUM_ICON = loadIcon("enum"); - public static final Icon ANNOTATION_ICON = loadIcon("annotation"); - public static final Icon RECORD_ICON = loadIcon("record"); - public static final Icon METHOD_ICON = loadIcon("method"); - public static final Icon FIELD_ICON = loadIcon("field"); - public static final Icon CONSTRUCTOR_ICON = loadIcon("constructor"); - - public static void openUrl(String url) { - try { - switch (Os.getOs()) { - case LINUX: - new ProcessBuilder("/usr/bin/env", "xdg-open", url).start(); - break; - default: - if (Desktop.isDesktopSupported()) { - Desktop desktop = Desktop.getDesktop(); - desktop.browse(new URI(url)); - } - } - } catch (IOException ex) { - throw new RuntimeException(ex); - } catch (URISyntaxException ex) { - throw new IllegalArgumentException(ex); - } - } - - public static JLabel unboldLabel(JLabel label) { - Font font = label.getFont(); - label.setFont(font.deriveFont(font.getStyle() & ~Font.BOLD)); - return label; - } - - /** - * Puts the provided {@code text} in the system clipboard. - */ - public static void copyToClipboard(String text) { - Toolkit.getDefaultToolkit().getSystemClipboard().setContents(new StringSelection(text), null); - } - - public static void showPopup(JComponent component, String text, int x, int y) { - // from https://stackoverflow.com/questions/39955015/java-swing-show-tooltip-as-a-message-dialog - JToolTip tooltip = new JToolTip(); - tooltip.setTipText(text); - Popup p = PopupFactory.getSharedInstance().getPopup(component, tooltip, x + 10, y); - p.show(); - Timer t = new Timer(1000, e -> p.hide()); - t.setRepeats(false); - t.start(); - } - - public static void showToolTipNow(JComponent component) { - // HACKHACK: trick the tooltip manager into showing the tooltip right now - ToolTipManager manager = ToolTipManager.sharedInstance(); - int oldDelay = manager.getInitialDelay(); - manager.setInitialDelay(0); - manager.mouseMoved(new MouseEvent(component, MouseEvent.MOUSE_MOVED, System.currentTimeMillis(), 0, 0, 0, 0, false)); - manager.setInitialDelay(oldDelay); - } - - public static JLabel createLink(String text, Runnable action) { - JLabel link = new JLabel(text); - link.setForeground(Color.BLUE.darker()); - link.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); - @SuppressWarnings("unchecked") - Map attributes = (Map) link.getFont().getAttributes(); - attributes.put(TextAttribute.UNDERLINE, TextAttribute.UNDERLINE_ON); - link.setFont(link.getFont().deriveFont(attributes)); - link.addMouseListener(new MouseAdapter() { - @Override - public void mousePressed(MouseEvent e) { - action.run(); - } - }); - return link; - } - - public static Icon loadIcon(String name) { - String path = "icons/" + name + ".svg"; - - // Do an eager check for a missing icon since FlatSVGIcon does it later at render time - if (GuiUtil.class.getResource('/' + path) == null) { - throw new NoSuchElementException("Missing icon: '" + name + "' at " + path); - } - - // Note: the width and height are scaled automatically because the FlatLaf UI scale - // is set in LookAndFeel.setGlobalLAF() - return new FlatSVGIcon(path, 16, 16, GuiUtil.class.getClassLoader()); - } - - public static Icon getClassIcon(Gui gui, ClassEntry entry) { - EntryIndex entryIndex = gui.getController().project.getJarIndex().getEntryIndex(); - AccessFlags access = entryIndex.getClassAccess(entry); - - if (access != null) { - if (access.isAnnotation()) { - return ANNOTATION_ICON; - } else if (access.isInterface()) { - return INTERFACE_ICON; - } else if (access.isEnum()) { - return ENUM_ICON; - } else if (entryIndex.getDefinition(entry).isRecord()) { - return RECORD_ICON; - } - } - - return CLASS_ICON; - } - - public static Icon getMethodIcon(MethodEntry entry) { - if (entry.isConstructor()) { - return CONSTRUCTOR_ICON; - } - return METHOD_ICON; - } - - public static TreePath getPathToRoot(TreeNode node) { - List nodes = Lists.newArrayList(); - TreeNode n = node; - - do { - nodes.add(n); - n = n.getParent(); - } while (n != null); - - Collections.reverse(nodes); - return new TreePath(nodes.toArray()); - } - - public static MouseListener onMouseClick(Consumer op) { - return new MouseAdapter() { - @Override - public void mouseClicked(MouseEvent e) { - op.accept(e); - } - }; - } - - public static MouseListener onMousePress(Consumer op) { - return new MouseAdapter() { - @Override - public void mousePressed(MouseEvent e) { - op.accept(e); - } - }; - } - - public static WindowListener onWindowClose(Consumer op) { - return new WindowAdapter() { - @Override - public void windowClosing(WindowEvent e) { - op.accept(e); - } - }; - } + public static final Icon CLASS_ICON = loadIcon("class"); + public static final Icon INTERFACE_ICON = loadIcon("interface"); + public static final Icon ENUM_ICON = loadIcon("enum"); + public static final Icon ANNOTATION_ICON = loadIcon("annotation"); + public static final Icon RECORD_ICON = loadIcon("record"); + public static final Icon METHOD_ICON = loadIcon("method"); + public static final Icon FIELD_ICON = loadIcon("field"); + public static final Icon CONSTRUCTOR_ICON = loadIcon("constructor"); + + public static void openUrl(String url) { + try { + switch (Os.getOs()) { + case LINUX: + new ProcessBuilder("/usr/bin/env", "xdg-open", url).start(); + break; + default: + if (Desktop.isDesktopSupported()) { + Desktop desktop = Desktop.getDesktop(); + desktop.browse(new URI(url)); + } + } + } catch (IOException ex) { + throw new RuntimeException(ex); + } catch (URISyntaxException ex) { + throw new IllegalArgumentException(ex); + } + } + + public static JLabel unboldLabel(JLabel label) { + Font font = label.getFont(); + label.setFont(font.deriveFont(font.getStyle() & ~Font.BOLD)); + return label; + } + + /** + * Puts the provided {@code text} in the system clipboard. + */ + public static void copyToClipboard(String text) { + Toolkit.getDefaultToolkit().getSystemClipboard().setContents(new StringSelection(text), null); + } + + public static void showPopup(JComponent component, String text, int x, int y) { + // from https://stackoverflow.com/questions/39955015/java-swing-show-tooltip-as-a-message-dialog + JToolTip tooltip = new JToolTip(); + tooltip.setTipText(text); + Popup p = PopupFactory.getSharedInstance().getPopup(component, tooltip, x + 10, y); + p.show(); + Timer t = new Timer(1000, e -> p.hide()); + t.setRepeats(false); + t.start(); + } + + public static void showToolTipNow(JComponent component) { + // HACKHACK: trick the tooltip manager into showing the tooltip right now + ToolTipManager manager = ToolTipManager.sharedInstance(); + int oldDelay = manager.getInitialDelay(); + manager.setInitialDelay(0); + manager.mouseMoved(new MouseEvent(component, MouseEvent.MOUSE_MOVED, System.currentTimeMillis(), 0, 0, 0, 0, false)); + manager.setInitialDelay(oldDelay); + } + + public static JLabel createLink(String text, Runnable action) { + JLabel link = new JLabel(text); + link.setForeground(Color.BLUE.darker()); + link.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); + @SuppressWarnings("unchecked") Map attributes = (Map) link.getFont().getAttributes(); + attributes.put(TextAttribute.UNDERLINE, TextAttribute.UNDERLINE_ON); + link.setFont(link.getFont().deriveFont(attributes)); + link.addMouseListener(new MouseAdapter() { + @Override + public void mousePressed(MouseEvent e) { + action.run(); + } + }); + return link; + } + + public static Icon loadIcon(String name) { + String path = "icons/" + name + ".svg"; + + // Do an eager check for a missing icon since FlatSVGIcon does it later at render time + if (GuiUtil.class.getResource('/' + path) == null) { + throw new NoSuchElementException("Missing icon: '" + name + "' at " + path); + } + + // Note: the width and height are scaled automatically because the FlatLaf UI scale + // is set in LookAndFeel.setGlobalLAF() + return new FlatSVGIcon(path, 16, 16, GuiUtil.class.getClassLoader()); + } + + public static Icon getClassIcon(Gui gui, ClassEntry entry) { + EntryIndex entryIndex = gui.getController().project.getJarIndex().getEntryIndex(); + AccessFlags access = entryIndex.getClassAccess(entry); + + if (access != null) { + if (access.isAnnotation()) { + return ANNOTATION_ICON; + } else if (access.isInterface()) { + return INTERFACE_ICON; + } else if (access.isEnum()) { + return ENUM_ICON; + } else if (entryIndex.getDefinition(entry).isRecord()) { + return RECORD_ICON; + } + } + + return CLASS_ICON; + } + + public static Icon getMethodIcon(MethodEntry entry) { + if (entry.isConstructor()) { + return CONSTRUCTOR_ICON; + } + + return METHOD_ICON; + } + + public static TreePath getPathToRoot(TreeNode node) { + List nodes = Lists.newArrayList(); + TreeNode n = node; + + do { + nodes.add(n); + n = n.getParent(); + } while (n != null); + + Collections.reverse(nodes); + return new TreePath(nodes.toArray()); + } + + public static MouseListener onMouseClick(Consumer op) { + return new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent e) { + op.accept(e); + } + }; + } + + public static MouseListener onMousePress(Consumer op) { + return new MouseAdapter() { + @Override + public void mousePressed(MouseEvent e) { + op.accept(e); + } + }; + } + + public static WindowListener onWindowClose(Consumer op) { + return new WindowAdapter() { + @Override + public void windowClosing(WindowEvent e) { + op.accept(e); + } + }; + } } diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/util/History.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/util/History.java index b1286998..f1a8a7a9 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/util/History.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/util/History.java @@ -1,9 +1,9 @@ package cuchaz.enigma.gui.util; -import com.google.common.collect.Queues; - import java.util.Deque; +import com.google.common.collect.Queues; + public class History { private final Deque previous = Queues.newArrayDeque(); private final Deque next = Queues.newArrayDeque(); diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/util/LanguageChangeListener.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/util/LanguageChangeListener.java index 9f53a44f..818e112b 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/util/LanguageChangeListener.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/util/LanguageChangeListener.java @@ -1,11 +1,9 @@ package cuchaz.enigma.gui.util; public interface LanguageChangeListener { - void retranslateUi(); default boolean isValid() { return true; } - } diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/util/LanguageUtil.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/util/LanguageUtil.java index d3e63763..30a91809 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/util/LanguageUtil.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/util/LanguageUtil.java @@ -4,7 +4,6 @@ import java.util.ArrayList; import java.util.List; public final class LanguageUtil { - private static final List listeners = new ArrayList<>(); public LanguageUtil() { @@ -21,5 +20,4 @@ public final class LanguageUtil { public static void dispatchLanguageChange() { listeners.forEach(LanguageChangeListener::retranslateUi); } - } diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/util/ScaleChangeListener.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/util/ScaleChangeListener.java index d045c6d5..243f26fc 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/util/ScaleChangeListener.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/util/ScaleChangeListener.java @@ -2,7 +2,5 @@ package cuchaz.enigma.gui.util; @FunctionalInterface public interface ScaleChangeListener { - void onScaleChanged(float scale, float oldScale); - } diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/util/ScaleUtil.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/util/ScaleUtil.java index 28e37693..bc587fa7 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/util/ScaleUtil.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/util/ScaleUtil.java @@ -21,7 +21,6 @@ import de.sciss.syntaxpane.DefaultSyntaxKit; import cuchaz.enigma.gui.config.UiConfig; public class ScaleUtil { - private static List listeners = new ArrayList<>(); public static void setScaleFactor(float scaleFactor) { @@ -110,15 +109,19 @@ public class ScaleUtil { private static BasicTweaker createTweakerForCurrentLook(float dpiScaling) { String testString = UIManager.getLookAndFeel().getName().toLowerCase(); + if (testString.contains("windows")) { return new WindowsTweaker(dpiScaling, testString.contains("classic")); } + if (testString.contains("metal")) { return new MetalTweaker(dpiScaling); } + if (testString.contains("nimbus")) { return new NimbusTweaker(dpiScaling); } + return new BasicTweaker(dpiScaling); } } diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/util/SingleTreeSelectionModel.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/util/SingleTreeSelectionModel.java index 8915264b..9d967b8c 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/util/SingleTreeSelectionModel.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/util/SingleTreeSelectionModel.java @@ -4,8 +4,7 @@ import javax.swing.tree.DefaultTreeSelectionModel; import javax.swing.tree.TreeSelectionModel; public class SingleTreeSelectionModel extends DefaultTreeSelectionModel { - - public SingleTreeSelectionModel() { - this.setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION); - } + public SingleTreeSelectionModel() { + this.setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION); + } } -- cgit v1.2.3