diff options
| author | 2020-08-04 20:42:39 +0200 | |
|---|---|---|
| committer | 2020-08-04 14:42:39 -0400 | |
| commit | 75a3442f9ff38222606a1e24753d4a57da1e8c0a (patch) | |
| tree | 5b0e12fb055d15fcb31b0fd07ae4cf9512e0758e | |
| parent | Revamp About dialog (diff) | |
| download | enigma-75a3442f9ff38222606a1e24753d4a57da1e8c0a.tar.gz enigma-75a3442f9ff38222606a1e24753d4a57da1e8c0a.tar.xz enigma-75a3442f9ff38222606a1e24753d4a57da1e8c0a.zip | |
Configuration stuff (#301)
* Begin writing new config system
* Make config work
* Save window size and position
* Add editor font chooser
* Use *.ini for windows and mac instead of *rc
* Allow for changing language without having to restart the program
* Save selected directory in file dialogs
* Make dialog visible after moving it to the correct position
* Don't change theme on the fly since it's broken
* Remove unused gui parameter
* Use xdg-open to open URLs on Linux since Desktop.browse doesn't work, at least not on my PC
* Fix default proposed highlight color
* Multi font selection dialog thingy
* Remember network options
* Make font selection dialog actually work
* Collapse general actions ("OK", "Cancel", ..) into one translation
* Localize font dialog
* Use enum name when saving colors for consistency with currently selected theme
* Save size of split panes
* Import old config
* Add test & fix some parts of the config serializer
* TranslationChangeListener/TranslationUtil -> LanguageChangeListener/LanguageUtil
44 files changed, 1985 insertions, 522 deletions
diff --git a/enigma-server/src/main/java/cuchaz/enigma/network/EnigmaServer.java b/enigma-server/src/main/java/cuchaz/enigma/network/EnigmaServer.java index 6027a6bd..75981c3b 100644 --- a/enigma-server/src/main/java/cuchaz/enigma/network/EnigmaServer.java +++ b/enigma-server/src/main/java/cuchaz/enigma/network/EnigmaServer.java | |||
| @@ -1,40 +1,22 @@ | |||
| 1 | package cuchaz.enigma.network; | 1 | package cuchaz.enigma.network; |
| 2 | 2 | ||
| 3 | import cuchaz.enigma.network.packet.KickS2CPacket; | 3 | import java.io.*; |
| 4 | import cuchaz.enigma.network.packet.MessageS2CPacket; | ||
| 5 | import cuchaz.enigma.network.packet.Packet; | ||
| 6 | import cuchaz.enigma.network.packet.PacketRegistry; | ||
| 7 | import cuchaz.enigma.network.packet.RemoveMappingS2CPacket; | ||
| 8 | import cuchaz.enigma.network.packet.RenameS2CPacket; | ||
| 9 | import cuchaz.enigma.network.packet.UserListS2CPacket; | ||
| 10 | import cuchaz.enigma.translation.mapping.EntryMapping; | ||
| 11 | import cuchaz.enigma.translation.mapping.EntryRemapper; | ||
| 12 | import cuchaz.enigma.translation.representation.entry.Entry; | ||
| 13 | |||
| 14 | import java.io.DataInput; | ||
| 15 | import java.io.DataInputStream; | ||
| 16 | import java.io.DataOutput; | ||
| 17 | import java.io.DataOutputStream; | ||
| 18 | import java.io.EOFException; | ||
| 19 | import java.io.IOException; | ||
| 20 | import java.net.ServerSocket; | 4 | import java.net.ServerSocket; |
| 21 | import java.net.Socket; | 5 | import java.net.Socket; |
| 22 | import java.net.SocketException; | 6 | import java.net.SocketException; |
| 23 | import java.util.ArrayList; | 7 | import java.util.*; |
| 24 | import java.util.Collections; | ||
| 25 | import java.util.HashMap; | ||
| 26 | import java.util.HashSet; | ||
| 27 | import java.util.List; | ||
| 28 | import java.util.Map; | ||
| 29 | import java.util.Set; | ||
| 30 | import java.util.concurrent.CopyOnWriteArrayList; | 8 | import java.util.concurrent.CopyOnWriteArrayList; |
| 31 | 9 | ||
| 10 | import cuchaz.enigma.network.packet.*; | ||
| 11 | import cuchaz.enigma.translation.mapping.EntryMapping; | ||
| 12 | import cuchaz.enigma.translation.mapping.EntryRemapper; | ||
| 13 | import cuchaz.enigma.translation.representation.entry.Entry; | ||
| 14 | |||
| 32 | public abstract class EnigmaServer { | 15 | public abstract class EnigmaServer { |
| 33 | 16 | ||
| 34 | // https://discordapp.com/channels/507304429255393322/566418023372816394/700292322918793347 | 17 | // https://discordapp.com/channels/507304429255393322/566418023372816394/700292322918793347 |
| 35 | public static final int DEFAULT_PORT = 34712; | 18 | public static final int DEFAULT_PORT = 34712; |
| 36 | public static final int PROTOCOL_VERSION = 0; | 19 | public static final int PROTOCOL_VERSION = 0; |
| 37 | public static final String OWNER_USERNAME = "Owner"; | ||
| 38 | public static final int CHECKSUM_SIZE = 20; | 20 | public static final int CHECKSUM_SIZE = 20; |
| 39 | public static final int MAX_PASSWORD_LENGTH = 255; // length is written as a byte in the login packet | 21 | public static final int MAX_PASSWORD_LENGTH = 255; // length is written as a byte in the login packet |
| 40 | 22 | ||
diff --git a/enigma-swing/build.gradle b/enigma-swing/build.gradle index 0ebc90c5..6d2cd5f2 100644 --- a/enigma-swing/build.gradle +++ b/enigma-swing/build.gradle | |||
| @@ -10,6 +10,7 @@ dependencies { | |||
| 10 | implementation 'com.bulenkov:darcula:1.0.0-bobbylight' | 10 | implementation 'com.bulenkov:darcula:1.0.0-bobbylight' |
| 11 | implementation 'de.sciss:syntaxpane:1.2.0' | 11 | implementation 'de.sciss:syntaxpane:1.2.0' |
| 12 | implementation 'com.github.lukeu:swing-dpi:0.6' | 12 | implementation 'com.github.lukeu:swing-dpi:0.6' |
| 13 | implementation 'org.drjekyll:fontchooser:2.4' | ||
| 13 | } | 14 | } |
| 14 | 15 | ||
| 15 | jar.manifest.attributes 'Main-Class': 'cuchaz.enigma.gui.Main' | 16 | jar.manifest.attributes 'Main-Class': 'cuchaz.enigma.gui.Main' |
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 45f87bb4..27c866ca 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/EnigmaSyntaxKit.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/EnigmaSyntaxKit.java | |||
| @@ -5,22 +5,22 @@ import de.sciss.syntaxpane.components.LineNumbersRuler; | |||
| 5 | import de.sciss.syntaxpane.syntaxkits.JavaSyntaxKit; | 5 | import de.sciss.syntaxpane.syntaxkits.JavaSyntaxKit; |
| 6 | import de.sciss.syntaxpane.util.Configuration; | 6 | import de.sciss.syntaxpane.util.Configuration; |
| 7 | 7 | ||
| 8 | import cuchaz.enigma.gui.config.Config; | 8 | import cuchaz.enigma.gui.config.UiConfig; |
| 9 | 9 | ||
| 10 | public class EnigmaSyntaxKit extends JavaSyntaxKit { | 10 | public class EnigmaSyntaxKit extends JavaSyntaxKit { |
| 11 | 11 | ||
| 12 | private static Configuration configuration = null; | 12 | private static Configuration configuration = null; |
| 13 | 13 | ||
| 14 | @Override | 14 | @Override |
| 15 | public Configuration getConfig() { | 15 | public Configuration getConfig() { |
| 16 | if (configuration == null) { | 16 | if (configuration == null) { |
| 17 | initConfig(DefaultSyntaxKit.getConfig(JavaSyntaxKit.class)); | 17 | initConfig(DefaultSyntaxKit.getConfig(JavaSyntaxKit.class)); |
| 18 | } | 18 | } |
| 19 | return configuration; | 19 | return configuration; |
| 20 | } | 20 | } |
| 21 | 21 | ||
| 22 | public void initConfig(Configuration baseConfig) { | 22 | public void initConfig(Configuration baseConfig) { |
| 23 | configuration = flattenConfiguration(baseConfig, EnigmaSyntaxKit.class); | 23 | configuration = flattenConfiguration(baseConfig, EnigmaSyntaxKit.class); |
| 24 | 24 | ||
| 25 | // Remove all actions except a select few because they disregard the | 25 | // Remove all actions except a select few because they disregard the |
| 26 | // editable state of the editor, or at least are useless anyway because | 26 | // editable state of the editor, or at least are useless anyway because |
| @@ -34,38 +34,36 @@ public class EnigmaSyntaxKit extends JavaSyntaxKit { | |||
| 34 | s.startsWith("Action.jump-to-pair") || | 34 | s.startsWith("Action.jump-to-pair") || |
| 35 | s.startsWith("Action.quick-find"))); | 35 | s.startsWith("Action.quick-find"))); |
| 36 | 36 | ||
| 37 | // See de.sciss.syntaxpane.TokenType | 37 | // See de.sciss.syntaxpane.TokenType |
| 38 | configuration.put("Style.KEYWORD", Config.getInstance().highlightColor + ", 0"); | 38 | configuration.put("Style.KEYWORD", String.format("%d, 0", UiConfig.getHighlightColor().getRGB())); |
| 39 | configuration.put("Style.KEYWORD2", Config.getInstance().highlightColor + ", 3"); | 39 | configuration.put("Style.KEYWORD2", String.format("%d, 3", UiConfig.getHighlightColor().getRGB())); |
| 40 | configuration.put("Style.STRING", Config.getInstance().stringColor + ", 0"); | 40 | configuration.put("Style.STRING", String.format("%d, 0", UiConfig.getStringColor().getRGB())); |
| 41 | configuration.put("Style.STRING2", Config.getInstance().stringColor + ", 1"); | 41 | configuration.put("Style.STRING2", String.format("%d, 1", UiConfig.getStringColor().getRGB())); |
| 42 | configuration.put("Style.NUMBER", Config.getInstance().numberColor + ", 1"); | 42 | configuration.put("Style.NUMBER", String.format("%d, 1", UiConfig.getNumberColor().getRGB())); |
| 43 | configuration.put("Style.OPERATOR", Config.getInstance().operatorColor + ", 0"); | 43 | configuration.put("Style.OPERATOR", String.format("%d, 0", UiConfig.getOperatorColor().getRGB())); |
| 44 | configuration.put("Style.DELIMITER", Config.getInstance().delimiterColor + ", 1"); | 44 | configuration.put("Style.DELIMITER", String.format("%d, 1", UiConfig.getDelimiterColor().getRGB())); |
| 45 | configuration.put("Style.TYPE", Config.getInstance().typeColor + ", 2"); | 45 | configuration.put("Style.TYPE", String.format("%d, 2", UiConfig.getTypeColor().getRGB())); |
| 46 | configuration.put("Style.TYPE2", Config.getInstance().typeColor + ", 1"); | 46 | configuration.put("Style.TYPE2", String.format("%d, 1", UiConfig.getTypeColor().getRGB())); |
| 47 | configuration.put("Style.IDENTIFIER", Config.getInstance().identifierColor + ", 0"); | 47 | configuration.put("Style.IDENTIFIER", String.format("%d, 0", UiConfig.getIdentifierColor().getRGB())); |
| 48 | configuration.put("Style.DEFAULT", Config.getInstance().defaultTextColor + ", 0"); | 48 | configuration.put("Style.DEFAULT", String.format("%d, 0", UiConfig.getTextColor().getRGB())); |
| 49 | configuration.put(LineNumbersRuler.PROPERTY_BACKGROUND, Config.getInstance().lineNumbersBackground + ""); | 49 | configuration.put(LineNumbersRuler.PROPERTY_BACKGROUND, String.format("%d", UiConfig.getLineNumbersBackgroundColor().getRGB())); |
| 50 | configuration.put(LineNumbersRuler.PROPERTY_FOREGROUND, Config.getInstance().lineNumbersForeground + ""); | 50 | configuration.put(LineNumbersRuler.PROPERTY_FOREGROUND, String.format("%d", UiConfig.getLineNumbersForegroundColor().getRGB())); |
| 51 | configuration.put(LineNumbersRuler.PROPERTY_CURRENT_BACK, Config.getInstance().lineNumbersSelected + ""); | 51 | configuration.put(LineNumbersRuler.PROPERTY_CURRENT_BACK, String.format("%d", UiConfig.getLineNumbersSelectedColor().getRGB())); |
| 52 | configuration.put("RightMarginColumn", "999"); //No need to have a right margin, if someone wants it add a config | 52 | configuration.put("RightMarginColumn", "999"); //No need to have a right margin, if someone wants it add a config |
| 53 | 53 | ||
| 54 | configuration.put("Action.quick-find", "cuchaz.enigma.gui.QuickFindAction, menu F"); | 54 | configuration.put("Action.quick-find", "cuchaz.enigma.gui.QuickFindAction, menu F"); |
| 55 | 55 | ||
| 56 | if (Config.getInstance().editorFont != null) { | 56 | configuration.put("DefaultFont", UiConfig.encodeFont(UiConfig.getEditorFont())); |
| 57 | configuration.put("DefaultFont", Config.getInstance().editorFont); | ||
| 58 | } | ||
| 59 | } | 57 | } |
| 60 | 58 | ||
| 61 | /** | 59 | /** |
| 62 | * Creates a new configuration from the passed configuration so that it has | 60 | * Creates a new configuration from the passed configuration so that it has |
| 63 | * no parents and all its values are on the same level. This is needed since | 61 | * no parents and all its values are on the same level. This is needed since |
| 64 | * there is no way to remove map entries from parent configurations. | 62 | * there is no way to remove map entries from parent configurations. |
| 65 | * | 63 | * |
| 66 | * @param source the configuration to flatten | 64 | * @param source the configuration to flatten |
| 67 | * @param configClass the class for the new configuration | 65 | * @param configClass the class for the new configuration |
| 68 | * @return a new configuration | 66 | * @return a new configuration |
| 69 | */ | 67 | */ |
| 70 | private static Configuration flattenConfiguration(Configuration source, Class<?> configClass) { | 68 | private static Configuration flattenConfiguration(Configuration source, Class<?> configClass) { |
| 71 | Configuration config = new Configuration(configClass, null); | 69 | Configuration config = new Configuration(configClass, null); |
| @@ -73,10 +71,10 @@ public class EnigmaSyntaxKit extends JavaSyntaxKit { | |||
| 73 | config.put(p, source.getString(p)); | 71 | config.put(p, source.getString(p)); |
| 74 | } | 72 | } |
| 75 | return config; | 73 | return config; |
| 76 | } | 74 | } |
| 77 | 75 | ||
| 78 | public static void invalidate() { | 76 | public static void invalidate() { |
| 79 | configuration = null; | 77 | configuration = null; |
| 80 | } | 78 | } |
| 81 | 79 | ||
| 82 | } | 80 | } |
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 3aec6b82..92b6e52e 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/Gui.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/Gui.java | |||
| @@ -14,6 +14,7 @@ package cuchaz.enigma.gui; | |||
| 14 | import java.awt.BorderLayout; | 14 | import java.awt.BorderLayout; |
| 15 | import java.awt.Container; | 15 | import java.awt.Container; |
| 16 | import java.awt.FileDialog; | 16 | import java.awt.FileDialog; |
| 17 | import java.awt.Point; | ||
| 17 | import java.awt.event.*; | 18 | import java.awt.event.*; |
| 18 | import java.nio.file.Path; | 19 | import java.nio.file.Path; |
| 19 | import java.util.*; | 20 | import java.util.*; |
| @@ -31,8 +32,8 @@ import cuchaz.enigma.Enigma; | |||
| 31 | import cuchaz.enigma.EnigmaProfile; | 32 | import cuchaz.enigma.EnigmaProfile; |
| 32 | import cuchaz.enigma.analysis.*; | 33 | import cuchaz.enigma.analysis.*; |
| 33 | import cuchaz.enigma.classhandle.ClassHandle; | 34 | import cuchaz.enigma.classhandle.ClassHandle; |
| 34 | import cuchaz.enigma.gui.config.Config; | ||
| 35 | import cuchaz.enigma.gui.config.Themes; | 35 | import cuchaz.enigma.gui.config.Themes; |
| 36 | import cuchaz.enigma.gui.config.UiConfig; | ||
| 36 | import cuchaz.enigma.gui.dialog.CrashDialog; | 37 | import cuchaz.enigma.gui.dialog.CrashDialog; |
| 37 | import cuchaz.enigma.gui.dialog.JavadocDialog; | 38 | import cuchaz.enigma.gui.dialog.JavadocDialog; |
| 38 | import cuchaz.enigma.gui.dialog.SearchDialog; | 39 | import cuchaz.enigma.gui.dialog.SearchDialog; |
| @@ -43,6 +44,8 @@ import cuchaz.enigma.gui.elements.ValidatableUi; | |||
| 43 | import cuchaz.enigma.gui.events.EditorActionListener; | 44 | import cuchaz.enigma.gui.events.EditorActionListener; |
| 44 | import cuchaz.enigma.gui.panels.*; | 45 | import cuchaz.enigma.gui.panels.*; |
| 45 | import cuchaz.enigma.gui.util.History; | 46 | import cuchaz.enigma.gui.util.History; |
| 47 | import cuchaz.enigma.gui.util.LanguageChangeListener; | ||
| 48 | import cuchaz.enigma.gui.util.LanguageUtil; | ||
| 46 | import cuchaz.enigma.gui.util.ScaleUtil; | 49 | import cuchaz.enigma.gui.util.ScaleUtil; |
| 47 | import cuchaz.enigma.network.Message; | 50 | import cuchaz.enigma.network.Message; |
| 48 | import cuchaz.enigma.network.packet.MarkDeobfuscatedC2SPacket; | 51 | import cuchaz.enigma.network.packet.MarkDeobfuscatedC2SPacket; |
| @@ -59,7 +62,7 @@ import cuchaz.enigma.utils.I18n; | |||
| 59 | import cuchaz.enigma.utils.validation.ParameterizedMessage; | 62 | import cuchaz.enigma.utils.validation.ParameterizedMessage; |
| 60 | import cuchaz.enigma.utils.validation.ValidationContext; | 63 | import cuchaz.enigma.utils.validation.ValidationContext; |
| 61 | 64 | ||
| 62 | public class Gui { | 65 | public class Gui implements LanguageChangeListener { |
| 63 | 66 | ||
| 64 | private final ObfPanel obfPanel; | 67 | private final ObfPanel obfPanel; |
| 65 | private final DeobfPanel deobfPanel; | 68 | private final DeobfPanel deobfPanel; |
| @@ -73,10 +76,10 @@ public class Gui { | |||
| 73 | 76 | ||
| 74 | public FileDialog jarFileChooser; | 77 | public FileDialog jarFileChooser; |
| 75 | public FileDialog tinyMappingsFileChooser; | 78 | public FileDialog tinyMappingsFileChooser; |
| 76 | public SearchDialog searchDialog; | ||
| 77 | public JFileChooser enigmaMappingsFileChooser; | 79 | public JFileChooser enigmaMappingsFileChooser; |
| 78 | public JFileChooser exportSourceFileChooser; | 80 | public JFileChooser exportSourceFileChooser; |
| 79 | public FileDialog exportJarFileChooser; | 81 | public FileDialog exportJarFileChooser; |
| 82 | public SearchDialog searchDialog; | ||
| 80 | private GuiController controller; | 83 | private GuiController controller; |
| 81 | private JFrame frame; | 84 | private JFrame frame; |
| 82 | private JPanel classesPanel; | 85 | private JPanel classesPanel; |
| @@ -88,6 +91,7 @@ public class Gui { | |||
| 88 | private JList<Token> tokens; | 91 | private JList<Token> tokens; |
| 89 | private JTabbedPane tabs; | 92 | private JTabbedPane tabs; |
| 90 | 93 | ||
| 94 | private JSplitPane splitCenter; | ||
| 91 | private JSplitPane splitRight; | 95 | private JSplitPane splitRight; |
| 92 | private JSplitPane logSplit; | 96 | private JSplitPane logSplit; |
| 93 | private CollapsibleTabbedPane logTabs; | 97 | private CollapsibleTabbedPane logTabs; |
| @@ -107,7 +111,7 @@ public class Gui { | |||
| 107 | private final HashBiMap<ClassEntry, EditorPanel> editors = HashBiMap.create(); | 111 | private final HashBiMap<ClassEntry, EditorPanel> editors = HashBiMap.create(); |
| 108 | 112 | ||
| 109 | public Gui(EnigmaProfile profile) { | 113 | public Gui(EnigmaProfile profile) { |
| 110 | Config.getInstance().lookAndFeel.setGlobalLAF(); | 114 | UiConfig.getLookAndFeel().setGlobalLAF(); |
| 111 | 115 | ||
| 112 | // init frame | 116 | // init frame |
| 113 | this.frame = new JFrame(Enigma.NAME); | 117 | this.frame = new JFrame(Enigma.NAME); |
| @@ -328,10 +332,19 @@ public class Gui { | |||
| 328 | splitRight = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true, centerPanel, this.logSplit); | 332 | splitRight = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true, centerPanel, this.logSplit); |
| 329 | splitRight.setResizeWeight(1); // let the left side take all the slack | 333 | splitRight.setResizeWeight(1); // let the left side take all the slack |
| 330 | splitRight.resetToPreferredSizes(); | 334 | splitRight.resetToPreferredSizes(); |
| 331 | JSplitPane splitCenter = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true, this.classesPanel, splitRight); | 335 | splitCenter = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true, this.classesPanel, splitRight); |
| 332 | splitCenter.setResizeWeight(0); // let the right side take all the slack | 336 | splitCenter.setResizeWeight(0); // let the right side take all the slack |
| 333 | pane.add(splitCenter, BorderLayout.CENTER); | 337 | pane.add(splitCenter, BorderLayout.CENTER); |
| 334 | 338 | ||
| 339 | // restore state | ||
| 340 | int[] layout = UiConfig.getLayout(); | ||
| 341 | if (layout.length >= 4) { | ||
| 342 | this.splitClasses.setDividerLocation(layout[0]); | ||
| 343 | this.splitCenter.setDividerLocation(layout[1]); | ||
| 344 | this.splitRight.setDividerLocation(layout[2]); | ||
| 345 | this.logSplit.setDividerLocation(layout[3]); | ||
| 346 | } | ||
| 347 | |||
| 335 | // init menus | 348 | // init menus |
| 336 | this.menuBar = new MenuBar(this); | 349 | this.menuBar = new MenuBar(this); |
| 337 | this.frame.setJMenuBar(this.menuBar.getUi()); | 350 | this.frame.setJMenuBar(this.menuBar.getUi()); |
| @@ -358,11 +371,20 @@ public class Gui { | |||
| 358 | 371 | ||
| 359 | // show the frame | 372 | // show the frame |
| 360 | pane.doLayout(); | 373 | pane.doLayout(); |
| 361 | this.frame.setSize(ScaleUtil.getDimension(1024, 576)); | 374 | this.frame.setSize(UiConfig.getWindowSize("Main Window", ScaleUtil.getDimension(1024, 576))); |
| 362 | this.frame.setMinimumSize(ScaleUtil.getDimension(640, 480)); | 375 | this.frame.setMinimumSize(ScaleUtil.getDimension(640, 480)); |
| 363 | this.frame.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE); | 376 | this.frame.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE); |
| 364 | this.frame.setLocationRelativeTo(null); | 377 | |
| 378 | Point windowPos = UiConfig.getWindowPos("Main Window", null); | ||
| 379 | if (windowPos != null) { | ||
| 380 | this.frame.setLocation(windowPos); | ||
| 381 | } else { | ||
| 382 | this.frame.setLocationRelativeTo(null); | ||
| 383 | } | ||
| 384 | |||
| 365 | this.frame.setVisible(true); | 385 | this.frame.setVisible(true); |
| 386 | |||
| 387 | LanguageUtil.addListener(this); | ||
| 366 | } | 388 | } |
| 367 | 389 | ||
| 368 | public JFrame getFrame() { | 390 | public JFrame getFrame() { |
| @@ -692,11 +714,20 @@ public class Gui { | |||
| 692 | } | 714 | } |
| 693 | 715 | ||
| 694 | return null; | 716 | return null; |
| 695 | }, I18n.translate("prompt.close.save"), I18n.translate("prompt.close.discard"), I18n.translate("prompt.close.cancel")); | 717 | }, I18n.translate("prompt.save"), I18n.translate("prompt.close.discard"), I18n.translate("prompt.cancel")); |
| 696 | } | 718 | } |
| 697 | } | 719 | } |
| 698 | 720 | ||
| 699 | private void exit() { | 721 | private void exit() { |
| 722 | UiConfig.setWindowPos("Main Window", this.frame.getLocationOnScreen()); | ||
| 723 | UiConfig.setWindowSize("Main Window", this.frame.getSize()); | ||
| 724 | UiConfig.setLayout( | ||
| 725 | this.splitClasses.getDividerLocation(), | ||
| 726 | this.splitCenter.getDividerLocation(), | ||
| 727 | this.splitRight.getDividerLocation(), | ||
| 728 | this.logSplit.getDividerLocation()); | ||
| 729 | UiConfig.save(); | ||
| 730 | |||
| 700 | if (searchDialog != null) { | 731 | if (searchDialog != null) { |
| 701 | searchDialog.dispose(); | 732 | searchDialog.dispose(); |
| 702 | } | 733 | } |
| @@ -848,6 +879,25 @@ public class Gui { | |||
| 848 | } | 879 | } |
| 849 | } | 880 | } |
| 850 | 881 | ||
| 882 | @Override | ||
| 883 | public void retranslateUi() { | ||
| 884 | this.jarFileChooser.setTitle(I18n.translate("menu.file.jar.open")); | ||
| 885 | this.exportJarFileChooser.setTitle(I18n.translate("menu.file.export.jar")); | ||
| 886 | this.tabs.setTitleAt(0, I18n.translate("info_panel.tree.inheritance")); | ||
| 887 | this.tabs.setTitleAt(1, I18n.translate("info_panel.tree.implementations")); | ||
| 888 | this.tabs.setTitleAt(2, I18n.translate("info_panel.tree.calls")); | ||
| 889 | this.logTabs.setTitleAt(0, I18n.translate("log_panel.users")); | ||
| 890 | this.logTabs.setTitleAt(1, I18n.translate("log_panel.messages")); | ||
| 891 | this.connectionStatusLabel.setText(I18n.translate(connectionState == ConnectionState.NOT_CONNECTED ? "status.disconnected" : "status.connected")); | ||
| 892 | |||
| 893 | this.updateUiState(); | ||
| 894 | |||
| 895 | this.menuBar.retranslateUi(); | ||
| 896 | this.obfPanel.retranslateUi(); | ||
| 897 | this.deobfPanel.retranslateUi(); | ||
| 898 | this.infoPanel.retranslateUi(); | ||
| 899 | } | ||
| 900 | |||
| 851 | public void setConnectionState(ConnectionState state) { | 901 | public void setConnectionState(ConnectionState state) { |
| 852 | connectionState = state; | 902 | connectionState = state; |
| 853 | statusLabel.setText(I18n.translate("status.ready")); | 903 | statusLabel.setText(I18n.translate("status.ready")); |
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 2f5e5e1e..88ebd64e 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/GuiController.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/GuiController.java | |||
| @@ -34,10 +34,11 @@ import cuchaz.enigma.EnigmaProfile; | |||
| 34 | import cuchaz.enigma.EnigmaProject; | 34 | import cuchaz.enigma.EnigmaProject; |
| 35 | import cuchaz.enigma.analysis.*; | 35 | import cuchaz.enigma.analysis.*; |
| 36 | import cuchaz.enigma.api.service.ObfuscationTestService; | 36 | import cuchaz.enigma.api.service.ObfuscationTestService; |
| 37 | import cuchaz.enigma.classprovider.ClasspathClassProvider; | ||
| 38 | import cuchaz.enigma.classhandle.ClassHandle; | 37 | import cuchaz.enigma.classhandle.ClassHandle; |
| 39 | import cuchaz.enigma.classhandle.ClassHandleProvider; | 38 | import cuchaz.enigma.classhandle.ClassHandleProvider; |
| 40 | import cuchaz.enigma.gui.config.Config; | 39 | import cuchaz.enigma.classprovider.ClasspathClassProvider; |
| 40 | import cuchaz.enigma.gui.config.NetConfig; | ||
| 41 | import cuchaz.enigma.gui.config.UiConfig; | ||
| 41 | import cuchaz.enigma.gui.dialog.ProgressDialog; | 42 | import cuchaz.enigma.gui.dialog.ProgressDialog; |
| 42 | import cuchaz.enigma.gui.stats.StatsGenerator; | 43 | import cuchaz.enigma.gui.stats.StatsGenerator; |
| 43 | import cuchaz.enigma.gui.stats.StatsMember; | 44 | import cuchaz.enigma.gui.stats.StatsMember; |
| @@ -98,7 +99,7 @@ public class GuiController implements ClientPacketHandler { | |||
| 98 | return ProgressDialog.runOffThread(gui.getFrame(), progress -> { | 99 | return ProgressDialog.runOffThread(gui.getFrame(), progress -> { |
| 99 | project = enigma.openJar(jarPath, new ClasspathClassProvider(), progress); | 100 | project = enigma.openJar(jarPath, new ClasspathClassProvider(), progress); |
| 100 | indexTreeBuilder = new IndexTreeBuilder(project.getJarIndex()); | 101 | indexTreeBuilder = new IndexTreeBuilder(project.getJarIndex()); |
| 101 | chp = new ClassHandleProvider(project, Config.getInstance().decompiler.service); | 102 | chp = new ClassHandleProvider(project, UiConfig.getDecompiler().service); |
| 102 | gui.onFinishOpenJar(jarPath.getFileName().toString()); | 103 | gui.onFinishOpenJar(jarPath.getFileName().toString()); |
| 103 | refreshClasses(); | 104 | refreshClasses(); |
| 104 | }); | 105 | }); |
| @@ -563,7 +564,7 @@ public class GuiController implements ClientPacketHandler { | |||
| 563 | server.start(); | 564 | server.start(); |
| 564 | client = new EnigmaClient(this, "127.0.0.1", port); | 565 | client = new EnigmaClient(this, "127.0.0.1", port); |
| 565 | client.connect(); | 566 | client.connect(); |
| 566 | client.sendPacket(new LoginC2SPacket(project.getJarChecksum(), password, EnigmaServer.OWNER_USERNAME)); | 567 | client.sendPacket(new LoginC2SPacket(project.getJarChecksum(), password, NetConfig.getUsername())); |
| 567 | gui.setConnectionState(ConnectionState.HOSTING); | 568 | gui.setConnectionState(ConnectionState.HOSTING); |
| 568 | } | 569 | } |
| 569 | 570 | ||
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 1f3aa2cb..0589f362 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/Main.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/Main.java | |||
| @@ -11,19 +11,18 @@ | |||
| 11 | 11 | ||
| 12 | package cuchaz.enigma.gui; | 12 | package cuchaz.enigma.gui; |
| 13 | 13 | ||
| 14 | import cuchaz.enigma.EnigmaProfile; | ||
| 15 | import cuchaz.enigma.gui.config.Config; | ||
| 16 | import cuchaz.enigma.translation.mapping.serde.MappingFormat; | ||
| 17 | |||
| 18 | import cuchaz.enigma.utils.I18n; | ||
| 19 | import joptsimple.*; | ||
| 20 | |||
| 21 | import java.io.IOException; | 14 | import java.io.IOException; |
| 22 | import java.nio.file.Files; | 15 | import java.nio.file.Files; |
| 23 | import java.nio.file.Path; | 16 | import java.nio.file.Path; |
| 24 | import java.nio.file.Paths; | 17 | import java.nio.file.Paths; |
| 25 | 18 | ||
| 26 | import com.google.common.io.MoreFiles; | 19 | import com.google.common.io.MoreFiles; |
| 20 | import joptsimple.*; | ||
| 21 | |||
| 22 | import cuchaz.enigma.EnigmaProfile; | ||
| 23 | import cuchaz.enigma.gui.config.UiConfig; | ||
| 24 | import cuchaz.enigma.translation.mapping.serde.MappingFormat; | ||
| 25 | import cuchaz.enigma.utils.I18n; | ||
| 27 | 26 | ||
| 28 | public class Main { | 27 | public class Main { |
| 29 | 28 | ||
| @@ -54,7 +53,7 @@ public class Main { | |||
| 54 | 53 | ||
| 55 | EnigmaProfile parsedProfile = EnigmaProfile.read(options.valueOf(profile)); | 54 | EnigmaProfile parsedProfile = EnigmaProfile.read(options.valueOf(profile)); |
| 56 | 55 | ||
| 57 | I18n.setLanguage(Config.getInstance().language); | 56 | I18n.setLanguage(UiConfig.getLanguage()); |
| 58 | Gui gui = new Gui(parsedProfile); | 57 | Gui gui = new Gui(parsedProfile); |
| 59 | GuiController controller = gui.getController(); | 58 | GuiController controller = gui.getController(); |
| 60 | 59 | ||
diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/MethodTreeCellRenderer.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/MethodTreeCellRenderer.java index 1eead6eb..3f38f4fc 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/MethodTreeCellRenderer.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/MethodTreeCellRenderer.java | |||
| @@ -11,12 +11,14 @@ | |||
| 11 | 11 | ||
| 12 | package cuchaz.enigma.gui; | 12 | package cuchaz.enigma.gui; |
| 13 | 13 | ||
| 14 | import cuchaz.enigma.analysis.MethodInheritanceTreeNode; | 14 | import java.awt.Component; |
| 15 | import cuchaz.enigma.gui.config.Config; | 15 | import java.awt.Font; |
| 16 | 16 | ||
| 17 | import javax.swing.*; | 17 | import javax.swing.JTree; |
| 18 | import javax.swing.tree.TreeCellRenderer; | 18 | import javax.swing.tree.TreeCellRenderer; |
| 19 | import java.awt.*; | 19 | |
| 20 | import cuchaz.enigma.analysis.MethodInheritanceTreeNode; | ||
| 21 | import cuchaz.enigma.gui.config.UiConfig; | ||
| 20 | 22 | ||
| 21 | class MethodTreeCellRenderer implements TreeCellRenderer { | 23 | class MethodTreeCellRenderer implements TreeCellRenderer { |
| 22 | 24 | ||
| @@ -29,12 +31,11 @@ class MethodTreeCellRenderer implements TreeCellRenderer { | |||
| 29 | @Override | 31 | @Override |
| 30 | public Component getTreeCellRendererComponent(JTree tree, Object value, boolean selected, boolean expanded, boolean leaf, int row, boolean hasFocus) { | 32 | public Component getTreeCellRendererComponent(JTree tree, Object value, boolean selected, boolean expanded, boolean leaf, int row, boolean hasFocus) { |
| 31 | Component ret = parent.getTreeCellRendererComponent(tree, value, selected, expanded, leaf, row, hasFocus); | 33 | Component ret = parent.getTreeCellRendererComponent(tree, value, selected, expanded, leaf, row, hasFocus); |
| 32 | Config config = Config.getInstance(); | ||
| 33 | if (!(value instanceof MethodInheritanceTreeNode) || ((MethodInheritanceTreeNode) value).isImplemented()) { | 34 | if (!(value instanceof MethodInheritanceTreeNode) || ((MethodInheritanceTreeNode) value).isImplemented()) { |
| 34 | ret.setForeground(new Color(config.defaultTextColor)); | 35 | ret.setForeground(UiConfig.getTextColor()); |
| 35 | ret.setFont(ret.getFont().deriveFont(Font.PLAIN)); | 36 | ret.setFont(ret.getFont().deriveFont(Font.PLAIN)); |
| 36 | } else { | 37 | } else { |
| 37 | ret.setForeground(new Color(config.numberColor)); | 38 | ret.setForeground(UiConfig.getNumberColor()); |
| 38 | ret.setFont(ret.getFont().deriveFont(Font.ITALIC)); | 39 | ret.setFont(ret.getFont().deriveFont(Font.ITALIC)); |
| 39 | } | 40 | } |
| 40 | return ret; | 41 | return ret; |
diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/config/Config.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/config/Config.java deleted file mode 100644 index a389196f..00000000 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/config/Config.java +++ /dev/null | |||
| @@ -1,263 +0,0 @@ | |||
| 1 | package cuchaz.enigma.gui.config; | ||
| 2 | |||
| 3 | import com.bulenkov.darcula.DarculaLaf; | ||
| 4 | import com.google.common.io.Files; | ||
| 5 | import com.google.gson.*; | ||
| 6 | import cuchaz.enigma.source.DecompilerService; | ||
| 7 | import cuchaz.enigma.source.Decompilers; | ||
| 8 | |||
| 9 | import cuchaz.enigma.utils.I18n; | ||
| 10 | |||
| 11 | import javax.swing.*; | ||
| 12 | import javax.swing.plaf.metal.MetalLookAndFeel; | ||
| 13 | import java.awt.*; | ||
| 14 | import java.awt.image.BufferedImage; | ||
| 15 | import java.io.File; | ||
| 16 | import java.io.IOException; | ||
| 17 | import java.lang.reflect.Type; | ||
| 18 | import java.nio.charset.Charset; | ||
| 19 | |||
| 20 | public class Config { | ||
| 21 | public static class AlphaColorEntry { | ||
| 22 | public Integer rgb; | ||
| 23 | public float alpha = 1.0f; | ||
| 24 | |||
| 25 | public AlphaColorEntry(Integer rgb, float alpha) { | ||
| 26 | this.rgb = rgb; | ||
| 27 | this.alpha = alpha; | ||
| 28 | } | ||
| 29 | |||
| 30 | public Color get() { | ||
| 31 | if (rgb == null) { | ||
| 32 | return new Color(0, 0, 0, 0); | ||
| 33 | } | ||
| 34 | |||
| 35 | Color baseColor = new Color(rgb); | ||
| 36 | return new Color(baseColor.getRed(), baseColor.getGreen(), baseColor.getBlue(), (int)(255 * alpha)); | ||
| 37 | } | ||
| 38 | } | ||
| 39 | |||
| 40 | public enum LookAndFeel { | ||
| 41 | DEFAULT("Default"), | ||
| 42 | DARCULA("Darcula"), | ||
| 43 | SYSTEM("System"), | ||
| 44 | NONE("None (JVM default)"); | ||
| 45 | |||
| 46 | // the "JVM default" look and feel, get it at the beginning and store it so we can set it later | ||
| 47 | private static javax.swing.LookAndFeel NONE_LAF = UIManager.getLookAndFeel(); | ||
| 48 | private final String name; | ||
| 49 | |||
| 50 | LookAndFeel(String name) { | ||
| 51 | this.name = name; | ||
| 52 | } | ||
| 53 | |||
| 54 | public String getName() { | ||
| 55 | return name; | ||
| 56 | } | ||
| 57 | |||
| 58 | public void setGlobalLAF() { | ||
| 59 | try { | ||
| 60 | switch (this) { | ||
| 61 | case NONE: | ||
| 62 | UIManager.setLookAndFeel(NONE_LAF); | ||
| 63 | break; | ||
| 64 | case DEFAULT: | ||
| 65 | UIManager.setLookAndFeel(new MetalLookAndFeel()); | ||
| 66 | break; | ||
| 67 | case DARCULA: | ||
| 68 | UIManager.setLookAndFeel(new DarculaLaf()); | ||
| 69 | break; | ||
| 70 | case SYSTEM: | ||
| 71 | UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); | ||
| 72 | } | ||
| 73 | } catch (Exception e){ | ||
| 74 | throw new Error("Failed to set global look and feel", e); | ||
| 75 | } | ||
| 76 | } | ||
| 77 | |||
| 78 | public static boolean isDarkLaf() { | ||
| 79 | // a bit of a hack because swing doesn't give any API for that, and we need colors that aren't defined in look and feel | ||
| 80 | JPanel panel = new JPanel(); | ||
| 81 | panel.setSize(new Dimension(10, 10)); | ||
| 82 | panel.doLayout(); | ||
| 83 | |||
| 84 | BufferedImage image = new BufferedImage(panel.getSize().width, panel.getSize().height, BufferedImage.TYPE_INT_RGB); | ||
| 85 | panel.printAll(image.getGraphics()); | ||
| 86 | |||
| 87 | Color c = new Color(image.getRGB(0, 0)); | ||
| 88 | |||
| 89 | // convert the color we got to grayscale | ||
| 90 | int b = (int) (0.3 * c.getRed() + 0.59 * c.getGreen() + 0.11 * c.getBlue()); | ||
| 91 | return b < 85; | ||
| 92 | } | ||
| 93 | |||
| 94 | public void apply(Config config) { | ||
| 95 | boolean isDark = this == LookAndFeel.DARCULA || isDarkLaf(); | ||
| 96 | if (!isDark) {//Defaults found here: https://github.com/Sciss/SyntaxPane/blob/122da367ff7a5d31627a70c62a48a9f0f4f85a0a/src/main/resources/de/sciss/syntaxpane/defaultsyntaxkit/config.properties#L139 | ||
| 97 | config.lineNumbersForeground = 0x333300; | ||
| 98 | config.lineNumbersBackground = 0xEEEEFF; | ||
| 99 | config.lineNumbersSelected = 0xCCCCEE; | ||
| 100 | config.obfuscatedColor = new AlphaColorEntry(0xFFDCDC, 1.0f); | ||
| 101 | config.obfuscatedColorOutline = new AlphaColorEntry(0xA05050, 1.0f); | ||
| 102 | config.proposedColor = new AlphaColorEntry(0x000000, 0.075f); | ||
| 103 | config.proposedColorOutline = new AlphaColorEntry(0x000000, 0.15f); | ||
| 104 | config.deobfuscatedColor = new AlphaColorEntry(0xDCFFDC, 1.0f); | ||
| 105 | config.deobfuscatedColorOutline = new AlphaColorEntry(0x50A050, 1.0f); | ||
| 106 | config.editorBackground = 0xFFFFFF; | ||
| 107 | config.highlightColor = 0x3333EE; | ||
| 108 | config.caretColor = 0x000000; | ||
| 109 | config.selectionHighlightColor = 0x000000; | ||
| 110 | config.stringColor = 0xCC6600; | ||
| 111 | config.numberColor = 0x999933; | ||
| 112 | config.operatorColor = 0x000000; | ||
| 113 | config.delimiterColor = 0x000000; | ||
| 114 | config.typeColor = 0x000000; | ||
| 115 | config.identifierColor = 0x000000; | ||
| 116 | config.defaultTextColor = 0x000000; | ||
| 117 | } else {//Based off colors found here: https://github.com/dracula/dracula-theme/ | ||
| 118 | config.lineNumbersForeground = 0xA4A4A3; | ||
| 119 | config.lineNumbersBackground = 0x313335; | ||
| 120 | config.lineNumbersSelected = 0x606366; | ||
| 121 | config.obfuscatedColor = new AlphaColorEntry(0xFF5555, 0.3f); | ||
| 122 | config.obfuscatedColorOutline = new AlphaColorEntry(0xFF5555, 0.5f); | ||
| 123 | config.deobfuscatedColor = new AlphaColorEntry(0x50FA7B, 0.3f); | ||
| 124 | config.deobfuscatedColorOutline = new AlphaColorEntry(0x50FA7B, 0.5f); | ||
| 125 | config.proposedColor = new AlphaColorEntry(0x606366, 0.3f); | ||
| 126 | config.proposedColorOutline = new AlphaColorEntry(0x606366, 0.5f); | ||
| 127 | config.editorBackground = 0x282A36; | ||
| 128 | config.highlightColor = 0xFF79C6; | ||
| 129 | config.caretColor = 0xF8F8F2; | ||
| 130 | config.selectionHighlightColor = 0xF8F8F2; | ||
| 131 | config.stringColor = 0xF1FA8C; | ||
| 132 | config.numberColor = 0xBD93F9; | ||
| 133 | config.operatorColor = 0xF8F8F2; | ||
| 134 | config.delimiterColor = 0xF8F8F2; | ||
| 135 | config.typeColor = 0xF8F8F2; | ||
| 136 | config.identifierColor = 0xF8F8F2; | ||
| 137 | config.defaultTextColor = 0xF8F8F2; | ||
| 138 | } | ||
| 139 | } | ||
| 140 | } | ||
| 141 | |||
| 142 | public enum Decompiler { | ||
| 143 | PROCYON("Procyon", Decompilers.PROCYON), | ||
| 144 | CFR("CFR", Decompilers.CFR); | ||
| 145 | |||
| 146 | public final DecompilerService service; | ||
| 147 | public final String name; | ||
| 148 | |||
| 149 | Decompiler(String name, DecompilerService service) { | ||
| 150 | this.name = name; | ||
| 151 | this.service = service; | ||
| 152 | } | ||
| 153 | } | ||
| 154 | |||
| 155 | private static final File DIR_HOME = new File(System.getProperty("user.home")); | ||
| 156 | private static final File ENIGMA_DIR = new File(DIR_HOME, ".enigma"); | ||
| 157 | private static final File CONFIG_FILE = new File(ENIGMA_DIR, "config.json"); | ||
| 158 | private static final Config INSTANCE = new Config(); | ||
| 159 | |||
| 160 | private final transient Gson gson; // transient to exclude it from being exposed | ||
| 161 | |||
| 162 | public AlphaColorEntry obfuscatedColor; | ||
| 163 | public AlphaColorEntry obfuscatedColorOutline; | ||
| 164 | public AlphaColorEntry proposedColor; | ||
| 165 | public AlphaColorEntry proposedColorOutline; | ||
| 166 | public AlphaColorEntry deobfuscatedColor; | ||
| 167 | public AlphaColorEntry deobfuscatedColorOutline; | ||
| 168 | |||
| 169 | public String editorFont; | ||
| 170 | |||
| 171 | public Integer editorBackground; | ||
| 172 | public Integer highlightColor; | ||
| 173 | public Integer caretColor; | ||
| 174 | public Integer selectionHighlightColor; | ||
| 175 | |||
| 176 | public Integer stringColor; | ||
| 177 | public Integer numberColor; | ||
| 178 | public Integer operatorColor; | ||
| 179 | public Integer delimiterColor; | ||
| 180 | public Integer typeColor; | ||
| 181 | public Integer identifierColor; | ||
| 182 | public Integer defaultTextColor; | ||
| 183 | |||
| 184 | public Integer lineNumbersBackground; | ||
| 185 | public Integer lineNumbersSelected; | ||
| 186 | public Integer lineNumbersForeground; | ||
| 187 | |||
| 188 | public String language = I18n.DEFAULT_LANGUAGE; | ||
| 189 | |||
| 190 | public LookAndFeel lookAndFeel = LookAndFeel.DEFAULT; | ||
| 191 | |||
| 192 | public float scaleFactor = 1.0f; | ||
| 193 | |||
| 194 | public Decompiler decompiler = Decompiler.PROCYON; | ||
| 195 | |||
| 196 | private Config() { | ||
| 197 | gson = new GsonBuilder() | ||
| 198 | .registerTypeAdapter(Integer.class, new IntSerializer()) | ||
| 199 | .registerTypeAdapter(Integer.class, new IntDeserializer()) | ||
| 200 | .registerTypeAdapter(Config.class, (InstanceCreator<Config>) type -> this) | ||
| 201 | .setPrettyPrinting() | ||
| 202 | .create(); | ||
| 203 | try { | ||
| 204 | this.loadConfig(); | ||
| 205 | } catch (IOException ignored) { | ||
| 206 | try { | ||
| 207 | this.reset(); | ||
| 208 | } catch (IOException ignored1) { | ||
| 209 | } | ||
| 210 | } | ||
| 211 | } | ||
| 212 | |||
| 213 | public void loadConfig() throws IOException { | ||
| 214 | if (!ENIGMA_DIR.exists()) ENIGMA_DIR.mkdirs(); | ||
| 215 | File configFile = new File(ENIGMA_DIR, "config.json"); | ||
| 216 | boolean loaded = false; | ||
| 217 | |||
| 218 | if (configFile.exists()) { | ||
| 219 | try { | ||
| 220 | gson.fromJson(Files.asCharSource(configFile, Charset.defaultCharset()).read(), Config.class); | ||
| 221 | loaded = true; | ||
| 222 | } catch (Exception e) { | ||
| 223 | e.printStackTrace(); | ||
| 224 | } | ||
| 225 | } | ||
| 226 | |||
| 227 | if (!loaded) { | ||
| 228 | this.reset(); | ||
| 229 | Files.touch(configFile); | ||
| 230 | } | ||
| 231 | saveConfig(); | ||
| 232 | } | ||
| 233 | |||
| 234 | public void saveConfig() throws IOException { | ||
| 235 | Files.asCharSink(CONFIG_FILE, Charset.defaultCharset()).write(gson.toJson(this)); | ||
| 236 | } | ||
| 237 | |||
| 238 | public void reset() throws IOException { | ||
| 239 | this.lookAndFeel = LookAndFeel.DEFAULT; | ||
| 240 | this.lookAndFeel.apply(this); | ||
| 241 | this.decompiler = Decompiler.PROCYON; | ||
| 242 | this.language = I18n.DEFAULT_LANGUAGE; | ||
| 243 | this.saveConfig(); | ||
| 244 | } | ||
| 245 | |||
| 246 | private static class IntSerializer implements JsonSerializer<Integer> { | ||
| 247 | @Override | ||
| 248 | public JsonElement serialize(Integer src, Type typeOfSrc, JsonSerializationContext context) { | ||
| 249 | return new JsonPrimitive("#" + Integer.toHexString(src).toUpperCase()); | ||
| 250 | } | ||
| 251 | } | ||
| 252 | |||
| 253 | private static class IntDeserializer implements JsonDeserializer<Integer> { | ||
| 254 | @Override | ||
| 255 | public Integer deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) { | ||
| 256 | return (int) Long.parseLong(json.getAsString().replace("#", ""), 16); | ||
| 257 | } | ||
| 258 | } | ||
| 259 | |||
| 260 | public static Config getInstance() { | ||
| 261 | return INSTANCE; | ||
| 262 | } | ||
| 263 | } | ||
diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/config/Decompiler.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/config/Decompiler.java new file mode 100644 index 00000000..0f9e41c6 --- /dev/null +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/config/Decompiler.java | |||
| @@ -0,0 +1,17 @@ | |||
| 1 | package cuchaz.enigma.gui.config; | ||
| 2 | |||
| 3 | import cuchaz.enigma.source.DecompilerService; | ||
| 4 | import cuchaz.enigma.source.Decompilers; | ||
| 5 | |||
| 6 | public enum Decompiler { | ||
| 7 | PROCYON("Procyon", Decompilers.PROCYON), | ||
| 8 | CFR("CFR", Decompilers.CFR); | ||
| 9 | |||
| 10 | public final DecompilerService service; | ||
| 11 | public final String name; | ||
| 12 | |||
| 13 | Decompiler(String name, DecompilerService service) { | ||
| 14 | this.name = name; | ||
| 15 | this.service = service; | ||
| 16 | } | ||
| 17 | } | ||
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 new file mode 100644 index 00000000..1c70d439 --- /dev/null +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/config/LookAndFeel.java | |||
| @@ -0,0 +1,67 @@ | |||
| 1 | package cuchaz.enigma.gui.config; | ||
| 2 | |||
| 3 | import java.awt.Color; | ||
| 4 | import java.awt.Dimension; | ||
| 5 | import java.awt.image.BufferedImage; | ||
| 6 | |||
| 7 | import javax.swing.JPanel; | ||
| 8 | import javax.swing.UIManager; | ||
| 9 | import javax.swing.plaf.metal.MetalLookAndFeel; | ||
| 10 | |||
| 11 | import com.bulenkov.darcula.DarculaLaf; | ||
| 12 | |||
| 13 | public enum LookAndFeel { | ||
| 14 | DEFAULT("Default"), | ||
| 15 | DARCULA("Darcula"), | ||
| 16 | SYSTEM("System"), | ||
| 17 | NONE("None (JVM default)"); | ||
| 18 | |||
| 19 | // the "JVM default" look and feel, get it at the beginning and store it so we can set it later | ||
| 20 | private static javax.swing.LookAndFeel NONE_LAF = UIManager.getLookAndFeel(); | ||
| 21 | private final String name; | ||
| 22 | |||
| 23 | LookAndFeel(String name) { | ||
| 24 | this.name = name; | ||
| 25 | } | ||
| 26 | |||
| 27 | public String getName() { | ||
| 28 | return name; | ||
| 29 | } | ||
| 30 | |||
| 31 | public void setGlobalLAF() { | ||
| 32 | try { | ||
| 33 | switch (this) { | ||
| 34 | case NONE: | ||
| 35 | UIManager.setLookAndFeel(NONE_LAF); | ||
| 36 | break; | ||
| 37 | case DEFAULT: | ||
| 38 | UIManager.setLookAndFeel(new MetalLookAndFeel()); | ||
| 39 | break; | ||
| 40 | case DARCULA: | ||
| 41 | UIManager.setLookAndFeel(new DarculaLaf()); | ||
| 42 | break; | ||
| 43 | case SYSTEM: | ||
| 44 | UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); | ||
| 45 | } | ||
| 46 | } catch (Exception e) { | ||
| 47 | throw new Error("Failed to set global look and feel", e); | ||
| 48 | } | ||
| 49 | } | ||
| 50 | |||
| 51 | public static boolean isDarkLaf() { | ||
| 52 | // a bit of a hack because swing doesn't give any API for that, and we need colors that aren't defined in look and feel | ||
| 53 | JPanel panel = new JPanel(); | ||
| 54 | panel.setSize(new Dimension(10, 10)); | ||
| 55 | panel.doLayout(); | ||
| 56 | |||
| 57 | BufferedImage image = new BufferedImage(panel.getSize().width, panel.getSize().height, BufferedImage.TYPE_INT_RGB); | ||
| 58 | panel.printAll(image.getGraphics()); | ||
| 59 | |||
| 60 | Color c = new Color(image.getRGB(0, 0)); | ||
| 61 | |||
| 62 | // convert the color we got to grayscale | ||
| 63 | int b = (int) (0.3 * c.getRed() + 0.59 * c.getGreen() + 0.11 * c.getBlue()); | ||
| 64 | return b < 85; | ||
| 65 | } | ||
| 66 | |||
| 67 | } | ||
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 new file mode 100644 index 00000000..4439cb8a --- /dev/null +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/config/NetConfig.java | |||
| @@ -0,0 +1,57 @@ | |||
| 1 | package cuchaz.enigma.gui.config; | ||
| 2 | |||
| 3 | import cuchaz.enigma.config.ConfigContainer; | ||
| 4 | import cuchaz.enigma.network.EnigmaServer; | ||
| 5 | |||
| 6 | public final class NetConfig { | ||
| 7 | |||
| 8 | private NetConfig() { | ||
| 9 | } | ||
| 10 | |||
| 11 | private static final ConfigContainer cfg = ConfigContainer.getOrCreate("enigma/net"); | ||
| 12 | |||
| 13 | public static void save() { | ||
| 14 | cfg.save(); | ||
| 15 | } | ||
| 16 | |||
| 17 | public static String getUsername() { | ||
| 18 | return cfg.data().section("User").setIfAbsentString("Username", System.getProperty("user.name", "user")); | ||
| 19 | } | ||
| 20 | |||
| 21 | public static void setUsername(String username) { | ||
| 22 | cfg.data().section("User").setString("Username", username); | ||
| 23 | } | ||
| 24 | |||
| 25 | public static String getPassword() { | ||
| 26 | return cfg.data().section("Remote").getString("Password").orElse(""); | ||
| 27 | } | ||
| 28 | |||
| 29 | public static void setPassword(String password) { | ||
| 30 | cfg.data().section("Remote").setString("Password", password); | ||
| 31 | } | ||
| 32 | |||
| 33 | public static String getRemoteAddress() { | ||
| 34 | return cfg.data().section("Remote").getString("Address").orElse(""); | ||
| 35 | } | ||
| 36 | |||
| 37 | public static void setRemoteAddress(String address) { | ||
| 38 | cfg.data().section("Remote").setString("Address", address); | ||
| 39 | } | ||
| 40 | |||
| 41 | public static String getServerPassword() { | ||
| 42 | return cfg.data().section("Server").getString("Password").orElse(""); | ||
| 43 | } | ||
| 44 | |||
| 45 | public static void setServerPassword(String password) { | ||
| 46 | cfg.data().section("Server").setString("Password", password); | ||
| 47 | } | ||
| 48 | |||
| 49 | public static int getServerPort() { | ||
| 50 | return cfg.data().section("Server").setIfAbsentInt("Port", EnigmaServer.DEFAULT_PORT); | ||
| 51 | } | ||
| 52 | |||
| 53 | public static void setServerPort(int port) { | ||
| 54 | cfg.data().section("Server").setInt("Port", port); | ||
| 55 | } | ||
| 56 | |||
| 57 | } | ||
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 new file mode 100644 index 00000000..660d2313 --- /dev/null +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/config/OldConfigImporter.java | |||
| @@ -0,0 +1,26 @@ | |||
| 1 | package cuchaz.enigma.gui.config; | ||
| 2 | |||
| 3 | import java.awt.Font; | ||
| 4 | |||
| 5 | import cuchaz.enigma.gui.config.legacy.Config; | ||
| 6 | |||
| 7 | public final class OldConfigImporter { | ||
| 8 | |||
| 9 | private OldConfigImporter() { | ||
| 10 | } | ||
| 11 | |||
| 12 | @SuppressWarnings("deprecation") | ||
| 13 | public static void doImport() { | ||
| 14 | if (Config.CONFIG_FILE.exists()) { | ||
| 15 | Config config = new Config(); | ||
| 16 | if (config.editorFont != null) { | ||
| 17 | UiConfig.setEditorFont(Font.decode(config.editorFont)); | ||
| 18 | } | ||
| 19 | UiConfig.setLanguage(config.language); | ||
| 20 | UiConfig.setLookAndFeel(config.lookAndFeel); | ||
| 21 | UiConfig.setScaleFactor(config.scaleFactor); | ||
| 22 | UiConfig.setDecompiler(config.decompiler); | ||
| 23 | } | ||
| 24 | } | ||
| 25 | |||
| 26 | } | ||
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 fd40cb74..4905b1c5 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 | |||
| @@ -1,49 +1,92 @@ | |||
| 1 | package cuchaz.enigma.gui.config; | 1 | package cuchaz.enigma.gui.config; |
| 2 | 2 | ||
| 3 | import java.io.IOException; | 3 | import java.awt.Font; |
| 4 | import java.util.HashSet; | 4 | import java.util.HashSet; |
| 5 | import java.util.Set; | 5 | import java.util.Set; |
| 6 | 6 | ||
| 7 | import javax.swing.UIManager; | ||
| 8 | |||
| 7 | import com.google.common.collect.ImmutableMap; | 9 | import com.google.common.collect.ImmutableMap; |
| 10 | import de.sciss.syntaxpane.DefaultSyntaxKit; | ||
| 11 | |||
| 8 | import cuchaz.enigma.gui.EnigmaSyntaxKit; | 12 | import cuchaz.enigma.gui.EnigmaSyntaxKit; |
| 9 | import cuchaz.enigma.gui.events.ThemeChangeListener; | 13 | import cuchaz.enigma.gui.events.ThemeChangeListener; |
| 10 | import cuchaz.enigma.gui.highlight.BoxHighlightPainter; | 14 | import cuchaz.enigma.gui.highlight.BoxHighlightPainter; |
| 11 | import cuchaz.enigma.gui.util.ScaleUtil; | 15 | import cuchaz.enigma.gui.util.ScaleUtil; |
| 12 | import cuchaz.enigma.source.RenamableTokenType; | 16 | import cuchaz.enigma.source.RenamableTokenType; |
| 13 | import de.sciss.syntaxpane.DefaultSyntaxKit; | ||
| 14 | 17 | ||
| 15 | public class Themes { | 18 | public class Themes { |
| 16 | 19 | ||
| 17 | private static final Set<ThemeChangeListener> listeners = new HashSet<>(); | 20 | private static final Set<ThemeChangeListener> listeners = new HashSet<>(); |
| 18 | 21 | ||
| 19 | public static void setLookAndFeel(Config.LookAndFeel lookAndFeel) { | ||
| 20 | Config.getInstance().lookAndFeel = lookAndFeel; | ||
| 21 | updateTheme(); | ||
| 22 | } | ||
| 23 | |||
| 24 | public static void updateTheme() { | 22 | public static void updateTheme() { |
| 25 | Config config = Config.getInstance(); | 23 | LookAndFeel laf = UiConfig.getLookAndFeel(); |
| 26 | config.lookAndFeel.setGlobalLAF(); | 24 | laf.setGlobalLAF(); |
| 27 | config.lookAndFeel.apply(config); | 25 | setFonts(); |
| 28 | try { | 26 | UiConfig.setLookAndFeelDefaults(laf, LookAndFeel.isDarkLaf()); |
| 29 | config.saveConfig(); | ||
| 30 | } catch (IOException e) { | ||
| 31 | e.printStackTrace(); | ||
| 32 | } | ||
| 33 | EnigmaSyntaxKit.invalidate(); | 27 | EnigmaSyntaxKit.invalidate(); |
| 34 | DefaultSyntaxKit.initKit(); | 28 | DefaultSyntaxKit.initKit(); |
| 35 | DefaultSyntaxKit.registerContentType("text/enigma-sources", EnigmaSyntaxKit.class.getName()); | 29 | DefaultSyntaxKit.registerContentType("text/enigma-sources", EnigmaSyntaxKit.class.getName()); |
| 36 | ImmutableMap<RenamableTokenType, BoxHighlightPainter> boxHighlightPainters = getBoxHighlightPainters(); | 30 | ImmutableMap<RenamableTokenType, BoxHighlightPainter> boxHighlightPainters = getBoxHighlightPainters(); |
| 37 | listeners.forEach(l -> l.onThemeChanged(config.lookAndFeel, boxHighlightPainters)); | 31 | listeners.forEach(l -> l.onThemeChanged(laf, boxHighlightPainters)); |
| 38 | ScaleUtil.applyScaling(); | 32 | ScaleUtil.applyScaling(); |
| 33 | UiConfig.save(); | ||
| 34 | } | ||
| 35 | |||
| 36 | private static void setFonts() { | ||
| 37 | if (UiConfig.shouldUseCustomFonts()) { | ||
| 38 | Font small = UiConfig.getSmallFont(); | ||
| 39 | Font bold = UiConfig.getDefaultFont(); | ||
| 40 | Font normal = UiConfig.getDefault2Font(); | ||
| 41 | |||
| 42 | UIManager.put("CheckBox.font", bold); | ||
| 43 | UIManager.put("CheckBoxMenuItem.font", bold); | ||
| 44 | UIManager.put("CheckBoxMenuItem.acceleratorFont", small); | ||
| 45 | UIManager.put("ColorChooser.font", normal); | ||
| 46 | UIManager.put("ComboBox.font", bold); | ||
| 47 | UIManager.put("DesktopIcon.font", bold); | ||
| 48 | UIManager.put("EditorPane.font", normal); | ||
| 49 | UIManager.put("InternalFrame.titleFont", bold); | ||
| 50 | UIManager.put("FormattedTextField.font", normal); | ||
| 51 | UIManager.put("Label.font", bold); | ||
| 52 | UIManager.put("List.font", bold); | ||
| 53 | UIManager.put("Menu.acceleratorFont", small); | ||
| 54 | UIManager.put("Menu.font", bold); | ||
| 55 | UIManager.put("MenuBar.font", bold); | ||
| 56 | UIManager.put("MenuItem.acceleratorFont", small); | ||
| 57 | UIManager.put("MenuItem.font", bold); | ||
| 58 | UIManager.put("OptionPane.font", normal); | ||
| 59 | UIManager.put("Panel.font", normal); | ||
| 60 | UIManager.put("PasswordField.font", normal); | ||
| 61 | UIManager.put("PopupMenu.font", bold); | ||
| 62 | UIManager.put("ProgressBar.font", bold); | ||
| 63 | UIManager.put("RadioButton.font", bold); | ||
| 64 | UIManager.put("RadioButtonMenuItem.acceleratorFont", small); | ||
| 65 | UIManager.put("RadioButtonMenuItem.font", bold); | ||
| 66 | UIManager.put("ScrollPane.font", normal); | ||
| 67 | UIManager.put("Slider.font", bold); | ||
| 68 | UIManager.put("Spinner.font", bold); | ||
| 69 | UIManager.put("TabbedPane.font", bold); | ||
| 70 | UIManager.put("Table.font", normal); | ||
| 71 | UIManager.put("TableHeader.font", normal); | ||
| 72 | UIManager.put("TextArea.font", normal); | ||
| 73 | UIManager.put("TextField.font", normal); | ||
| 74 | UIManager.put("TextPane.font", normal); | ||
| 75 | UIManager.put("TitledBorder.font", bold); | ||
| 76 | UIManager.put("ToggleButton.font", bold); | ||
| 77 | UIManager.put("ToolBar.font", bold); | ||
| 78 | UIManager.put("ToolTip.font", normal); | ||
| 79 | UIManager.put("Tree.font", normal); | ||
| 80 | UIManager.put("Viewport.font", normal); | ||
| 81 | UIManager.put("Button.font", bold); | ||
| 82 | } | ||
| 39 | } | 83 | } |
| 40 | 84 | ||
| 41 | public static ImmutableMap<RenamableTokenType, BoxHighlightPainter> getBoxHighlightPainters() { | 85 | public static ImmutableMap<RenamableTokenType, BoxHighlightPainter> getBoxHighlightPainters() { |
| 42 | Config config = Config.getInstance(); | ||
| 43 | return ImmutableMap.of( | 86 | return ImmutableMap.of( |
| 44 | RenamableTokenType.OBFUSCATED, BoxHighlightPainter.create(config.obfuscatedColor, config.obfuscatedColorOutline), | 87 | RenamableTokenType.OBFUSCATED, BoxHighlightPainter.create(UiConfig.getObfuscatedColor(), UiConfig.getObfuscatedOutlineColor()), |
| 45 | RenamableTokenType.PROPOSED, BoxHighlightPainter.create(config.proposedColor, config.proposedColorOutline), | 88 | RenamableTokenType.PROPOSED, BoxHighlightPainter.create(UiConfig.getProposedColor(), UiConfig.getProposedOutlineColor()), |
| 46 | RenamableTokenType.DEOBFUSCATED, BoxHighlightPainter.create(config.deobfuscatedColor, config.deobfuscatedColorOutline) | 89 | RenamableTokenType.DEOBFUSCATED, BoxHighlightPainter.create(UiConfig.getDeobfuscatedColor(), UiConfig.getDeobfuscatedOutlineColor()) |
| 47 | ); | 90 | ); |
| 48 | } | 91 | } |
| 49 | 92 | ||
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 new file mode 100644 index 00000000..fa2c4e32 --- /dev/null +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/config/UiConfig.java | |||
| @@ -0,0 +1,330 @@ | |||
| 1 | package cuchaz.enigma.gui.config; | ||
| 2 | |||
| 3 | import java.awt.*; | ||
| 4 | import java.util.Optional; | ||
| 5 | import java.util.OptionalInt; | ||
| 6 | |||
| 7 | import cuchaz.enigma.config.ConfigContainer; | ||
| 8 | import cuchaz.enigma.config.ConfigSection; | ||
| 9 | import cuchaz.enigma.gui.util.ScaleUtil; | ||
| 10 | import cuchaz.enigma.utils.I18n; | ||
| 11 | |||
| 12 | public final class UiConfig { | ||
| 13 | |||
| 14 | private UiConfig() { | ||
| 15 | } | ||
| 16 | |||
| 17 | // General UI configuration such as localization | ||
| 18 | private static final ConfigContainer ui = ConfigContainer.getOrCreate("enigma/enigmaui"); | ||
| 19 | // Swing specific configuration such as theming | ||
| 20 | private static final ConfigContainer swing = ConfigContainer.getOrCreate("enigma/enigmaswing"); | ||
| 21 | |||
| 22 | static { | ||
| 23 | if (!swing.existsOnDisk() && !ui.existsOnDisk()) { | ||
| 24 | OldConfigImporter.doImport(); | ||
| 25 | } | ||
| 26 | } | ||
| 27 | |||
| 28 | public static void save() { | ||
| 29 | ui.save(); | ||
| 30 | swing.save(); | ||
| 31 | } | ||
| 32 | |||
| 33 | public static String getLanguage() { | ||
| 34 | return ui.data().section("General").setIfAbsentString("Language", I18n.DEFAULT_LANGUAGE); | ||
| 35 | } | ||
| 36 | |||
| 37 | public static void setLanguage(String language) { | ||
| 38 | ui.data().section("General").setString("Language", language); | ||
| 39 | } | ||
| 40 | |||
| 41 | public static float getScaleFactor() { | ||
| 42 | return (float) swing.data().section("General").setIfAbsentDouble("Scale Factor", 1.0); | ||
| 43 | } | ||
| 44 | |||
| 45 | public static void setScaleFactor(float scale) { | ||
| 46 | swing.data().section("General").setDouble("Scale Factor", scale); | ||
| 47 | } | ||
| 48 | |||
| 49 | public static int[] getLayout() { | ||
| 50 | return swing.data().section("Main Window").getIntArray("Layout").orElseGet(() -> new int[] { -1, -1, -1, -1 }); | ||
| 51 | } | ||
| 52 | |||
| 53 | public static void setLayout(int leftV, int left, int right, int rightH) { | ||
| 54 | swing.data().section("Main Window").setIntArray("Layout", new int[] { leftV, left, right, rightH }); | ||
| 55 | } | ||
| 56 | |||
| 57 | public static LookAndFeel getLookAndFeel() { | ||
| 58 | return swing.data().section("Themes").setIfAbsentEnum(LookAndFeel::valueOf, "Current", LookAndFeel.NONE); | ||
| 59 | } | ||
| 60 | |||
| 61 | public static void setLookAndFeel(LookAndFeel laf) { | ||
| 62 | swing.data().section("Themes").setEnum("Current", laf); | ||
| 63 | } | ||
| 64 | |||
| 65 | public static Decompiler getDecompiler() { | ||
| 66 | return ui.data().section("Decompiler").setIfAbsentEnum(Decompiler::valueOf, "Current", Decompiler.PROCYON); | ||
| 67 | } | ||
| 68 | |||
| 69 | public static void setDecompiler(Decompiler d) { | ||
| 70 | ui.data().section("Decompiler").setEnum("Current", d); | ||
| 71 | } | ||
| 72 | |||
| 73 | private static Color fromComponents(int rgb, double alpha) { | ||
| 74 | int rgba = rgb & 0xFFFFFF | (int) (alpha * 255) << 24; | ||
| 75 | return new Color(rgba, true); | ||
| 76 | } | ||
| 77 | |||
| 78 | private static Color getThemeColorRgba(String colorName) { | ||
| 79 | ConfigSection s = swing.data().section("Themes").section(getLookAndFeel().name()).section("Colors"); | ||
| 80 | return fromComponents(s.getRgbColor(colorName).orElse(0), s.getDouble(String.format("%s Alpha", colorName)).orElse(0)); | ||
| 81 | } | ||
| 82 | |||
| 83 | private static Color getThemeColorRgb(String colorName) { | ||
| 84 | ConfigSection s = swing.data().section("Themes").section(getLookAndFeel().name()).section("Colors"); | ||
| 85 | return new Color(s.getRgbColor(colorName).orElse(0)); | ||
| 86 | } | ||
| 87 | |||
| 88 | public static Color getObfuscatedColor() { | ||
| 89 | return getThemeColorRgba("Obfuscated"); | ||
| 90 | } | ||
| 91 | |||
| 92 | public static Color getObfuscatedOutlineColor() { | ||
| 93 | return getThemeColorRgba("Obfuscated Outline"); | ||
| 94 | } | ||
| 95 | |||
| 96 | public static Color getProposedColor() { | ||
| 97 | return getThemeColorRgba("Proposed"); | ||
| 98 | } | ||
| 99 | |||
| 100 | public static Color getProposedOutlineColor() { | ||
| 101 | return getThemeColorRgba("Proposed Outline"); | ||
| 102 | } | ||
| 103 | |||
| 104 | public static Color getDeobfuscatedColor() { | ||
| 105 | return getThemeColorRgba("Deobfuscated"); | ||
| 106 | } | ||
| 107 | |||
| 108 | public static Color getDeobfuscatedOutlineColor() { | ||
| 109 | return getThemeColorRgba("Deobfuscated Outline"); | ||
| 110 | } | ||
| 111 | |||
| 112 | public static Color getEditorBackgroundColor() { | ||
| 113 | return getThemeColorRgb("Editor Background"); | ||
| 114 | } | ||
| 115 | |||
| 116 | public static Color getHighlightColor() { | ||
| 117 | return getThemeColorRgb("Highlight"); | ||
| 118 | } | ||
| 119 | |||
| 120 | public static Color getCaretColor() { | ||
| 121 | return getThemeColorRgb("Caret"); | ||
| 122 | } | ||
| 123 | |||
| 124 | public static Color getSelectionHighlightColor() { | ||
| 125 | return getThemeColorRgb("Selection Highlight"); | ||
| 126 | } | ||
| 127 | |||
| 128 | public static Color getStringColor() { | ||
| 129 | return getThemeColorRgb("String"); | ||
| 130 | } | ||
| 131 | |||
| 132 | public static Color getNumberColor() { | ||
| 133 | return getThemeColorRgb("Number"); | ||
| 134 | } | ||
| 135 | |||
| 136 | public static Color getOperatorColor() { | ||
| 137 | return getThemeColorRgb("Operator"); | ||
| 138 | } | ||
| 139 | |||
| 140 | public static Color getDelimiterColor() { | ||
| 141 | return getThemeColorRgb("Delimiter"); | ||
| 142 | } | ||
| 143 | |||
| 144 | public static Color getTypeColor() { | ||
| 145 | return getThemeColorRgb("Type"); | ||
| 146 | } | ||
| 147 | |||
| 148 | public static Color getIdentifierColor() { | ||
| 149 | return getThemeColorRgb("Identifier"); | ||
| 150 | } | ||
| 151 | |||
| 152 | public static Color getTextColor() { | ||
| 153 | return getThemeColorRgb("Text"); | ||
| 154 | } | ||
| 155 | |||
| 156 | public static Color getLineNumbersForegroundColor() { | ||
| 157 | return getThemeColorRgb("Line Numbers Foreground"); | ||
| 158 | } | ||
| 159 | |||
| 160 | public static Color getLineNumbersBackgroundColor() { | ||
| 161 | return getThemeColorRgb("Line Numbers Background"); | ||
| 162 | } | ||
| 163 | |||
| 164 | public static Color getLineNumbersSelectedColor() { | ||
| 165 | return getThemeColorRgb("Line Numbers Selected"); | ||
| 166 | } | ||
| 167 | |||
| 168 | public static boolean shouldUseCustomFonts() { | ||
| 169 | return swing.data().section("Themes").section(getLookAndFeel().name()).section("Fonts").setIfAbsentBool("Use Custom", false); | ||
| 170 | } | ||
| 171 | |||
| 172 | public static void setUseCustomFonts(boolean b) { | ||
| 173 | swing.data().section("Themes").section(getLookAndFeel().name()).section("Fonts").setBool("Use Custom", b); | ||
| 174 | } | ||
| 175 | |||
| 176 | public static Optional<Font> getFont(String name) { | ||
| 177 | Optional<String> spec = swing.data().section("Themes").section(getLookAndFeel().name()).section("Fonts").getString(name); | ||
| 178 | return spec.map(Font::decode); | ||
| 179 | } | ||
| 180 | |||
| 181 | public static void setFont(String name, Font font) { | ||
| 182 | swing.data().section("Themes").section(getLookAndFeel().name()).section("Fonts").setString(name, encodeFont(font)); | ||
| 183 | } | ||
| 184 | |||
| 185 | public static Font getDefaultFont() { | ||
| 186 | return getFont("Default").orElseGet(() -> ScaleUtil.scaleFont(Font.decode(Font.DIALOG).deriveFont(Font.BOLD))); | ||
| 187 | } | ||
| 188 | |||
| 189 | public static void setDefaultFont(Font font) { | ||
| 190 | setFont("Default", font); | ||
| 191 | } | ||
| 192 | |||
| 193 | public static Font getDefault2Font() { | ||
| 194 | return getFont("Default 2").orElseGet(() -> ScaleUtil.scaleFont(Font.decode(Font.DIALOG))); | ||
| 195 | } | ||
| 196 | |||
| 197 | public static void setDefault2Font(Font font) { | ||
| 198 | setFont("Default 2", font); | ||
| 199 | } | ||
| 200 | |||
| 201 | public static Font getSmallFont() { | ||
| 202 | return getFont("Small").orElseGet(() -> ScaleUtil.scaleFont(Font.decode(Font.DIALOG))); | ||
| 203 | } | ||
| 204 | |||
| 205 | public static void setSmallFont(Font font) { | ||
| 206 | setFont("Small", font); | ||
| 207 | } | ||
| 208 | |||
| 209 | public static Font getEditorFont() { | ||
| 210 | return getFont("Editor").orElseGet(() -> ScaleUtil.scaleFont(Font.decode(Font.MONOSPACED))); | ||
| 211 | } | ||
| 212 | |||
| 213 | public static void setEditorFont(Font font) { | ||
| 214 | setFont("Editor", font); | ||
| 215 | } | ||
| 216 | |||
| 217 | public static String encodeFont(Font font) { | ||
| 218 | int style = font.getStyle(); | ||
| 219 | String s = style == (Font.BOLD | Font.ITALIC) ? "bolditalic" : style == Font.ITALIC ? "italic" : style == Font.BOLD ? "bold" : "plain"; | ||
| 220 | return String.format("%s-%s-%s", font.getName(), s, font.getSize()); | ||
| 221 | } | ||
| 222 | |||
| 223 | public static Dimension getWindowSize(String window, Dimension fallback) { | ||
| 224 | Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); | ||
| 225 | ConfigSection section = swing.data().section(window); | ||
| 226 | OptionalInt width = section.getInt(String.format("Width %s", screenSize.width)); | ||
| 227 | OptionalInt height = section.getInt(String.format("Height %s", screenSize.height)); | ||
| 228 | if (width.isPresent() && height.isPresent()) { | ||
| 229 | return new Dimension(width.getAsInt(), height.getAsInt()); | ||
| 230 | } else { | ||
| 231 | return fallback; | ||
| 232 | } | ||
| 233 | } | ||
| 234 | |||
| 235 | public static void setWindowSize(String window, Dimension dim) { | ||
| 236 | Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); | ||
| 237 | ConfigSection section = swing.data().section(window); | ||
| 238 | section.setInt(String.format("Width %s", screenSize.width), dim.width); | ||
| 239 | section.setInt(String.format("Height %s", screenSize.height), dim.height); | ||
| 240 | } | ||
| 241 | |||
| 242 | public static Point getWindowPos(String window, Point fallback) { | ||
| 243 | Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); | ||
| 244 | ConfigSection section = swing.data().section(window); | ||
| 245 | OptionalInt x = section.getInt(String.format("X %s", screenSize.width)); | ||
| 246 | OptionalInt y = section.getInt(String.format("Y %s", screenSize.height)); | ||
| 247 | if (x.isPresent() && y.isPresent()) { | ||
| 248 | return new Point(x.getAsInt(), y.getAsInt()); | ||
| 249 | } else { | ||
| 250 | return fallback; | ||
| 251 | } | ||
| 252 | } | ||
| 253 | |||
| 254 | public static void setWindowPos(String window, Point rect) { | ||
| 255 | Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); | ||
| 256 | ConfigSection section = swing.data().section(window); | ||
| 257 | section.setInt(String.format("X %s", screenSize.width), rect.x); | ||
| 258 | section.setInt(String.format("Y %s", screenSize.height), rect.y); | ||
| 259 | } | ||
| 260 | |||
| 261 | public static String getLastSelectedDir() { | ||
| 262 | return swing.data().section("File Dialog").getString("Selected").orElse(""); | ||
| 263 | } | ||
| 264 | |||
| 265 | public static void setLastSelectedDir(String directory) { | ||
| 266 | swing.data().section("File Dialog").setString("Selected", directory); | ||
| 267 | } | ||
| 268 | |||
| 269 | public static void setLookAndFeelDefaults(LookAndFeel laf, boolean isDark) { | ||
| 270 | ConfigSection s = swing.data().section("Themes").section(laf.name()).section("Colors"); | ||
| 271 | if (!isDark) { | ||
| 272 | // Defaults found here: https://github.com/Sciss/SyntaxPane/blob/122da367ff7a5d31627a70c62a48a9f0f4f85a0a/src/main/resources/de/sciss/syntaxpane/defaultsyntaxkit/config.properties#L139 | ||
| 273 | s.setIfAbsentRgbColor("Line Numbers Foreground", 0x333300); | ||
| 274 | s.setIfAbsentRgbColor("Line Numbers Background", 0xEEEEFF); | ||
| 275 | s.setIfAbsentRgbColor("Line Numbers Selected", 0xCCCCEE); | ||
| 276 | s.setIfAbsentRgbColor("Obfuscated", 0xFFDCDC); | ||
| 277 | s.setIfAbsentDouble("Obfuscated Alpha", 1.0); | ||
| 278 | s.setIfAbsentRgbColor("Obfuscated Outline", 0xA05050); | ||
| 279 | s.setIfAbsentDouble("Obfuscated Outline Alpha", 1.0); | ||
| 280 | s.setIfAbsentRgbColor("Proposed", 0x000000); | ||
| 281 | s.setIfAbsentDouble("Proposed Alpha", 0.15); | ||
| 282 | s.setIfAbsentRgbColor("Proposed Outline", 0x000000); | ||
| 283 | s.setIfAbsentDouble("Proposed Outline Alpha", 0.75); | ||
| 284 | s.setIfAbsentRgbColor("Deobfuscated", 0xDCFFDC); | ||
| 285 | s.setIfAbsentDouble("Deobfuscated Alpha", 1.0); | ||
| 286 | s.setIfAbsentRgbColor("Deobfuscated Outline", 0x50A050); | ||
| 287 | s.setIfAbsentDouble("Deobfuscated Outline Alpha", 1.0); | ||
| 288 | s.setIfAbsentRgbColor("Editor Background", 0xFFFFFF); | ||
| 289 | s.setIfAbsentRgbColor("Highlight", 0x3333EE); | ||
| 290 | s.setIfAbsentRgbColor("Caret", 0x000000); | ||
| 291 | s.setIfAbsentRgbColor("Selection Highlight", 0x000000); | ||
| 292 | s.setIfAbsentRgbColor("String", 0xCC6600); | ||
| 293 | s.setIfAbsentRgbColor("Number", 0x999933); | ||
| 294 | s.setIfAbsentRgbColor("Operator", 0x000000); | ||
| 295 | s.setIfAbsentRgbColor("Delimiter", 0x000000); | ||
| 296 | s.setIfAbsentRgbColor("Type", 0x000000); | ||
| 297 | s.setIfAbsentRgbColor("Identifier", 0x000000); | ||
| 298 | s.setIfAbsentRgbColor("Text", 0x000000); | ||
| 299 | } else { | ||
| 300 | // Based off colors found here: https://github.com/dracula/dracula-theme/ | ||
| 301 | s.setIfAbsentRgbColor("Line Numbers Foreground", 0xA4A4A3); | ||
| 302 | s.setIfAbsentRgbColor("Line Numbers Background", 0x313335); | ||
| 303 | s.setIfAbsentRgbColor("Line Numbers Selected", 0x606366); | ||
| 304 | s.setIfAbsentRgbColor("Obfuscated", 0xFF5555); | ||
| 305 | s.setIfAbsentDouble("Obfuscated Alpha", 0.3); | ||
| 306 | s.setIfAbsentRgbColor("Obfuscated Outline", 0xFF5555); | ||
| 307 | s.setIfAbsentDouble("Obfuscated Outline Alpha", 0.5); | ||
| 308 | s.setIfAbsentRgbColor("Proposed", 0x606366); | ||
| 309 | s.setIfAbsentDouble("Proposed Alpha", 0.3); | ||
| 310 | s.setIfAbsentRgbColor("Proposed Outline", 0x606366); | ||
| 311 | s.setIfAbsentDouble("Proposed Outline Alpha", 0.5); | ||
| 312 | s.setIfAbsentRgbColor("Deobfuscated", 0x50FA7B); | ||
| 313 | s.setIfAbsentDouble("Deobfuscated Alpha", 0.3); | ||
| 314 | s.setIfAbsentRgbColor("Deobfuscated Outline", 0x50FA7B); | ||
| 315 | s.setIfAbsentDouble("Deobfuscated Outline Alpha", 0.5); | ||
| 316 | s.setIfAbsentRgbColor("Editor Background", 0x282A36); | ||
| 317 | s.setIfAbsentRgbColor("Highlight", 0xFF79C6); | ||
| 318 | s.setIfAbsentRgbColor("Caret", 0xF8F8F2); | ||
| 319 | s.setIfAbsentRgbColor("Selection Highlight", 0xF8F8F2); | ||
| 320 | s.setIfAbsentRgbColor("String", 0xF1FA8C); | ||
| 321 | s.setIfAbsentRgbColor("Number", 0xBD93F9); | ||
| 322 | s.setIfAbsentRgbColor("Operator", 0xF8F8F2); | ||
| 323 | s.setIfAbsentRgbColor("Delimiter", 0xF8F8F2); | ||
| 324 | s.setIfAbsentRgbColor("Type", 0xF8F8F2); | ||
| 325 | s.setIfAbsentRgbColor("Identifier", 0xF8F8F2); | ||
| 326 | s.setIfAbsentRgbColor("Text", 0xF8F8F2); | ||
| 327 | } | ||
| 328 | } | ||
| 329 | |||
| 330 | } | ||
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 new file mode 100644 index 00000000..fdd17d25 --- /dev/null +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/config/legacy/Config.java | |||
| @@ -0,0 +1,109 @@ | |||
| 1 | package cuchaz.enigma.gui.config.legacy; | ||
| 2 | |||
| 3 | import java.awt.Color; | ||
| 4 | import java.io.File; | ||
| 5 | import java.lang.reflect.Type; | ||
| 6 | import java.nio.charset.Charset; | ||
| 7 | |||
| 8 | import com.google.common.io.Files; | ||
| 9 | import com.google.gson.*; | ||
| 10 | |||
| 11 | import cuchaz.enigma.gui.config.Decompiler; | ||
| 12 | import cuchaz.enigma.utils.I18n; | ||
| 13 | |||
| 14 | @Deprecated | ||
| 15 | public class Config { | ||
| 16 | public static class AlphaColorEntry { | ||
| 17 | public Integer rgb; | ||
| 18 | public float alpha; | ||
| 19 | |||
| 20 | public AlphaColorEntry(Integer rgb, float alpha) { | ||
| 21 | this.rgb = rgb; | ||
| 22 | this.alpha = alpha; | ||
| 23 | } | ||
| 24 | |||
| 25 | public Color get() { | ||
| 26 | if (rgb == null) { | ||
| 27 | return new Color(0, 0, 0, 0); | ||
| 28 | } | ||
| 29 | |||
| 30 | Color baseColor = new Color(rgb); | ||
| 31 | return new Color(baseColor.getRed(), baseColor.getGreen(), baseColor.getBlue(), (int)(255 * alpha)); | ||
| 32 | } | ||
| 33 | } | ||
| 34 | |||
| 35 | private static final File DIR_HOME = new File(System.getProperty("user.home")); | ||
| 36 | private static final File ENIGMA_DIR = new File(DIR_HOME, ".enigma"); | ||
| 37 | public static final File CONFIG_FILE = new File(ENIGMA_DIR, "config.json"); | ||
| 38 | |||
| 39 | private final transient Gson gson; // transient to exclude it from being exposed | ||
| 40 | |||
| 41 | public AlphaColorEntry obfuscatedColor; | ||
| 42 | public AlphaColorEntry obfuscatedColorOutline; | ||
| 43 | public AlphaColorEntry proposedColor; | ||
| 44 | public AlphaColorEntry proposedColorOutline; | ||
| 45 | public AlphaColorEntry deobfuscatedColor; | ||
| 46 | public AlphaColorEntry deobfuscatedColorOutline; | ||
| 47 | |||
| 48 | public String editorFont; | ||
| 49 | |||
| 50 | public Integer editorBackground; | ||
| 51 | public Integer highlightColor; | ||
| 52 | public Integer caretColor; | ||
| 53 | public Integer selectionHighlightColor; | ||
| 54 | |||
| 55 | public Integer stringColor; | ||
| 56 | public Integer numberColor; | ||
| 57 | public Integer operatorColor; | ||
| 58 | public Integer delimiterColor; | ||
| 59 | public Integer typeColor; | ||
| 60 | public Integer identifierColor; | ||
| 61 | public Integer defaultTextColor; | ||
| 62 | |||
| 63 | public Integer lineNumbersBackground; | ||
| 64 | public Integer lineNumbersSelected; | ||
| 65 | public Integer lineNumbersForeground; | ||
| 66 | |||
| 67 | public String language = I18n.DEFAULT_LANGUAGE; | ||
| 68 | |||
| 69 | public cuchaz.enigma.gui.config.LookAndFeel lookAndFeel = cuchaz.enigma.gui.config.LookAndFeel.DEFAULT; | ||
| 70 | |||
| 71 | public float scaleFactor = 1.0f; | ||
| 72 | |||
| 73 | public Decompiler decompiler = Decompiler.PROCYON; | ||
| 74 | |||
| 75 | public Config() { | ||
| 76 | gson = new GsonBuilder() | ||
| 77 | .registerTypeAdapter(Integer.class, new IntSerializer()) | ||
| 78 | .registerTypeAdapter(Integer.class, new IntDeserializer()) | ||
| 79 | .registerTypeAdapter(Config.class, (InstanceCreator<Config>) type -> this) | ||
| 80 | .setPrettyPrinting() | ||
| 81 | .create(); | ||
| 82 | this.loadConfig(); | ||
| 83 | } | ||
| 84 | |||
| 85 | public void loadConfig() { | ||
| 86 | if (CONFIG_FILE.exists()) { | ||
| 87 | try { | ||
| 88 | gson.fromJson(Files.asCharSource(CONFIG_FILE, Charset.defaultCharset()).read(), Config.class); | ||
| 89 | } catch (Exception e) { | ||
| 90 | e.printStackTrace(); | ||
| 91 | } | ||
| 92 | } | ||
| 93 | } | ||
| 94 | |||
| 95 | private static class IntSerializer implements JsonSerializer<Integer> { | ||
| 96 | @Override | ||
| 97 | public JsonElement serialize(Integer src, Type typeOfSrc, JsonSerializationContext context) { | ||
| 98 | return new JsonPrimitive("#" + Integer.toHexString(src).toUpperCase()); | ||
| 99 | } | ||
| 100 | } | ||
| 101 | |||
| 102 | private static class IntDeserializer implements JsonDeserializer<Integer> { | ||
| 103 | @Override | ||
| 104 | public Integer deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) { | ||
| 105 | return (int) Long.parseLong(json.getAsString().replace("#", ""), 16); | ||
| 106 | } | ||
| 107 | } | ||
| 108 | |||
| 109 | } | ||
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 18510602..f8922e64 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 | |||
| @@ -37,7 +37,7 @@ public class AboutDialog { | |||
| 37 | JLabel title = new JLabel(Enigma.NAME); | 37 | JLabel title = new JLabel(Enigma.NAME); |
| 38 | title.setFont(title.getFont().deriveFont(title.getFont().getSize2D() * 1.5f)); | 38 | title.setFont(title.getFont().deriveFont(title.getFont().getSize2D() * 1.5f)); |
| 39 | 39 | ||
| 40 | JButton okButton = new JButton(I18n.translate("menu.help.about.ok")); | 40 | JButton okButton = new JButton(I18n.translate("prompt.ok")); |
| 41 | okButton.addActionListener(e -> frame.dispose()); | 41 | okButton.addActionListener(e -> frame.dispose()); |
| 42 | 42 | ||
| 43 | pane.add(title, cb.pos(0, 0).build()); | 43 | pane.add(title, cb.pos(0, 0).build()); |
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 64219ab8..df65473c 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 | |||
| @@ -1,22 +1,23 @@ | |||
| 1 | package cuchaz.enigma.gui.dialog; | 1 | package cuchaz.enigma.gui.dialog; |
| 2 | 2 | ||
| 3 | import java.awt.BorderLayout; | 3 | import java.awt.BorderLayout; |
| 4 | import java.awt.Dialog; | ||
| 5 | import java.awt.Window; | ||
| 4 | import java.awt.event.KeyAdapter; | 6 | import java.awt.event.KeyAdapter; |
| 5 | import java.awt.event.KeyEvent; | 7 | import java.awt.event.KeyEvent; |
| 6 | 8 | ||
| 7 | import javax.swing.JButton; | 9 | import javax.swing.JButton; |
| 8 | import javax.swing.JFrame; | 10 | import javax.swing.JDialog; |
| 9 | import javax.swing.JLabel; | 11 | import javax.swing.JLabel; |
| 10 | import javax.swing.JPanel; | 12 | import javax.swing.JPanel; |
| 11 | 13 | ||
| 12 | import cuchaz.enigma.gui.Gui; | ||
| 13 | import cuchaz.enigma.utils.I18n; | 14 | import cuchaz.enigma.utils.I18n; |
| 14 | 15 | ||
| 15 | public class ChangeDialog { | 16 | public class ChangeDialog { |
| 16 | 17 | ||
| 17 | public static void show(Gui gui) { | 18 | public static void show(Window parent) { |
| 18 | // init frame | 19 | // init frame |
| 19 | JFrame frame = new JFrame(I18n.translate("menu.view.change.title")); | 20 | JDialog frame = new JDialog(parent, I18n.translate("menu.view.change.title"), Dialog.DEFAULT_MODALITY_TYPE); |
| 20 | JPanel textPanel = new JPanel(); | 21 | JPanel textPanel = new JPanel(); |
| 21 | JPanel buttonPanel = new JPanel(); | 22 | JPanel buttonPanel = new JPanel(); |
| 22 | frame.setLayout(new BorderLayout()); | 23 | frame.setLayout(new BorderLayout()); |
| @@ -29,7 +30,7 @@ public class ChangeDialog { | |||
| 29 | textPanel.add(text); | 30 | textPanel.add(text); |
| 30 | 31 | ||
| 31 | // show ok button | 32 | // show ok button |
| 32 | JButton okButton = new JButton(I18n.translate("menu.view.change.ok")); | 33 | JButton okButton = new JButton(I18n.translate("prompt.ok")); |
| 33 | buttonPanel.add(okButton); | 34 | buttonPanel.add(okButton); |
| 34 | okButton.addActionListener(event -> frame.dispose()); | 35 | okButton.addActionListener(event -> frame.dispose()); |
| 35 | okButton.addKeyListener(new KeyAdapter() { | 36 | okButton.addKeyListener(new KeyAdapter() { |
| @@ -43,8 +44,9 @@ public class ChangeDialog { | |||
| 43 | 44 | ||
| 44 | // show the frame | 45 | // show the frame |
| 45 | frame.pack(); | 46 | frame.pack(); |
| 46 | frame.setVisible(true); | ||
| 47 | frame.setResizable(false); | 47 | frame.setResizable(false); |
| 48 | frame.setLocationRelativeTo(gui.getFrame()); | 48 | frame.setLocationRelativeTo(parent); |
| 49 | frame.setVisible(true); | ||
| 49 | } | 50 | } |
| 51 | |||
| 50 | } | 52 | } |
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 201f4dcb..2486dfe1 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 | |||
| @@ -10,6 +10,7 @@ import java.util.Objects; | |||
| 10 | import javax.swing.JPasswordField; | 10 | import javax.swing.JPasswordField; |
| 11 | import javax.swing.JTextField; | 11 | import javax.swing.JTextField; |
| 12 | 12 | ||
| 13 | import cuchaz.enigma.gui.config.NetConfig; | ||
| 13 | import cuchaz.enigma.gui.elements.ValidatableTextField; | 14 | import cuchaz.enigma.gui.elements.ValidatableTextField; |
| 14 | import cuchaz.enigma.gui.util.ScaleUtil; | 15 | import cuchaz.enigma.gui.util.ScaleUtil; |
| 15 | import cuchaz.enigma.network.EnigmaServer; | 16 | import cuchaz.enigma.network.EnigmaServer; |
| @@ -36,9 +37,9 @@ public class ConnectToServerDialog extends AbstractDialog { | |||
| 36 | 37 | ||
| 37 | @Override | 38 | @Override |
| 38 | protected List<Pair<String, Component>> createComponents() { | 39 | protected List<Pair<String, Component>> createComponents() { |
| 39 | usernameField = new JTextField(System.getProperty("user.name")); | 40 | usernameField = new JTextField(NetConfig.getUsername()); |
| 40 | ipField = new ValidatableTextField(); | 41 | ipField = new ValidatableTextField(NetConfig.getRemoteAddress()); |
| 41 | passwordField = new JPasswordField(); | 42 | passwordField = new JPasswordField(NetConfig.getPassword()); |
| 42 | 43 | ||
| 43 | usernameField.addActionListener(event -> confirm()); | 44 | usernameField.addActionListener(event -> confirm()); |
| 44 | ipField.addActionListener(event -> confirm()); | 45 | ipField.addActionListener(event -> confirm()); |
| @@ -51,6 +52,7 @@ public class ConnectToServerDialog extends AbstractDialog { | |||
| 51 | ); | 52 | ); |
| 52 | } | 53 | } |
| 53 | 54 | ||
| 55 | @Override | ||
| 54 | public void validateInputs() { | 56 | public void validateInputs() { |
| 55 | vc.setActiveElement(ipField); | 57 | vc.setActiveElement(ipField); |
| 56 | if (StandardValidation.notBlank(vc, ipField.getText())) { | 58 | if (StandardValidation.notBlank(vc, ipField.getText())) { |
| @@ -67,6 +69,7 @@ public class ConnectToServerDialog extends AbstractDialog { | |||
| 67 | if (!vc.canProceed()) return null; | 69 | if (!vc.canProceed()) return null; |
| 68 | return new Result( | 70 | return new Result( |
| 69 | usernameField.getText(), | 71 | usernameField.getText(), |
| 72 | ipField.getText(), | ||
| 70 | Objects.requireNonNull(ServerAddress.from(ipField.getText(), EnigmaServer.DEFAULT_PORT)), | 73 | Objects.requireNonNull(ServerAddress.from(ipField.getText(), EnigmaServer.DEFAULT_PORT)), |
| 71 | passwordField.getPassword() | 74 | passwordField.getPassword() |
| 72 | ); | 75 | ); |
| @@ -84,11 +87,13 @@ public class ConnectToServerDialog extends AbstractDialog { | |||
| 84 | 87 | ||
| 85 | public static class Result { | 88 | public static class Result { |
| 86 | private final String username; | 89 | private final String username; |
| 90 | private final String addressStr; | ||
| 87 | private final ServerAddress address; | 91 | private final ServerAddress address; |
| 88 | private final char[] password; | 92 | private final char[] password; |
| 89 | 93 | ||
| 90 | public Result(String username, ServerAddress address, char[] password) { | 94 | public Result(String username, String addressStr, ServerAddress address, char[] password) { |
| 91 | this.username = username; | 95 | this.username = username; |
| 96 | this.addressStr = addressStr; | ||
| 92 | this.address = address; | 97 | this.address = address; |
| 93 | this.password = password; | 98 | this.password = password; |
| 94 | } | 99 | } |
| @@ -97,6 +102,10 @@ public class ConnectToServerDialog extends AbstractDialog { | |||
| 97 | return username; | 102 | return username; |
| 98 | } | 103 | } |
| 99 | 104 | ||
| 105 | public String getAddressStr() { | ||
| 106 | return addressStr; | ||
| 107 | } | ||
| 108 | |||
| 100 | public ServerAddress getAddress() { | 109 | public ServerAddress getAddress() { |
| 101 | return address; | 110 | return address; |
| 102 | } | 111 | } |
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 ddd3bc37..07daf6dc 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 | |||
| @@ -6,6 +6,7 @@ import java.awt.Frame; | |||
| 6 | import java.util.Arrays; | 6 | import java.util.Arrays; |
| 7 | import java.util.List; | 7 | import java.util.List; |
| 8 | 8 | ||
| 9 | import cuchaz.enigma.gui.config.NetConfig; | ||
| 9 | import cuchaz.enigma.gui.elements.ValidatablePasswordField; | 10 | import cuchaz.enigma.gui.elements.ValidatablePasswordField; |
| 10 | import cuchaz.enigma.gui.elements.ValidatableTextField; | 11 | import cuchaz.enigma.gui.elements.ValidatableTextField; |
| 11 | import cuchaz.enigma.gui.util.ScaleUtil; | 12 | import cuchaz.enigma.gui.util.ScaleUtil; |
| @@ -31,8 +32,8 @@ public class CreateServerDialog extends AbstractDialog { | |||
| 31 | 32 | ||
| 32 | @Override | 33 | @Override |
| 33 | protected List<Pair<String, Component>> createComponents() { | 34 | protected List<Pair<String, Component>> createComponents() { |
| 34 | portField = new ValidatableTextField(Integer.toString(EnigmaServer.DEFAULT_PORT)); | 35 | portField = new ValidatableTextField(Integer.toString(NetConfig.getServerPort())); |
| 35 | passwordField = new ValidatablePasswordField(); | 36 | passwordField = new ValidatablePasswordField(NetConfig.getServerPassword()); |
| 36 | 37 | ||
| 37 | portField.addActionListener(event -> confirm()); | 38 | portField.addActionListener(event -> confirm()); |
| 38 | passwordField.addActionListener(event -> confirm()); | 39 | passwordField.addActionListener(event -> confirm()); |
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 new file mode 100644 index 00000000..de019adb --- /dev/null +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/dialog/FontDialog.java | |||
| @@ -0,0 +1,126 @@ | |||
| 1 | package cuchaz.enigma.gui.dialog; | ||
| 2 | |||
| 3 | import java.awt.*; | ||
| 4 | import java.util.Arrays; | ||
| 5 | import java.util.List; | ||
| 6 | |||
| 7 | import javax.swing.JButton; | ||
| 8 | import javax.swing.JCheckBox; | ||
| 9 | import javax.swing.JDialog; | ||
| 10 | import javax.swing.JList; | ||
| 11 | |||
| 12 | import org.drjekyll.fontchooser.FontChooser; | ||
| 13 | |||
| 14 | import cuchaz.enigma.gui.config.UiConfig; | ||
| 15 | import cuchaz.enigma.gui.util.GridBagConstraintsBuilder; | ||
| 16 | import cuchaz.enigma.gui.util.ScaleUtil; | ||
| 17 | import cuchaz.enigma.utils.I18n; | ||
| 18 | |||
| 19 | public class FontDialog extends JDialog { | ||
| 20 | |||
| 21 | private static final List<String> CATEGORIES = Arrays.asList( | ||
| 22 | "Default", | ||
| 23 | "Default 2", | ||
| 24 | "Small", | ||
| 25 | "Editor" | ||
| 26 | ); | ||
| 27 | |||
| 28 | private static final List<String> CATEGORY_TEXTS = Arrays.asList( | ||
| 29 | "fonts.cat.default", | ||
| 30 | "fonts.cat.default2", | ||
| 31 | "fonts.cat.small", | ||
| 32 | "fonts.cat.editor" | ||
| 33 | ); | ||
| 34 | |||
| 35 | private final JList<String> entries = new JList<>(CATEGORY_TEXTS.stream().map(I18n::translate).toArray(String[]::new)); | ||
| 36 | private final FontChooser chooser = new FontChooser(Font.decode(Font.DIALOG)); | ||
| 37 | private final JCheckBox customCheckBox = new JCheckBox(I18n.translate("fonts.use_custom")); | ||
| 38 | private final JButton okButton = new JButton(I18n.translate("prompt.ok")); | ||
| 39 | private final JButton cancelButton = new JButton(I18n.translate("prompt.cancel")); | ||
| 40 | |||
| 41 | private final Font[] fonts = CATEGORIES.stream().map(name -> UiConfig.getFont(name).orElseGet(() -> ScaleUtil.scaleFont(Font.decode(Font.DIALOG)))).toArray(Font[]::new); | ||
| 42 | |||
| 43 | public FontDialog(Frame owner) { | ||
| 44 | super(owner, "Fonts", true); | ||
| 45 | |||
| 46 | this.customCheckBox.setSelected(UiConfig.shouldUseCustomFonts()); | ||
| 47 | |||
| 48 | this.entries.setPreferredSize(ScaleUtil.getDimension(100, 0)); | ||
| 49 | |||
| 50 | this.entries.addListSelectionListener(_e -> this.categoryChanged()); | ||
| 51 | this.chooser.addChangeListener(_e -> this.selectedFontChanged()); | ||
| 52 | this.customCheckBox.addActionListener(_e -> this.customFontsClicked()); | ||
| 53 | this.okButton.addActionListener(_e -> this.apply()); | ||
| 54 | this.cancelButton.addActionListener(_e -> this.cancel()); | ||
| 55 | |||
| 56 | Container contentPane = this.getContentPane(); | ||
| 57 | contentPane.setLayout(new GridBagLayout()); | ||
| 58 | |||
| 59 | GridBagConstraintsBuilder cb = GridBagConstraintsBuilder.create() | ||
| 60 | .insets(2); | ||
| 61 | |||
| 62 | contentPane.add(this.entries, cb.pos(0, 0).weight(0.0, 1.0).fill(GridBagConstraints.BOTH).build()); | ||
| 63 | contentPane.add(this.chooser, cb.pos(1, 0).weight(1.0, 1.0).fill(GridBagConstraints.BOTH).size(2, 1).build()); | ||
| 64 | contentPane.add(this.customCheckBox, cb.pos(0, 1).anchor(GridBagConstraints.WEST).size(2, 1).build()); | ||
| 65 | contentPane.add(this.okButton, cb.pos(1, 1).anchor(GridBagConstraints.EAST).weight(1.0, 0.0).build()); | ||
| 66 | contentPane.add(this.cancelButton, cb.pos(2, 1).anchor(GridBagConstraints.EAST).weight(0.0, 0.0).build()); | ||
| 67 | |||
| 68 | this.updateUiState(); | ||
| 69 | |||
| 70 | this.setSize(ScaleUtil.getDimension(640, 360)); | ||
| 71 | this.setLocationRelativeTo(owner); | ||
| 72 | } | ||
| 73 | |||
| 74 | private void customFontsClicked() { | ||
| 75 | this.updateUiState(); | ||
| 76 | } | ||
| 77 | |||
| 78 | private void categoryChanged() { | ||
| 79 | this.updateUiState(); | ||
| 80 | int selectedIndex = this.entries.getSelectedIndex(); | ||
| 81 | if (selectedIndex != -1) { | ||
| 82 | this.chooser.setSelectedFont(this.fonts[selectedIndex]); | ||
| 83 | } | ||
| 84 | } | ||
| 85 | |||
| 86 | private void selectedFontChanged() { | ||
| 87 | int selectedIndex = this.entries.getSelectedIndex(); | ||
| 88 | if (selectedIndex != -1) { | ||
| 89 | this.fonts[selectedIndex] = this.chooser.getSelectedFont(); | ||
| 90 | } | ||
| 91 | } | ||
| 92 | |||
| 93 | private void updateUiState() { | ||
| 94 | recursiveSetEnabled(this.chooser, this.entries.getSelectedIndex() != -1 && this.customCheckBox.isSelected()); | ||
| 95 | this.entries.setEnabled(this.customCheckBox.isSelected()); | ||
| 96 | } | ||
| 97 | |||
| 98 | private void apply() { | ||
| 99 | for (int i = 0; i < CATEGORIES.size(); i++) { | ||
| 100 | UiConfig.setFont(CATEGORIES.get(i), this.fonts[i]); | ||
| 101 | } | ||
| 102 | UiConfig.setUseCustomFonts(this.customCheckBox.isSelected()); | ||
| 103 | UiConfig.save(); | ||
| 104 | ChangeDialog.show(this); | ||
| 105 | this.dispose(); | ||
| 106 | } | ||
| 107 | |||
| 108 | private void cancel() { | ||
| 109 | this.dispose(); | ||
| 110 | } | ||
| 111 | |||
| 112 | public static void display(Frame parent) { | ||
| 113 | FontDialog d = new FontDialog(parent); | ||
| 114 | d.setVisible(true); | ||
| 115 | } | ||
| 116 | |||
| 117 | private static void recursiveSetEnabled(Component self, boolean enabled) { | ||
| 118 | if (self instanceof Container) { | ||
| 119 | for (Component component : ((Container) self).getComponents()) { | ||
| 120 | recursiveSetEnabled(component, enabled); | ||
| 121 | } | ||
| 122 | self.setEnabled(enabled); | ||
| 123 | } | ||
| 124 | } | ||
| 125 | |||
| 126 | } | ||
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 9fbe65af..a934d34c 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 | |||
| @@ -11,23 +11,18 @@ | |||
| 11 | 11 | ||
| 12 | package cuchaz.enigma.gui.dialog; | 12 | package cuchaz.enigma.gui.dialog; |
| 13 | 13 | ||
| 14 | import cuchaz.enigma.gui.util.GuiUtil; | ||
| 15 | import cuchaz.enigma.utils.I18n; | ||
| 16 | import cuchaz.enigma.gui.util.ScaleUtil; | ||
| 17 | |||
| 18 | import javax.swing.*; | ||
| 19 | import javax.swing.text.html.HTML; | ||
| 20 | |||
| 21 | import java.awt.*; | ||
| 22 | import java.awt.BorderLayout; | 14 | import java.awt.BorderLayout; |
| 23 | import java.awt.Container; | 15 | import java.awt.Container; |
| 16 | import java.awt.Dimension; | ||
| 24 | import java.awt.FlowLayout; | 17 | import java.awt.FlowLayout; |
| 25 | import java.awt.event.KeyAdapter; | 18 | import java.awt.event.KeyAdapter; |
| 26 | import java.awt.event.KeyEvent; | 19 | import java.awt.event.KeyEvent; |
| 27 | 20 | ||
| 28 | import javax.swing.*; | 21 | import javax.swing.*; |
| 22 | import javax.swing.text.html.HTML; | ||
| 29 | 23 | ||
| 30 | import com.google.common.base.Strings; | 24 | import com.google.common.base.Strings; |
| 25 | |||
| 31 | import cuchaz.enigma.analysis.EntryReference; | 26 | import cuchaz.enigma.analysis.EntryReference; |
| 32 | import cuchaz.enigma.gui.GuiController; | 27 | import cuchaz.enigma.gui.GuiController; |
| 33 | import cuchaz.enigma.gui.elements.ValidatableTextArea; | 28 | import cuchaz.enigma.gui.elements.ValidatableTextArea; |
| @@ -88,10 +83,10 @@ public class JavadocDialog { | |||
| 88 | JPanel buttonsPanel = new JPanel(); | 83 | JPanel buttonsPanel = new JPanel(); |
| 89 | buttonsPanel.setLayout(new FlowLayout(FlowLayout.RIGHT)); | 84 | buttonsPanel.setLayout(new FlowLayout(FlowLayout.RIGHT)); |
| 90 | buttonsPanel.add(GuiUtil.unboldLabel(new JLabel(I18n.translate("javadocs.instruction")))); | 85 | buttonsPanel.add(GuiUtil.unboldLabel(new JLabel(I18n.translate("javadocs.instruction")))); |
| 91 | JButton cancelButton = new JButton(I18n.translate("javadocs.cancel")); | 86 | JButton cancelButton = new JButton(I18n.translate("prompt.cancel")); |
| 92 | cancelButton.addActionListener(event -> close()); | 87 | cancelButton.addActionListener(event -> close()); |
| 93 | buttonsPanel.add(cancelButton); | 88 | buttonsPanel.add(cancelButton); |
| 94 | JButton saveButton = new JButton(I18n.translate("javadocs.save")); | 89 | JButton saveButton = new JButton(I18n.translate("prompt.save")); |
| 95 | saveButton.addActionListener(event -> doSave()); | 90 | saveButton.addActionListener(event -> doSave()); |
| 96 | buttonsPanel.add(saveButton); | 91 | buttonsPanel.add(saveButton); |
| 97 | contentPane.add(buttonsPanel, BorderLayout.SOUTH); | 92 | contentPane.add(buttonsPanel, BorderLayout.SOUTH); |
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 d6c60e04..d5d657dc 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 | |||
| @@ -1,12 +1,10 @@ | |||
| 1 | package cuchaz.enigma.gui.elements; | 1 | package cuchaz.enigma.gui.elements; |
| 2 | 2 | ||
| 3 | import java.awt.Desktop; | 3 | import java.awt.FileDialog; |
| 4 | import java.awt.event.InputEvent; | 4 | import java.awt.event.InputEvent; |
| 5 | import java.awt.event.KeyEvent; | 5 | import java.awt.event.KeyEvent; |
| 6 | import java.io.File; | 6 | import java.io.File; |
| 7 | import java.io.IOException; | 7 | import java.io.IOException; |
| 8 | import java.net.URISyntaxException; | ||
| 9 | import java.net.URL; | ||
| 10 | import java.nio.file.Files; | 8 | import java.nio.file.Files; |
| 11 | import java.nio.file.Path; | 9 | import java.nio.file.Path; |
| 12 | import java.nio.file.Paths; | 10 | import java.nio.file.Paths; |
| @@ -20,9 +18,13 @@ import javax.swing.*; | |||
| 20 | 18 | ||
| 21 | import cuchaz.enigma.gui.ConnectionState; | 19 | import cuchaz.enigma.gui.ConnectionState; |
| 22 | import cuchaz.enigma.gui.Gui; | 20 | import cuchaz.enigma.gui.Gui; |
| 23 | import cuchaz.enigma.gui.config.Config; | 21 | import cuchaz.enigma.gui.config.Decompiler; |
| 24 | import cuchaz.enigma.gui.config.Themes; | 22 | import cuchaz.enigma.gui.config.LookAndFeel; |
| 23 | import cuchaz.enigma.gui.config.NetConfig; | ||
| 24 | import cuchaz.enigma.gui.config.UiConfig; | ||
| 25 | import cuchaz.enigma.gui.dialog.*; | 25 | import cuchaz.enigma.gui.dialog.*; |
| 26 | import cuchaz.enigma.gui.util.GuiUtil; | ||
| 27 | import cuchaz.enigma.gui.util.LanguageUtil; | ||
| 26 | import cuchaz.enigma.gui.util.ScaleUtil; | 28 | import cuchaz.enigma.gui.util.ScaleUtil; |
| 27 | import cuchaz.enigma.translation.mapping.serde.MappingFormat; | 29 | import cuchaz.enigma.translation.mapping.serde.MappingFormat; |
| 28 | import cuchaz.enigma.utils.I18n; | 30 | import cuchaz.enigma.utils.I18n; |
| @@ -32,48 +34,51 @@ public class MenuBar { | |||
| 32 | 34 | ||
| 33 | private final JMenuBar ui = new JMenuBar(); | 35 | private final JMenuBar ui = new JMenuBar(); |
| 34 | 36 | ||
| 35 | private final JMenu fileMenu = new JMenu(I18n.translate("menu.file")); | 37 | private final JMenu fileMenu = new JMenu(); |
| 36 | private final JMenuItem jarOpenItem = new JMenuItem(I18n.translate("menu.file.jar.open")); | 38 | private final JMenuItem jarOpenItem = new JMenuItem(); |
| 37 | private final JMenuItem jarCloseItem = new JMenuItem(I18n.translate("menu.file.jar.close")); | 39 | private final JMenuItem jarCloseItem = new JMenuItem(); |
| 38 | private final JMenu openMenu = new JMenu(I18n.translate("menu.file.mappings.open")); | 40 | private final JMenu openMenu = new JMenu(); |
| 39 | private final JMenuItem saveMappingsItem = new JMenuItem(I18n.translate("menu.file.mappings.save")); | 41 | private final JMenuItem saveMappingsItem = new JMenuItem(); |
| 40 | private final JMenu saveMappingsAsMenu = new JMenu(I18n.translate("menu.file.mappings.save_as")); | 42 | private final JMenu saveMappingsAsMenu = new JMenu(); |
| 41 | private final JMenuItem closeMappingsItem = new JMenuItem(I18n.translate("menu.file.mappings.close")); | 43 | private final JMenuItem closeMappingsItem = new JMenuItem(); |
| 42 | private final JMenuItem dropMappingsItem = new JMenuItem(I18n.translate("menu.file.mappings.drop")); | 44 | private final JMenuItem dropMappingsItem = new JMenuItem(); |
| 43 | private final JMenuItem reloadMappingsItem = new JMenuItem(I18n.translate("menu.file.reload_mappings")); | 45 | private final JMenuItem reloadMappingsItem = new JMenuItem(); |
| 44 | private final JMenuItem reloadAllItem = new JMenuItem(I18n.translate("menu.file.reload_all")); | 46 | private final JMenuItem reloadAllItem = new JMenuItem(); |
| 45 | private final JMenuItem exportSourceItem = new JMenuItem(I18n.translate("menu.file.export.source")); | 47 | private final JMenuItem exportSourceItem = new JMenuItem(); |
| 46 | private final JMenuItem exportJarItem = new JMenuItem(I18n.translate("menu.file.export.jar")); | 48 | private final JMenuItem exportJarItem = new JMenuItem(); |
| 47 | private final JMenuItem statsItem = new JMenuItem(I18n.translate("menu.file.stats")); | 49 | private final JMenuItem statsItem = new JMenuItem(); |
| 48 | private final JMenuItem exitItem = new JMenuItem(I18n.translate("menu.file.exit")); | 50 | private final JMenuItem exitItem = new JMenuItem(); |
| 49 | 51 | ||
| 50 | private final JMenu decompilerMenu = new JMenu(I18n.translate("menu.decompiler")); | 52 | private final JMenu decompilerMenu = new JMenu(); |
| 51 | 53 | ||
| 52 | private final JMenu viewMenu = new JMenu(I18n.translate("menu.view")); | 54 | private final JMenu viewMenu = new JMenu(); |
| 53 | private final JMenu themesMenu = new JMenu(I18n.translate("menu.view.themes")); | 55 | private final JMenu themesMenu = new JMenu(); |
| 54 | private final JMenu languagesMenu = new JMenu(I18n.translate("menu.view.languages")); | 56 | private final JMenu languagesMenu = new JMenu(); |
| 55 | private final JMenu scaleMenu = new JMenu(I18n.translate("menu.view.scale")); | 57 | private final JMenu scaleMenu = new JMenu(); |
| 56 | private final JMenuItem customScaleItem = new JMenuItem(I18n.translate("menu.view.scale.custom")); | 58 | private final JMenuItem fontItem = new JMenuItem(); |
| 57 | private final JMenuItem searchItem = new JMenuItem(I18n.translate("menu.view.search")); | 59 | private final JMenuItem customScaleItem = new JMenuItem(); |
| 58 | 60 | private final JMenuItem searchItem = new JMenuItem(); | |
| 59 | private final JMenu collabMenu = new JMenu(I18n.translate("menu.collab")); | 61 | |
| 60 | private final JMenuItem connectItem = new JMenuItem(I18n.translate("menu.collab.connect")); | 62 | private final JMenu collabMenu = new JMenu(); |
| 61 | private final JMenuItem startServerItem = new JMenuItem(I18n.translate("menu.collab.server.start")); | 63 | private final JMenuItem connectItem = new JMenuItem(); |
| 62 | 64 | private final JMenuItem startServerItem = new JMenuItem(); | |
| 63 | private final JMenu helpMenu = new JMenu(I18n.translate("menu.help")); | 65 | |
| 64 | private final JMenuItem aboutItem = new JMenuItem(I18n.translate("menu.help.about")); | 66 | private final JMenu helpMenu = new JMenu(); |
| 65 | private final JMenuItem githubItem = new JMenuItem(I18n.translate("menu.help.github")); | 67 | private final JMenuItem aboutItem = new JMenuItem(); |
| 68 | private final JMenuItem githubItem = new JMenuItem(); | ||
| 66 | 69 | ||
| 67 | private final Gui gui; | 70 | private final Gui gui; |
| 68 | 71 | ||
| 69 | public MenuBar(Gui gui) { | 72 | public MenuBar(Gui gui) { |
| 70 | this.gui = gui; | 73 | this.gui = gui; |
| 71 | 74 | ||
| 75 | this.retranslateUi(); | ||
| 76 | |||
| 72 | prepareOpenMenu(this.openMenu, gui); | 77 | prepareOpenMenu(this.openMenu, gui); |
| 73 | prepareSaveMappingsAsMenu(this.saveMappingsAsMenu, this.saveMappingsItem, gui); | 78 | prepareSaveMappingsAsMenu(this.saveMappingsAsMenu, this.saveMappingsItem, gui); |
| 74 | prepareDecompilerMenu(this.decompilerMenu, gui); | 79 | prepareDecompilerMenu(this.decompilerMenu, gui); |
| 75 | prepareThemesMenu(this.themesMenu, gui); | 80 | prepareThemesMenu(this.themesMenu, gui); |
| 76 | prepareLanguagesMenu(this.languagesMenu, gui); | 81 | prepareLanguagesMenu(this.languagesMenu); |
| 77 | prepareScaleMenu(this.scaleMenu, gui); | 82 | prepareScaleMenu(this.scaleMenu, gui); |
| 78 | 83 | ||
| 79 | this.fileMenu.add(this.jarOpenItem); | 84 | this.fileMenu.add(this.jarOpenItem); |
| @@ -102,6 +107,7 @@ public class MenuBar { | |||
| 102 | this.viewMenu.add(this.languagesMenu); | 107 | this.viewMenu.add(this.languagesMenu); |
| 103 | this.scaleMenu.add(this.customScaleItem); | 108 | this.scaleMenu.add(this.customScaleItem); |
| 104 | this.viewMenu.add(this.scaleMenu); | 109 | this.viewMenu.add(this.scaleMenu); |
| 110 | this.viewMenu.add(this.fontItem); | ||
| 105 | this.viewMenu.addSeparator(); | 111 | this.viewMenu.addSeparator(); |
| 106 | this.viewMenu.add(this.searchItem); | 112 | this.viewMenu.add(this.searchItem); |
| 107 | this.ui.add(this.viewMenu); | 113 | this.ui.add(this.viewMenu); |
| @@ -129,6 +135,7 @@ public class MenuBar { | |||
| 129 | this.statsItem.addActionListener(_e -> StatsDialog.show(this.gui)); | 135 | this.statsItem.addActionListener(_e -> StatsDialog.show(this.gui)); |
| 130 | this.exitItem.addActionListener(_e -> this.gui.close()); | 136 | this.exitItem.addActionListener(_e -> this.gui.close()); |
| 131 | this.customScaleItem.addActionListener(_e -> this.onCustomScaleClicked()); | 137 | this.customScaleItem.addActionListener(_e -> this.onCustomScaleClicked()); |
| 138 | this.fontItem.addActionListener(_e -> this.onFontClicked(this.gui)); | ||
| 132 | this.searchItem.addActionListener(_e -> this.onSearchClicked()); | 139 | this.searchItem.addActionListener(_e -> this.onSearchClicked()); |
| 133 | this.connectItem.addActionListener(_e -> this.onConnectClicked()); | 140 | this.connectItem.addActionListener(_e -> this.onConnectClicked()); |
| 134 | this.startServerItem.addActionListener(_e -> this.onStartServerClicked()); | 141 | this.startServerItem.addActionListener(_e -> this.onStartServerClicked()); |
| @@ -157,20 +164,58 @@ public class MenuBar { | |||
| 157 | this.statsItem.setEnabled(jarOpen); | 164 | this.statsItem.setEnabled(jarOpen); |
| 158 | } | 165 | } |
| 159 | 166 | ||
| 167 | public void retranslateUi() { | ||
| 168 | this.fileMenu.setText(I18n.translate("menu.file")); | ||
| 169 | this.jarOpenItem.setText(I18n.translate("menu.file.jar.open")); | ||
| 170 | this.jarCloseItem.setText(I18n.translate("menu.file.jar.close")); | ||
| 171 | this.openMenu.setText(I18n.translate("menu.file.mappings.open")); | ||
| 172 | this.saveMappingsItem.setText(I18n.translate("menu.file.mappings.save")); | ||
| 173 | this.saveMappingsAsMenu.setText(I18n.translate("menu.file.mappings.save_as")); | ||
| 174 | this.closeMappingsItem.setText(I18n.translate("menu.file.mappings.close")); | ||
| 175 | this.dropMappingsItem.setText(I18n.translate("menu.file.mappings.drop")); | ||
| 176 | this.reloadMappingsItem.setText(I18n.translate("menu.file.reload_mappings")); | ||
| 177 | this.reloadAllItem.setText(I18n.translate("menu.file.reload_all")); | ||
| 178 | this.exportSourceItem.setText(I18n.translate("menu.file.export.source")); | ||
| 179 | this.exportJarItem.setText(I18n.translate("menu.file.export.jar")); | ||
| 180 | this.statsItem.setText(I18n.translate("menu.file.stats")); | ||
| 181 | this.exitItem.setText(I18n.translate("menu.file.exit")); | ||
| 182 | |||
| 183 | this.decompilerMenu.setText(I18n.translate("menu.decompiler")); | ||
| 184 | |||
| 185 | this.viewMenu.setText(I18n.translate("menu.view")); | ||
| 186 | this.themesMenu.setText(I18n.translate("menu.view.themes")); | ||
| 187 | this.languagesMenu.setText(I18n.translate("menu.view.languages")); | ||
| 188 | this.scaleMenu.setText(I18n.translate("menu.view.scale")); | ||
| 189 | this.fontItem.setText(I18n.translate("menu.view.font")); | ||
| 190 | this.customScaleItem.setText(I18n.translate("menu.view.scale.custom")); | ||
| 191 | this.searchItem.setText(I18n.translate("menu.view.search")); | ||
| 192 | |||
| 193 | this.collabMenu.setText(I18n.translate("menu.collab")); | ||
| 194 | this.connectItem.setText(I18n.translate("menu.collab.connect")); | ||
| 195 | this.startServerItem.setText(I18n.translate("menu.collab.server.start")); | ||
| 196 | |||
| 197 | this.helpMenu.setText(I18n.translate("menu.help")); | ||
| 198 | this.aboutItem.setText(I18n.translate("menu.help.about")); | ||
| 199 | this.githubItem.setText(I18n.translate("menu.help.github")); | ||
| 200 | } | ||
| 201 | |||
| 160 | public JMenuBar getUi() { | 202 | public JMenuBar getUi() { |
| 161 | return this.ui; | 203 | return this.ui; |
| 162 | } | 204 | } |
| 163 | 205 | ||
| 164 | private void onOpenJarClicked() { | 206 | private void onOpenJarClicked() { |
| 165 | this.gui.jarFileChooser.setVisible(true); | 207 | FileDialog d = this.gui.jarFileChooser; |
| 166 | String file = this.gui.jarFileChooser.getFile(); | 208 | d.setDirectory(UiConfig.getLastSelectedDir()); |
| 209 | d.setVisible(true); | ||
| 210 | String file = d.getFile(); | ||
| 167 | // checks if the file name is not empty | 211 | // checks if the file name is not empty |
| 168 | if (file != null) { | 212 | if (file != null) { |
| 169 | Path path = Paths.get(this.gui.jarFileChooser.getDirectory()).resolve(file); | 213 | Path path = Paths.get(d.getDirectory()).resolve(file); |
| 170 | // checks if the file name corresponds to an existing file | 214 | // checks if the file name corresponds to an existing file |
| 171 | if (Files.exists(path)) { | 215 | if (Files.exists(path)) { |
| 172 | this.gui.getController().openJar(path); | 216 | this.gui.getController().openJar(path); |
| 173 | } | 217 | } |
| 218 | UiConfig.setLastSelectedDir(d.getDirectory()); | ||
| 174 | } | 219 | } |
| 175 | } | 220 | } |
| 176 | 221 | ||
| @@ -187,7 +232,7 @@ public class MenuBar { | |||
| 187 | } else if (response == JOptionPane.NO_OPTION) | 232 | } else if (response == JOptionPane.NO_OPTION) |
| 188 | then.run(); | 233 | then.run(); |
| 189 | return null; | 234 | return null; |
| 190 | }), I18n.translate("prompt.close.save"), I18n.translate("prompt.close.discard"), I18n.translate("prompt.close.cancel")); | 235 | }), I18n.translate("prompt.close.save"), I18n.translate("prompt.close.discard"), I18n.translate("prompt.cancel")); |
| 191 | } else { | 236 | } else { |
| 192 | then.run(); | 237 | then.run(); |
| 193 | } | 238 | } |
| @@ -206,16 +251,20 @@ public class MenuBar { | |||
| 206 | } | 251 | } |
| 207 | 252 | ||
| 208 | private void onExportSourceClicked() { | 253 | private void onExportSourceClicked() { |
| 254 | this.gui.exportSourceFileChooser.setCurrentDirectory(new File(UiConfig.getLastSelectedDir())); | ||
| 209 | if (this.gui.exportSourceFileChooser.showSaveDialog(this.gui.getFrame()) == JFileChooser.APPROVE_OPTION) { | 255 | if (this.gui.exportSourceFileChooser.showSaveDialog(this.gui.getFrame()) == JFileChooser.APPROVE_OPTION) { |
| 256 | UiConfig.setLastSelectedDir(this.gui.exportSourceFileChooser.getCurrentDirectory().toString()); | ||
| 210 | this.gui.getController().exportSource(this.gui.exportSourceFileChooser.getSelectedFile().toPath()); | 257 | this.gui.getController().exportSource(this.gui.exportSourceFileChooser.getSelectedFile().toPath()); |
| 211 | } | 258 | } |
| 212 | } | 259 | } |
| 213 | 260 | ||
| 214 | private void onExportJarClicked() { | 261 | private void onExportJarClicked() { |
| 262 | this.gui.exportJarFileChooser.setDirectory(UiConfig.getLastSelectedDir()); | ||
| 215 | this.gui.exportJarFileChooser.setVisible(true); | 263 | this.gui.exportJarFileChooser.setVisible(true); |
| 216 | if (this.gui.exportJarFileChooser.getFile() != null) { | 264 | if (this.gui.exportJarFileChooser.getFile() != null) { |
| 217 | Path path = Paths.get(this.gui.exportJarFileChooser.getDirectory(), this.gui.exportJarFileChooser.getFile()); | 265 | Path path = Paths.get(this.gui.exportJarFileChooser.getDirectory(), this.gui.exportJarFileChooser.getFile()); |
| 218 | this.gui.getController().exportJar(path); | 266 | this.gui.getController().exportJar(path); |
| 267 | UiConfig.setLastSelectedDir(this.gui.exportJarFileChooser.getDirectory()); | ||
| 219 | } | 268 | } |
| 220 | } | 269 | } |
| 221 | 270 | ||
| @@ -229,7 +278,21 @@ public class MenuBar { | |||
| 229 | } catch (NumberFormatException ignored) { | 278 | } catch (NumberFormatException ignored) { |
| 230 | } | 279 | } |
| 231 | ScaleUtil.setScaleFactor(newScale); | 280 | ScaleUtil.setScaleFactor(newScale); |
| 232 | ChangeDialog.show(this.gui); | 281 | ChangeDialog.show(this.gui.getFrame()); |
| 282 | } | ||
| 283 | |||
| 284 | private void onFontClicked(Gui gui) { | ||
| 285 | // FontDialog fd = new FontDialog(gui.getFrame(), "Choose Font", true); | ||
| 286 | // fd.setLocationRelativeTo(gui.getFrame()); | ||
| 287 | // fd.setSelectedFont(UiConfig.getEditorFont()); | ||
| 288 | // fd.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE); | ||
| 289 | // fd.setVisible(true); | ||
| 290 | // | ||
| 291 | // if (!fd.isCancelSelected()) { | ||
| 292 | // UiConfig.setEditorFont(fd.getSelectedFont()); | ||
| 293 | // UiConfig.save(); | ||
| 294 | // } | ||
| 295 | FontDialog.display(gui.getFrame()); | ||
| 233 | } | 296 | } |
| 234 | 297 | ||
| 235 | private void onSearchClicked() { | 298 | private void onSearchClicked() { |
| @@ -250,6 +313,10 @@ public class MenuBar { | |||
| 250 | this.gui.getController().disconnectIfConnected(null); | 313 | this.gui.getController().disconnectIfConnected(null); |
| 251 | try { | 314 | try { |
| 252 | this.gui.getController().createClient(result.getUsername(), result.getAddress().address, result.getAddress().port, result.getPassword()); | 315 | this.gui.getController().createClient(result.getUsername(), result.getAddress().address, result.getAddress().port, result.getPassword()); |
| 316 | NetConfig.setUsername(result.getUsername()); | ||
| 317 | NetConfig.setRemoteAddress(result.getAddressStr()); | ||
| 318 | NetConfig.setPassword(String.valueOf(result.getPassword())); | ||
| 319 | NetConfig.save(); | ||
| 253 | } catch (IOException e) { | 320 | } catch (IOException e) { |
| 254 | JOptionPane.showMessageDialog(this.gui.getFrame(), e.toString(), I18n.translate("menu.collab.connect.error"), JOptionPane.ERROR_MESSAGE); | 321 | JOptionPane.showMessageDialog(this.gui.getFrame(), e.toString(), I18n.translate("menu.collab.connect.error"), JOptionPane.ERROR_MESSAGE); |
| 255 | this.gui.getController().disconnectIfConnected(null); | 322 | this.gui.getController().disconnectIfConnected(null); |
| @@ -269,6 +336,9 @@ public class MenuBar { | |||
| 269 | this.gui.getController().disconnectIfConnected(null); | 336 | this.gui.getController().disconnectIfConnected(null); |
| 270 | try { | 337 | try { |
| 271 | this.gui.getController().createServer(result.getPort(), result.getPassword()); | 338 | this.gui.getController().createServer(result.getPort(), result.getPassword()); |
| 339 | NetConfig.setServerPort(result.getPort()); | ||
| 340 | NetConfig.setServerPassword(String.valueOf(result.getPassword())); | ||
| 341 | NetConfig.save(); | ||
| 272 | } catch (IOException e) { | 342 | } catch (IOException e) { |
| 273 | JOptionPane.showMessageDialog(this.gui.getFrame(), e.toString(), I18n.translate("menu.collab.server.start.error"), JOptionPane.ERROR_MESSAGE); | 343 | JOptionPane.showMessageDialog(this.gui.getFrame(), e.toString(), I18n.translate("menu.collab.server.start.error"), JOptionPane.ERROR_MESSAGE); |
| 274 | this.gui.getController().disconnectIfConnected(null); | 344 | this.gui.getController().disconnectIfConnected(null); |
| @@ -276,10 +346,7 @@ public class MenuBar { | |||
| 276 | } | 346 | } |
| 277 | 347 | ||
| 278 | private void onGithubClicked() { | 348 | private void onGithubClicked() { |
| 279 | try { | 349 | GuiUtil.openUrl("https://github.com/FabricMC/Enigma"); |
| 280 | Desktop.getDesktop().browse(new URL("https://github.com/FabricMC/Enigma").toURI()); | ||
| 281 | } catch (URISyntaxException | IOException ignored) { | ||
| 282 | } | ||
| 283 | } | 350 | } |
| 284 | 351 | ||
| 285 | private static void prepareOpenMenu(JMenu openMenu, Gui gui) { | 352 | private static void prepareOpenMenu(JMenu openMenu, Gui gui) { |
| @@ -287,9 +354,11 @@ public class MenuBar { | |||
| 287 | if (format.getReader() != null) { | 354 | if (format.getReader() != null) { |
| 288 | JMenuItem item = new JMenuItem(I18n.translate("mapping_format." + format.name().toLowerCase(Locale.ROOT))); | 355 | JMenuItem item = new JMenuItem(I18n.translate("mapping_format." + format.name().toLowerCase(Locale.ROOT))); |
| 289 | item.addActionListener(event -> { | 356 | item.addActionListener(event -> { |
| 357 | gui.enigmaMappingsFileChooser.setCurrentDirectory(new File(UiConfig.getLastSelectedDir())); | ||
| 290 | if (gui.enigmaMappingsFileChooser.showOpenDialog(gui.getFrame()) == JFileChooser.APPROVE_OPTION) { | 358 | if (gui.enigmaMappingsFileChooser.showOpenDialog(gui.getFrame()) == JFileChooser.APPROVE_OPTION) { |
| 291 | File selectedFile = gui.enigmaMappingsFileChooser.getSelectedFile(); | 359 | File selectedFile = gui.enigmaMappingsFileChooser.getSelectedFile(); |
| 292 | gui.getController().openMappings(format, selectedFile.toPath()); | 360 | gui.getController().openMappings(format, selectedFile.toPath()); |
| 361 | UiConfig.setLastSelectedDir(gui.enigmaMappingsFileChooser.getCurrentDirectory().toString()); | ||
| 293 | } | 362 | } |
| 294 | }); | 363 | }); |
| 295 | openMenu.add(item); | 364 | openMenu.add(item); |
| @@ -303,9 +372,14 @@ public class MenuBar { | |||
| 303 | JMenuItem item = new JMenuItem(I18n.translate("mapping_format." + format.name().toLowerCase(Locale.ROOT))); | 372 | JMenuItem item = new JMenuItem(I18n.translate("mapping_format." + format.name().toLowerCase(Locale.ROOT))); |
| 304 | item.addActionListener(event -> { | 373 | item.addActionListener(event -> { |
| 305 | // TODO: Use a specific file chooser for it | 374 | // TODO: Use a specific file chooser for it |
| 375 | if (gui.enigmaMappingsFileChooser.getCurrentDirectory() == null) { | ||
| 376 | gui.enigmaMappingsFileChooser.setCurrentDirectory(new File(UiConfig.getLastSelectedDir())); | ||
| 377 | } | ||
| 378 | |||
| 306 | if (gui.enigmaMappingsFileChooser.showSaveDialog(gui.getFrame()) == JFileChooser.APPROVE_OPTION) { | 379 | if (gui.enigmaMappingsFileChooser.showSaveDialog(gui.getFrame()) == JFileChooser.APPROVE_OPTION) { |
| 307 | gui.getController().saveMappings(gui.enigmaMappingsFileChooser.getSelectedFile().toPath(), format); | 380 | gui.getController().saveMappings(gui.enigmaMappingsFileChooser.getSelectedFile().toPath(), format); |
| 308 | saveMappingsItem.setEnabled(true); | 381 | saveMappingsItem.setEnabled(true); |
| 382 | UiConfig.setLastSelectedDir(gui.enigmaMappingsFileChooser.getCurrentDirectory().toString()); | ||
| 309 | } | 383 | } |
| 310 | }); | 384 | }); |
| 311 | saveMappingsAsMenu.add(item); | 385 | saveMappingsAsMenu.add(item); |
| @@ -316,21 +390,17 @@ public class MenuBar { | |||
| 316 | private static void prepareDecompilerMenu(JMenu decompilerMenu, Gui gui) { | 390 | private static void prepareDecompilerMenu(JMenu decompilerMenu, Gui gui) { |
| 317 | ButtonGroup decompilerGroup = new ButtonGroup(); | 391 | ButtonGroup decompilerGroup = new ButtonGroup(); |
| 318 | 392 | ||
| 319 | for (Config.Decompiler decompiler : Config.Decompiler.values()) { | 393 | for (Decompiler decompiler : Decompiler.values()) { |
| 320 | JRadioButtonMenuItem decompilerButton = new JRadioButtonMenuItem(decompiler.name); | 394 | JRadioButtonMenuItem decompilerButton = new JRadioButtonMenuItem(decompiler.name); |
| 321 | decompilerGroup.add(decompilerButton); | 395 | decompilerGroup.add(decompilerButton); |
| 322 | if (decompiler.equals(Config.getInstance().decompiler)) { | 396 | if (decompiler.equals(UiConfig.getDecompiler())) { |
| 323 | decompilerButton.setSelected(true); | 397 | decompilerButton.setSelected(true); |
| 324 | } | 398 | } |
| 325 | decompilerButton.addActionListener(event -> { | 399 | decompilerButton.addActionListener(event -> { |
| 326 | gui.getController().setDecompiler(decompiler.service); | 400 | gui.getController().setDecompiler(decompiler.service); |
| 327 | 401 | ||
| 328 | try { | 402 | UiConfig.setDecompiler(decompiler); |
| 329 | Config.getInstance().decompiler = decompiler; | 403 | UiConfig.save(); |
| 330 | Config.getInstance().saveConfig(); | ||
| 331 | } catch (IOException e) { | ||
| 332 | throw new RuntimeException(e); | ||
| 333 | } | ||
| 334 | }); | 404 | }); |
| 335 | decompilerMenu.add(decompilerButton); | 405 | decompilerMenu.add(decompilerButton); |
| 336 | } | 406 | } |
| @@ -338,33 +408,34 @@ public class MenuBar { | |||
| 338 | 408 | ||
| 339 | private static void prepareThemesMenu(JMenu themesMenu, Gui gui) { | 409 | private static void prepareThemesMenu(JMenu themesMenu, Gui gui) { |
| 340 | ButtonGroup themeGroup = new ButtonGroup(); | 410 | ButtonGroup themeGroup = new ButtonGroup(); |
| 341 | for (Config.LookAndFeel lookAndFeel : Config.LookAndFeel.values()) { | 411 | for (LookAndFeel lookAndFeel : LookAndFeel.values()) { |
| 342 | JRadioButtonMenuItem themeButton = new JRadioButtonMenuItem(I18n.translate("menu.view.themes." + lookAndFeel.name().toLowerCase(Locale.ROOT))); | 412 | JRadioButtonMenuItem themeButton = new JRadioButtonMenuItem(I18n.translate("menu.view.themes." + lookAndFeel.name().toLowerCase(Locale.ROOT))); |
| 343 | themeGroup.add(themeButton); | 413 | themeGroup.add(themeButton); |
| 344 | if (lookAndFeel.equals(Config.getInstance().lookAndFeel)) { | 414 | if (lookAndFeel.equals(UiConfig.getLookAndFeel())) { |
| 345 | themeButton.setSelected(true); | 415 | themeButton.setSelected(true); |
| 346 | } | 416 | } |
| 347 | themeButton.addActionListener(_e -> Themes.setLookAndFeel(lookAndFeel)); | 417 | themeButton.addActionListener(_e -> { |
| 418 | UiConfig.setLookAndFeel(lookAndFeel); | ||
| 419 | UiConfig.save(); | ||
| 420 | ChangeDialog.show(gui.getFrame()); | ||
| 421 | }); | ||
| 348 | themesMenu.add(themeButton); | 422 | themesMenu.add(themeButton); |
| 349 | } | 423 | } |
| 350 | } | 424 | } |
| 351 | 425 | ||
| 352 | private static void prepareLanguagesMenu(JMenu languagesMenu, Gui gui) { | 426 | private static void prepareLanguagesMenu(JMenu languagesMenu) { |
| 353 | ButtonGroup languageGroup = new ButtonGroup(); | 427 | ButtonGroup languageGroup = new ButtonGroup(); |
| 354 | for (String lang : I18n.getAvailableLanguages()) { | 428 | for (String lang : I18n.getAvailableLanguages()) { |
| 355 | JRadioButtonMenuItem languageButton = new JRadioButtonMenuItem(I18n.getLanguageName(lang)); | 429 | JRadioButtonMenuItem languageButton = new JRadioButtonMenuItem(I18n.getLanguageName(lang)); |
| 356 | languageGroup.add(languageButton); | 430 | languageGroup.add(languageButton); |
| 357 | if (lang.equals(Config.getInstance().language)) { | 431 | if (lang.equals(UiConfig.getLanguage())) { |
| 358 | languageButton.setSelected(true); | 432 | languageButton.setSelected(true); |
| 359 | } | 433 | } |
| 360 | languageButton.addActionListener(event -> { | 434 | languageButton.addActionListener(event -> { |
| 361 | Config.getInstance().language = lang; | 435 | UiConfig.setLanguage(lang); |
| 362 | try { | 436 | I18n.setLanguage(lang); |
| 363 | Config.getInstance().saveConfig(); | 437 | LanguageUtil.dispatchLanguageChange(); |
| 364 | } catch (IOException e) { | 438 | UiConfig.save(); |
| 365 | e.printStackTrace(); | ||
| 366 | } | ||
| 367 | ChangeDialog.show(gui); | ||
| 368 | }); | 439 | }); |
| 369 | languagesMenu.add(languageButton); | 440 | languagesMenu.add(languageButton); |
| 370 | } | 441 | } |
| @@ -377,7 +448,7 @@ public class MenuBar { | |||
| 377 | float realScaleFactor = scaleFactor / 100f; | 448 | float realScaleFactor = scaleFactor / 100f; |
| 378 | JRadioButtonMenuItem menuItem = new JRadioButtonMenuItem(String.format("%d%%", scaleFactor)); | 449 | JRadioButtonMenuItem menuItem = new JRadioButtonMenuItem(String.format("%d%%", scaleFactor)); |
| 379 | menuItem.addActionListener(event -> ScaleUtil.setScaleFactor(realScaleFactor)); | 450 | menuItem.addActionListener(event -> ScaleUtil.setScaleFactor(realScaleFactor)); |
| 380 | menuItem.addActionListener(event -> ChangeDialog.show(gui)); | 451 | menuItem.addActionListener(event -> ChangeDialog.show(gui.getFrame())); |
| 381 | scaleGroup.add(menuItem); | 452 | scaleGroup.add(menuItem); |
| 382 | scaleMenu.add(menuItem); | 453 | scaleMenu.add(menuItem); |
| 383 | return new Pair<>(realScaleFactor, menuItem); | 454 | return new Pair<>(realScaleFactor, menuItem); |
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 d4962f7b..10d7ce1c 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 | |||
| @@ -2,7 +2,7 @@ package cuchaz.enigma.gui.events; | |||
| 2 | 2 | ||
| 3 | import java.util.Map; | 3 | import java.util.Map; |
| 4 | 4 | ||
| 5 | import cuchaz.enigma.gui.config.Config.LookAndFeel; | 5 | import cuchaz.enigma.gui.config.LookAndFeel; |
| 6 | import cuchaz.enigma.gui.highlight.BoxHighlightPainter; | 6 | import cuchaz.enigma.gui.highlight.BoxHighlightPainter; |
| 7 | import cuchaz.enigma.source.RenamableTokenType; | 7 | import cuchaz.enigma.source.RenamableTokenType; |
| 8 | 8 | ||
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 3ae4380f..2d8d76a7 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 | |||
| @@ -11,12 +11,14 @@ | |||
| 11 | 11 | ||
| 12 | package cuchaz.enigma.gui.highlight; | 12 | package cuchaz.enigma.gui.highlight; |
| 13 | 13 | ||
| 14 | import cuchaz.enigma.gui.config.Config; | 14 | import java.awt.Color; |
| 15 | import java.awt.Graphics; | ||
| 16 | import java.awt.Rectangle; | ||
| 17 | import java.awt.Shape; | ||
| 15 | 18 | ||
| 16 | import javax.swing.text.BadLocationException; | 19 | import javax.swing.text.BadLocationException; |
| 17 | import javax.swing.text.Highlighter; | 20 | import javax.swing.text.Highlighter; |
| 18 | import javax.swing.text.JTextComponent; | 21 | import javax.swing.text.JTextComponent; |
| 19 | import java.awt.*; | ||
| 20 | 22 | ||
| 21 | public class BoxHighlightPainter implements Highlighter.HighlightPainter { | 23 | public class BoxHighlightPainter implements Highlighter.HighlightPainter { |
| 22 | private Color fillColor; | 24 | private Color fillColor; |
| @@ -27,8 +29,8 @@ public class BoxHighlightPainter implements Highlighter.HighlightPainter { | |||
| 27 | this.borderColor = borderColor; | 29 | this.borderColor = borderColor; |
| 28 | } | 30 | } |
| 29 | 31 | ||
| 30 | public static BoxHighlightPainter create(Config.AlphaColorEntry entry, Config.AlphaColorEntry entryOutline) { | 32 | public static BoxHighlightPainter create(Color color, Color outline) { |
| 31 | return new BoxHighlightPainter(entry != null ? entry.get() : null, entryOutline != null ? entryOutline.get() : null); | 33 | return new BoxHighlightPainter(color, outline); |
| 32 | } | 34 | } |
| 33 | 35 | ||
| 34 | public static Rectangle getBounds(JTextComponent text, int start, int end) { | 36 | public static Rectangle getBounds(JTextComponent text, int start, int end) { |
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 c899e689..22d64201 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 | |||
| @@ -11,11 +11,12 @@ | |||
| 11 | 11 | ||
| 12 | package cuchaz.enigma.gui.highlight; | 12 | package cuchaz.enigma.gui.highlight; |
| 13 | 13 | ||
| 14 | import cuchaz.enigma.gui.config.Config; | 14 | import java.awt.*; |
| 15 | 15 | ||
| 16 | import javax.swing.text.Highlighter; | 16 | import javax.swing.text.Highlighter; |
| 17 | import javax.swing.text.JTextComponent; | 17 | import javax.swing.text.JTextComponent; |
| 18 | import java.awt.*; | 18 | |
| 19 | import cuchaz.enigma.gui.config.UiConfig; | ||
| 19 | 20 | ||
| 20 | public class SelectionHighlightPainter implements Highlighter.HighlightPainter { | 21 | public class SelectionHighlightPainter implements Highlighter.HighlightPainter { |
| 21 | 22 | ||
| @@ -26,7 +27,7 @@ public class SelectionHighlightPainter implements Highlighter.HighlightPainter { | |||
| 26 | // draw a thick border | 27 | // draw a thick border |
| 27 | Graphics2D g2d = (Graphics2D) g; | 28 | Graphics2D g2d = (Graphics2D) g; |
| 28 | Rectangle bounds = BoxHighlightPainter.getBounds(text, start, end); | 29 | Rectangle bounds = BoxHighlightPainter.getBounds(text, start, end); |
| 29 | g2d.setColor(new Color(Config.getInstance().selectionHighlightColor)); | 30 | g2d.setColor(UiConfig.getSelectionHighlightColor()); |
| 30 | g2d.setStroke(new BasicStroke(2.0f)); | 31 | g2d.setStroke(new BasicStroke(2.0f)); |
| 31 | g2d.drawRoundRect(bounds.x, bounds.y, bounds.width, bounds.height, 4, 4); | 32 | g2d.drawRoundRect(bounds.x, bounds.y, bounds.width, bounds.height, 4, 4); |
| 32 | } | 33 | } |
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 bb8acc8e..3e357cbd 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 | |||
| @@ -13,6 +13,8 @@ import cuchaz.enigma.utils.I18n; | |||
| 13 | public class DeobfPanel extends JPanel { | 13 | public class DeobfPanel extends JPanel { |
| 14 | 14 | ||
| 15 | public final ClassSelector deobfClasses; | 15 | public final ClassSelector deobfClasses; |
| 16 | private final JLabel title = new JLabel(); | ||
| 17 | |||
| 16 | private final Gui gui; | 18 | private final Gui gui; |
| 17 | 19 | ||
| 18 | public DeobfPanel(Gui gui) { | 20 | public DeobfPanel(Gui gui) { |
| @@ -23,7 +25,14 @@ public class DeobfPanel extends JPanel { | |||
| 23 | this.deobfClasses.setRenameSelectionListener(gui::onPanelRename); | 25 | this.deobfClasses.setRenameSelectionListener(gui::onPanelRename); |
| 24 | 26 | ||
| 25 | this.setLayout(new BorderLayout()); | 27 | this.setLayout(new BorderLayout()); |
| 26 | this.add(new JLabel(I18n.translate("info_panel.classes.deobfuscated")), BorderLayout.NORTH); | 28 | this.add(this.title, BorderLayout.NORTH); |
| 27 | this.add(new JScrollPane(this.deobfClasses), BorderLayout.CENTER); | 29 | this.add(new JScrollPane(this.deobfClasses), BorderLayout.CENTER); |
| 30 | |||
| 31 | this.retranslateUi(); | ||
| 28 | } | 32 | } |
| 33 | |||
| 34 | public void retranslateUi() { | ||
| 35 | this.title.setText(I18n.translate("info_panel.classes.deobfuscated")); | ||
| 36 | } | ||
| 37 | |||
| 29 | } | 38 | } |
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 3409fc14..ab9de335 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 | |||
| @@ -24,8 +24,9 @@ import cuchaz.enigma.events.ClassHandleListener; | |||
| 24 | import cuchaz.enigma.gui.BrowserCaret; | 24 | import cuchaz.enigma.gui.BrowserCaret; |
| 25 | import cuchaz.enigma.gui.Gui; | 25 | import cuchaz.enigma.gui.Gui; |
| 26 | import cuchaz.enigma.gui.GuiController; | 26 | import cuchaz.enigma.gui.GuiController; |
| 27 | import cuchaz.enigma.gui.config.Config; | 27 | import cuchaz.enigma.gui.config.LookAndFeel; |
| 28 | import cuchaz.enigma.gui.config.Themes; | 28 | import cuchaz.enigma.gui.config.Themes; |
| 29 | import cuchaz.enigma.gui.config.UiConfig; | ||
| 29 | import cuchaz.enigma.gui.elements.PopupMenuBar; | 30 | import cuchaz.enigma.gui.elements.PopupMenuBar; |
| 30 | import cuchaz.enigma.gui.events.EditorActionListener; | 31 | import cuchaz.enigma.gui.events.EditorActionListener; |
| 31 | import cuchaz.enigma.gui.events.ThemeChangeListener; | 32 | import cuchaz.enigma.gui.events.ThemeChangeListener; |
| @@ -61,7 +62,7 @@ public class EditorPanel { | |||
| 61 | private final JLabel errorLabel = new JLabel(); | 62 | private final JLabel errorLabel = new JLabel(); |
| 62 | private final JTextArea errorTextArea = new JTextArea(); | 63 | private final JTextArea errorTextArea = new JTextArea(); |
| 63 | private final JScrollPane errorScrollPane = new JScrollPane(this.errorTextArea); | 64 | private final JScrollPane errorScrollPane = new JScrollPane(this.errorTextArea); |
| 64 | private final JButton retryButton = new JButton(I18n.translate("general.retry")); | 65 | private final JButton retryButton = new JButton(I18n.translate("prompt.retry")); |
| 65 | 66 | ||
| 66 | private DisplayMode mode = DisplayMode.INACTIVE; | 67 | private DisplayMode mode = DisplayMode.INACTIVE; |
| 67 | 68 | ||
| @@ -73,7 +74,7 @@ public class EditorPanel { | |||
| 73 | private boolean mouseIsPressed = false; | 74 | private boolean mouseIsPressed = false; |
| 74 | private boolean shouldNavigateOnClick; | 75 | private boolean shouldNavigateOnClick; |
| 75 | 76 | ||
| 76 | public Config.LookAndFeel editorLaf; | 77 | public LookAndFeel editorLaf; |
| 77 | private int fontSize = 12; | 78 | private int fontSize = 12; |
| 78 | private Map<RenamableTokenType, BoxHighlightPainter> boxHighlightPainters; | 79 | private Map<RenamableTokenType, BoxHighlightPainter> boxHighlightPainters; |
| 79 | 80 | ||
| @@ -94,9 +95,9 @@ public class EditorPanel { | |||
| 94 | this.editor.setCaret(new BrowserCaret()); | 95 | this.editor.setCaret(new BrowserCaret()); |
| 95 | this.editor.setFont(ScaleUtil.getFont(this.editor.getFont().getFontName(), Font.PLAIN, this.fontSize)); | 96 | this.editor.setFont(ScaleUtil.getFont(this.editor.getFont().getFontName(), Font.PLAIN, this.fontSize)); |
| 96 | this.editor.addCaretListener(event -> onCaretMove(event.getDot(), this.mouseIsPressed)); | 97 | this.editor.addCaretListener(event -> onCaretMove(event.getDot(), this.mouseIsPressed)); |
| 97 | this.editor.setCaretColor(new Color(Config.getInstance().caretColor)); | 98 | this.editor.setCaretColor(UiConfig.getCaretColor()); |
| 98 | this.editor.setContentType("text/enigma-sources"); | 99 | this.editor.setContentType("text/enigma-sources"); |
| 99 | this.editor.setBackground(new Color(Config.getInstance().editorBackground)); | 100 | this.editor.setBackground(UiConfig.getEditorBackgroundColor()); |
| 100 | DefaultSyntaxKit kit = (DefaultSyntaxKit) this.editor.getEditorKit(); | 101 | DefaultSyntaxKit kit = (DefaultSyntaxKit) this.editor.getEditorKit(); |
| 101 | kit.toggleComponent(this.editor, "de.sciss.syntaxpane.components.TokenMarker"); | 102 | kit.toggleComponent(this.editor, "de.sciss.syntaxpane.components.TokenMarker"); |
| 102 | 103 | ||
| @@ -240,7 +241,7 @@ public class EditorPanel { | |||
| 240 | this.themeChangeListener = (laf, boxHighlightPainters) -> { | 241 | this.themeChangeListener = (laf, boxHighlightPainters) -> { |
| 241 | if ((this.editorLaf == null || this.editorLaf != laf)) { | 242 | if ((this.editorLaf == null || this.editorLaf != laf)) { |
| 242 | this.editor.updateUI(); | 243 | this.editor.updateUI(); |
| 243 | this.editor.setBackground(new Color(Config.getInstance().editorBackground)); | 244 | this.editor.setBackground(UiConfig.getEditorBackgroundColor()); |
| 244 | if (this.editorLaf != null) { | 245 | if (this.editorLaf != null) { |
| 245 | this.classHandle.invalidateMapped(); | 246 | this.classHandle.invalidateMapped(); |
| 246 | } | 247 | } |
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 248d50d0..4c506404 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 | |||
| @@ -164,6 +164,11 @@ public class IdentifierPanel { | |||
| 164 | gui.getController().sendPacket(new RenameC2SPacket(entry, newName, true)); | 164 | gui.getController().sendPacket(new RenameC2SPacket(entry, newName, true)); |
| 165 | } | 165 | } |
| 166 | 166 | ||
| 167 | public void retranslateUi() { | ||
| 168 | this.ui.setBorder(BorderFactory.createTitledBorder(I18n.translate("info_panel.identifier"))); | ||
| 169 | this.refreshReference(); | ||
| 170 | } | ||
| 171 | |||
| 167 | public JPanel getUi() { | 172 | public JPanel getUi() { |
| 168 | return ui; | 173 | return ui; |
| 169 | } | 174 | } |
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 0ca05837..b384968d 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 | |||
| @@ -15,6 +15,8 @@ import cuchaz.enigma.utils.I18n; | |||
| 15 | public class ObfPanel extends JPanel { | 15 | public class ObfPanel extends JPanel { |
| 16 | 16 | ||
| 17 | public final ClassSelector obfClasses; | 17 | public final ClassSelector obfClasses; |
| 18 | private final JLabel title = new JLabel(); | ||
| 19 | |||
| 18 | private final Gui gui; | 20 | private final Gui gui; |
| 19 | 21 | ||
| 20 | public ObfPanel(Gui gui) { | 22 | public ObfPanel(Gui gui) { |
| @@ -34,7 +36,14 @@ public class ObfPanel extends JPanel { | |||
| 34 | this.obfClasses.setRenameSelectionListener(gui::onPanelRename); | 36 | this.obfClasses.setRenameSelectionListener(gui::onPanelRename); |
| 35 | 37 | ||
| 36 | this.setLayout(new BorderLayout()); | 38 | this.setLayout(new BorderLayout()); |
| 37 | this.add(new JLabel(I18n.translate("info_panel.classes.obfuscated")), BorderLayout.NORTH); | 39 | this.add(this.title, BorderLayout.NORTH); |
| 38 | this.add(new JScrollPane(this.obfClasses), BorderLayout.CENTER); | 40 | this.add(new JScrollPane(this.obfClasses), BorderLayout.CENTER); |
| 41 | |||
| 42 | this.retranslateUi(); | ||
| 39 | } | 43 | } |
| 44 | |||
| 45 | public void retranslateUi() { | ||
| 46 | this.title.setText(I18n.translate("info_panel.classes.obfuscated")); | ||
| 47 | } | ||
| 48 | |||
| 40 | } | 49 | } |
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 631e065c..7fe942d0 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 | |||
| @@ -16,17 +16,25 @@ import javax.swing.JComponent; | |||
| 16 | import javax.swing.JLabel; | 16 | import javax.swing.JLabel; |
| 17 | import javax.swing.ToolTipManager; | 17 | import javax.swing.ToolTipManager; |
| 18 | 18 | ||
| 19 | import cuchaz.enigma.utils.Os; | ||
| 20 | |||
| 19 | public class GuiUtil { | 21 | public class GuiUtil { |
| 20 | public static void openUrl(String url) { | 22 | public static void openUrl(String url) { |
| 21 | if (Desktop.isDesktopSupported()) { | 23 | try { |
| 22 | Desktop desktop = Desktop.getDesktop(); | 24 | switch (Os.getOs()) { |
| 23 | try { | 25 | case LINUX: |
| 24 | desktop.browse(new URI(url)); | 26 | new ProcessBuilder("/usr/bin/env", "xdg-open", url).start(); |
| 25 | } catch (IOException ex) { | 27 | break; |
| 26 | throw new Error(ex); | 28 | default: |
| 27 | } catch (URISyntaxException ex) { | 29 | if (Desktop.isDesktopSupported()) { |
| 28 | throw new IllegalArgumentException(ex); | 30 | Desktop desktop = Desktop.getDesktop(); |
| 31 | desktop.browse(new URI(url)); | ||
| 32 | } | ||
| 29 | } | 33 | } |
| 34 | } catch (IOException ex) { | ||
| 35 | throw new RuntimeException(ex); | ||
| 36 | } catch (URISyntaxException ex) { | ||
| 37 | throw new IllegalArgumentException(ex); | ||
| 30 | } | 38 | } |
| 31 | } | 39 | } |
| 32 | 40 | ||
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 new file mode 100644 index 00000000..69612288 --- /dev/null +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/util/LanguageChangeListener.java | |||
| @@ -0,0 +1,7 @@ | |||
| 1 | package cuchaz.enigma.gui.util; | ||
| 2 | |||
| 3 | public interface LanguageChangeListener { | ||
| 4 | |||
| 5 | void retranslateUi(); | ||
| 6 | |||
| 7 | } | ||
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 new file mode 100644 index 00000000..d3e63763 --- /dev/null +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/util/LanguageUtil.java | |||
| @@ -0,0 +1,25 @@ | |||
| 1 | package cuchaz.enigma.gui.util; | ||
| 2 | |||
| 3 | import java.util.ArrayList; | ||
| 4 | import java.util.List; | ||
| 5 | |||
| 6 | public final class LanguageUtil { | ||
| 7 | |||
| 8 | private static final List<LanguageChangeListener> listeners = new ArrayList<>(); | ||
| 9 | |||
| 10 | public LanguageUtil() { | ||
| 11 | } | ||
| 12 | |||
| 13 | public static void addListener(LanguageChangeListener listener) { | ||
| 14 | listeners.add(listener); | ||
| 15 | } | ||
| 16 | |||
| 17 | public static void removeListener(LanguageChangeListener listener) { | ||
| 18 | listeners.remove(listener); | ||
| 19 | } | ||
| 20 | |||
| 21 | public static void dispatchLanguageChange() { | ||
| 22 | listeners.forEach(LanguageChangeListener::retranslateUi); | ||
| 23 | } | ||
| 24 | |||
| 25 | } | ||
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 985615a4..47799fad 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 | |||
| @@ -3,7 +3,6 @@ package cuchaz.enigma.gui.util; | |||
| 3 | import java.awt.Dimension; | 3 | import java.awt.Dimension; |
| 4 | import java.awt.Font; | 4 | import java.awt.Font; |
| 5 | import java.awt.Insets; | 5 | import java.awt.Insets; |
| 6 | import java.io.IOException; | ||
| 7 | import java.lang.reflect.Field; | 6 | import java.lang.reflect.Field; |
| 8 | import java.util.ArrayList; | 7 | import java.util.ArrayList; |
| 9 | import java.util.List; | 8 | import java.util.List; |
| @@ -17,26 +16,27 @@ import com.github.swingdpi.plaf.BasicTweaker; | |||
| 17 | import com.github.swingdpi.plaf.MetalTweaker; | 16 | import com.github.swingdpi.plaf.MetalTweaker; |
| 18 | import com.github.swingdpi.plaf.NimbusTweaker; | 17 | import com.github.swingdpi.plaf.NimbusTweaker; |
| 19 | import com.github.swingdpi.plaf.WindowsTweaker; | 18 | import com.github.swingdpi.plaf.WindowsTweaker; |
| 20 | import cuchaz.enigma.gui.config.Config; | ||
| 21 | import de.sciss.syntaxpane.DefaultSyntaxKit; | 19 | import de.sciss.syntaxpane.DefaultSyntaxKit; |
| 22 | 20 | ||
| 21 | import cuchaz.enigma.gui.config.UiConfig; | ||
| 22 | |||
| 23 | public class ScaleUtil { | 23 | public class ScaleUtil { |
| 24 | 24 | ||
| 25 | private static List<ScaleChangeListener> listeners = new ArrayList<>(); | 25 | private static List<ScaleChangeListener> listeners = new ArrayList<>(); |
| 26 | 26 | ||
| 27 | public static float getScaleFactor() { | 27 | public static float getScaleFactor() { |
| 28 | return Config.getInstance().scaleFactor; | 28 | return UiConfig.getScaleFactor(); |
| 29 | } | 29 | } |
| 30 | 30 | ||
| 31 | public static void setScaleFactor(float scaleFactor) { | 31 | public static void setScaleFactor(float scaleFactor) { |
| 32 | float oldScale = getScaleFactor(); | 32 | float oldScale = getScaleFactor(); |
| 33 | float clamped = Math.min(Math.max(0.25f, scaleFactor), 10.0f); | 33 | float clamped = Math.min(Math.max(0.25f, scaleFactor), 10.0f); |
| 34 | Config.getInstance().scaleFactor = clamped; | 34 | UiConfig.setScaleFactor(clamped); |
| 35 | try { | 35 | rescaleFontInConfig("Default", oldScale); |
| 36 | Config.getInstance().saveConfig(); | 36 | rescaleFontInConfig("Default 2", oldScale); |
| 37 | } catch (IOException e) { | 37 | rescaleFontInConfig("Small", oldScale); |
| 38 | e.printStackTrace(); | 38 | rescaleFontInConfig("Editor", oldScale); |
| 39 | } | 39 | UiConfig.save(); |
| 40 | listeners.forEach(l -> l.onScaleChanged(clamped, oldScale)); | 40 | listeners.forEach(l -> l.onScaleChanged(clamped, oldScale)); |
| 41 | } | 41 | } |
| 42 | 42 | ||
| @@ -64,6 +64,15 @@ public class ScaleUtil { | |||
| 64 | return createTweakerForCurrentLook(getScaleFactor()).modifyFont("", font); | 64 | return createTweakerForCurrentLook(getScaleFactor()).modifyFont("", font); |
| 65 | } | 65 | } |
| 66 | 66 | ||
| 67 | private static void rescaleFontInConfig(String name, float oldScale) { | ||
| 68 | UiConfig.getFont(name).ifPresent(font -> UiConfig.setFont(name, rescaleFont(font, oldScale))); | ||
| 69 | } | ||
| 70 | |||
| 71 | public static Font rescaleFont(Font font, float oldScale) { | ||
| 72 | float newSize = Math.round(font.getSize() / oldScale * getScaleFactor()); | ||
| 73 | return font.deriveFont(newSize); | ||
| 74 | } | ||
| 75 | |||
| 67 | public static float scale(float f) { | 76 | public static float scale(float f) { |
| 68 | return f * getScaleFactor(); | 77 | return f * getScaleFactor(); |
| 69 | } | 78 | } |
diff --git a/enigma/src/main/java/cuchaz/enigma/config/ConfigContainer.java b/enigma/src/main/java/cuchaz/enigma/config/ConfigContainer.java new file mode 100644 index 00000000..cb9cbc2e --- /dev/null +++ b/enigma/src/main/java/cuchaz/enigma/config/ConfigContainer.java | |||
| @@ -0,0 +1,97 @@ | |||
| 1 | package cuchaz.enigma.config; | ||
| 2 | |||
| 3 | import java.io.IOException; | ||
| 4 | import java.nio.charset.StandardCharsets; | ||
| 5 | import java.nio.file.Files; | ||
| 6 | import java.nio.file.Path; | ||
| 7 | import java.util.Deque; | ||
| 8 | import java.util.LinkedList; | ||
| 9 | |||
| 10 | public class ConfigContainer { | ||
| 11 | |||
| 12 | private Path configPath; | ||
| 13 | private boolean existsOnDisk; | ||
| 14 | |||
| 15 | private final ConfigSection root = new ConfigSection(); | ||
| 16 | |||
| 17 | public ConfigSection data() { | ||
| 18 | return this.root; | ||
| 19 | } | ||
| 20 | |||
| 21 | public void save() { | ||
| 22 | if (this.configPath == null) throw new IllegalStateException("File has no config path set!"); | ||
| 23 | try { | ||
| 24 | Files.createDirectories(this.configPath.getParent()); | ||
| 25 | Files.write(this.configPath, this.serialize().getBytes(StandardCharsets.UTF_8)); | ||
| 26 | this.existsOnDisk = true; | ||
| 27 | } catch (IOException e) { | ||
| 28 | e.printStackTrace(); | ||
| 29 | } | ||
| 30 | } | ||
| 31 | |||
| 32 | public void saveAs(Path path) { | ||
| 33 | this.configPath = path; | ||
| 34 | this.save(); | ||
| 35 | } | ||
| 36 | |||
| 37 | public boolean existsOnDisk() { | ||
| 38 | return this.existsOnDisk; | ||
| 39 | } | ||
| 40 | |||
| 41 | public String serialize() { | ||
| 42 | return ConfigSerializer.structureToString(this.root); | ||
| 43 | } | ||
| 44 | |||
| 45 | public static ConfigContainer create() { | ||
| 46 | return new ConfigContainer(); | ||
| 47 | } | ||
| 48 | |||
| 49 | public static ConfigContainer getOrCreate(String name) { | ||
| 50 | return ConfigContainer.getOrCreate(ConfigPaths.getConfigFilePath(name)); | ||
| 51 | } | ||
| 52 | |||
| 53 | public static ConfigContainer getOrCreate(Path path) { | ||
| 54 | ConfigContainer cc = null; | ||
| 55 | try { | ||
| 56 | if (Files.exists(path)) { | ||
| 57 | String s = String.join("\n", Files.readAllLines(path)); | ||
| 58 | cc = ConfigContainer.parse(s); | ||
| 59 | cc.existsOnDisk = true; | ||
| 60 | } | ||
| 61 | } catch (IOException e) { | ||
| 62 | e.printStackTrace(); | ||
| 63 | } | ||
| 64 | |||
| 65 | if (cc == null) { | ||
| 66 | cc = ConfigContainer.create(); | ||
| 67 | } | ||
| 68 | |||
| 69 | cc.configPath = path; | ||
| 70 | return cc; | ||
| 71 | } | ||
| 72 | |||
| 73 | public static ConfigContainer parse(String source) { | ||
| 74 | ConfigContainer cc = ConfigContainer.create(); | ||
| 75 | Deque<ConfigSection> stack = new LinkedList<>(); | ||
| 76 | stack.push(cc.root); | ||
| 77 | ConfigSerializer.parse(source, new ConfigStructureVisitor() { | ||
| 78 | @Override | ||
| 79 | public void visitKeyValue(String key, String value) { | ||
| 80 | stack.peekLast().setString(key, value); | ||
| 81 | } | ||
| 82 | |||
| 83 | @Override | ||
| 84 | public void visitSection(String section) { | ||
| 85 | stack.add(stack.peekLast().section(section)); | ||
| 86 | } | ||
| 87 | |||
| 88 | @Override | ||
| 89 | public void jumpToRootSection() { | ||
| 90 | stack.clear(); | ||
| 91 | stack.push(cc.root); | ||
| 92 | } | ||
| 93 | }); | ||
| 94 | return cc; | ||
| 95 | } | ||
| 96 | |||
| 97 | } | ||
diff --git a/enigma/src/main/java/cuchaz/enigma/config/ConfigPaths.java b/enigma/src/main/java/cuchaz/enigma/config/ConfigPaths.java new file mode 100644 index 00000000..6e668f86 --- /dev/null +++ b/enigma/src/main/java/cuchaz/enigma/config/ConfigPaths.java | |||
| @@ -0,0 +1,40 @@ | |||
| 1 | package cuchaz.enigma.config; | ||
| 2 | |||
| 3 | import java.nio.file.Path; | ||
| 4 | import java.nio.file.Paths; | ||
| 5 | |||
| 6 | import cuchaz.enigma.utils.Os; | ||
| 7 | |||
| 8 | public class ConfigPaths { | ||
| 9 | |||
| 10 | public static Path getConfigFilePath(String name) { | ||
| 11 | String fileName = Os.getOs() == Os.LINUX ? String.format("%src", name) : String.format("%s.ini", name); | ||
| 12 | return getConfigPathRoot().resolve(fileName); | ||
| 13 | } | ||
| 14 | |||
| 15 | public static Path getConfigPathRoot() { | ||
| 16 | switch (Os.getOs()) { | ||
| 17 | case LINUX: | ||
| 18 | String configHome = System.getenv("XDG_CONFIG_HOME"); | ||
| 19 | if (configHome == null) { | ||
| 20 | return getUserHomeUnix().resolve(".config"); | ||
| 21 | } | ||
| 22 | return Paths.get(configHome); | ||
| 23 | case MAC: | ||
| 24 | return getUserHomeUnix().resolve("Library").resolve("Preferences"); | ||
| 25 | case WINDOWS: | ||
| 26 | return Paths.get(System.getenv("LOCALAPPDATA")); | ||
| 27 | default: | ||
| 28 | return Paths.get(System.getProperty("user.dir")); | ||
| 29 | } | ||
| 30 | } | ||
| 31 | |||
| 32 | private static Path getUserHomeUnix() { | ||
| 33 | String userHome = System.getenv("HOME"); | ||
| 34 | if (userHome == null) { | ||
| 35 | userHome = System.getProperty("user.dir"); | ||
| 36 | } | ||
| 37 | return Paths.get(userHome); | ||
| 38 | } | ||
| 39 | |||
| 40 | } | ||
diff --git a/enigma/src/main/java/cuchaz/enigma/config/ConfigSection.java b/enigma/src/main/java/cuchaz/enigma/config/ConfigSection.java new file mode 100644 index 00000000..3e7bf6d4 --- /dev/null +++ b/enigma/src/main/java/cuchaz/enigma/config/ConfigSection.java | |||
| @@ -0,0 +1,183 @@ | |||
| 1 | package cuchaz.enigma.config; | ||
| 2 | |||
| 3 | import java.util.*; | ||
| 4 | import java.util.function.Function; | ||
| 5 | |||
| 6 | public class ConfigSection { | ||
| 7 | |||
| 8 | private final Map<String, String> values; | ||
| 9 | private final Map<String, ConfigSection> sections; | ||
| 10 | |||
| 11 | private ConfigSection(Map<String, String> values, Map<String, ConfigSection> sections) { | ||
| 12 | this.values = values; | ||
| 13 | this.sections = sections; | ||
| 14 | } | ||
| 15 | |||
| 16 | public ConfigSection() { | ||
| 17 | this(new HashMap<>(), new HashMap<>()); | ||
| 18 | } | ||
| 19 | |||
| 20 | public ConfigSection section(String name) { | ||
| 21 | return this.sections.computeIfAbsent(name, _s -> new ConfigSection()); | ||
| 22 | } | ||
| 23 | |||
| 24 | public Map<String, String> values() { | ||
| 25 | return Collections.unmodifiableMap(this.values); | ||
| 26 | } | ||
| 27 | |||
| 28 | public Map<String, ConfigSection> sections() { | ||
| 29 | return Collections.unmodifiableMap(this.sections); | ||
| 30 | } | ||
| 31 | |||
| 32 | public boolean remove(String key) { | ||
| 33 | return this.values.remove(key) != null; | ||
| 34 | } | ||
| 35 | |||
| 36 | public boolean removeSection(String key) { | ||
| 37 | return this.sections.remove(key) != null; | ||
| 38 | } | ||
| 39 | |||
| 40 | public Optional<String> getString(String key) { | ||
| 41 | return Optional.ofNullable(this.values.get(key)); | ||
| 42 | } | ||
| 43 | |||
| 44 | public void setString(String key, String value) { | ||
| 45 | this.values.put(key, value); | ||
| 46 | } | ||
| 47 | |||
| 48 | public String setIfAbsentString(String key, String value) { | ||
| 49 | this.values.putIfAbsent(key, value); | ||
| 50 | return this.values.get(key); | ||
| 51 | } | ||
| 52 | |||
| 53 | public Optional<Boolean> getBool(String key) { | ||
| 54 | return ConfigSerializer.parseBool(this.values.get(key)); | ||
| 55 | } | ||
| 56 | |||
| 57 | public boolean setIfAbsentBool(String key, boolean value) { | ||
| 58 | return this.getBool(key).orElseGet(() -> { | ||
| 59 | this.setBool(key, value); | ||
| 60 | return value; | ||
| 61 | }); | ||
| 62 | } | ||
| 63 | |||
| 64 | public void setBool(String key, boolean value) { | ||
| 65 | this.values.put(key, Boolean.toString(value)); | ||
| 66 | } | ||
| 67 | |||
| 68 | public OptionalInt getInt(String key) { | ||
| 69 | return ConfigSerializer.parseInt(this.values.get(key)); | ||
| 70 | } | ||
| 71 | |||
| 72 | public void setInt(String key, int value) { | ||
| 73 | this.values.put(key, Integer.toString(value)); | ||
| 74 | } | ||
| 75 | |||
| 76 | public int setIfAbsentInt(String key, int value) { | ||
| 77 | return this.getInt(key).orElseGet(() -> { | ||
| 78 | this.setInt(key, value); | ||
| 79 | return value; | ||
| 80 | }); | ||
| 81 | } | ||
| 82 | |||
| 83 | public OptionalDouble getDouble(String key) { | ||
| 84 | return ConfigSerializer.parseDouble(this.values.get(key)); | ||
| 85 | } | ||
| 86 | |||
| 87 | public void setDouble(String key, double value) { | ||
| 88 | this.values.put(key, Double.toString(value)); | ||
| 89 | } | ||
| 90 | |||
| 91 | public double setIfAbsentDouble(String key, double value) { | ||
| 92 | return this.getDouble(key).orElseGet(() -> { | ||
| 93 | this.setDouble(key, value); | ||
| 94 | return value; | ||
| 95 | }); | ||
| 96 | } | ||
| 97 | |||
| 98 | public OptionalInt getRgbColor(String key) { | ||
| 99 | return ConfigSerializer.parseRgbColor(this.values.get(key)); | ||
| 100 | } | ||
| 101 | |||
| 102 | public void setRgbColor(String key, int value) { | ||
| 103 | this.values.put(key, ConfigSerializer.rgbColorToString(value)); | ||
| 104 | } | ||
| 105 | |||
| 106 | public int setIfAbsentRgbColor(String key, int value) { | ||
| 107 | return this.getRgbColor(key).orElseGet(() -> { | ||
| 108 | this.setRgbColor(key, value); | ||
| 109 | return value; | ||
| 110 | }); | ||
| 111 | } | ||
| 112 | |||
| 113 | public Optional<String[]> getArray(String key) { | ||
| 114 | return ConfigSerializer.parseArray(this.values.get(key)); | ||
| 115 | } | ||
| 116 | |||
| 117 | public void setArray(String key, String[] value) { | ||
| 118 | this.values.put(key, ConfigSerializer.arrayToString(value)); | ||
| 119 | } | ||
| 120 | |||
| 121 | public String[] setIfAbsentArray(String key, String[] value) { | ||
| 122 | return this.getArray(key).orElseGet(() -> { | ||
| 123 | this.setArray(key, value); | ||
| 124 | return value; | ||
| 125 | }); | ||
| 126 | } | ||
| 127 | |||
| 128 | public Optional<int[]> getIntArray(String key) { | ||
| 129 | return this.getArray(key).map(arr -> Arrays.stream(arr).mapToInt(s -> ConfigSerializer.parseInt(s).orElse(0)).toArray()); | ||
| 130 | } | ||
| 131 | |||
| 132 | public void setIntArray(String key, int[] value) { | ||
| 133 | this.setArray(key, Arrays.stream(value).mapToObj(Integer::toString).toArray(String[]::new)); | ||
| 134 | } | ||
| 135 | |||
| 136 | public int[] setIfAbsentIntArray(String key, int[] value) { | ||
| 137 | return this.getIntArray(key).orElseGet(() -> { | ||
| 138 | this.setIntArray(key, value); | ||
| 139 | return value; | ||
| 140 | }); | ||
| 141 | } | ||
| 142 | |||
| 143 | public <T extends Enum<T>> Optional<T> getEnum(Function<String, T> byName, String key) { | ||
| 144 | return ConfigSerializer.parseEnum(byName, this.values.get(key)); | ||
| 145 | } | ||
| 146 | |||
| 147 | public <T extends Enum<T>> void setEnum(String key, T value) { | ||
| 148 | this.values.put(key, value.name()); | ||
| 149 | } | ||
| 150 | |||
| 151 | public <T extends Enum<T>> T setIfAbsentEnum(Function<String, T> byName, String key, T value) { | ||
| 152 | return this.getEnum(byName, key).orElseGet(() -> { | ||
| 153 | this.setEnum(key, value); | ||
| 154 | return value; | ||
| 155 | }); | ||
| 156 | } | ||
| 157 | |||
| 158 | public ConfigSection copy() { | ||
| 159 | Map<String, ConfigSection> sections = new HashMap<>(this.sections); | ||
| 160 | sections.replaceAll((k, v) -> v.copy()); | ||
| 161 | return new ConfigSection(new HashMap<>(this.values), sections); | ||
| 162 | } | ||
| 163 | |||
| 164 | @Override | ||
| 165 | public boolean equals(Object o) { | ||
| 166 | if (this == o) return true; | ||
| 167 | if (!(o instanceof ConfigSection)) return false; | ||
| 168 | ConfigSection that = (ConfigSection) o; | ||
| 169 | return values.equals(that.values) && | ||
| 170 | sections.equals(that.sections); | ||
| 171 | } | ||
| 172 | |||
| 173 | @Override | ||
| 174 | public int hashCode() { | ||
| 175 | return Objects.hash(values, sections); | ||
| 176 | } | ||
| 177 | |||
| 178 | @Override | ||
| 179 | public String toString() { | ||
| 180 | return String.format("ConfigSection { values: %s, sections: %s }", values, sections); | ||
| 181 | } | ||
| 182 | |||
| 183 | } | ||
diff --git a/enigma/src/main/java/cuchaz/enigma/config/ConfigSerializer.java b/enigma/src/main/java/cuchaz/enigma/config/ConfigSerializer.java new file mode 100644 index 00000000..dccb5858 --- /dev/null +++ b/enigma/src/main/java/cuchaz/enigma/config/ConfigSerializer.java | |||
| @@ -0,0 +1,303 @@ | |||
| 1 | package cuchaz.enigma.config; | ||
| 2 | |||
| 3 | import java.util.*; | ||
| 4 | import java.util.Map.Entry; | ||
| 5 | import java.util.function.Function; | ||
| 6 | import java.util.regex.Pattern; | ||
| 7 | import java.util.stream.Collectors; | ||
| 8 | |||
| 9 | public final class ConfigSerializer { | ||
| 10 | |||
| 11 | private static final Pattern FULL_RGB_COLOR = Pattern.compile("#[0-9A-Fa-f]{6}"); | ||
| 12 | private static final Pattern MIN_RGB_COLOR = Pattern.compile("#[0-9A-Fa-f]{3}"); | ||
| 13 | |||
| 14 | private static final int UNEXPECTED_TOKEN = -1; | ||
| 15 | private static final int NO_MATCH = -2; | ||
| 16 | |||
| 17 | public static void parse(String v, ConfigStructureVisitor visitor) { | ||
| 18 | String[] lines = v.split("\n"); | ||
| 19 | |||
| 20 | // join escaped newlines | ||
| 21 | int len = lines.length; | ||
| 22 | for (int i = len - 2; i >= 0; i--) { | ||
| 23 | if (lines[i].endsWith("\\")) { | ||
| 24 | lines[i] = String.format("%s\n%s", lines[i], lines[i + 1]); | ||
| 25 | len -= 1; | ||
| 26 | } | ||
| 27 | } | ||
| 28 | |||
| 29 | // parse for real | ||
| 30 | for (int i = 0; i < len; i++) { | ||
| 31 | String line = lines[i]; | ||
| 32 | |||
| 33 | // skip empty lines and comment lines | ||
| 34 | if (line.trim().isEmpty() || line.trim().startsWith(";")) continue; | ||
| 35 | |||
| 36 | int r; | ||
| 37 | boolean fail = (r = parseSectionLine(line, 0, visitor)) == NO_MATCH && | ||
| 38 | (r = parseKeyValue(line, 0, visitor)) == NO_MATCH; | ||
| 39 | } | ||
| 40 | } | ||
| 41 | |||
| 42 | private static int parseSectionLine(String v, int idx, ConfigStructureVisitor visitor) { | ||
| 43 | if (v.startsWith("[")) { | ||
| 44 | List<String> path = new ArrayList<>(); | ||
| 45 | while (idx < v.length() && v.charAt(idx) == '[') { | ||
| 46 | idx = parseSection(v, idx, path); | ||
| 47 | if (idx == UNEXPECTED_TOKEN) return UNEXPECTED_TOKEN; | ||
| 48 | } | ||
| 49 | |||
| 50 | if (!path.isEmpty()) { | ||
| 51 | visitor.jumpToRootSection(); | ||
| 52 | for (String s : path) { | ||
| 53 | visitor.visitSection(s); | ||
| 54 | } | ||
| 55 | } | ||
| 56 | |||
| 57 | return v.length(); | ||
| 58 | } else { | ||
| 59 | return NO_MATCH; | ||
| 60 | } | ||
| 61 | } | ||
| 62 | |||
| 63 | private static int parseSection(String v, int idx, List<String> path) { | ||
| 64 | idx += 1; // skip leading [ | ||
| 65 | StringBuilder sb = new StringBuilder(); | ||
| 66 | while (idx < v.length()) { | ||
| 67 | int nextCloseBracket = v.indexOf(']', idx); | ||
| 68 | int nextEscape = v.indexOf('\\', idx); | ||
| 69 | int next = optMin(nextCloseBracket, nextEscape); | ||
| 70 | if (next == -1) { | ||
| 71 | // unexpected | ||
| 72 | return UNEXPECTED_TOKEN; | ||
| 73 | } else if (next == nextCloseBracket) { | ||
| 74 | sb.append(v, idx, nextCloseBracket); | ||
| 75 | path.add(sb.toString()); | ||
| 76 | return nextCloseBracket + 1; | ||
| 77 | } else if (next == nextEscape) { | ||
| 78 | sb.append(v, idx, nextEscape); | ||
| 79 | idx = parseEscape(v, nextEscape, sb); | ||
| 80 | } | ||
| 81 | } | ||
| 82 | return idx; | ||
| 83 | } | ||
| 84 | |||
| 85 | private static int parseKeyValue(String v, int idx, ConfigStructureVisitor visitor) { | ||
| 86 | StringBuilder sb = new StringBuilder(); | ||
| 87 | String k = null; | ||
| 88 | while (idx < v.length()) { | ||
| 89 | int nextEq = v.indexOf('=', idx); | ||
| 90 | int nextEscape = v.indexOf('\\', idx); | ||
| 91 | int next = optMin(nextEq, nextEscape); | ||
| 92 | if (next == -1) { | ||
| 93 | break; | ||
| 94 | } else if (next == nextEq) { | ||
| 95 | sb.append(v, idx, nextEq); | ||
| 96 | k = sb.toString(); | ||
| 97 | sb.delete(0, sb.length()); | ||
| 98 | idx = nextEq + 1; | ||
| 99 | break; | ||
| 100 | } else if (next == nextEscape) { | ||
| 101 | sb.append(v, idx, nextEscape); | ||
| 102 | idx = parseEscape(v, nextEscape, sb); | ||
| 103 | } | ||
| 104 | } | ||
| 105 | while (idx < v.length()) { | ||
| 106 | int nextEscape = v.indexOf('\\', idx); | ||
| 107 | if (nextEscape != -1) { | ||
| 108 | sb.append(v, idx, nextEscape); | ||
| 109 | idx = parseEscape(v, nextEscape, sb); | ||
| 110 | } else { | ||
| 111 | break; | ||
| 112 | } | ||
| 113 | } | ||
| 114 | sb.append(v, idx, v.length()); | ||
| 115 | if (k == null) return NO_MATCH; | ||
| 116 | visitor.visitKeyValue(k, sb.toString()); | ||
| 117 | return idx; | ||
| 118 | } | ||
| 119 | |||
| 120 | private static int parseEscape(String v, int idx, StringBuilder sb) { | ||
| 121 | if (idx + 1 < v.length()) { | ||
| 122 | if (v.charAt(idx + 1) == 'u') { | ||
| 123 | if (idx + 5 < v.length()) { | ||
| 124 | String codePoint = v.substring(idx + 2, idx + 6); | ||
| 125 | try { | ||
| 126 | int c = Integer.parseUnsignedInt(codePoint, 16); | ||
| 127 | sb.append((char) c); | ||
| 128 | } catch (NumberFormatException ignored) { | ||
| 129 | } | ||
| 130 | idx = idx + 6; | ||
| 131 | } | ||
| 132 | } else if (v.charAt(idx + 1) == 'n') { | ||
| 133 | sb.append('\n'); | ||
| 134 | idx = idx + 2; | ||
| 135 | } else { | ||
| 136 | sb.append(v.charAt(idx + 1)); | ||
| 137 | idx = idx + 2; | ||
| 138 | } | ||
| 139 | } else { | ||
| 140 | idx = idx + 1; | ||
| 141 | } | ||
| 142 | return idx; | ||
| 143 | } | ||
| 144 | |||
| 145 | public static String structureToString(ConfigSection section) { | ||
| 146 | StringBuilder sb = new StringBuilder(); | ||
| 147 | structureToString(section, sb, new ArrayList<>()); | ||
| 148 | return sb.toString(); | ||
| 149 | } | ||
| 150 | |||
| 151 | private static void structureToString(ConfigSection section, StringBuilder sb, List<String> pathStack) { | ||
| 152 | if (!section.values().isEmpty()) { | ||
| 153 | if (sb.length() > 0) sb.append('\n'); | ||
| 154 | pathStack.forEach(n -> sb.append('[').append(escapeSection(n)).append(']')); | ||
| 155 | if (!pathStack.isEmpty()) sb.append('\n'); | ||
| 156 | section.values().entrySet().stream() | ||
| 157 | .sorted(Entry.comparingByKey()) | ||
| 158 | .forEach(e -> sb.append(escapeKey(e.getKey())).append('=').append(escapeValue(e.getValue())).append('\n')); | ||
| 159 | } | ||
| 160 | |||
| 161 | section.sections().entrySet().stream().sorted(Entry.comparingByKey()).forEach(e -> { | ||
| 162 | pathStack.add(e.getKey()); | ||
| 163 | structureToString(e.getValue(), sb, pathStack); | ||
| 164 | pathStack.remove(pathStack.size() - 1); | ||
| 165 | }); | ||
| 166 | } | ||
| 167 | |||
| 168 | private static String escapeSection(String s) { | ||
| 169 | return s | ||
| 170 | .replace("\\", "\\\\") | ||
| 171 | .replace("\n", "\\n") | ||
| 172 | .replace("]", "\\]") | ||
| 173 | .chars().mapToObj(c -> c >= 32 && c < 127 ? Character.toString((char) c) : String.format("\\u%04x", c)).collect(Collectors.joining()); | ||
| 174 | } | ||
| 175 | |||
| 176 | private static String escapeKey(String s) { | ||
| 177 | return s | ||
| 178 | .replace("\\", "\\\\") | ||
| 179 | .replace("[", "\\[") | ||
| 180 | .replace("\n", "\\n") | ||
| 181 | .replace("=", "\\=") | ||
| 182 | .chars().mapToObj(c -> c >= 32 && c < 127 ? Character.toString((char) c) : String.format("\\u%04x", c)).collect(Collectors.joining()); | ||
| 183 | } | ||
| 184 | |||
| 185 | private static String escapeValue(String s) { | ||
| 186 | return s | ||
| 187 | .replace("\\", "\\\\") | ||
| 188 | .replace("\n", "\\n") | ||
| 189 | .chars().mapToObj(c -> c >= 32 && c < 127 ? Character.toString((char) c) : String.format("\\u%04x", c)).collect(Collectors.joining()); | ||
| 190 | } | ||
| 191 | |||
| 192 | public static Optional<Boolean> parseBool(String v) { | ||
| 193 | if (v == null) return Optional.empty(); | ||
| 194 | switch (v) { | ||
| 195 | case "true": | ||
| 196 | return Optional.of(true); | ||
| 197 | case "false": | ||
| 198 | return Optional.of(false); | ||
| 199 | default: | ||
| 200 | return Optional.empty(); | ||
| 201 | } | ||
| 202 | } | ||
| 203 | |||
| 204 | public static OptionalInt parseInt(String v) { | ||
| 205 | if (v == null) return OptionalInt.empty(); | ||
| 206 | try { | ||
| 207 | return OptionalInt.of(Integer.parseInt(v)); | ||
| 208 | } catch (NumberFormatException e) { | ||
| 209 | return OptionalInt.empty(); | ||
| 210 | } | ||
| 211 | } | ||
| 212 | |||
| 213 | public static OptionalDouble parseDouble(String v) { | ||
| 214 | if (v == null) return OptionalDouble.empty(); | ||
| 215 | try { | ||
| 216 | return OptionalDouble.of(Double.parseDouble(v)); | ||
| 217 | } catch (NumberFormatException e) { | ||
| 218 | return OptionalDouble.empty(); | ||
| 219 | } | ||
| 220 | } | ||
| 221 | |||
| 222 | public static OptionalInt parseRgbColor(String v) { | ||
| 223 | if (v == null) return OptionalInt.empty(); | ||
| 224 | try { | ||
| 225 | if (FULL_RGB_COLOR.matcher(v).matches()) { | ||
| 226 | return OptionalInt.of(Integer.parseUnsignedInt(v.substring(1), 16)); | ||
| 227 | } else if (MIN_RGB_COLOR.matcher(v).matches()) { | ||
| 228 | int result = Integer.parseUnsignedInt(v.substring(1), 16); | ||
| 229 | // change 0xABC to 0xAABBCC | ||
| 230 | result = (result & 0x00F) | (result & 0x0F0) << 4 | (result & 0xF00) << 8; | ||
| 231 | result = result | result << 4; | ||
| 232 | return OptionalInt.of(result); | ||
| 233 | } else { | ||
| 234 | return OptionalInt.empty(); | ||
| 235 | } | ||
| 236 | } catch (NumberFormatException e) { | ||
| 237 | return OptionalInt.empty(); | ||
| 238 | } | ||
| 239 | } | ||
| 240 | |||
| 241 | public static String rgbColorToString(int color) { | ||
| 242 | color = color & 0xFFFFFF; | ||
| 243 | boolean isShort = ((color & 0xF0F0F0) >> 4 ^ color & 0x0F0F0F) == 0; | ||
| 244 | if (isShort) { | ||
| 245 | int packed = color & 0x0F0F0F; | ||
| 246 | packed = packed & 0xF | packed >> 4; | ||
| 247 | packed = packed & 0xFF | (packed & ~0xFF) >> 4; | ||
| 248 | return String.format("#%03x", packed); | ||
| 249 | } else { | ||
| 250 | return String.format("#%06x", color); | ||
| 251 | } | ||
| 252 | } | ||
| 253 | |||
| 254 | public static Optional<String[]> parseArray(String v) { | ||
| 255 | if (v == null) return Optional.empty(); | ||
| 256 | List<String> l = new ArrayList<>(); | ||
| 257 | int idx = 0; | ||
| 258 | StringBuilder cur = new StringBuilder(); | ||
| 259 | while (true) { | ||
| 260 | int nextSep = v.indexOf(',', idx); | ||
| 261 | int nextEsc = v.indexOf('\\', idx); | ||
| 262 | int next = optMin(nextSep, nextEsc); | ||
| 263 | if (next == -1) { | ||
| 264 | cur.append(v, idx, v.length()); | ||
| 265 | l.add(cur.toString()); | ||
| 266 | return Optional.of(l.toArray(new String[0])); | ||
| 267 | } else if (next == nextSep) { | ||
| 268 | cur.append(v, idx, nextSep); | ||
| 269 | l.add(cur.toString()); | ||
| 270 | cur.delete(0, cur.length()); | ||
| 271 | idx = nextSep + 1; | ||
| 272 | } else if (next == nextEsc) { | ||
| 273 | cur.append(v, idx, nextEsc); | ||
| 274 | if (nextEsc + 1 < v.length()) { | ||
| 275 | cur.append(v.charAt(nextEsc + 1)); | ||
| 276 | } | ||
| 277 | idx = nextEsc + 2; | ||
| 278 | } | ||
| 279 | } | ||
| 280 | } | ||
| 281 | |||
| 282 | public static String arrayToString(String[] values) { | ||
| 283 | return Arrays.stream(values) | ||
| 284 | .map(s -> s.replace("\\", "\\\\").replace(",", "\\,")) | ||
| 285 | .collect(Collectors.joining(",")); | ||
| 286 | } | ||
| 287 | |||
| 288 | public static <T extends Enum<T>> Optional<T> parseEnum(Function<String, T> byName, String v) { | ||
| 289 | if (v == null) return Optional.empty(); | ||
| 290 | try { | ||
| 291 | return Optional.of(byName.apply(v)); | ||
| 292 | } catch (IllegalArgumentException e) { | ||
| 293 | return Optional.empty(); | ||
| 294 | } | ||
| 295 | } | ||
| 296 | |||
| 297 | private static int optMin(int v1, int v2) { | ||
| 298 | if (v1 == -1) return v2; | ||
| 299 | if (v2 == -1) return v1; | ||
| 300 | return Math.min(v1, v2); | ||
| 301 | } | ||
| 302 | |||
| 303 | } | ||
diff --git a/enigma/src/main/java/cuchaz/enigma/config/ConfigStructureVisitor.java b/enigma/src/main/java/cuchaz/enigma/config/ConfigStructureVisitor.java new file mode 100644 index 00000000..12d7ec4e --- /dev/null +++ b/enigma/src/main/java/cuchaz/enigma/config/ConfigStructureVisitor.java | |||
| @@ -0,0 +1,11 @@ | |||
| 1 | package cuchaz.enigma.config; | ||
| 2 | |||
| 3 | public interface ConfigStructureVisitor { | ||
| 4 | |||
| 5 | void visitKeyValue(String key, String value); | ||
| 6 | |||
| 7 | void visitSection(String section); | ||
| 8 | |||
| 9 | void jumpToRootSection(); | ||
| 10 | |||
| 11 | } | ||
diff --git a/enigma/src/main/java/cuchaz/enigma/utils/Os.java b/enigma/src/main/java/cuchaz/enigma/utils/Os.java new file mode 100644 index 00000000..b493c041 --- /dev/null +++ b/enigma/src/main/java/cuchaz/enigma/utils/Os.java | |||
| @@ -0,0 +1,33 @@ | |||
| 1 | package cuchaz.enigma.utils; | ||
| 2 | |||
| 3 | import java.util.Locale; | ||
| 4 | |||
| 5 | public enum Os { | ||
| 6 | LINUX, | ||
| 7 | MAC, | ||
| 8 | SOLARIS, | ||
| 9 | WINDOWS, | ||
| 10 | OTHER; | ||
| 11 | |||
| 12 | private static Os os = null; | ||
| 13 | |||
| 14 | public static Os getOs() { | ||
| 15 | if (os == null) { | ||
| 16 | String osName = System.getProperty("os.name").toLowerCase(Locale.ROOT); | ||
| 17 | if (osName.contains("mac") || osName.contains("darwin")) { | ||
| 18 | os = MAC; | ||
| 19 | } else if (osName.contains("win")) { | ||
| 20 | os = WINDOWS; | ||
| 21 | } else if (osName.contains("nix") || osName.contains("nux") | ||
| 22 | || osName.contains("aix")) { | ||
| 23 | os = LINUX; | ||
| 24 | } else if (osName.contains("sunos")) { | ||
| 25 | os = SOLARIS; | ||
| 26 | } else { | ||
| 27 | os = OTHER; | ||
| 28 | } | ||
| 29 | } | ||
| 30 | return os; | ||
| 31 | } | ||
| 32 | |||
| 33 | } \ No newline at end of file | ||
diff --git a/enigma/src/main/resources/lang/de_de.json b/enigma/src/main/resources/lang/de_de.json index 31acb5c2..2c69bcd6 100644 --- a/enigma/src/main/resources/lang/de_de.json +++ b/enigma/src/main/resources/lang/de_de.json | |||
| @@ -1,10 +1,9 @@ | |||
| 1 | { | 1 | { |
| 2 | "language": "German", | 2 | "language": "German", |
| 3 | 3 | ||
| 4 | "general.retry": "Wiederholen", | ||
| 5 | |||
| 6 | "menu.file.stats.title": "Mapping-Statistiken", | 4 | "menu.file.stats.title": "Mapping-Statistiken", |
| 7 | "menu.file.stats.generate": "Diagramm generieren", | 5 | "menu.file.stats.generate": "Diagramm generieren", |
| 6 | "menu.view.font": "Schriftarten...", | ||
| 8 | "menu.help.about.description": "Ein Tool zur Dekompilierung von Java-Code", | 7 | "menu.help.about.description": "Ein Tool zur Dekompilierung von Java-Code", |
| 9 | "menu.help.about.version": "Version: %s", | 8 | "menu.help.about.version": "Version: %s", |
| 10 | 9 | ||
| @@ -18,6 +17,18 @@ | |||
| 18 | "editor.decompile_error": "Ein Fehler ist während des Dekompilierens aufgetreten.", | 17 | "editor.decompile_error": "Ein Fehler ist während des Dekompilierens aufgetreten.", |
| 19 | "editor.remap_error": "Ein Fehler ist während des Remappens aufgetreten.", | 18 | "editor.remap_error": "Ein Fehler ist während des Remappens aufgetreten.", |
| 20 | 19 | ||
| 20 | "fonts.cat.default": "Standard", | ||
| 21 | "fonts.cat.default2": "Standard 2", | ||
| 22 | "fonts.cat.small": "Klein", | ||
| 23 | "fonts.cat.editor": "Editor", | ||
| 24 | "fonts.use_custom": "Benutzerdefinierte Schriftarten verwenden", | ||
| 25 | |||
| 26 | "prompt.ok": "OK", | ||
| 27 | "prompt.cancel": "Abbrechen", | ||
| 28 | "prompt.retry": "Wiederholen", | ||
| 29 | "prompt.save": "Speichern", | ||
| 30 | "prompt.open": "Öffnen", | ||
| 31 | |||
| 21 | "validation.message.empty_field": "Dieses Feld muss ausgefüllt werden.", | 32 | "validation.message.empty_field": "Dieses Feld muss ausgefüllt werden.", |
| 22 | "validation.message.not_int": "Wert muss eine ganze Zahl sein.", | 33 | "validation.message.not_int": "Wert muss eine ganze Zahl sein.", |
| 23 | "validation.message.field_out_of_range_int": "Wert muss eine ganze Zahl zwischen %d und %d sein.", | 34 | "validation.message.field_out_of_range_int": "Wert muss eine ganze Zahl zwischen %d und %d sein.", |
diff --git a/enigma/src/main/resources/lang/en_us.json b/enigma/src/main/resources/lang/en_us.json index ca0b007c..b08962d5 100644 --- a/enigma/src/main/resources/lang/en_us.json +++ b/enigma/src/main/resources/lang/en_us.json | |||
| @@ -1,8 +1,6 @@ | |||
| 1 | { | 1 | { |
| 2 | "language": "English", | 2 | "language": "English", |
| 3 | 3 | ||
| 4 | "general.retry": "Retry", | ||
| 5 | |||
| 6 | "mapping_format.enigma_file": "Enigma File", | 4 | "mapping_format.enigma_file": "Enigma File", |
| 7 | "mapping_format.enigma_directory": "Enigma Directory", | 5 | "mapping_format.enigma_directory": "Enigma Directory", |
| 8 | "mapping_format.enigma_zip": "Enigma ZIP", | 6 | "mapping_format.enigma_zip": "Enigma ZIP", |
| @@ -43,9 +41,9 @@ | |||
| 43 | "menu.view.scale": "Scale", | 41 | "menu.view.scale": "Scale", |
| 44 | "menu.view.scale.custom": "Custom...", | 42 | "menu.view.scale.custom": "Custom...", |
| 45 | "menu.view.scale.custom.title": "Custom Scale", | 43 | "menu.view.scale.custom.title": "Custom Scale", |
| 44 | "menu.view.font": "Fonts...", | ||
| 46 | "menu.view.change.title": "Changes", | 45 | "menu.view.change.title": "Changes", |
| 47 | "menu.view.change.summary": "Changes will be applied after the next restart.", | 46 | "menu.view.change.summary": "Changes will be applied after the next restart.", |
| 48 | "menu.view.change.ok": "Ok", | ||
| 49 | "menu.view.search": "Search", | 47 | "menu.view.search": "Search", |
| 50 | "menu.collab": "Collab", | 48 | "menu.collab": "Collab", |
| 51 | "menu.collab.connect": "Connect to server", | 49 | "menu.collab.connect": "Connect to server", |
| @@ -57,7 +55,6 @@ | |||
| 57 | "menu.help": "Help", | 55 | "menu.help": "Help", |
| 58 | "menu.help.about": "About", | 56 | "menu.help.about": "About", |
| 59 | "menu.help.about.title": "%s - About", | 57 | "menu.help.about.title": "%s - About", |
| 60 | "menu.help.about.ok": "Ok", | ||
| 61 | "menu.help.about.description": "A tool for deobfuscation of Java code", | 58 | "menu.help.about.description": "A tool for deobfuscation of Java code", |
| 62 | "menu.help.about.version": "Version: %s", | 59 | "menu.help.about.version": "Version: %s", |
| 63 | "menu.help.github": "Github Page", | 60 | "menu.help.github": "Github Page", |
| @@ -132,16 +129,23 @@ | |||
| 132 | 129 | ||
| 133 | "javadocs.edit": "Edit Javadocs", | 130 | "javadocs.edit": "Edit Javadocs", |
| 134 | "javadocs.instruction": "Edit javadocs here.", | 131 | "javadocs.instruction": "Edit javadocs here.", |
| 135 | "javadocs.cancel": "Cancel", | 132 | |
| 136 | "javadocs.save": "Save", | 133 | "fonts.cat.default": "Default", |
| 134 | "fonts.cat.default2": "Default 2", | ||
| 135 | "fonts.cat.small": "Small", | ||
| 136 | "fonts.cat.editor": "Editor", | ||
| 137 | "fonts.use_custom": "Use Custom Fonts", | ||
| 138 | |||
| 139 | "prompt.ok": "OK", | ||
| 140 | "prompt.cancel": "Cancel", | ||
| 141 | "prompt.retry": "Retry", | ||
| 142 | "prompt.save": "Save", | ||
| 143 | "prompt.open": "Open", | ||
| 137 | 144 | ||
| 138 | "prompt.close.title": "Save your changes?", | 145 | "prompt.close.title": "Save your changes?", |
| 139 | "prompt.close.summary": "Your mappings have not been saved yet. Do you want to save?", | 146 | "prompt.close.summary": "Your mappings have not been saved yet. Do you want to save?", |
| 140 | "prompt.close.save": "Save and close", | 147 | "prompt.close.save": "Save and close", |
| 141 | "prompt.close.discard": "Discard changes", | 148 | "prompt.close.discard": "Discard changes", |
| 142 | "prompt.close.cancel": "Cancel", | ||
| 143 | "prompt.open": "Open", | ||
| 144 | "prompt.cancel": "Cancel", | ||
| 145 | "prompt.connect.title": "Connect to Server", | 149 | "prompt.connect.title": "Connect to Server", |
| 146 | "prompt.connect.username": "Username:", | 150 | "prompt.connect.username": "Username:", |
| 147 | "prompt.connect.address": "Address:", | 151 | "prompt.connect.address": "Address:", |
diff --git a/enigma/src/main/resources/lang/fr_fr.json b/enigma/src/main/resources/lang/fr_fr.json index 567ad2b4..aad8f29e 100644 --- a/enigma/src/main/resources/lang/fr_fr.json +++ b/enigma/src/main/resources/lang/fr_fr.json | |||
| @@ -1,8 +1,6 @@ | |||
| 1 | { | 1 | { |
| 2 | "language": "Français", | 2 | "language": "Français", |
| 3 | 3 | ||
| 4 | "general.retry": "Réessayer", | ||
| 5 | |||
| 6 | "mapping_format.enigma_file": "Fichier Enigma", | 4 | "mapping_format.enigma_file": "Fichier Enigma", |
| 7 | "mapping_format.enigma_directory": "Répertoire Enigma", | 5 | "mapping_format.enigma_directory": "Répertoire Enigma", |
| 8 | "mapping_format.enigma_zip": "ZIP Enigma", | 6 | "mapping_format.enigma_zip": "ZIP Enigma", |
| @@ -43,7 +41,6 @@ | |||
| 43 | "menu.view.scale.custom.title": "Échelle personnalisée", | 41 | "menu.view.scale.custom.title": "Échelle personnalisée", |
| 44 | "menu.view.change.title": "Modifications", | 42 | "menu.view.change.title": "Modifications", |
| 45 | "menu.view.change.summary": "Les modifications seront appliquées lors du prochain redémarrage.", | 43 | "menu.view.change.summary": "Les modifications seront appliquées lors du prochain redémarrage.", |
| 46 | "menu.view.change.ok": "Ok", | ||
| 47 | "menu.view.search": "Rechercher", | 44 | "menu.view.search": "Rechercher", |
| 48 | "menu.collab": "Collab", | 45 | "menu.collab": "Collab", |
| 49 | "menu.collab.connect": "Se connecter à un serveur", | 46 | "menu.collab.connect": "Se connecter à un serveur", |
| @@ -55,7 +52,6 @@ | |||
| 55 | "menu.help": "Aide", | 52 | "menu.help": "Aide", |
| 56 | "menu.help.about": "À propos", | 53 | "menu.help.about": "À propos", |
| 57 | "menu.help.about.title": "%s - À propos", | 54 | "menu.help.about.title": "%s - À propos", |
| 58 | "menu.help.about.ok": "Ok", | ||
| 59 | "menu.help.github": "Page Github", | 55 | "menu.help.github": "Page Github", |
| 60 | 56 | ||
| 61 | "popup_menu.rename": "Renommer", | 57 | "popup_menu.rename": "Renommer", |
| @@ -128,16 +124,17 @@ | |||
| 128 | 124 | ||
| 129 | "javadocs.edit": "Éditer les Javadocs", | 125 | "javadocs.edit": "Éditer les Javadocs", |
| 130 | "javadocs.instruction": "Éditer les Javadocs ici.", | 126 | "javadocs.instruction": "Éditer les Javadocs ici.", |
| 131 | "javadocs.cancel": "Annuler", | 127 | |
| 132 | "javadocs.save": "Enregistrer", | 128 | "prompt.ok": "OK", |
| 129 | "prompt.cancel": "Annuler", | ||
| 130 | "prompt.retry": "Réessayer", | ||
| 131 | "prompt.open": "Ouvrir", | ||
| 132 | "prompt.save": "Enregistrer", | ||
| 133 | 133 | ||
| 134 | "prompt.close.title": "Enregistrer les modifications ?", | 134 | "prompt.close.title": "Enregistrer les modifications ?", |
| 135 | "prompt.close.summary": "Vos mappings n'ont pas encore été enregistrés. Souhaitez-vous enregistrer ?", | 135 | "prompt.close.summary": "Vos mappings n'ont pas encore été enregistrés. Souhaitez-vous enregistrer ?", |
| 136 | "prompt.close.save": "Enregistrer et fermer", | 136 | "prompt.close.save": "Enregistrer et fermer", |
| 137 | "prompt.close.discard": "Annuler les modifications", | 137 | "prompt.close.discard": "Annuler les modifications", |
| 138 | "prompt.close.cancel": "Annuler", | ||
| 139 | "prompt.open": "Ouvrir", | ||
| 140 | "prompt.cancel": "Annuler", | ||
| 141 | "prompt.connect.title": "Se connecter à un serveur", | 138 | "prompt.connect.title": "Se connecter à un serveur", |
| 142 | "prompt.connect.username": "Nom d'utilisateur :", | 139 | "prompt.connect.username": "Nom d'utilisateur :", |
| 143 | "prompt.connect.address": "Adresse :", | 140 | "prompt.connect.address": "Adresse :", |
diff --git a/enigma/src/main/resources/lang/zh_cn.json b/enigma/src/main/resources/lang/zh_cn.json index f3f503aa..82f0c641 100644 --- a/enigma/src/main/resources/lang/zh_cn.json +++ b/enigma/src/main/resources/lang/zh_cn.json | |||
| @@ -37,12 +37,10 @@ | |||
| 37 | "menu.view.languages": "语言", | 37 | "menu.view.languages": "语言", |
| 38 | "menu.view.languages.title": "更改语言", | 38 | "menu.view.languages.title": "更改语言", |
| 39 | "menu.view.languages.summary": "新语言将在下次重新启动后应用.", | 39 | "menu.view.languages.summary": "新语言将在下次重新启动后应用.", |
| 40 | "menu.view.languages.ok": "确定", | ||
| 41 | "menu.view.search": "搜索", | 40 | "menu.view.search": "搜索", |
| 42 | "menu.help": "帮助", | 41 | "menu.help": "帮助", |
| 43 | "menu.help.about": "关于", | 42 | "menu.help.about": "关于", |
| 44 | "menu.help.about.title": "%s - 关于", | 43 | "menu.help.about.title": "%s - 关于", |
| 45 | "menu.help.about.ok": "确定", | ||
| 46 | "menu.help.github": "GitHub 页面", | 44 | "menu.help.github": "GitHub 页面", |
| 47 | 45 | ||
| 48 | "popup_menu.rename": "改名", | 46 | "popup_menu.rename": "改名", |
| @@ -100,14 +98,16 @@ | |||
| 100 | 98 | ||
| 101 | "javadocs.edit": "编辑注释", | 99 | "javadocs.edit": "编辑注释", |
| 102 | "javadocs.instruction": "在此处编辑编辑注释.", | 100 | "javadocs.instruction": "在此处编辑编辑注释.", |
| 103 | "javadocs.cancel": "取消", | 101 | |
| 104 | "javadocs.save": "保存", | 102 | "prompt.ok": "确定", |
| 103 | "prompt.cancel": "取消", | ||
| 104 | "prompt.save": "保存", | ||
| 105 | "prompt.open": "打开", | ||
| 105 | 106 | ||
| 106 | "prompt.close.title": "保存更改?", | 107 | "prompt.close.title": "保存更改?", |
| 107 | "prompt.close.summary": "您的映射尚未保存。你想保存吗?", | 108 | "prompt.close.summary": "您的映射尚未保存。你想保存吗?", |
| 108 | "prompt.close.save": "保存并关闭", | 109 | "prompt.close.save": "保存并关闭", |
| 109 | "prompt.close.discard": "放弃更改", | 110 | "prompt.close.discard": "放弃更改", |
| 110 | "prompt.close.cancel": "取消", | ||
| 111 | 111 | ||
| 112 | "crash.title": "%s - 崩溃报告", | 112 | "crash.title": "%s - 崩溃报告", |
| 113 | "crash.summary": "%s 已经崩溃! =(", | 113 | "crash.summary": "%s 已经崩溃! =(", |
diff --git a/enigma/src/test/java/cuchaz/enigma/ConfigTest.java b/enigma/src/test/java/cuchaz/enigma/ConfigTest.java new file mode 100644 index 00000000..a44f0375 --- /dev/null +++ b/enigma/src/test/java/cuchaz/enigma/ConfigTest.java | |||
| @@ -0,0 +1,86 @@ | |||
| 1 | package cuchaz.enigma; | ||
| 2 | |||
| 3 | import org.junit.Test; | ||
| 4 | |||
| 5 | import cuchaz.enigma.config.ConfigContainer; | ||
| 6 | |||
| 7 | import static org.junit.Assert.assertEquals; | ||
| 8 | |||
| 9 | public class ConfigTest { | ||
| 10 | |||
| 11 | @Test | ||
| 12 | public void serialize() { | ||
| 13 | ConfigContainer cc = new ConfigContainer(); | ||
| 14 | cc.data().setString("a", "a"); | ||
| 15 | cc.data().section("a").section("b").section("c").setString("a", "abcd"); | ||
| 16 | cc.data().section("a").section("b").section("c").setBool("b", true); | ||
| 17 | cc.data().section("a").section("b").section("c").setInt("c", 5); | ||
| 18 | cc.data().section("a").section("b").section("c").setDouble("d", 3.5); | ||
| 19 | cc.data().section("a").section("b").section("c").setRgbColor("e", 0x123456); | ||
| 20 | assertEquals("a=a\n" + | ||
| 21 | "\n" + | ||
| 22 | "[a][b][c]\n" + | ||
| 23 | "a=abcd\n" + | ||
| 24 | "b=true\n" + | ||
| 25 | "c=5\n" + | ||
| 26 | "d=3.5\n" + | ||
| 27 | "e=#123456\n", | ||
| 28 | cc.serialize()); | ||
| 29 | } | ||
| 30 | |||
| 31 | @Test | ||
| 32 | public void deserialize() { | ||
| 33 | ConfigContainer cc = new ConfigContainer(); | ||
| 34 | cc.data().setString("a", "a"); | ||
| 35 | cc.data().section("a").section("b").section("c").setString("a", "abcd"); | ||
| 36 | cc.data().section("a").section("b").section("c").setBool("b", true); | ||
| 37 | cc.data().section("a").section("b").section("c").setInt("c", 5); | ||
| 38 | cc.data().section("a").section("b").section("c").setDouble("d", 3.5); | ||
| 39 | cc.data().section("a").section("b").section("c").setRgbColor("e", 0x123456); | ||
| 40 | assertEquals(ConfigContainer.parse("a=a\n" + | ||
| 41 | "\n" + | ||
| 42 | "[a][b][c]\n" + | ||
| 43 | "a=abcd\n" + | ||
| 44 | "b=true\n" + | ||
| 45 | "c=5\n" + | ||
| 46 | "d=3.5\n" + | ||
| 47 | "e=#123456\n").data(), cc.data()); | ||
| 48 | } | ||
| 49 | |||
| 50 | @Test | ||
| 51 | public void weirdChars() { | ||
| 52 | ConfigContainer cc = new ConfigContainer(); | ||
| 53 | String thing = "\\[],\\,./'\"`~!@#$%^&*()_+-=|}{\n\\\\\r\b\u0000\uffff\u1234"; | ||
| 54 | cc.data().section(thing).setString(thing, thing); | ||
| 55 | cc.data().section(thing).setArray("arr", new String[] { thing, thing, thing, thing }); | ||
| 56 | |||
| 57 | assertEquals( | ||
| 58 | "[\\\\[\\],\\\\,./'\"`~!@#$%^&*()_+-=|}{\\n\\\\\\\\\\u000d\\u0008\\u0000\\uffff\\u1234]\n" + | ||
| 59 | "\\\\\\[],\\\\,./'\"`~!@#$%^&*()_+-\\=|}{\\n\\\\\\\\\\u000d\\u0008\\u0000\\uffff\\u1234=\\\\[],\\\\,./'\"`~!@#$%^&*()_+-=|}{\\n\\\\\\\\\\u000d\\u0008\\u0000\\uffff\\u1234\n" + | ||
| 60 | "arr=\\\\\\\\[]\\\\,\\\\\\\\\\\\,./'\"`~!@#$%^&*()_+-=|}{\\n\\\\\\\\\\\\\\\\\\u000d\\u0008\\u0000\\uffff\\u1234,\\\\\\\\[]\\\\,\\\\\\\\\\\\,./'\"`~!@#$%^&*()_+-=|}{\\n\\\\\\\\\\\\\\\\\\u000d\\u0008\\u0000\\uffff\\u1234,\\\\\\\\[]\\\\,\\\\\\\\\\\\,./'\"`~!@#$%^&*()_+-=|}{\\n\\\\\\\\\\\\\\\\\\u000d\\u0008\\u0000\\uffff\\u1234,\\\\\\\\[]\\\\,\\\\\\\\\\\\,./'\"`~!@#$%^&*()_+-=|}{\\n\\\\\\\\\\\\\\\\\\u000d\\u0008\\u0000\\uffff\\u1234\n", | ||
| 61 | cc.serialize()); | ||
| 62 | |||
| 63 | ConfigContainer cc1 = ConfigContainer.parse(cc.serialize()); | ||
| 64 | assertEquals(cc.data(), cc1.data()); | ||
| 65 | |||
| 66 | cc1 = ConfigContainer.parse(cc1.serialize()); | ||
| 67 | assertEquals(cc.data(), cc1.data()); | ||
| 68 | } | ||
| 69 | |||
| 70 | @Test | ||
| 71 | public void syntaxErrors() { | ||
| 72 | assertEquals("", ConfigContainer.parse("abcde").serialize()); | ||
| 73 | assertEquals("", ConfigContainer.parse("what\\=?").serialize()); | ||
| 74 | |||
| 75 | assertEquals("[a]\nb=c\n", ConfigContainer.parse("[a] what is this\nb=c").serialize()); | ||
| 76 | assertEquals("b=c\n", ConfigContainer.parse("[a][ what is this\nb=c").serialize()); | ||
| 77 | assertEquals("", ConfigContainer.parse("[").serialize()); | ||
| 78 | assertEquals("[a]\na=b\nc=d\n", ConfigContainer.parse("[a]\na=b\n[\nc=d").serialize()); | ||
| 79 | |||
| 80 | |||
| 81 | // not technically syntax errors but never something that gets generated | ||
| 82 | assertEquals("", ConfigContainer.parse("[a]").serialize()); | ||
| 83 | assertEquals("", ConfigContainer.parse("[a]\n[b]").serialize()); | ||
| 84 | } | ||
| 85 | |||
| 86 | } | ||