package cuchaz.enigma.gui.config; import java.awt.Color; import java.awt.Dimension; import java.awt.Font; import java.awt.Point; import java.awt.Toolkit; import java.util.Optional; import java.util.OptionalInt; import cuchaz.enigma.config.ConfigContainer; import cuchaz.enigma.config.ConfigSection; import cuchaz.enigma.gui.util.ScaleUtil; import cuchaz.enigma.utils.I18n; public final class UiConfig { private UiConfig() { } // General UI configuration such as localization private static final ConfigContainer ui = ConfigContainer.getOrCreate("enigma/enigmaui"); // Swing specific configuration such as theming private static final ConfigContainer swing = ConfigContainer.getOrCreate("enigma/enigmaswing"); // These are used for getting stuff that needs to stay constant for the // runtime of the program, e.g. the current theme, because changing of these // settings without a restart isn't implemented correctly yet. // Don't change the values in this container with the expectation that they // get saved, this is purely a backup of the configuration that existed at // startup. private static ConfigSection runningSwing; static { if (!swing.existsOnDisk() && !ui.existsOnDisk()) { OldConfigImporter.doImport(); } UiConfig.snapshotConfig(); } // Saves the current configuration state so a consistent user interface can // be provided for parts of the interface that don't support changing the // configuration at runtime. Calling this after any UI elements are // displayed can lead to visual glitches! public static void snapshotConfig() { runningSwing = swing.data().copy(); } public static void save() { ui.save(); swing.save(); } public static String getLanguage() { return ui.data().section("General").setIfAbsentString("Language", I18n.DEFAULT_LANGUAGE); } public static void setLanguage(String language) { ui.data().section("General").setString("Language", language); } public static float getScaleFactor() { return (float) swing.data().section("General").setIfAbsentDouble("Scale Factor", 1.0); } public static float getActiveScaleFactor() { return (float) runningSwing.section("General").setIfAbsentDouble("Scale Factor", 1.0); } public static void setScaleFactor(float scale) { swing.data().section("General").setDouble("Scale Factor", scale); } /** * Gets the dimensions of the different panels of the GUI. *

These dimensions are used to determine the location of the separators between these panels.

* * * * @return an integer array composed of these 4 dimensions */ public static int[] getLayout() { return swing.data().section("Main Window").getIntArray("Layout").orElseGet(() -> new int[]{-1, -1, -1, -1}); } public static void setLayout(int leftV, int left, int right, int rightH) { swing.data().section("Main Window").setIntArray("Layout", new int[]{leftV, left, right, rightH}); } public static LookAndFeel getLookAndFeel() { return swing.data().section("Themes").setIfAbsentEnum(LookAndFeel::valueOf, "Current", LookAndFeel.NONE); } public static LookAndFeel getActiveLookAndFeel() { return runningSwing.section("Themes").setIfAbsentEnum(LookAndFeel::valueOf, "Current", LookAndFeel.NONE); } public static void setLookAndFeel(LookAndFeel laf) { swing.data().section("Themes").setEnum("Current", laf); } public static Decompiler getDecompiler() { return ui.data().section("Decompiler").setIfAbsentEnum(Decompiler::valueOf, "Current", Decompiler.VINEFLOWER); } public static void setDecompiler(Decompiler d) { ui.data().section("Decompiler").setEnum("Current", d); } private static Color fromComponents(int rgb, double alpha) { int rgba = rgb & 0xFFFFFF | (int) (alpha * 255) << 24; return new Color(rgba, true); } private static Color getThemeColorRgba(String colorName) { ConfigSection s = runningSwing.section("Themes").section(getActiveLookAndFeel().name()).section("Colors"); return fromComponents(s.getRgbColor(colorName).orElse(0), s.getDouble(String.format("%s Alpha", colorName)).orElse(0)); } private static Color getThemeColorRgb(String colorName) { ConfigSection s = runningSwing.section("Themes").section(getActiveLookAndFeel().name()).section("Colors"); return new Color(s.getRgbColor(colorName).orElse(0)); } public static Color getObfuscatedColor() { return getThemeColorRgba("Obfuscated"); } public static Color getObfuscatedOutlineColor() { return getThemeColorRgba("Obfuscated Outline"); } public static Color getProposedColor() { return getThemeColorRgba("Proposed"); } public static Color getProposedOutlineColor() { return getThemeColorRgba("Proposed Outline"); } public static Color getDeobfuscatedColor() { return getThemeColorRgba("Deobfuscated"); } public static Color getDeobfuscatedOutlineColor() { return getThemeColorRgba("Deobfuscated Outline"); } public static Color getUnobfuscatedColor() { return getThemeColorRgba("Unobfuscated"); } public static Color getUnobfuscatedOutlineColor() { return getThemeColorRgba("Unobfuscated Outline"); } public static Color getEditorBackgroundColor() { return getThemeColorRgb("Editor Background"); } public static Color getHighlightColor() { return getThemeColorRgb("Highlight"); } public static Color getCaretColor() { return getThemeColorRgb("Caret"); } public static Color getSelectionHighlightColor() { return getThemeColorRgb("Selection Highlight"); } public static Color getStringColor() { return getThemeColorRgb("String"); } public static Color getNumberColor() { return getThemeColorRgb("Number"); } public static Color getOperatorColor() { return getThemeColorRgb("Operator"); } public static Color getDelimiterColor() { return getThemeColorRgb("Delimiter"); } public static Color getTypeColor() { return getThemeColorRgb("Type"); } public static Color getIdentifierColor() { return getThemeColorRgb("Identifier"); } public static Color getTextColor() { return getThemeColorRgb("Text"); } public static Color getLineNumbersForegroundColor() { return getThemeColorRgb("Line Numbers Foreground"); } public static Color getLineNumbersBackgroundColor() { return getThemeColorRgb("Line Numbers Background"); } public static Color getLineNumbersSelectedColor() { return getThemeColorRgb("Line Numbers Selected"); } public static boolean useCustomFonts() { return swing.data().section("Themes").section(getActiveLookAndFeel().name()).section("Fonts").setIfAbsentBool("Use Custom", false); } public static boolean activeUseCustomFonts() { return runningSwing.section("Themes").section(getActiveLookAndFeel().name()).section("Fonts").setIfAbsentBool("Use Custom", false); } public static void setUseCustomFonts(boolean b) { swing.data().section("Themes").section(getActiveLookAndFeel().name()).section("Fonts").setBool("Use Custom", b); } public static Optional getFont(String name) { Optional spec = swing.data().section("Themes").section(getActiveLookAndFeel().name()).section("Fonts").getString(name); return spec.map(Font::decode); } public static Optional getActiveFont(String name) { Optional spec = runningSwing.section("Themes").section(getActiveLookAndFeel().name()).section("Fonts").getString(name); return spec.map(Font::decode); } public static void setFont(String name, Font font) { swing.data().section("Themes").section(getLookAndFeel().name()).section("Fonts").setString(name, encodeFont(font)); } public static Font getDefaultFont() { return getActiveFont("Default").orElseGet(() -> ScaleUtil.scaleFont(Font.decode(Font.DIALOG).deriveFont(Font.BOLD))); } public static void setDefaultFont(Font font) { setFont("Default", font); } public static Font getDefault2Font() { return getActiveFont("Default 2").orElseGet(() -> ScaleUtil.scaleFont(Font.decode(Font.DIALOG))); } public static void setDefault2Font(Font font) { setFont("Default 2", font); } public static Font getSmallFont() { return getActiveFont("Small").orElseGet(() -> ScaleUtil.scaleFont(Font.decode(Font.DIALOG))); } public static void setSmallFont(Font font) { setFont("Small", font); } public static Font getEditorFont() { return getActiveFont("Editor").orElseGet(UiConfig::getFallbackEditorFont); } public static void setEditorFont(Font font) { setFont("Editor", font); } /** * Gets the fallback editor font. * It is used *
    *
  • when there is no custom editor font chosen
  • *
  • when custom fonts are disabled
  • *
* * @return the fallback editor font */ public static Font getFallbackEditorFont() { return ScaleUtil.scaleFont(Font.decode(Font.MONOSPACED).deriveFont(12f)); } public static String encodeFont(Font font) { int style = font.getStyle(); String s = style == (Font.BOLD | Font.ITALIC) ? "bolditalic" : style == Font.ITALIC ? "italic" : style == Font.BOLD ? "bold" : "plain"; return String.format("%s-%s-%s", font.getName(), s, font.getSize()); } public static Dimension getWindowSize(String window, Dimension fallback) { Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); ConfigSection section = swing.data().section(window); OptionalInt width = section.getInt(String.format("Width %s", screenSize.width)); OptionalInt height = section.getInt(String.format("Height %s", screenSize.height)); if (width.isPresent() && height.isPresent()) { return new Dimension(width.getAsInt(), height.getAsInt()); } else { return fallback; } } public static void setWindowSize(String window, Dimension dim) { Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); ConfigSection section = swing.data().section(window); section.setInt(String.format("Width %s", screenSize.width), dim.width); section.setInt(String.format("Height %s", screenSize.height), dim.height); } public static Point getWindowPos(String window, Point fallback) { Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); ConfigSection section = swing.data().section(window); OptionalInt x = section.getInt(String.format("X %s", screenSize.width)); OptionalInt y = section.getInt(String.format("Y %s", screenSize.height)); if (x.isPresent() && y.isPresent()) { int ix = x.getAsInt(); int iy = y.getAsInt(); // Ensure that the position is on the screen. if (ix < 0 || iy < 0 || ix > screenSize.width || iy > screenSize.height) { return fallback; } return new Point(ix, iy); } else { return fallback; } } public static void setWindowPos(String window, Point rect) { Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); ConfigSection section = swing.data().section(window); section.setInt(String.format("X %s", screenSize.width), rect.x); section.setInt(String.format("Y %s", screenSize.height), rect.y); } public static boolean isFullscreen(String window) { return swing.data().section(window).setIfAbsentBool("Fullscreen", false); } public static void setFullscreen(String window, boolean fullscreen) { swing.data().section(window).setBool("Fullscreen", fullscreen); } public static String getLastSelectedDir() { return swing.data().section("File Dialog").getString("Selected").orElse(""); } public static void setLastSelectedDir(String directory) { swing.data().section("File Dialog").setString("Selected", directory); } public static String getLastTopLevelPackage() { return swing.data().section("Mapping Stats").getString("Top-Level Package").orElse(""); } public static void setLastTopLevelPackage(String topLevelPackage) { swing.data().section("Mapping Stats").setString("Top-Level Package", topLevelPackage); } public static boolean shouldIncludeSyntheticParameters() { return swing.data().section("Mapping Stats").setIfAbsentBool("Synthetic Parameters", false); } public static void setIncludeSyntheticParameters(boolean b) { swing.data().section("Mapping Stats").setBool("Synthetic Parameters", b); } public static void setLookAndFeelDefaults(LookAndFeel laf, boolean isDark) { ConfigSection s = swing.data().section("Themes").section(laf.name()).section("Colors"); if (!isDark) { // Defaults found here: https://github.com/Sciss/SyntaxPane/blob/122da367ff7a5d31627a70c62a48a9f0f4f85a0a/src/main/resources/de/sciss/syntaxpane/defaultsyntaxkit/config.properties#L139 s.setIfAbsentRgbColor("Line Numbers Foreground", 0x333300); s.setIfAbsentRgbColor("Line Numbers Background", 0xEEEEFF); s.setIfAbsentRgbColor("Line Numbers Selected", 0xCCCCEE); s.setIfAbsentRgbColor("Obfuscated", 0xFFDCDC); s.setIfAbsentDouble("Obfuscated Alpha", 1.0); s.setIfAbsentRgbColor("Obfuscated Outline", 0xA05050); s.setIfAbsentDouble("Obfuscated Outline Alpha", 1.0); s.setIfAbsentRgbColor("Proposed", 0x000000); s.setIfAbsentDouble("Proposed Alpha", 0.15); s.setIfAbsentRgbColor("Proposed Outline", 0x000000); s.setIfAbsentDouble("Proposed Outline Alpha", 0.75); s.setIfAbsentRgbColor("Deobfuscated", 0xDCFFDC); s.setIfAbsentDouble("Deobfuscated Alpha", 1.0); s.setIfAbsentRgbColor("Deobfuscated Outline", 0x50A050); s.setIfAbsentDouble("Deobfuscated Outline Alpha", 1.0); s.setIfAbsentRgbColor("Unobfuscated", 0x7FAAFF); s.setIfAbsentDouble("Unobfuscated Alpha", 1.0); s.setIfAbsentRgbColor("Unobfuscated Outline", 0x183060); s.setIfAbsentDouble("Unobfuscated Outline Alpha", 1.0); s.setIfAbsentRgbColor("Editor Background", 0xFFFFFF); s.setIfAbsentRgbColor("Highlight", 0x3333EE); s.setIfAbsentRgbColor("Caret", 0x000000); s.setIfAbsentRgbColor("Selection Highlight", 0x000000); s.setIfAbsentRgbColor("String", 0xCC6600); s.setIfAbsentRgbColor("Number", 0x999933); s.setIfAbsentRgbColor("Operator", 0x000000); s.setIfAbsentRgbColor("Delimiter", 0x000000); s.setIfAbsentRgbColor("Type", 0x000000); s.setIfAbsentRgbColor("Identifier", 0x000000); s.setIfAbsentRgbColor("Text", 0x000000); } else { // Based off colors found here: https://github.com/dracula/dracula-theme/ s.setIfAbsentRgbColor("Line Numbers Foreground", 0xA4A4A3); s.setIfAbsentRgbColor("Line Numbers Background", 0x313335); s.setIfAbsentRgbColor("Line Numbers Selected", 0x606366); s.setIfAbsentRgbColor("Obfuscated", 0xFF5555); s.setIfAbsentDouble("Obfuscated Alpha", 0.3); s.setIfAbsentRgbColor("Obfuscated Outline", 0xFF5555); s.setIfAbsentDouble("Obfuscated Outline Alpha", 0.5); s.setIfAbsentRgbColor("Proposed", 0x606366); s.setIfAbsentDouble("Proposed Alpha", 0.3); s.setIfAbsentRgbColor("Proposed Outline", 0x606366); s.setIfAbsentDouble("Proposed Outline Alpha", 0.5); s.setIfAbsentRgbColor("Deobfuscated", 0x50FA7B); s.setIfAbsentDouble("Deobfuscated Alpha", 0.3); s.setIfAbsentRgbColor("Deobfuscated Outline", 0x50FA7B); s.setIfAbsentDouble("Deobfuscated Outline Alpha", 0.5); s.setIfAbsentRgbColor("Unobfuscated", 0x3794FF); s.setIfAbsentDouble("Unobfuscated Alpha", 0.3); s.setIfAbsentRgbColor("Unobfuscated Outline", 0x3794FF); s.setIfAbsentDouble("Unobfuscated Outline Alpha", 0.5); s.setIfAbsentRgbColor("Editor Background", 0x282A36); s.setIfAbsentRgbColor("Highlight", 0xFF79C6); s.setIfAbsentRgbColor("Caret", 0xF8F8F2); s.setIfAbsentRgbColor("Selection Highlight", 0xF8F8F2); s.setIfAbsentRgbColor("String", 0xF1FA8C); s.setIfAbsentRgbColor("Number", 0xBD93F9); s.setIfAbsentRgbColor("Operator", 0xF8F8F2); s.setIfAbsentRgbColor("Delimiter", 0xF8F8F2); s.setIfAbsentRgbColor("Type", 0xF8F8F2); s.setIfAbsentRgbColor("Identifier", 0xF8F8F2); s.setIfAbsentRgbColor("Text", 0xF8F8F2); } } }