From 433a4e33a118a2fde6263bf9d6518216e30c9a21 Mon Sep 17 00:00:00 2001 From: Thorsten Sommer Date: Mon, 3 Oct 2022 17:53:49 +0200 Subject: [PATCH 01/16] Fixed DeepL settings' cache --- I18N Commander/Processor/AppSettings.cs | 198 ++++++++++++++---------- 1 file changed, 113 insertions(+), 85 deletions(-) diff --git a/I18N Commander/Processor/AppSettings.cs b/I18N Commander/Processor/AppSettings.cs index 86d2a2e..9b75cb0 100644 --- a/I18N Commander/Processor/AppSettings.cs +++ b/I18N Commander/Processor/AppSettings.cs @@ -51,29 +51,36 @@ public static class AppSettings { if (CACHE_DEEPL_MODE_IS_LOADED) return CACHE_DEEPL_MODE; - - // Get the database: - await using var db = ProcessorMeta.ServiceProvider.GetRequiredService(); - - // Check, if the setting is already set: - if (await db.Settings.FirstOrDefaultAsync(n => n.Code == SettingNames.DEEPL_MODE) is { } existingSetting) - return (SettingDeepLMode) existingSetting.IntegerValue; - // Does not exist, so create it: - var setting = new Setting + var mode = SettingDeepLMode.DISABLED; + try { - Code = SettingNames.DEEPL_MODE, - IntegerValue = (int)SettingDeepLMode.DISABLED, - }; - - await db.Settings.AddAsync(setting); - await db.SaveChangesAsync(); - - var mode = (SettingDeepLMode) setting.IntegerValue; - CACHE_DEEPL_MODE = mode; - CACHE_DEEPL_MODE_IS_LOADED = true; - - return mode; + // Get the database: + await using var db = ProcessorMeta.ServiceProvider.GetRequiredService(); + + // Check, if the setting is already set: + if (await db.Settings.FirstOrDefaultAsync(n => n.Code == SettingNames.DEEPL_MODE) is { } existingSetting) + { + mode = (SettingDeepLMode) existingSetting.IntegerValue; + return mode; + } + + // Does not exist, so create it: + var setting = new Setting + { + Code = SettingNames.DEEPL_MODE, + IntegerValue = (int) mode, + }; + + await db.Settings.AddAsync(setting); + await db.SaveChangesAsync(); + return mode; + } + finally + { + CACHE_DEEPL_MODE = mode; + CACHE_DEEPL_MODE_IS_LOADED = true; + } } #endregion @@ -118,29 +125,36 @@ public static class AppSettings // Check the cache: if (CACHE_DEEPL_API_KEY_IS_LOADED) return CACHE_DEEPL_API_KEY; - - // Get the database: - await using var db = ProcessorMeta.ServiceProvider.GetRequiredService(); - - // Check, if the setting is already set: - if (await db.Settings.FirstOrDefaultAsync(n => n.Code == SettingNames.DEEPL_API_KEY) is { } existingSetting) - return existingSetting.TextValue; - - // Does not exist, so create it: - var setting = new Setting + + var key = string.Empty; + try { - Code = SettingNames.DEEPL_API_KEY, - TextValue = string.Empty, - }; - - await db.Settings.AddAsync(setting); - await db.SaveChangesAsync(); - - var key = setting.TextValue; - CACHE_DEEPL_API_KEY = key; - CACHE_DEEPL_API_KEY_IS_LOADED = true; - - return key; + // Get the database: + await using var db = ProcessorMeta.ServiceProvider.GetRequiredService(); + + // Check, if the setting is already set: + if (await db.Settings.FirstOrDefaultAsync(n => n.Code == SettingNames.DEEPL_API_KEY) is { } existingSetting) + { + key = existingSetting.TextValue; + return key; + } + + // Does not exist, so create it: + var setting = new Setting + { + Code = SettingNames.DEEPL_API_KEY, + TextValue = key, + }; + + await db.Settings.AddAsync(setting); + await db.SaveChangesAsync(); + return key; + } + finally + { + CACHE_DEEPL_API_KEY = key; + CACHE_DEEPL_API_KEY_IS_LOADED = true; + } } #endregion @@ -189,28 +203,35 @@ public static class AppSettings if (CACHE_DEEPL_ACTION_IS_LOADED) return CACHE_DEEPL_ACTION; - // Get the database: - await using var db = ProcessorMeta.ServiceProvider.GetRequiredService(); - - // Check, if the setting is already set: - if (await db.Settings.FirstOrDefaultAsync(n => n.Code == SettingNames.DEEPL_ACTION) is { } existingSetting) - return (SettingDeepLAction) existingSetting.IntegerValue; - - // Does not exist, so create it: - var setting = new Setting + var action = SettingDeepLAction.MANUAL; + try { - Code = SettingNames.DEEPL_ACTION, - IntegerValue = (int)SettingDeepLAction.MANUAL, - }; - - await db.Settings.AddAsync(setting); - await db.SaveChangesAsync(); - - var action = (SettingDeepLAction) setting.IntegerValue; - CACHE_DEEPL_ACTION = action; - CACHE_DEEPL_ACTION_IS_LOADED = true; - - return action; + // Get the database: + await using var db = ProcessorMeta.ServiceProvider.GetRequiredService(); + + // Check, if the setting is already set: + if (await db.Settings.FirstOrDefaultAsync(n => n.Code == SettingNames.DEEPL_ACTION) is { } existingSetting) + { + action = (SettingDeepLAction) existingSetting.IntegerValue; + return action; + } + + // Does not exist, so create it: + var setting = new Setting + { + Code = SettingNames.DEEPL_ACTION, + IntegerValue = (int) action, + }; + + await db.Settings.AddAsync(setting); + await db.SaveChangesAsync(); + return action; + } + finally + { + CACHE_DEEPL_ACTION = action; + CACHE_DEEPL_ACTION_IS_LOADED = true; + } } #endregion @@ -256,28 +277,35 @@ public static class AppSettings if (CACHE_DEEPL_SOURCE_CULTURE_IS_LOADED) return CACHE_DEEPL_SOURCE_CULTURE; - // Get the database: - await using var db = ProcessorMeta.ServiceProvider.GetRequiredService(); - - // Check, if the setting is already set: - if (await db.Settings.FirstOrDefaultAsync(n => n.Code == SettingNames.DEEPL_SOURCE_CULTURE) is { } existingSetting) - return existingSetting.IntegerValue; - - // Does not exist, so create it: - var setting = new Setting + var cultureIndex = -1; + try { - Code = SettingNames.DEEPL_SOURCE_CULTURE, - IntegerValue = -1, - }; - - await db.Settings.AddAsync(setting); - await db.SaveChangesAsync(); - - var cultureIndex = setting.IntegerValue; - CACHE_DEEPL_SOURCE_CULTURE = cultureIndex; - CACHE_DEEPL_SOURCE_CULTURE_IS_LOADED = true; - - return cultureIndex; + // Get the database: + await using var db = ProcessorMeta.ServiceProvider.GetRequiredService(); + + // Check, if the setting is already set: + if (await db.Settings.FirstOrDefaultAsync(n => n.Code == SettingNames.DEEPL_SOURCE_CULTURE) is { } existingSetting) + { + cultureIndex = existingSetting.IntegerValue; + return cultureIndex; + } + + // Does not exist, so create it: + var setting = new Setting + { + Code = SettingNames.DEEPL_SOURCE_CULTURE, + IntegerValue = cultureIndex, + }; + + await db.Settings.AddAsync(setting); + await db.SaveChangesAsync(); + return cultureIndex; + } + finally + { + CACHE_DEEPL_SOURCE_CULTURE = cultureIndex; + CACHE_DEEPL_SOURCE_CULTURE_IS_LOADED = true; + } } #endregion From d242d02ececb9880f2c44592931f5ec1373fbb13 Mon Sep 17 00:00:00 2001 From: Thorsten Sommer Date: Mon, 3 Oct 2022 18:13:07 +0200 Subject: [PATCH 02/16] Added the generator mode setting --- .../Database/SettingGeneratorMode.cs | 7 ++ .../DataModel/Database/SettingNames.cs | 3 +- I18N Commander/Processor/AppSettings.cs | 78 ++++++++++++++++++ .../UI WinForms/Components/Setting.cs | 46 +++++++++++ .../UI WinForms/Resources/Icons.Designer.cs | 10 +++ .../UI WinForms/Resources/Icons.resx | 3 + .../UI WinForms/Resources/icons8-code-512.png | Bin 0 -> 4576 bytes 7 files changed, 146 insertions(+), 1 deletion(-) create mode 100644 I18N Commander/DataModel/Database/SettingGeneratorMode.cs create mode 100644 I18N Commander/UI WinForms/Resources/icons8-code-512.png diff --git a/I18N Commander/DataModel/Database/SettingGeneratorMode.cs b/I18N Commander/DataModel/Database/SettingGeneratorMode.cs new file mode 100644 index 0000000..bcfab42 --- /dev/null +++ b/I18N Commander/DataModel/Database/SettingGeneratorMode.cs @@ -0,0 +1,7 @@ +namespace DataModel.Database; + +public enum SettingGeneratorMode +{ + AUTOMATIC, + MANUAL, +} \ No newline at end of file diff --git a/I18N Commander/DataModel/Database/SettingNames.cs b/I18N Commander/DataModel/Database/SettingNames.cs index d4ee3fb..734eaed 100644 --- a/I18N Commander/DataModel/Database/SettingNames.cs +++ b/I18N Commander/DataModel/Database/SettingNames.cs @@ -2,9 +2,10 @@ public static class SettingNames { - public static readonly string DEEPL_SOURCE_CULTURE = "DeepL Source Culture"; public static readonly string CULTURE = "Culture"; public static readonly string DEEPL_ACTION = "DeepL Action"; public static readonly string DEEPL_API_KEY = "DeepL API Key"; + public static readonly string DEEPL_SOURCE_CULTURE = "DeepL Source Culture"; public static readonly string DEEPL_MODE = "DeepL Mode"; + public static readonly string GENERATOR_MODE = "Generator Mode"; } \ No newline at end of file diff --git a/I18N Commander/Processor/AppSettings.cs b/I18N Commander/Processor/AppSettings.cs index 9b75cb0..8526d7a 100644 --- a/I18N Commander/Processor/AppSettings.cs +++ b/I18N Commander/Processor/AppSettings.cs @@ -460,4 +460,82 @@ public static class AppSettings #endregion #endregion + + #region Generator Settings + + #region Generator Mode + + private static SettingGeneratorMode CACHE_GENERATOR_MODE = SettingGeneratorMode.MANUAL; + private static bool CACHE_GENERATOR_MODE_IS_LOADED = false; + + public static async Task GetGeneratorMode() + { + // When possible, use the cache: + if (CACHE_GENERATOR_MODE_IS_LOADED) + return CACHE_GENERATOR_MODE; + + var generatorMode = SettingGeneratorMode.MANUAL; + try + { + // Get the database: + await using var db = ProcessorMeta.ServiceProvider.GetRequiredService(); + + // Check, if the setting is already set: + if (await db.Settings.FirstOrDefaultAsync(n => n.Code == SettingNames.GENERATOR_MODE) is { } existingSetting) + { + generatorMode = (SettingGeneratorMode) existingSetting.IntegerValue; + return generatorMode; + } + + // Does not exist, so create it: + var setting = new Setting + { + Code = SettingNames.GENERATOR_MODE, + IntegerValue = (int) generatorMode, + }; + + await db.Settings.AddAsync(setting); + await db.SaveChangesAsync(); + return generatorMode; + } + finally + { + CACHE_GENERATOR_MODE_IS_LOADED = true; + CACHE_GENERATOR_MODE = generatorMode; + } + } + + public static async Task SetGeneratorMode(SettingGeneratorMode mode) + { + // Update the cache: + CACHE_GENERATOR_MODE = mode; + CACHE_GENERATOR_MODE_IS_LOADED = true; + + // Get the database: + await using var db = ProcessorMeta.ServiceProvider.GetRequiredService(); + + // Check, if the setting is already set: + if (await db.Settings.FirstOrDefaultAsync(n => n.Code == SettingNames.GENERATOR_MODE) is { } existingSetting) + { + existingSetting.IntegerValue = (int) mode; + await db.SaveChangesAsync(); + } + + // Does not exist, so create it: + else + { + var setting = new Setting + { + Code = SettingNames.GENERATOR_MODE, + IntegerValue = (int) mode, + }; + + await db.Settings.AddAsync(setting); + await db.SaveChangesAsync(); + } + } + + #endregion + + #endregion } \ No newline at end of file diff --git a/I18N Commander/UI WinForms/Components/Setting.cs b/I18N Commander/UI WinForms/Components/Setting.cs index 2bee739..c354430 100644 --- a/I18N Commander/UI WinForms/Components/Setting.cs +++ b/I18N Commander/UI WinForms/Components/Setting.cs @@ -388,9 +388,55 @@ public sealed partial class Setting : UserControl cultureIndices.Remove(innerLoopIndex); } } + + private static async Task ShowGeneratorModeSettingAsync() + { + var currentSetting = await AppSettings.GetGeneratorMode(); + + var settingData = new SettingUIData( + Icon: Icons.icons8_code_512, + SettingName: () => "Generator Mode", + ChangeNeedsRestart: false, + SettingExplanation: () => "The generator mode determines how the translation files are generated.", + SettingExplanationLink: () => (string.Empty, string.Empty), + SetupDataControl: (changeTrigger) => + { + // We set up a combo box with the available actions: + var dropdown = new ComboBox(); + dropdown.Items.Add("Automatic generation"); + dropdown.Items.Add("Manual generation"); + dropdown.SelectedIndex = currentSetting switch + { + SettingGeneratorMode.AUTOMATIC => 0, + SettingGeneratorMode.MANUAL => 1, + + _ => 0, + }; + + // Setup the change event handler: + dropdown.SelectedValueChanged += (sender, args) => changeTrigger(); + dropdown.SelectedValueChanged += async (sender, args) => await AppSettings.SetGeneratorMode(dropdown.SelectedIndex switch + { + 0 => SettingGeneratorMode.AUTOMATIC, + 1 => SettingGeneratorMode.MANUAL, + + _ => SettingGeneratorMode.AUTOMATIC, + }); + + // Apply the desired layout: + dropdown.Dock = DockStyle.Fill; + dropdown.DropDownStyle = ComboBoxStyle.DropDownList; + + return dropdown; + } + ); + + return new Setting(settingData); + } public static IEnumerable> GetAllSettings() { + yield return ShowGeneratorModeSettingAsync(); yield return ShowDeepLSourceCultureSettingAsync(); foreach (var setting in ShowCultureSettingsAsync()) { diff --git a/I18N Commander/UI WinForms/Resources/Icons.Designer.cs b/I18N Commander/UI WinForms/Resources/Icons.Designer.cs index cedf6c1..95a9b6e 100644 --- a/I18N Commander/UI WinForms/Resources/Icons.Designer.cs +++ b/I18N Commander/UI WinForms/Resources/Icons.Designer.cs @@ -140,6 +140,16 @@ namespace UI_WinForms.Resources { } } + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap icons8_code_512 { + get { + object obj = ResourceManager.GetObject("icons8_code_512", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + /// /// Looks up a localized resource of type System.Drawing.Bitmap. /// diff --git a/I18N Commander/UI WinForms/Resources/Icons.resx b/I18N Commander/UI WinForms/Resources/Icons.resx index 9cfa843..15f9723 100644 --- a/I18N Commander/UI WinForms/Resources/Icons.resx +++ b/I18N Commander/UI WinForms/Resources/Icons.resx @@ -142,6 +142,9 @@ icons8-chat-bubble-512.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + icons8-code-512.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + icons8-collectibles-512.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a diff --git a/I18N Commander/UI WinForms/Resources/icons8-code-512.png b/I18N Commander/UI WinForms/Resources/icons8-code-512.png new file mode 100644 index 0000000000000000000000000000000000000000..00d10ebc55ac5cd23b3f3be92e09ef56d0e358ea GIT binary patch literal 4576 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 literal 0 HcmV?d00001 From 4a32cd9339ffafd64a6c750c76f37327394cbe99 Mon Sep 17 00:00:00 2001 From: Thorsten Sommer Date: Mon, 3 Oct 2022 19:10:39 +0200 Subject: [PATCH 03/16] Implemented .NET generator settings --- .../DataModel/Database/SettingNames.cs | 2 + I18N Commander/Processor/AppSettings.cs | 148 ++++++++++++++++++ .../UI WinForms/Components/Setting.cs | 89 +++++++++++ .../UI WinForms/Resources/Icons.Designer.cs | 10 ++ .../UI WinForms/Resources/Icons.resx | 3 + .../Resources/icons8-folder-tree-512.png | Bin 0 -> 2439 bytes 6 files changed, 252 insertions(+) create mode 100644 I18N Commander/UI WinForms/Resources/icons8-folder-tree-512.png diff --git a/I18N Commander/DataModel/Database/SettingNames.cs b/I18N Commander/DataModel/Database/SettingNames.cs index 734eaed..6ed3ee9 100644 --- a/I18N Commander/DataModel/Database/SettingNames.cs +++ b/I18N Commander/DataModel/Database/SettingNames.cs @@ -8,4 +8,6 @@ public static class SettingNames public static readonly string DEEPL_SOURCE_CULTURE = "DeepL Source Culture"; public static readonly string DEEPL_MODE = "DeepL Mode"; public static readonly string GENERATOR_MODE = "Generator Mode"; + public static readonly string GENERATOR_DOTNET_ENABLED = "Generator .NET Enabled"; + public static readonly string GENERATOR_DOTNET_DESTINATION_PATH = "Generator .NET Destination Path"; } \ No newline at end of file diff --git a/I18N Commander/Processor/AppSettings.cs b/I18N Commander/Processor/AppSettings.cs index 8526d7a..3efcaf5 100644 --- a/I18N Commander/Processor/AppSettings.cs +++ b/I18N Commander/Processor/AppSettings.cs @@ -537,5 +537,153 @@ public static class AppSettings #endregion + #region .NET Generator Enabled/Disabled + + private static bool CACHE_GENERATOR_DOTNET_ENABLED = true; + private static bool CACHE_GENERATOR_DOTNET_ENABLED_IS_LOADED = false; + + public static async Task GetGeneratorDotnetEnabled() + { + // When possible, use the cache: + if (CACHE_GENERATOR_DOTNET_ENABLED_IS_LOADED) + return CACHE_GENERATOR_DOTNET_ENABLED; + + var generatorDotnetEnabled = false; + try + { + // Get the database: + await using var db = ProcessorMeta.ServiceProvider.GetRequiredService(); + + // Check, if the setting is already set: + if (await db.Settings.FirstOrDefaultAsync(n => n.Code == SettingNames.GENERATOR_DOTNET_ENABLED) is { } existingSetting) + { + generatorDotnetEnabled = existingSetting.BoolValue; + return generatorDotnetEnabled; + } + + // Does not exist, so create it: + var setting = new Setting + { + Code = SettingNames.GENERATOR_DOTNET_ENABLED, + BoolValue = generatorDotnetEnabled, + }; + + await db.Settings.AddAsync(setting); + await db.SaveChangesAsync(); + return generatorDotnetEnabled; + } + finally + { + CACHE_GENERATOR_DOTNET_ENABLED_IS_LOADED = true; + CACHE_GENERATOR_DOTNET_ENABLED = generatorDotnetEnabled; + } + } + + public static async Task SetGeneratorDotnetEnabled(bool enabled) + { + // Update the cache: + CACHE_GENERATOR_DOTNET_ENABLED = enabled; + CACHE_GENERATOR_DOTNET_ENABLED_IS_LOADED = true; + + // Get the database: + await using var db = ProcessorMeta.ServiceProvider.GetRequiredService(); + + // Check, if the setting is already set: + if (await db.Settings.FirstOrDefaultAsync(n => n.Code == SettingNames.GENERATOR_DOTNET_ENABLED) is { } existingSetting) + { + existingSetting.BoolValue = enabled; + await db.SaveChangesAsync(); + } + + // Does not exist, so create it: + else + { + var setting = new Setting + { + Code = SettingNames.GENERATOR_DOTNET_ENABLED, + BoolValue = enabled, + }; + + await db.Settings.AddAsync(setting); + await db.SaveChangesAsync(); + } + } + + #endregion + + #region .NET Generator Destination Path + + private static string CACHE_GENERATOR_DOTNET_DESTINATION_PATH = string.Empty; + private static bool CACHE_GENERATOR_DOTNET_DESTINATION_PATH_IS_LOADED = false; + + public static async Task GetGeneratorDotnetDestinationPath() + { + // When possible, use the cache: + if (CACHE_GENERATOR_DOTNET_DESTINATION_PATH_IS_LOADED) + return CACHE_GENERATOR_DOTNET_DESTINATION_PATH; + + var generatorDotnetDestinationPath = string.Empty; + try + { + // Get the database: + await using var db = ProcessorMeta.ServiceProvider.GetRequiredService(); + + // Check, if the setting is already set: + if (await db.Settings.FirstOrDefaultAsync(n => n.Code == SettingNames.GENERATOR_DOTNET_DESTINATION_PATH) is { } existingSetting) + { + generatorDotnetDestinationPath = existingSetting.TextValue; + return generatorDotnetDestinationPath; + } + + // Does not exist, so create it: + var setting = new Setting + { + Code = SettingNames.GENERATOR_DOTNET_DESTINATION_PATH, + TextValue = generatorDotnetDestinationPath, + }; + + await db.Settings.AddAsync(setting); + await db.SaveChangesAsync(); + return generatorDotnetDestinationPath; + } + finally + { + CACHE_GENERATOR_DOTNET_DESTINATION_PATH_IS_LOADED = true; + CACHE_GENERATOR_DOTNET_DESTINATION_PATH = generatorDotnetDestinationPath; + } + } + + public static async Task SetGeneratorDotnetDestinationPath(string path) + { + // Update the cache: + CACHE_GENERATOR_DOTNET_DESTINATION_PATH = path; + CACHE_GENERATOR_DOTNET_DESTINATION_PATH_IS_LOADED = true; + + // Get the database: + await using var db = ProcessorMeta.ServiceProvider.GetRequiredService(); + + // Check, if the setting is already set: + if (await db.Settings.FirstOrDefaultAsync(n => n.Code == SettingNames.GENERATOR_DOTNET_DESTINATION_PATH) is { } existingSetting) + { + existingSetting.TextValue = path; + await db.SaveChangesAsync(); + } + + // Does not exist, so create it: + else + { + var setting = new Setting + { + Code = SettingNames.GENERATOR_DOTNET_DESTINATION_PATH, + TextValue = path, + }; + + await db.Settings.AddAsync(setting); + await db.SaveChangesAsync(); + } + } + + #endregion + #endregion } \ No newline at end of file diff --git a/I18N Commander/UI WinForms/Components/Setting.cs b/I18N Commander/UI WinForms/Components/Setting.cs index c354430..af0abba 100644 --- a/I18N Commander/UI WinForms/Components/Setting.cs +++ b/I18N Commander/UI WinForms/Components/Setting.cs @@ -434,8 +434,97 @@ public sealed partial class Setting : UserControl return new Setting(settingData); } + private static async Task ShowGeneratorDotnetEnabledSettingAsync() + { + var currentSetting = await AppSettings.GetGeneratorDotnetEnabled(); + + var settingData = new SettingUIData( + Icon: Icons.icons8_code_512, + SettingName: () => "Generator: .NET", + ChangeNeedsRestart: false, + SettingExplanation: () => "When enabled, .NET translation files are generated. Requires a .NET 6 or newer project.", + SettingExplanationLink: () => (string.Empty, string.Empty), + SetupDataControl: (changeTrigger) => + { + // Set up an checkbox: + var checkbox = new CheckBox(); + checkbox.Checked = currentSetting; + checkbox.CheckedChanged += (sender, args) => changeTrigger(); + checkbox.CheckedChanged += async (sender, args) => await AppSettings.SetGeneratorDotnetEnabled(checkbox.Checked); + checkbox.Text = "Enable .NET Generator"; + + // Apply the desired layout: + checkbox.Dock = DockStyle.Fill; + return checkbox; + } + ); + + return new Setting(settingData); + } + + private static async Task ShowGeneratorDotnetDestinationPathSettingAsync() + { + var currentSetting = await AppSettings.GetGeneratorDotnetDestinationPath(); + + var settingData = new SettingUIData( + Icon: Icons.icons8_code_512, + SettingName: () => "Generator: .NET Destination Path", + ChangeNeedsRestart: false, + SettingExplanation: () => "The destination path for the .NET translation files. You might use environment variables like %USERPROFILE%.", + SettingExplanationLink: () => (string.Empty, string.Empty), + SetupDataControl: (changeTrigger) => + { + // Set up a horizontal layout: + var layout = new TableLayoutPanel(); + layout.ColumnCount = 2; + layout.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 100F)); + layout.ColumnStyles.Add(new ColumnStyle(SizeType.Absolute, 66F)); + layout.RowCount = 1; + layout.RowStyles.Add(new RowStyle(SizeType.Percent, 100F)); + layout.Dock = DockStyle.Fill; + + // Set up a textbox: + var textbox = new TextBox(); + textbox.Text = currentSetting; + textbox.TextChanged += (sender, args) => changeTrigger(); + textbox.TextChanged += async (sender, args) => await AppSettings.SetGeneratorDotnetDestinationPath(textbox.Text); + textbox.Dock = DockStyle.Fill; + textbox.Margin = new Padding(0, 13, 0, 13); + layout.Controls.Add(textbox, 0, 0); + + // Set up a button: + var button = new Button(); + button.Text = string.Empty; + button.Image = Icons.icons8_folder_tree_512; + button.FlatStyle = FlatStyle.Flat; + button.FlatAppearance.BorderSize = 0; + button.BackColor = Color.Empty; + button.UseVisualStyleBackColor = true; + button.Size = new Size(60, 60); + button.Click += (sender, args) => + { + var dialog = new FolderBrowserDialog(); + dialog.SelectedPath = textbox.Text; + dialog.InitialDirectory = textbox.Text; + dialog.Description = "Select the destination path for the .NET translation files."; + dialog.ShowNewFolderButton = true; + if (dialog.ShowDialog() == DialogResult.OK) + textbox.Text = dialog.SelectedPath; + }; + button.Dock = DockStyle.Fill; + layout.Controls.Add(button, 1, 0); + + return layout; + } + ); + + return new Setting(settingData); + } + public static IEnumerable> GetAllSettings() { + yield return ShowGeneratorDotnetDestinationPathSettingAsync(); + yield return ShowGeneratorDotnetEnabledSettingAsync(); yield return ShowGeneratorModeSettingAsync(); yield return ShowDeepLSourceCultureSettingAsync(); foreach (var setting in ShowCultureSettingsAsync()) diff --git a/I18N Commander/UI WinForms/Resources/Icons.Designer.cs b/I18N Commander/UI WinForms/Resources/Icons.Designer.cs index 95a9b6e..7a66bfe 100644 --- a/I18N Commander/UI WinForms/Resources/Icons.Designer.cs +++ b/I18N Commander/UI WinForms/Resources/Icons.Designer.cs @@ -190,6 +190,16 @@ namespace UI_WinForms.Resources { } } + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap icons8_folder_tree_512 { + get { + object obj = ResourceManager.GetObject("icons8_folder_tree_512", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + /// /// Looks up a localized resource of type System.Drawing.Bitmap. /// diff --git a/I18N Commander/UI WinForms/Resources/Icons.resx b/I18N Commander/UI WinForms/Resources/Icons.resx index 15f9723..538f121 100644 --- a/I18N Commander/UI WinForms/Resources/Icons.resx +++ b/I18N Commander/UI WinForms/Resources/Icons.resx @@ -157,6 +157,9 @@ icons8-document-512.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + icons8-folder-tree-512.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + icons8-increase-512.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a diff --git a/I18N Commander/UI WinForms/Resources/icons8-folder-tree-512.png b/I18N Commander/UI WinForms/Resources/icons8-folder-tree-512.png new file mode 100644 index 0000000000000000000000000000000000000000..60db4b817ec593685ed118896223caba42aa5748 GIT binary patch literal 2439 zcmaJ@dt4J&77ieSns{Lb<-1~j!JLjHr?wRb0 zjS08Kx?(XHj4daE6^DM~4Ud%t`bl%`*I+R7>Y?}qB!L@E6TmV8UkImx1hq_o;us7) zM6KWpGC%~E28tm$19+nS2Eai=2EYs8lDG;cD1jpKl;HZjn0P^6hJY#rLe^sGY8oma z0}(z>EtASsG&KV->eA5PhHWB%Gg6QY29Usw#W7(ehzlSD5JPBIer9KDJJ?-sZ=6~OeB->CBeBe5n))o=ljQL5oI1Z6Q%Ei;tizG70TXUlN|=7w;QH^QWvP zk!d87QPxOBFM^Pd@c)u*=w%!orE|Ho2uOv%%3R~tbc3%4GpAS6ODSaRY#h#5Um9Ov z$TtH}C}Cli02CTMqjJ-z3Kk(bd?gqvMoY^8LPa77txPU1*3h#!UxF`*FkRPKiyR4v zs=eb>#F?;&hKu%lTy)|ak<*QYKio{zZh8;B@#yFv&dv^WF*|)gIXZHb=xmA9?})-+ zOo|}18gEnqgYl`zs?bK6g)Z0;vWUH_V%}FREmjU+7O+YVl(UQ0h2kr%IyCpMGp`P` zMeRvl-DBa%a{I7k%KYEhuA&1zMG0kR4waXx2n!u9T_*?sXDVCVQ)?a-bo9vcb@oU6 zsCeh6UoT#~Z}SaePfP!i&oAse)O7lwj+8YrT4&+cH(qmQ`18r`B%a`$sV)B7fwIQI z;`(B7m#R@0{O!_6@~Kq?-goPD<|R*}8A(6$sunp-Q4(z|U#4bawGIL9U-FkX94#3f zY`fXe{w&{PboqFKXsy@DP3<Rh%@h{WzVqyLx$3+w%8zw62$IDjs)h!SnLQ zwg1wY6$jpLYUaZpd4uhOLCZRf_?1^z_5<`SVXcTrRADeSiwutmM)R3724gzGVTHyg zl#F~DEOGq6vGg0ingH*01EN5dqtj(e0Gq}?wellelH_pOvYk>-pN0OlwN`1%unlYG zWtQPDrDd2et32R^Jx1EQYa*;=*LM@&KmO%pEb`f>0m0qs+btf_(GREcw}_t9=8G)S zZ|zTxs6I}J+E%`Gl>2O}Tjj;mPQUPq!$}ZFz}VF|BZ}; z2QQqk273J|gdZ-vxU#S=Gd_99x$wC-Z7Mnl$?y*Kwsm;1$aSlklQ=Hf*1cAz*sBPO zj9TTgu+6pWWwvIO^^WEiYE$Q}B{j$Oujps=XD5iv@ZS0mx9^`1@wN|FVl>-|-=FfK z_Fcw1+9h|HxSzN9;z~}qnwOqJ-%zJjY*F@e;?`Qef0+|cj9H)Ip>U9lL7u}udQ$7I zSTF6(pWn>6rv>0LeC6M}RGdt^+rb{K9x0h3oFw#&*V4mD4SUXQw#+Uq(A!J*xgSku zhU4vzygX%E3OG#$A5cZQ`4{U-u>{CHT(Vev4oE} zj|AQ&eV;6T7Rc0g-fTN|?3~WMa)58!U zy((uVrF)EUz`pI_Wb(#+?93IXT95r~rFYSHnL$}UjowM_^H^5ZyrRl3JJ&ijaIdy~ zQhl_xve5srM@uW6JVe`0fc^S~+s!Ci`pu;0q5Lj-PfR!u?$9sp9_v6Tengi^7m7ob zzMhNaI{GEp7WUsu8M(2f+0;JT=EjMdM;043FJ30(L&upJlM9B*WMl1xKl&y;jiLmm zh5V{*?>=s4$nTaT~zj(;cLut*VARhLH(sL&B(oHhW}3-b_}aI HEVbzW3FMs7 literal 0 HcmV?d00001 From 9e755502cb913495d28bb0e97b63bab9d06259a0 Mon Sep 17 00:00:00 2001 From: Thorsten Sommer Date: Sat, 29 Oct 2022 18:47:06 +0200 Subject: [PATCH 04/16] Update --- .../.idea/.idea.I18N Commander/.idea/riderMarkupCache.xml | 6 ++++++ I18N Commander/.idea/.idea.I18N Commander/.idea/vcs.xml | 7 +++++++ 2 files changed, 13 insertions(+) create mode 100644 I18N Commander/.idea/.idea.I18N Commander/.idea/riderMarkupCache.xml diff --git a/I18N Commander/.idea/.idea.I18N Commander/.idea/riderMarkupCache.xml b/I18N Commander/.idea/.idea.I18N Commander/.idea/riderMarkupCache.xml new file mode 100644 index 0000000..4d68344 --- /dev/null +++ b/I18N Commander/.idea/.idea.I18N Commander/.idea/riderMarkupCache.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/I18N Commander/.idea/.idea.I18N Commander/.idea/vcs.xml b/I18N Commander/.idea/.idea.I18N Commander/.idea/vcs.xml index 2e3f692..fa8c83e 100644 --- a/I18N Commander/.idea/.idea.I18N Commander/.idea/vcs.xml +++ b/I18N Commander/.idea/.idea.I18N Commander/.idea/vcs.xml @@ -1,5 +1,12 @@ + + + + + + + From 42589c89d874e42b5e7e9e5541d0f5b403b53b59 Mon Sep 17 00:00:00 2001 From: Thorsten Sommer Date: Sat, 29 Oct 2022 18:47:50 +0200 Subject: [PATCH 05/16] Formatting --- I18N Commander/UI WinForms/Components/Setting.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/I18N Commander/UI WinForms/Components/Setting.cs b/I18N Commander/UI WinForms/Components/Setting.cs index af0abba..051a198 100644 --- a/I18N Commander/UI WinForms/Components/Setting.cs +++ b/I18N Commander/UI WinForms/Components/Setting.cs @@ -528,9 +528,7 @@ public sealed partial class Setting : UserControl yield return ShowGeneratorModeSettingAsync(); yield return ShowDeepLSourceCultureSettingAsync(); foreach (var setting in ShowCultureSettingsAsync()) - { yield return setting; - } yield return ShowDeepLUsageSettingAsync(); yield return ShowDeepLActionSettingAsync(); From 58f30240dc94ebc36c49955b4d4b01979c6a4831 Mon Sep 17 00:00:00 2001 From: Thorsten Sommer Date: Sat, 29 Oct 2022 18:48:38 +0200 Subject: [PATCH 06/16] Added Godot settings --- .../DataModel/Database/SettingNames.cs | 2 + I18N Commander/Processor/AppSettings.cs | 148 ++++++++++++++++++ .../UI WinForms/Components/Setting.cs | 89 +++++++++++ 3 files changed, 239 insertions(+) diff --git a/I18N Commander/DataModel/Database/SettingNames.cs b/I18N Commander/DataModel/Database/SettingNames.cs index 6ed3ee9..f6dd829 100644 --- a/I18N Commander/DataModel/Database/SettingNames.cs +++ b/I18N Commander/DataModel/Database/SettingNames.cs @@ -10,4 +10,6 @@ public static class SettingNames public static readonly string GENERATOR_MODE = "Generator Mode"; public static readonly string GENERATOR_DOTNET_ENABLED = "Generator .NET Enabled"; public static readonly string GENERATOR_DOTNET_DESTINATION_PATH = "Generator .NET Destination Path"; + public static readonly string GENERATOR_GODOT_ENABLED = "Generator Godot Enabled"; + public static readonly string GENERATOR_GODOT_DESTINATION_PATH = "Generator Godot Destination Path"; } \ No newline at end of file diff --git a/I18N Commander/Processor/AppSettings.cs b/I18N Commander/Processor/AppSettings.cs index 3efcaf5..894c57b 100644 --- a/I18N Commander/Processor/AppSettings.cs +++ b/I18N Commander/Processor/AppSettings.cs @@ -685,5 +685,153 @@ public static class AppSettings #endregion + #region Godot Generator Enabled/Disabled + + private static bool CACHE_GENERATOR_GODOT_ENABLED = true; + private static bool CACHE_GENERATOR_GODOT_ENABLED_IS_LOADED = false; + + public static async Task GetGeneratorGodotEnabled() + { + // When possible, use the cache: + if (CACHE_GENERATOR_GODOT_ENABLED_IS_LOADED) + return CACHE_GENERATOR_GODOT_ENABLED; + + var generatorGodotEnabled = false; + try + { + // Get the database: + await using var db = ProcessorMeta.ServiceProvider.GetRequiredService(); + + // Check, if the setting is already set: + if (await db.Settings.FirstOrDefaultAsync(n => n.Code == SettingNames.GENERATOR_GODOT_ENABLED) is { } existingSetting) + { + generatorGodotEnabled = existingSetting.BoolValue; + return generatorGodotEnabled; + } + + // Does not exist, so create it: + var setting = new Setting + { + Code = SettingNames.GENERATOR_GODOT_ENABLED, + BoolValue = generatorGodotEnabled, + }; + + await db.Settings.AddAsync(setting); + await db.SaveChangesAsync(); + return generatorGodotEnabled; + } + finally + { + CACHE_GENERATOR_GODOT_ENABLED_IS_LOADED = true; + CACHE_GENERATOR_GODOT_ENABLED = generatorGodotEnabled; + } + } + + public static async Task SetGeneratorGodotEnabled(bool enabled) + { + // Update the cache: + CACHE_GENERATOR_GODOT_ENABLED = enabled; + CACHE_GENERATOR_GODOT_ENABLED_IS_LOADED = true; + + // Get the database: + await using var db = ProcessorMeta.ServiceProvider.GetRequiredService(); + + // Check, if the setting is already set: + if (await db.Settings.FirstOrDefaultAsync(n => n.Code == SettingNames.GENERATOR_GODOT_ENABLED) is { } existingSetting) + { + existingSetting.BoolValue = enabled; + await db.SaveChangesAsync(); + } + + // Does not exist, so create it: + else + { + var setting = new Setting + { + Code = SettingNames.GENERATOR_GODOT_ENABLED, + BoolValue = enabled, + }; + + await db.Settings.AddAsync(setting); + await db.SaveChangesAsync(); + } + } + + #endregion + + #region Godot Generator Destination Path + + private static string CACHE_GENERATOR_GODOT_DESTINATION_PATH = string.Empty; + private static bool CACHE_GENERATOR_GODOT_DESTINATION_PATH_IS_LOADED = false; + + public static async Task GetGeneratorGodotDestinationPath() + { + // When possible, use the cache: + if (CACHE_GENERATOR_GODOT_DESTINATION_PATH_IS_LOADED) + return CACHE_GENERATOR_GODOT_DESTINATION_PATH; + + var generatorGodotDestinationPath = string.Empty; + try + { + // Get the database: + await using var db = ProcessorMeta.ServiceProvider.GetRequiredService(); + + // Check, if the setting is already set: + if (await db.Settings.FirstOrDefaultAsync(n => n.Code == SettingNames.GENERATOR_GODOT_DESTINATION_PATH) is { } existingSetting) + { + generatorGodotDestinationPath = existingSetting.TextValue; + return generatorGodotDestinationPath; + } + + // Does not exist, so create it: + var setting = new Setting + { + Code = SettingNames.GENERATOR_GODOT_DESTINATION_PATH, + TextValue = generatorGodotDestinationPath, + }; + + await db.Settings.AddAsync(setting); + await db.SaveChangesAsync(); + return generatorGodotDestinationPath; + } + finally + { + CACHE_GENERATOR_GODOT_DESTINATION_PATH_IS_LOADED = true; + CACHE_GENERATOR_GODOT_DESTINATION_PATH = generatorGodotDestinationPath; + } + } + + public static async Task SetGeneratorGodotDestinationPath(string path) + { + // Update the cache: + CACHE_GENERATOR_GODOT_DESTINATION_PATH = path; + CACHE_GENERATOR_GODOT_DESTINATION_PATH_IS_LOADED = true; + + // Get the database: + await using var db = ProcessorMeta.ServiceProvider.GetRequiredService(); + + // Check, if the setting is already set: + if (await db.Settings.FirstOrDefaultAsync(n => n.Code == SettingNames.GENERATOR_GODOT_DESTINATION_PATH) is { } existingSetting) + { + existingSetting.TextValue = path; + await db.SaveChangesAsync(); + } + + // Does not exist, so create it: + else + { + var setting = new Setting + { + Code = SettingNames.GENERATOR_GODOT_DESTINATION_PATH, + TextValue = path, + }; + + await db.Settings.AddAsync(setting); + await db.SaveChangesAsync(); + } + } + + #endregion + #endregion } \ No newline at end of file diff --git a/I18N Commander/UI WinForms/Components/Setting.cs b/I18N Commander/UI WinForms/Components/Setting.cs index 051a198..7be449f 100644 --- a/I18N Commander/UI WinForms/Components/Setting.cs +++ b/I18N Commander/UI WinForms/Components/Setting.cs @@ -520,9 +520,98 @@ public sealed partial class Setting : UserControl return new Setting(settingData); } + + private static async Task ShowGeneratorGodotEnabledSettingAsync() + { + var currentSetting = await AppSettings.GetGeneratorGodotEnabled(); + + var settingData = new SettingUIData( + Icon: Icons.icons8_code_512, + SettingName: () => "Generator: Godot", + ChangeNeedsRestart: false, + SettingExplanation: () => "When enabled, Godot translation files are generated. Requires a Godot 3.5 or newer project.", + SettingExplanationLink: () => (string.Empty, string.Empty), + SetupDataControl: (changeTrigger) => + { + // Set up an checkbox: + var checkbox = new CheckBox(); + checkbox.Checked = currentSetting; + checkbox.CheckedChanged += (sender, args) => changeTrigger(); + checkbox.CheckedChanged += async (sender, args) => await AppSettings.SetGeneratorGodotEnabled(checkbox.Checked); + checkbox.Text = "Enable Godot Generator"; + + // Apply the desired layout: + checkbox.Dock = DockStyle.Fill; + return checkbox; + } + ); + + return new Setting(settingData); + } + + private static async Task ShowGeneratorGodotDestinationPathSettingAsync() + { + var currentSetting = await AppSettings.GetGeneratorGodotDestinationPath(); + + var settingData = new SettingUIData( + Icon: Icons.icons8_code_512, + SettingName: () => "Generator: Godot Destination Path", + ChangeNeedsRestart: false, + SettingExplanation: () => "The destination path for the Godot translation files. You might use environment variables like %USERPROFILE%.", + SettingExplanationLink: () => (string.Empty, string.Empty), + SetupDataControl: (changeTrigger) => + { + // Set up a horizontal layout: + var layout = new TableLayoutPanel(); + layout.ColumnCount = 2; + layout.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 100F)); + layout.ColumnStyles.Add(new ColumnStyle(SizeType.Absolute, 66F)); + layout.RowCount = 1; + layout.RowStyles.Add(new RowStyle(SizeType.Percent, 100F)); + layout.Dock = DockStyle.Fill; + + // Set up a textbox: + var textbox = new TextBox(); + textbox.Text = currentSetting; + textbox.TextChanged += (sender, args) => changeTrigger(); + textbox.TextChanged += async (sender, args) => await AppSettings.SetGeneratorGodotDestinationPath(textbox.Text); + textbox.Dock = DockStyle.Fill; + textbox.Margin = new Padding(0, 13, 0, 13); + layout.Controls.Add(textbox, 0, 0); + + // Set up a button: + var button = new Button(); + button.Text = string.Empty; + button.Image = Icons.icons8_folder_tree_512; + button.FlatStyle = FlatStyle.Flat; + button.FlatAppearance.BorderSize = 0; + button.BackColor = Color.Empty; + button.UseVisualStyleBackColor = true; + button.Size = new Size(60, 60); + button.Click += (sender, args) => + { + var dialog = new FolderBrowserDialog(); + dialog.SelectedPath = textbox.Text; + dialog.InitialDirectory = textbox.Text; + dialog.Description = "Select the destination path for the Godot translation files."; + dialog.ShowNewFolderButton = true; + if (dialog.ShowDialog() == DialogResult.OK) + textbox.Text = dialog.SelectedPath; + }; + button.Dock = DockStyle.Fill; + layout.Controls.Add(button, 1, 0); + + return layout; + } + ); + + return new Setting(settingData); + } public static IEnumerable> GetAllSettings() { + yield return ShowGeneratorGodotDestinationPathSettingAsync(); + yield return ShowGeneratorGodotEnabledSettingAsync(); yield return ShowGeneratorDotnetDestinationPathSettingAsync(); yield return ShowGeneratorDotnetEnabledSettingAsync(); yield return ShowGeneratorModeSettingAsync(); From 9c0de0edd5fe08cc016b44f7c528bb774be04ce3 Mon Sep 17 00:00:00 2001 From: Thorsten Sommer Date: Sat, 29 Oct 2022 20:16:44 +0200 Subject: [PATCH 07/16] Fixed order of settings changed event & added app event for it --- I18N Commander/UI WinForms/AppEvents.cs | 8 ++++++++ I18N Commander/UI WinForms/Components/Setting.cs | 16 +++++++++------- 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/I18N Commander/UI WinForms/AppEvents.cs b/I18N Commander/UI WinForms/AppEvents.cs index 5ba9c0d..dfa47aa 100644 --- a/I18N Commander/UI WinForms/AppEvents.cs +++ b/I18N Commander/UI WinForms/AppEvents.cs @@ -4,6 +4,14 @@ namespace UI_WinForms; internal static class AppEvents { + #region Event: Settings were + + internal static event EventHandler WhenSettingsChanged; + + internal static void SettingsChanged() => WhenSettingsChanged?.Invoke(null, EventArgs.Empty); + + #endregion + #region Event: Section was changed // Section changed event which can be subscribed: diff --git a/I18N Commander/UI WinForms/Components/Setting.cs b/I18N Commander/UI WinForms/Components/Setting.cs index 7be449f..a2be64f 100644 --- a/I18N Commander/UI WinForms/Components/Setting.cs +++ b/I18N Commander/UI WinForms/Components/Setting.cs @@ -77,6 +77,8 @@ public sealed partial class Setting : UserControl { if(this.data.ChangeNeedsRestart) this.needRestart = true; + + AppEvents.SettingsChanged(); } private void UpdateExplanation() => this.labelExplanation.Text = this.data.SettingExplanation(); @@ -257,7 +259,6 @@ public sealed partial class Setting : UserControl }; // Setup the change event handler: - dropdown.SelectedValueChanged += (sender, args) => changeTrigger(); dropdown.SelectedValueChanged += async (sender, args) => await AppSettings.SetDeepLAction(dropdown.SelectedIndex switch { 0 => SettingDeepLAction.MANUAL, @@ -265,6 +266,7 @@ public sealed partial class Setting : UserControl _ => SettingDeepLAction.MANUAL, }); + dropdown.SelectedValueChanged += (sender, args) => changeTrigger(); // Apply the desired layout: dropdown.Dock = DockStyle.Fill; @@ -314,12 +316,12 @@ public sealed partial class Setting : UserControl dropdown.SelectedIndex = currentCultureDropdownIndex; // Setup the change event handler: - dropdown.SelectedValueChanged += (sender, args) => changeTrigger(); dropdown.SelectedValueChanged += async (sender, args) => { if(dropdown.SelectedItem is ComboBoxItem selectedItem) await AppSettings.SetDeepLSourceCultureIndex(selectedItem.CultureIndex); }; + dropdown.SelectedValueChanged += (sender, args) => changeTrigger(); // Apply the desired layout: dropdown.Dock = DockStyle.Fill; @@ -414,7 +416,6 @@ public sealed partial class Setting : UserControl }; // Setup the change event handler: - dropdown.SelectedValueChanged += (sender, args) => changeTrigger(); dropdown.SelectedValueChanged += async (sender, args) => await AppSettings.SetGeneratorMode(dropdown.SelectedIndex switch { 0 => SettingGeneratorMode.AUTOMATIC, @@ -422,6 +423,7 @@ public sealed partial class Setting : UserControl _ => SettingGeneratorMode.AUTOMATIC, }); + dropdown.SelectedValueChanged += (sender, args) => changeTrigger(); // Apply the desired layout: dropdown.Dock = DockStyle.Fill; @@ -449,8 +451,8 @@ public sealed partial class Setting : UserControl // Set up an checkbox: var checkbox = new CheckBox(); checkbox.Checked = currentSetting; - checkbox.CheckedChanged += (sender, args) => changeTrigger(); checkbox.CheckedChanged += async (sender, args) => await AppSettings.SetGeneratorDotnetEnabled(checkbox.Checked); + checkbox.CheckedChanged += (sender, args) => changeTrigger(); checkbox.Text = "Enable .NET Generator"; // Apply the desired layout: @@ -486,8 +488,8 @@ public sealed partial class Setting : UserControl // Set up a textbox: var textbox = new TextBox(); textbox.Text = currentSetting; - textbox.TextChanged += (sender, args) => changeTrigger(); textbox.TextChanged += async (sender, args) => await AppSettings.SetGeneratorDotnetDestinationPath(textbox.Text); + textbox.TextChanged += (sender, args) => changeTrigger(); textbox.Dock = DockStyle.Fill; textbox.Margin = new Padding(0, 13, 0, 13); layout.Controls.Add(textbox, 0, 0); @@ -536,8 +538,8 @@ public sealed partial class Setting : UserControl // Set up an checkbox: var checkbox = new CheckBox(); checkbox.Checked = currentSetting; - checkbox.CheckedChanged += (sender, args) => changeTrigger(); checkbox.CheckedChanged += async (sender, args) => await AppSettings.SetGeneratorGodotEnabled(checkbox.Checked); + checkbox.CheckedChanged += (sender, args) => changeTrigger(); checkbox.Text = "Enable Godot Generator"; // Apply the desired layout: @@ -573,8 +575,8 @@ public sealed partial class Setting : UserControl // Set up a textbox: var textbox = new TextBox(); textbox.Text = currentSetting; - textbox.TextChanged += (sender, args) => changeTrigger(); textbox.TextChanged += async (sender, args) => await AppSettings.SetGeneratorGodotDestinationPath(textbox.Text); + textbox.TextChanged += (sender, args) => changeTrigger(); textbox.Dock = DockStyle.Fill; textbox.Margin = new Padding(0, 13, 0, 13); layout.Controls.Add(textbox, 0, 0); From 547a22bfd2e69e8ef2df42788a475d425a24f38a Mon Sep 17 00:00:00 2001 From: Thorsten Sommer Date: Sat, 29 Oct 2022 20:18:00 +0200 Subject: [PATCH 08/16] Added button for manual triggered source generation --- .../Components/SectionTree.Designer.cs | 19 +++++++++++++++++ .../UI WinForms/Components/SectionTree.cs | 20 ++++++++++++++++++ .../UI WinForms/Resources/Icons.Designer.cs | 10 +++++++++ .../UI WinForms/Resources/Icons.resx | 3 +++ .../Resources/icons8-code-512 (2).png | Bin 0 -> 4648 bytes 5 files changed, 52 insertions(+) create mode 100644 I18N Commander/UI WinForms/Resources/icons8-code-512 (2).png diff --git a/I18N Commander/UI WinForms/Components/SectionTree.Designer.cs b/I18N Commander/UI WinForms/Components/SectionTree.Designer.cs index ebd2639..219fbad 100644 --- a/I18N Commander/UI WinForms/Components/SectionTree.Designer.cs +++ b/I18N Commander/UI WinForms/Components/SectionTree.Designer.cs @@ -34,6 +34,7 @@ this.buttonAdd = new System.Windows.Forms.Button(); this.buttonRemove = new System.Windows.Forms.Button(); this.buttonRename = new System.Windows.Forms.Button(); + this.buttonGenerate = new System.Windows.Forms.Button(); this.treeView = new System.Windows.Forms.TreeView(); this.toolTip = new System.Windows.Forms.ToolTip(this.components); this.tableLayout.SuspendLayout(); @@ -61,6 +62,7 @@ this.flowLayoutBottom.Controls.Add(this.buttonAdd); this.flowLayoutBottom.Controls.Add(this.buttonRemove); this.flowLayoutBottom.Controls.Add(this.buttonRename); + this.flowLayoutBottom.Controls.Add(this.buttonGenerate); this.flowLayoutBottom.Dock = System.Windows.Forms.DockStyle.Fill; this.flowLayoutBottom.Location = new System.Drawing.Point(0, 445); this.flowLayoutBottom.Margin = new System.Windows.Forms.Padding(0); @@ -115,6 +117,22 @@ this.buttonRename.UseVisualStyleBackColor = true; this.buttonRename.Click += new System.EventHandler(this.buttonRename_Click); // + // buttonGenerate + // + this.buttonGenerate.AutoSize = true; + this.buttonGenerate.Enabled = false; + this.buttonGenerate.FlatAppearance.BorderSize = 0; + this.buttonGenerate.FlatStyle = System.Windows.Forms.FlatStyle.Flat; + this.buttonGenerate.Image = global::UI_WinForms.Resources.Icons.icons8_code_512__2_; + this.buttonGenerate.Location = new System.Drawing.Point(201, 3); + this.buttonGenerate.Name = "buttonGenerate"; + this.buttonGenerate.Size = new System.Drawing.Size(60, 60); + this.buttonGenerate.TabIndex = 3; + this.buttonGenerate.TextImageRelation = System.Windows.Forms.TextImageRelation.ImageBeforeText; + this.toolTip.SetToolTip(this.buttonGenerate, "Triggers all enabled generators"); + this.buttonGenerate.UseVisualStyleBackColor = true; + this.buttonGenerate.Click += new System.EventHandler(this.buttonGenerate_Click); + // // treeView // this.treeView.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; @@ -159,5 +177,6 @@ private TreeView treeView; private ToolTip toolTip; private Button buttonRename; + private Button buttonGenerate; } } diff --git a/I18N Commander/UI WinForms/Components/SectionTree.cs b/I18N Commander/UI WinForms/Components/SectionTree.cs index d402363..a62c2f7 100644 --- a/I18N Commander/UI WinForms/Components/SectionTree.cs +++ b/I18N Commander/UI WinForms/Components/SectionTree.cs @@ -28,8 +28,23 @@ public partial class SectionTree : UserControl // Subscribe to the load event: this.Load += this.LoadNodes; + this.Load += (sender, args) => this.SetupGeneratorButton(); } + private async void SetupGeneratorButton() + { + this.buttonGenerate.Enabled = false; + + // Depend the generator button's visibility on the generator settings: + this.buttonGenerate.Enabled = await AppSettings.GetGeneratorDotnetEnabled() || await AppSettings.GetGeneratorGodotEnabled(); + + // Subscribe to the changed settings event: + AppEvents.WhenSettingsChanged += async (sender, args) => + { + this.buttonGenerate.Enabled = await AppSettings.GetGeneratorDotnetEnabled() || await AppSettings.GetGeneratorGodotEnabled(); + }; + } + private async void LoadNodes(object? sender, EventArgs e) { if(this.DesignMode) @@ -277,4 +292,9 @@ public partial class SectionTree : UserControl selectedNode.Text = alteredSection.Result!.Name; selectedNode.Name = alteredSection.Result.DataKey; // [sic] name is the key } + + private void buttonGenerate_Click(object sender, EventArgs e) + { + + } } \ No newline at end of file diff --git a/I18N Commander/UI WinForms/Resources/Icons.Designer.cs b/I18N Commander/UI WinForms/Resources/Icons.Designer.cs index 7a66bfe..cf5126b 100644 --- a/I18N Commander/UI WinForms/Resources/Icons.Designer.cs +++ b/I18N Commander/UI WinForms/Resources/Icons.Designer.cs @@ -150,6 +150,16 @@ namespace UI_WinForms.Resources { } } + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap icons8_code_512__2_ { + get { + object obj = ResourceManager.GetObject("icons8_code_512__2_", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + /// /// Looks up a localized resource of type System.Drawing.Bitmap. /// diff --git a/I18N Commander/UI WinForms/Resources/Icons.resx b/I18N Commander/UI WinForms/Resources/Icons.resx index 538f121..8e6f5c8 100644 --- a/I18N Commander/UI WinForms/Resources/Icons.resx +++ b/I18N Commander/UI WinForms/Resources/Icons.resx @@ -145,6 +145,9 @@ icons8-code-512.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + icons8-code-512 (2).png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + icons8-collectibles-512.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a diff --git a/I18N Commander/UI WinForms/Resources/icons8-code-512 (2).png b/I18N Commander/UI WinForms/Resources/icons8-code-512 (2).png new file mode 100644 index 0000000000000000000000000000000000000000..298896d04c932a90ea36cf54ce3609b76af8bd9a GIT binary patch literal 4648 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 literal 0 HcmV?d00001 From 630f014c1a7a54b9768f1c49d270a7094bac675b Mon Sep 17 00:00:00 2001 From: Thorsten Sommer Date: Sun, 30 Oct 2022 15:49:22 +0100 Subject: [PATCH 09/16] Implemented the .NET generator - Added .NET generator setting for the namespace to use - Added .NET generator setting to choose the default culture - Added get child section method to section processor - Added util to convert any string to a verbatim string literal - Removed redundant variables on exceptions --- .../DataModel/Database/SettingNames.cs | 2 + I18N Commander/Processor/AppSettings.cs | 148 ++++++++++++++++++ I18N Commander/Processor/DeepL.cs | 4 +- .../Processor/Generators/DotnetBigFile.cs | 141 +++++++++++++++++ .../Processor/Generators/Generator.cs | 26 +++ .../Processor/Generators/IGenerator.cs | 6 + I18N Commander/Processor/Generators/Type.cs | 9 ++ .../Processor/Generators/VoidGenerator.cs | 6 + I18N Commander/Processor/SectionProcessor.cs | 8 + .../Processor/TextElementProcessor.cs | 2 +- I18N Commander/Processor/Utils.cs | 22 +++ .../UI WinForms/Components/SectionTree.cs | 7 +- .../UI WinForms/Components/Setting.cs | 78 +++++++++ 13 files changed, 454 insertions(+), 5 deletions(-) create mode 100644 I18N Commander/Processor/Generators/DotnetBigFile.cs create mode 100644 I18N Commander/Processor/Generators/Generator.cs create mode 100644 I18N Commander/Processor/Generators/IGenerator.cs create mode 100644 I18N Commander/Processor/Generators/Type.cs create mode 100644 I18N Commander/Processor/Generators/VoidGenerator.cs diff --git a/I18N Commander/DataModel/Database/SettingNames.cs b/I18N Commander/DataModel/Database/SettingNames.cs index f6dd829..7d419cf 100644 --- a/I18N Commander/DataModel/Database/SettingNames.cs +++ b/I18N Commander/DataModel/Database/SettingNames.cs @@ -10,6 +10,8 @@ public static class SettingNames public static readonly string GENERATOR_MODE = "Generator Mode"; public static readonly string GENERATOR_DOTNET_ENABLED = "Generator .NET Enabled"; public static readonly string GENERATOR_DOTNET_DESTINATION_PATH = "Generator .NET Destination Path"; + public static readonly string GENERATOR_DOTNET_NAMESPACE = "Generator .NET Namespace"; + public static readonly string GENERATOR_DOTNET_DEFAULT_CULTURE = "Generator .NET Default Culture"; public static readonly string GENERATOR_GODOT_ENABLED = "Generator Godot Enabled"; public static readonly string GENERATOR_GODOT_DESTINATION_PATH = "Generator Godot Destination Path"; } \ No newline at end of file diff --git a/I18N Commander/Processor/AppSettings.cs b/I18N Commander/Processor/AppSettings.cs index 894c57b..20051b7 100644 --- a/I18N Commander/Processor/AppSettings.cs +++ b/I18N Commander/Processor/AppSettings.cs @@ -685,6 +685,154 @@ public static class AppSettings #endregion + #region .NET Generator Namespace + + private static string CACHE_GENERATOR_DOTNET_NAMESPACE = string.Empty; + private static bool CACHE_GENERATOR_DOTNET_NAMESPACE_IS_LOADED = false; + + public static async Task GetGeneratorDotnetNamespace() + { + // When possible, use the cache: + if (CACHE_GENERATOR_DOTNET_NAMESPACE_IS_LOADED) + return CACHE_GENERATOR_DOTNET_NAMESPACE; + + var generatorDotnetNamespace = "I18N"; + try + { + // Get the database: + await using var db = ProcessorMeta.ServiceProvider.GetRequiredService(); + + // Check, if the setting is already set: + if (await db.Settings.FirstOrDefaultAsync(n => n.Code == SettingNames.GENERATOR_DOTNET_NAMESPACE) is { } existingSetting) + { + generatorDotnetNamespace = existingSetting.TextValue; + return generatorDotnetNamespace; + } + + // Does not exist, so create it: + var setting = new Setting + { + Code = SettingNames.GENERATOR_DOTNET_NAMESPACE, + TextValue = generatorDotnetNamespace, + }; + + await db.Settings.AddAsync(setting); + await db.SaveChangesAsync(); + return generatorDotnetNamespace; + } + finally + { + CACHE_GENERATOR_DOTNET_NAMESPACE_IS_LOADED = true; + CACHE_GENERATOR_DOTNET_NAMESPACE = generatorDotnetNamespace; + } + } + + public static async Task SetGeneratorDotnetNamespace(string updatedNamespace) + { + // Update the cache: + CACHE_GENERATOR_DOTNET_NAMESPACE = updatedNamespace; + CACHE_GENERATOR_DOTNET_NAMESPACE_IS_LOADED = true; + + // Get the database: + await using var db = ProcessorMeta.ServiceProvider.GetRequiredService(); + + // Check, if the setting is already set: + if (await db.Settings.FirstOrDefaultAsync(n => n.Code == SettingNames.GENERATOR_DOTNET_NAMESPACE) is { } existingSetting) + { + existingSetting.TextValue = updatedNamespace; + await db.SaveChangesAsync(); + } + + // Does not exist, so create it: + else + { + var setting = new Setting + { + Code = SettingNames.GENERATOR_DOTNET_NAMESPACE, + TextValue = updatedNamespace, + }; + + await db.Settings.AddAsync(setting); + await db.SaveChangesAsync(); + } + } + + #endregion + + #region .NET Generator Default Culture + + private static int CACHE_GENERATOR_DOTNET_DEFAULT_CULTURE = -1; + private static bool CACHE_GENERATOR_DOTNET_DEFAULT_CULTURE_IS_LOADED = false; + + public static async Task GetGeneratorDotnetDefaultCultureIndex() + { + // When possible, use the cache: + if (CACHE_GENERATOR_DOTNET_DEFAULT_CULTURE_IS_LOADED) + return CACHE_GENERATOR_DOTNET_DEFAULT_CULTURE; + + var generatorDotnetDefaultCulture = 0; + try + { + // Get the database: + await using var db = ProcessorMeta.ServiceProvider.GetRequiredService(); + + // Check, if the setting is already set: + if (await db.Settings.FirstOrDefaultAsync(n => n.Code == SettingNames.GENERATOR_DOTNET_DEFAULT_CULTURE) is { } existingSetting) + { + generatorDotnetDefaultCulture = existingSetting.IntegerValue; + return generatorDotnetDefaultCulture; + } + + // Does not exist, so create it: + var setting = new Setting + { + Code = SettingNames.GENERATOR_DOTNET_DEFAULT_CULTURE, + IntegerValue = generatorDotnetDefaultCulture, + }; + + await db.Settings.AddAsync(setting); + await db.SaveChangesAsync(); + return generatorDotnetDefaultCulture; + } + finally + { + CACHE_GENERATOR_DOTNET_DEFAULT_CULTURE_IS_LOADED = true; + CACHE_GENERATOR_DOTNET_DEFAULT_CULTURE = generatorDotnetDefaultCulture; + } + } + + public static async Task SetGeneratorDotnetDefaultCultureIndex(int updatedCulture) + { + // Update the cache: + CACHE_GENERATOR_DOTNET_DEFAULT_CULTURE = updatedCulture; + CACHE_GENERATOR_DOTNET_DEFAULT_CULTURE_IS_LOADED = true; + + // Get the database: + await using var db = ProcessorMeta.ServiceProvider.GetRequiredService(); + + // Check, if the setting is already set: + if (await db.Settings.FirstOrDefaultAsync(n => n.Code == SettingNames.GENERATOR_DOTNET_DEFAULT_CULTURE) is { } existingSetting) + { + existingSetting.IntegerValue = updatedCulture; + await db.SaveChangesAsync(); + } + + // Does not exist, so create it: + else + { + var setting = new Setting + { + Code = SettingNames.GENERATOR_DOTNET_DEFAULT_CULTURE, + IntegerValue = updatedCulture, + }; + + await db.Settings.AddAsync(setting); + await db.SaveChangesAsync(); + } + } + + #endregion + #region Godot Generator Enabled/Disabled private static bool CACHE_GENERATOR_GODOT_ENABLED = true; diff --git a/I18N Commander/Processor/DeepL.cs b/I18N Commander/Processor/DeepL.cs index 3e7d8fd..af3ffe5 100644 --- a/I18N Commander/Processor/DeepL.cs +++ b/I18N Commander/Processor/DeepL.cs @@ -28,7 +28,7 @@ public static class DeepL return new DeepLUsage(true, usage.Character!.Count, usage.Character.Limit); } - catch (AuthorizationException e) + catch (AuthorizationException) { DEEPL_NOT_AVAILABLE = true; return new DeepLUsage(false, 0, 1, true); @@ -63,7 +63,7 @@ public static class DeepL return translation.Text; } - catch (AuthorizationException e) + catch (AuthorizationException) { DEEPL_NOT_AVAILABLE = true; return string.Empty; diff --git a/I18N Commander/Processor/Generators/DotnetBigFile.cs b/I18N Commander/Processor/Generators/DotnetBigFile.cs new file mode 100644 index 0000000..15625fc --- /dev/null +++ b/I18N Commander/Processor/Generators/DotnetBigFile.cs @@ -0,0 +1,141 @@ +using System.Text; +using DataModel.Database; + +namespace Processor.Generators; + +public class DotnetBigFile : IGenerator +{ + private static readonly List CULTURE_CODES = new(); + private static int DEFAULT_CULTURE_INDEX = -1; + + public async Task GenerateAsync() + { + const string filename = "I18N.cs"; + + var destPath = await AppSettings.GetGeneratorDotnetDestinationPath(); + destPath = Environment.ExpandEnvironmentVariables(destPath); + + var pathFinal = Path.Join(destPath, filename); + var pathTemp = Path.Join(destPath, filename + ".gen"); + if(File.Exists(pathTemp)) + File.Delete(pathTemp); + + CULTURE_CODES.Clear(); + var cultures = await AppSettings.GetCultureInfos(); + foreach (var (code, _) in cultures) + CULTURE_CODES.Add(code); + + DEFAULT_CULTURE_INDEX = await AppSettings.GetGeneratorDotnetDefaultCultureIndex(); + DEFAULT_CULTURE_INDEX -= 1; // 1-based to 0-based + + try + { + await using var fileStream = new FileStream(pathTemp, FileMode.CreateNew, FileAccess.Write, FileShare.None); + await using var writer = new StreamWriter(fileStream, Encoding.UTF8); + + await writer.WriteLineAsync($"namespace {await AppSettings.GetGeneratorDotnetNamespace()};"); + await this.CreateStaticClass(writer, "I18N", 0, async (streamWriter, indention) => + { + var indentionString = this.AddIndention(indention); + var buildTime = DateTime.UtcNow; + await writer.WriteLineAsync($"{indentionString}public static readonly string BUILD_TIME = \"{buildTime:yyyy.MM.dd HH:mm:ss}\";"); + await writer.WriteLineAsync($"{indentionString}public static readonly long BUILD_TIME_TICKS = {buildTime.Ticks};"); + await writer.WriteLineAsync(); + await writer.WriteLineAsync($"{indentionString}private static int PREVIOUS_CULTURE = -1;"); + + // Go through the first layer of sections: + var sections = await SectionProcessor.LoadLayer(0); + foreach (var section in sections) + await this.TransformSection(writer, indention, section); + }); + } + finally + { + if(new FileInfo(pathTemp).Length > 0) + { + if(File.Exists(pathFinal)) + File.Delete(pathFinal); + + File.Move(pathTemp, pathFinal); + } + } + } + + private string AddIndention(int indention) => new string(' ', indention * 3); + + private async Task TransformSection(TextWriter writer, int indention, Section section) + { + await this.CreateStaticClass(writer, section.DataKey, indention, async (_, innerIndention) => + { + var textElements = section.TextElements; + foreach (var textElement in textElements) + await this.TransformTextElement(writer, innerIndention, textElement); + + var childSections = await SectionProcessor.GetChildSections(section.DataKey); + foreach (var childSection in childSections) + await this.TransformSection(writer, innerIndention, childSection); + }); + } + + private async Task TransformTextElement(TextWriter writer, int indention, TextElement textElement) + { + var indentionString = this.AddIndention(indention); + var indentionPropString = this.AddIndention(indention + 1); + var indentionPropInner1String = this.AddIndention(indention + 2); + var indentionPropInner2String = this.AddIndention(indention + 3); + var indentionPropInner3String = this.AddIndention(indention + 4); + + await writer.WriteLineAsync($"{indentionString}private static string E_{textElement.Code}_CACHE = \"\";"); + await writer.WriteLineAsync($"{indentionString}public static string E_{textElement.Code}"); + await writer.WriteLineAsync($"{indentionString}{{"); + await writer.WriteLineAsync($"{indentionPropString}get"); + await writer.WriteLineAsync($"{indentionPropString}{{"); + await writer.WriteLineAsync($"{indentionPropInner1String}var currentCulture = CultureInfo.CurrentCulture.Name;"); + await writer.WriteLineAsync($"{indentionPropInner1String}if(PREVIOUS_CULTURE == currentCulture.GetHashCode())"); + await writer.WriteLineAsync($"{indentionPropInner2String}return E_{textElement.Code}_CACHE;"); + await writer.WriteLineAsync($"{indentionPropInner1String}else"); + await writer.WriteLineAsync($"{indentionPropInner1String}{{"); + await writer.WriteLineAsync($"{indentionPropInner2String}PREVIOUS_CULTURE = currentCulture.GetHashCode();"); + for (var cultureIndex = 0; cultureIndex < CULTURE_CODES.Count; cultureIndex++) + { + if(cultureIndex == 0) + await writer.WriteLineAsync($"{indentionPropInner2String}if (currentCulture.StartsWith(\"{CULTURE_CODES[cultureIndex]}\", StringComparison.InvariantCultureIgnoreCase))"); + else + await writer.WriteLineAsync($"{indentionPropInner2String}else if (currentCulture.StartsWith(\"{CULTURE_CODES[cultureIndex]}\", StringComparison.InvariantCultureIgnoreCase))"); + + await writer.WriteLineAsync($"{indentionPropInner2String}{{"); + var cultureTranslation = textElement.Translations.FirstOrDefault(x => x.Culture == CULTURE_CODES[cultureIndex]); + var cultureText = cultureTranslation?.Text ?? string.Empty; + await writer.WriteLineAsync($"{indentionPropInner3String}var text = @\"{Utils.MadeVerbatimStringLiteral(cultureText)}\";"); + await writer.WriteLineAsync($"{indentionPropInner3String}E_{textElement.Code}_CACHE = text;"); + await writer.WriteLineAsync($"{indentionPropInner3String}return text;"); + await writer.WriteLineAsync($"{indentionPropInner2String}}}"); + } + + // Add the default case: + await writer.WriteLineAsync($"{indentionPropInner2String}else"); + await writer.WriteLineAsync($"{indentionPropInner2String}{{"); + var defaultCultureTranslation = textElement.Translations.FirstOrDefault(x => x.Culture == CULTURE_CODES[DEFAULT_CULTURE_INDEX]); + var defaultCultureText = defaultCultureTranslation?.Text ?? string.Empty; + await writer.WriteLineAsync($"{indentionPropInner3String}var text = @\"{Utils.MadeVerbatimStringLiteral(defaultCultureText)}\";"); + await writer.WriteLineAsync($"{indentionPropInner3String}E_{textElement.Code}_CACHE = text;"); + await writer.WriteLineAsync($"{indentionPropInner3String}return text;"); + await writer.WriteLineAsync($"{indentionPropInner2String}}}"); + + await writer.WriteLineAsync($"{indentionPropInner1String}}}"); + await writer.WriteLineAsync($"{indentionPropString}}}"); + await writer.WriteLineAsync($"{indentionString}}}"); + await writer.WriteLineAsync(); + } + + private async Task CreateStaticClass(TextWriter writer, string name, int indention, Func content) + { + var indentionString = this.AddIndention(indention); + + await writer.WriteLineAsync(indentionString); + await writer.WriteLineAsync($"{indentionString}public static class {name}"); + await writer.WriteLineAsync($"{indentionString}{{"); + await content(writer, indention + 1); + await writer.WriteLineAsync($"{indentionString}}}"); + } +} \ No newline at end of file diff --git a/I18N Commander/Processor/Generators/Generator.cs b/I18N Commander/Processor/Generators/Generator.cs new file mode 100644 index 0000000..045911a --- /dev/null +++ b/I18N Commander/Processor/Generators/Generator.cs @@ -0,0 +1,26 @@ +namespace Processor.Generators; + +public static class Generator +{ + private static readonly Dictionary GENERATORS = new(); + private static readonly IGenerator VOID_GENERATOR = new VoidGenerator(); + + public static IGenerator Get(Type genType) => genType switch + { + Type.DOTNET => GENERATORS.ContainsKey(genType) ? GENERATORS[genType] : GENERATORS[genType] = new DotnetBigFile(), + + _ => VOID_GENERATOR, + }; + + public static async Task TriggerAllAsync() + { + var dotnetEnabled = await AppSettings.GetGeneratorDotnetEnabled(); + var godotEnabled = await AppSettings.GetGeneratorGodotEnabled(); + + if (dotnetEnabled) + await Generator.Get(Type.DOTNET).GenerateAsync(); + + if(godotEnabled) + await Generator.Get(Type.GODOT).GenerateAsync(); + } +} \ No newline at end of file diff --git a/I18N Commander/Processor/Generators/IGenerator.cs b/I18N Commander/Processor/Generators/IGenerator.cs new file mode 100644 index 0000000..14f87cc --- /dev/null +++ b/I18N Commander/Processor/Generators/IGenerator.cs @@ -0,0 +1,6 @@ +namespace Processor.Generators; + +public interface IGenerator +{ + public Task GenerateAsync(); +} \ No newline at end of file diff --git a/I18N Commander/Processor/Generators/Type.cs b/I18N Commander/Processor/Generators/Type.cs new file mode 100644 index 0000000..dc1ef79 --- /dev/null +++ b/I18N Commander/Processor/Generators/Type.cs @@ -0,0 +1,9 @@ +namespace Processor.Generators; + +public enum Type +{ + NONE, + + DOTNET, + GODOT, +} \ No newline at end of file diff --git a/I18N Commander/Processor/Generators/VoidGenerator.cs b/I18N Commander/Processor/Generators/VoidGenerator.cs new file mode 100644 index 0000000..c3334fe --- /dev/null +++ b/I18N Commander/Processor/Generators/VoidGenerator.cs @@ -0,0 +1,6 @@ +namespace Processor.Generators; + +public class VoidGenerator : IGenerator +{ + public Task GenerateAsync() => Task.CompletedTask; +} \ No newline at end of file diff --git a/I18N Commander/Processor/SectionProcessor.cs b/I18N Commander/Processor/SectionProcessor.cs index f66275b..614fa48 100644 --- a/I18N Commander/Processor/SectionProcessor.cs +++ b/I18N Commander/Processor/SectionProcessor.cs @@ -198,4 +198,12 @@ public static class SectionProcessor return $"Section's path: {path}"; } + + public static async Task> GetChildSections(string sectionKey) + { + await using var db = ProcessorMeta.ServiceProvider.GetRequiredService(); + var section = await db.Sections.FirstAsync(n => n.DataKey == sectionKey); + + return await db.Sections.Where(n => n.Parent == section).ToListAsync(); + } } \ No newline at end of file diff --git a/I18N Commander/Processor/TextElementProcessor.cs b/I18N Commander/Processor/TextElementProcessor.cs index ae71b46..1924cab 100644 --- a/I18N Commander/Processor/TextElementProcessor.cs +++ b/I18N Commander/Processor/TextElementProcessor.cs @@ -124,7 +124,7 @@ public static class TextElementProcessor // Save the changes: await db.SaveChangesAsync(); } - catch (DbUpdateException updateException) + catch (DbUpdateException) { } } diff --git a/I18N Commander/Processor/Utils.cs b/I18N Commander/Processor/Utils.cs index 9c19df2..13a625d 100644 --- a/I18N Commander/Processor/Utils.cs +++ b/I18N Commander/Processor/Utils.cs @@ -1,4 +1,5 @@ using System.Linq.Expressions; +using System.Text; using Microsoft.EntityFrameworkCore; namespace Processor; @@ -46,4 +47,25 @@ internal static class Utils return code; } + + public static string MadeVerbatimStringLiteral(string text) + { + IEnumerable ConvertAll(string source) + { + foreach(var c in source) + if(c == '"') + { + yield return '"'; + yield return '"'; + } + else + yield return c; + } + + var sb = new StringBuilder(); + foreach(var c in ConvertAll(text)) + sb.Append(c); + + return sb.ToString(); + } } \ No newline at end of file diff --git a/I18N Commander/UI WinForms/Components/SectionTree.cs b/I18N Commander/UI WinForms/Components/SectionTree.cs index a62c2f7..c67f531 100644 --- a/I18N Commander/UI WinForms/Components/SectionTree.cs +++ b/I18N Commander/UI WinForms/Components/SectionTree.cs @@ -1,5 +1,6 @@ using DataModel.Database; using Processor; +using Processor.Generators; using UI_WinForms.Dialogs; using UI_WinForms.Resources; @@ -293,8 +294,10 @@ public partial class SectionTree : UserControl selectedNode.Name = alteredSection.Result.DataKey; // [sic] name is the key } - private void buttonGenerate_Click(object sender, EventArgs e) + private async void buttonGenerate_Click(object sender, EventArgs e) { - + this.buttonGenerate.Enabled = false; + await Generator.TriggerAllAsync(); + this.buttonGenerate.Enabled = true; } } \ No newline at end of file diff --git a/I18N Commander/UI WinForms/Components/Setting.cs b/I18N Commander/UI WinForms/Components/Setting.cs index a2be64f..c73f7a0 100644 --- a/I18N Commander/UI WinForms/Components/Setting.cs +++ b/I18N Commander/UI WinForms/Components/Setting.cs @@ -523,6 +523,82 @@ public sealed partial class Setting : UserControl return new Setting(settingData); } + private static async Task ShowGeneratorDotnetNamespaceSettingAsync() + { + var currentSetting = await AppSettings.GetGeneratorDotnetNamespace(); + + var settingData = new SettingUIData( + Icon: Icons.icons8_code_512, + SettingName: () => "Generator: .NET Namespace", + ChangeNeedsRestart: false, + SettingExplanation: () => "The namespace for the .NET I18N files.", + SettingExplanationLink: () => (string.Empty, string.Empty), + SetupDataControl: (changeTrigger) => + { + // Set up a textbox: + var textbox = new TextBox(); + textbox.Text = currentSetting; + textbox.TextChanged += async (sender, args) => await AppSettings.SetGeneratorDotnetNamespace(textbox.Text); + textbox.TextChanged += (sender, args) => changeTrigger(); + textbox.Dock = DockStyle.Fill; + textbox.Margin = new Padding(0, 13, 0, 13); + return textbox; + } + ); + + return new Setting(settingData); + } + + private static async Task ShowGeneratorDotnetDefaultCultureSettingAsync() + { + var currentSourceCultureIndex = await AppSettings.GetGeneratorDotnetDefaultCultureIndex(); + + // We load the corresponding culture for that index. As dropdown items, we show + // all other available cultures: + var allCultures = await AppSettings.GetCultureInfos(); + + // Attention: We have to store the culture's index, because the index is not + // continuous and can change when the user adds or removes a culture! + var settingData = new SettingUIData( + Icon: Icons.icons8_code_512, + SettingName: () => "Generator: .NET Default Culture", + ChangeNeedsRestart: false, + SettingExplanation: () => "The default culture for the .NET, which is used when no culture is specified or available.", + SettingExplanationLink: () => (string.Empty, string.Empty), + SetupDataControl: (changeTrigger) => + { + var dropdown = new ComboBox(); + var currentCultureDropdownIndex = 0; + for (var n = 0; n < allCultures.Count; n++) + { + var cultureInfo = allCultures[n]; + if(cultureInfo.Index == currentSourceCultureIndex) + currentCultureDropdownIndex = n; + + dropdown.Items.Add(new ComboBoxItem($"{cultureInfo.Index}.: {cultureInfo.Code}", cultureInfo.Index)); + } + + dropdown.SelectedIndex = currentCultureDropdownIndex; + + // Setup the change event handler: + dropdown.SelectedValueChanged += async (sender, args) => + { + if(dropdown.SelectedItem is ComboBoxItem selectedItem) + await AppSettings.SetGeneratorDotnetDefaultCultureIndex(selectedItem.CultureIndex); + }; + dropdown.SelectedValueChanged += (sender, args) => changeTrigger(); + + // Apply the desired layout: + dropdown.Dock = DockStyle.Fill; + dropdown.DropDownStyle = ComboBoxStyle.DropDownList; + + return dropdown; + } + ); + + return new Setting(settingData); + } + private static async Task ShowGeneratorGodotEnabledSettingAsync() { var currentSetting = await AppSettings.GetGeneratorGodotEnabled(); @@ -614,6 +690,8 @@ public sealed partial class Setting : UserControl { yield return ShowGeneratorGodotDestinationPathSettingAsync(); yield return ShowGeneratorGodotEnabledSettingAsync(); + yield return ShowGeneratorDotnetDefaultCultureSettingAsync(); + yield return ShowGeneratorDotnetNamespaceSettingAsync(); yield return ShowGeneratorDotnetDestinationPathSettingAsync(); yield return ShowGeneratorDotnetEnabledSettingAsync(); yield return ShowGeneratorModeSettingAsync(); From 838fbdf94e1d49c9609d2faf67e63fd3cd5a301f Mon Sep 17 00:00:00 2001 From: Thorsten Sommer Date: Sun, 30 Oct 2022 16:23:20 +0100 Subject: [PATCH 10/16] Made events nullable --- I18N Commander/UI WinForms/AppEvents.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/I18N Commander/UI WinForms/AppEvents.cs b/I18N Commander/UI WinForms/AppEvents.cs index dfa47aa..0e63300 100644 --- a/I18N Commander/UI WinForms/AppEvents.cs +++ b/I18N Commander/UI WinForms/AppEvents.cs @@ -6,7 +6,7 @@ internal static class AppEvents { #region Event: Settings were - internal static event EventHandler WhenSettingsChanged; + internal static event EventHandler? WhenSettingsChanged; internal static void SettingsChanged() => WhenSettingsChanged?.Invoke(null, EventArgs.Empty); @@ -15,7 +15,7 @@ internal static class AppEvents #region Event: Section was changed // Section changed event which can be subscribed: - internal static event EventHandler
WhenSectionChanged; + internal static event EventHandler
? WhenSectionChanged; // Method to raise the section changed event: internal static void SectionChanged(Section section) => WhenSectionChanged?.Invoke(null, section); @@ -25,7 +25,7 @@ internal static class AppEvents #region Event: Text element was changed // Text element changed event which can be subscribed: - internal static event EventHandler WhenTextElementChanged; + internal static event EventHandler? WhenTextElementChanged; // Method to raise the text element changed event: internal static void TextElementChanged(TextElement? textElement) => WhenTextElementChanged?.Invoke(null, textElement); @@ -35,7 +35,7 @@ internal static class AppEvents #region Translation was changed // Translation changed event which can be subscribed: - internal static event EventHandler WhenTranslationChanged; + internal static event EventHandler? WhenTranslationChanged; // Method to raise the translation changed event: internal static void TranslationChanged(Translation? translation) => WhenTranslationChanged?.Invoke(null, translation); From c77bf8bd96c5c1afeca5c44359f192c920d140ba Mon Sep 17 00:00:00 2001 From: Thorsten Sommer Date: Sun, 30 Oct 2022 16:23:59 +0100 Subject: [PATCH 11/16] Added not null operators --- I18N Commander/UI WinForms/Components/Setting.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/I18N Commander/UI WinForms/Components/Setting.cs b/I18N Commander/UI WinForms/Components/Setting.cs index c73f7a0..0d248b0 100644 --- a/I18N Commander/UI WinForms/Components/Setting.cs +++ b/I18N Commander/UI WinForms/Components/Setting.cs @@ -212,7 +212,7 @@ public sealed partial class Setting : UserControl }; // Update the explanation text. Therefore, we need to get the setting object through the chain of parents: - var setting = (Setting) ((Control) sender).Parent.Parent.Parent; + var setting = (Setting) (((Control) sender!)!).Parent.Parent.Parent; setting.UpdateExplanation(); }; From 274d3641eb8327253f99f4b7bd38823f54426424 Mon Sep 17 00:00:00 2001 From: Thorsten Sommer Date: Sun, 30 Oct 2022 16:24:22 +0100 Subject: [PATCH 12/16] Fixed nullable issues --- I18N Commander/UI WinForms/Components/Translation.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/I18N Commander/UI WinForms/Components/Translation.cs b/I18N Commander/UI WinForms/Components/Translation.cs index 80cee88..7f664cc 100644 --- a/I18N Commander/UI WinForms/Components/Translation.cs +++ b/I18N Commander/UI WinForms/Components/Translation.cs @@ -22,6 +22,9 @@ public sealed partial class Translation : UserControl { this.InitializeComponent(); this.Dock = DockStyle.Top; + + this.saveTimer = new Timer(); + this.translationTimer = new Timer(); } public Translation(AppSettings.CultureInfo cultureInfo) @@ -121,7 +124,7 @@ public sealed partial class Translation : UserControl } } - private async void SaveChanges(object? sender, ElapsedEventArgs e) + private async void SaveChanges(object? sender, ElapsedEventArgs? e) { if (this.currentTranslationId > -1) { @@ -135,7 +138,7 @@ public sealed partial class Translation : UserControl this.isManualOnlyMode = this.checkBoxManual.Checked; } - private async void TriggerTranslateNow(object? sender, ElapsedEventArgs e) + private async void TriggerTranslateNow(object? sender, ElapsedEventArgs? e) { if (this.currentTranslationId < 0 || this.isDeepLSourceCulture == false) return; From c48466d91e6d064e6e389d6c950770650f67ea58 Mon Sep 17 00:00:00 2001 From: Thorsten Sommer Date: Sun, 30 Oct 2022 16:25:36 +0100 Subject: [PATCH 13/16] Implemented automatic generators --- .../UI WinForms/Components/SectionTree.cs | 38 ++++++++++++++++++- 1 file changed, 36 insertions(+), 2 deletions(-) diff --git a/I18N Commander/UI WinForms/Components/SectionTree.cs b/I18N Commander/UI WinForms/Components/SectionTree.cs index c67f531..d0a8e99 100644 --- a/I18N Commander/UI WinForms/Components/SectionTree.cs +++ b/I18N Commander/UI WinForms/Components/SectionTree.cs @@ -3,12 +3,14 @@ using Processor; using Processor.Generators; using UI_WinForms.Dialogs; using UI_WinForms.Resources; +using Timer = System.Timers.Timer; namespace UI_WinForms.Components; public partial class SectionTree : UserControl { - private static readonly Dictionary> NODE_PROGRESS_HANDLERS = new(); + private static readonly Dictionary?> NODE_PROGRESS_HANDLERS = new(); + private readonly Timer generatorTimer; public SectionTree() { @@ -27,9 +29,23 @@ public partial class SectionTree : UserControl // Set the image list to the tree view: this.treeView.ImageList = imgList; + // The generator timer: + this.generatorTimer = new Timer + { + Enabled = false, // disable timer for now, + Interval = 6_000, // 6 second interval, + AutoReset = false, // runs only once + }; + + this.generatorTimer.Elapsed += async (sender, args) => await this.PerformGenerators(); + // Subscribe to the load event: this.Load += this.LoadNodes; this.Load += (sender, args) => this.SetupGeneratorButton(); + + // Subscribe to all event for triggering the generators: + AppEvents.WhenTranslationChanged += (sender, translation) => this.GeneratorEvent(); + AppEvents.WhenSettingsChanged += (sender, args) => this.GeneratorEvent(); } private async void SetupGeneratorButton() @@ -294,8 +310,26 @@ public partial class SectionTree : UserControl selectedNode.Name = alteredSection.Result.DataKey; // [sic] name is the key } - private async void buttonGenerate_Click(object sender, EventArgs e) + private async void buttonGenerate_Click(object sender, EventArgs e) => await this.PerformGenerators(); + + private async void GeneratorEvent() { + if(await AppSettings.GetGeneratorMode() == SettingGeneratorMode.MANUAL) + return; + + if (this.generatorTimer.Enabled) + this.generatorTimer.Stop(); + this.generatorTimer.Start(); + } + + private async Task PerformGenerators() + { + if (this.buttonGenerate.InvokeRequired) + { + await this.buttonGenerate.Invoke(this.PerformGenerators); + return; + } + this.buttonGenerate.Enabled = false; await Generator.TriggerAllAsync(); this.buttonGenerate.Enabled = true; From dfc0647fb0f33b860add8145b2998422f3eb37a1 Mon Sep 17 00:00:00 2001 From: Thorsten Sommer Date: Tue, 1 Nov 2022 12:26:31 +0100 Subject: [PATCH 14/16] Bugfix: Subscribers of app events must be reset when restarting --- I18N Commander/UI WinForms/AppEvents.cs | 10 +++++++++- I18N Commander/UI WinForms/Components/Main.cs | 1 + 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/I18N Commander/UI WinForms/AppEvents.cs b/I18N Commander/UI WinForms/AppEvents.cs index 0e63300..1145e8e 100644 --- a/I18N Commander/UI WinForms/AppEvents.cs +++ b/I18N Commander/UI WinForms/AppEvents.cs @@ -4,7 +4,15 @@ namespace UI_WinForms; internal static class AppEvents { - #region Event: Settings were + internal static void ResetAllSubscriptions() + { + WhenSettingsChanged = null; + WhenSectionChanged = null; + WhenTextElementChanged = null; + WhenTranslationChanged = null; + } + + #region Event: Settings were internal static event EventHandler? WhenSettingsChanged; diff --git a/I18N Commander/UI WinForms/Components/Main.cs b/I18N Commander/UI WinForms/Components/Main.cs index af58cec..b4e1950 100644 --- a/I18N Commander/UI WinForms/Components/Main.cs +++ b/I18N Commander/UI WinForms/Components/Main.cs @@ -16,6 +16,7 @@ public partial class Main : UserControl if(result == DialogResult.Yes) { Program.RestartMainApp = true; + AppEvents.ResetAllSubscriptions(); this.ParentForm!.Close(); } } From 53f98d4e3c8c406478e329683fccbabadc34ebc2 Mon Sep 17 00:00:00 2001 From: Thorsten Sommer Date: Tue, 1 Nov 2022 12:28:51 +0100 Subject: [PATCH 15/16] Refactored setting processing --- I18N Commander/Processor/AppSettings.cs | 934 +++++------------------- 1 file changed, 180 insertions(+), 754 deletions(-) diff --git a/I18N Commander/Processor/AppSettings.cs b/I18N Commander/Processor/AppSettings.cs index 20051b7..fc3e43f 100644 --- a/I18N Commander/Processor/AppSettings.cs +++ b/I18N Commander/Processor/AppSettings.cs @@ -7,312 +7,200 @@ namespace Processor; public static class AppSettings { + #region Common DB Code + + private static readonly Dictionary CACHES = new(); + private static readonly Dictionary CACHE_LOADED = new(); + + private static async Task GetSetting(string settingName, T defaultValue) + { + // When possible, use the cache: + if (CACHE_LOADED.ContainsKey(settingName) && CACHE_LOADED[settingName]) + return (T)CACHES[settingName]; + + var settingValue = defaultValue; + try + { + // Get the database: + await using var db = ProcessorMeta.ServiceProvider.GetRequiredService(); + + // Check, if the setting is already set: + if (await db.Settings.FirstOrDefaultAsync(n => n.Code == settingName) is { } existingSetting) + { + settingValue = settingValue switch + { + bool => (T)(object)existingSetting.BoolValue, + string => (T)(object)existingSetting.TextValue, + int => (T)(object)existingSetting.IntegerValue, + Guid => (T)(object)existingSetting.GuidValue, + + SettingDeepLMode or + SettingDeepLAction or + SettingGeneratorMode => (T)(object)existingSetting.IntegerValue, + + _ => defaultValue, + }; + return settingValue; + } + + // Does not exist, so create it: + var setting = new Setting + { + Code = settingName, + }; + + switch (settingValue) + { + case bool: + setting.BoolValue = (bool)(object)settingValue; + break; + + case string: + setting.TextValue = (string)(object)settingValue; + break; + + case int: + setting.IntegerValue = (int)(object)settingValue; + break; + + case Guid: + setting.GuidValue = (Guid)(object)settingValue; + break; + + case SettingDeepLMode: + case SettingDeepLAction: + case SettingGeneratorMode: + setting.IntegerValue = (int)(object)settingValue; + break; + } + + await db.Settings.AddAsync(setting); + await db.SaveChangesAsync(); + return settingValue; + } + finally + { + CACHE_LOADED[settingName] = true; + CACHES[settingName] = settingValue!; + } + } + + public static async Task SetSetting(string settingName, T settingValue) + { + // Update the cache: + CACHES[settingName] = settingValue!; + CACHE_LOADED[settingName] = true; + + // Get the database: + await using var db = ProcessorMeta.ServiceProvider.GetRequiredService(); + + // Check, if the setting is already set: + if (await db.Settings.FirstOrDefaultAsync(n => n.Code == settingName) is { } existingSetting) + { + switch (settingValue) + { + case bool: + existingSetting.BoolValue = (bool)(object)settingValue; + break; + + case string: + existingSetting.TextValue = (string)(object)settingValue; + break; + + case int: + existingSetting.IntegerValue = (int)(object)settingValue; + break; + + case Guid: + existingSetting.GuidValue = (Guid)(object)settingValue; + break; + + case SettingDeepLMode: + case SettingDeepLAction: + case SettingGeneratorMode: + existingSetting.IntegerValue = (int)(object)settingValue; + break; + } + await db.SaveChangesAsync(); + } + + // Does not exist, so create it: + else + { + var setting = new Setting + { + Code = SettingNames.GENERATOR_DOTNET_ENABLED, + }; + + switch (settingValue) + { + case bool: + setting.BoolValue = (bool)(object)settingValue; + break; + + case string: + setting.TextValue = (string)(object)settingValue; + break; + + case int: + setting.IntegerValue = (int)(object)settingValue; + break; + + case Guid: + setting.GuidValue = (Guid)(object)settingValue; + break; + + case SettingDeepLMode: + case SettingDeepLAction: + case SettingGeneratorMode: + setting.IntegerValue = (int)(object)settingValue; + break; + } + + await db.Settings.AddAsync(setting); + await db.SaveChangesAsync(); + } + } + + #endregion + #region DeepL Settings #region DeepL Mode - private static SettingDeepLMode CACHE_DEEPL_MODE = SettingDeepLMode.DISABLED; - private static bool CACHE_DEEPL_MODE_IS_LOADED = false; - - public static async Task SetDeepLMode(SettingDeepLMode mode) - { - // Convert the enum to its int value: - var intValue = (int)mode; - - // Update the cache: - CACHE_DEEPL_MODE = mode; - CACHE_DEEPL_MODE_IS_LOADED = true; - - // Get the database: - await using var db = ProcessorMeta.ServiceProvider.GetRequiredService(); - - // Check, if the setting is already set: - if (await db.Settings.FirstOrDefaultAsync(n => n.Code == SettingNames.DEEPL_MODE) is {} existingSetting) - { - existingSetting.IntegerValue = intValue; - await db.SaveChangesAsync(); - } - - // Does not exist, so create it: - else - { - var setting = new Setting - { - Code = SettingNames.DEEPL_MODE, - IntegerValue = intValue, - }; - - await db.Settings.AddAsync(setting); - await db.SaveChangesAsync(); - } - } + public static async Task SetDeepLMode(SettingDeepLMode mode) => await AppSettings.SetSetting(SettingNames.DEEPL_MODE, mode); - public static async Task GetDeepLMode() - { - if (CACHE_DEEPL_MODE_IS_LOADED) - return CACHE_DEEPL_MODE; - - var mode = SettingDeepLMode.DISABLED; - try - { - // Get the database: - await using var db = ProcessorMeta.ServiceProvider.GetRequiredService(); - - // Check, if the setting is already set: - if (await db.Settings.FirstOrDefaultAsync(n => n.Code == SettingNames.DEEPL_MODE) is { } existingSetting) - { - mode = (SettingDeepLMode) existingSetting.IntegerValue; - return mode; - } - - // Does not exist, so create it: - var setting = new Setting - { - Code = SettingNames.DEEPL_MODE, - IntegerValue = (int) mode, - }; - - await db.Settings.AddAsync(setting); - await db.SaveChangesAsync(); - return mode; - } - finally - { - CACHE_DEEPL_MODE = mode; - CACHE_DEEPL_MODE_IS_LOADED = true; - } - } + public static async Task GetDeepLMode() => await AppSettings.GetSetting(SettingNames.DEEPL_MODE, SettingDeepLMode.DISABLED); #endregion #region DeepL API Key - - private static string CACHE_DEEPL_API_KEY = string.Empty; - private static bool CACHE_DEEPL_API_KEY_IS_LOADED = false; - - public static async Task SetDeepLAPIKey(string apiKey) - { - // Update the cache: - CACHE_DEEPL_API_KEY = apiKey; - CACHE_DEEPL_API_KEY_IS_LOADED = true; - - // Get the database: - await using var db = ProcessorMeta.ServiceProvider.GetRequiredService(); - - // Check, if the setting is already set: - if (await db.Settings.FirstOrDefaultAsync(n => n.Code == SettingNames.DEEPL_API_KEY) is {} existingSetting) - { - existingSetting.TextValue = apiKey; - await db.SaveChangesAsync(); - } - - // Does not exist, so create it: - else - { - var setting = new Setting - { - Code = SettingNames.DEEPL_API_KEY, - TextValue = apiKey, - }; - - await db.Settings.AddAsync(setting); - await db.SaveChangesAsync(); - } - } - - public static async Task GetDeepLAPIKey() - { - // Check the cache: - if (CACHE_DEEPL_API_KEY_IS_LOADED) - return CACHE_DEEPL_API_KEY; - var key = string.Empty; - try - { - // Get the database: - await using var db = ProcessorMeta.ServiceProvider.GetRequiredService(); + public static async Task SetDeepLAPIKey(string apiKey) => await AppSettings.SetSetting(SettingNames.DEEPL_API_KEY, apiKey); - // Check, if the setting is already set: - if (await db.Settings.FirstOrDefaultAsync(n => n.Code == SettingNames.DEEPL_API_KEY) is { } existingSetting) - { - key = existingSetting.TextValue; - return key; - } + public static async Task GetDeepLAPIKey() => await AppSettings.GetSetting(SettingNames.DEEPL_API_KEY, string.Empty); - // Does not exist, so create it: - var setting = new Setting - { - Code = SettingNames.DEEPL_API_KEY, - TextValue = key, - }; - - await db.Settings.AddAsync(setting); - await db.SaveChangesAsync(); - return key; - } - finally - { - CACHE_DEEPL_API_KEY = key; - CACHE_DEEPL_API_KEY_IS_LOADED = true; - } - } - #endregion #region DeepL Action - - private static SettingDeepLAction CACHE_DEEPL_ACTION = SettingDeepLAction.MANUAL; - private static bool CACHE_DEEPL_ACTION_IS_LOADED = false; - public static async Task SetDeepLAction(SettingDeepLAction action) - { - // Convert the enum to its int value: - var intValue = (int)action; - - // Update the cache: - CACHE_DEEPL_ACTION = action; - CACHE_DEEPL_ACTION_IS_LOADED = true; - - // Get the database: - await using var db = ProcessorMeta.ServiceProvider.GetRequiredService(); - - // Check, if the setting is already set: - if (await db.Settings.FirstOrDefaultAsync(n => n.Code == SettingNames.DEEPL_ACTION) is {} existingSetting) - { - existingSetting.IntegerValue = intValue; - await db.SaveChangesAsync(); - } - - // Does not exist, so create it: - else - { - var setting = new Setting - { - Code = SettingNames.DEEPL_ACTION, - IntegerValue = intValue, - }; - - await db.Settings.AddAsync(setting); - await db.SaveChangesAsync(); - } - } - - public static async Task GetDeepLAction() - { - // Check the cache: - if (CACHE_DEEPL_ACTION_IS_LOADED) - return CACHE_DEEPL_ACTION; - - var action = SettingDeepLAction.MANUAL; - try - { - // Get the database: - await using var db = ProcessorMeta.ServiceProvider.GetRequiredService(); + public static async Task SetDeepLAction(SettingDeepLAction action) => await AppSettings.SetSetting(SettingNames.DEEPL_ACTION, action); - // Check, if the setting is already set: - if (await db.Settings.FirstOrDefaultAsync(n => n.Code == SettingNames.DEEPL_ACTION) is { } existingSetting) - { - action = (SettingDeepLAction) existingSetting.IntegerValue; - return action; - } + public static async Task GetDeepLAction() => await AppSettings.GetSetting(SettingNames.DEEPL_ACTION, SettingDeepLAction.MANUAL); - // Does not exist, so create it: - var setting = new Setting - { - Code = SettingNames.DEEPL_ACTION, - IntegerValue = (int) action, - }; - - await db.Settings.AddAsync(setting); - await db.SaveChangesAsync(); - return action; - } - finally - { - CACHE_DEEPL_ACTION = action; - CACHE_DEEPL_ACTION_IS_LOADED = true; - } - } - #endregion #region DeepL Source Culture - private static int CACHE_DEEPL_SOURCE_CULTURE = -1; - private static bool CACHE_DEEPL_SOURCE_CULTURE_IS_LOADED = false; - - public static async Task SetDeepLSourceCultureIndex(int cultureIndex) - { - // Update the cache: - CACHE_DEEPL_SOURCE_CULTURE = cultureIndex; - CACHE_DEEPL_SOURCE_CULTURE_IS_LOADED = true; - - // Get the database: - await using var db = ProcessorMeta.ServiceProvider.GetRequiredService(); - - // Check, if the setting is already set: - if (await db.Settings.FirstOrDefaultAsync(n => n.Code == SettingNames.DEEPL_SOURCE_CULTURE) is {} existingSetting) - { - existingSetting.IntegerValue = cultureIndex; - await db.SaveChangesAsync(); - } - - // Does not exist, so create it: - else - { - var setting = new Setting - { - Code = SettingNames.DEEPL_SOURCE_CULTURE, - IntegerValue = cultureIndex, - }; - - await db.Settings.AddAsync(setting); - await db.SaveChangesAsync(); - } - } - - public static async Task GetDeepLSourceCultureIndex() - { - // Check the cache: - if (CACHE_DEEPL_SOURCE_CULTURE_IS_LOADED) - return CACHE_DEEPL_SOURCE_CULTURE; - - var cultureIndex = -1; - try - { - // Get the database: - await using var db = ProcessorMeta.ServiceProvider.GetRequiredService(); + public static async Task SetDeepLSourceCultureIndex(int cultureIndex) => await AppSettings.SetSetting(SettingNames.DEEPL_SOURCE_CULTURE, cultureIndex); - // Check, if the setting is already set: - if (await db.Settings.FirstOrDefaultAsync(n => n.Code == SettingNames.DEEPL_SOURCE_CULTURE) is { } existingSetting) - { - cultureIndex = existingSetting.IntegerValue; - return cultureIndex; - } + public static async Task GetDeepLSourceCultureIndex() => await AppSettings.GetSetting(SettingNames.DEEPL_SOURCE_CULTURE, -1); - // Does not exist, so create it: - var setting = new Setting - { - Code = SettingNames.DEEPL_SOURCE_CULTURE, - IntegerValue = cultureIndex, - }; - - await db.Settings.AddAsync(setting); - await db.SaveChangesAsync(); - return cultureIndex; - } - finally - { - CACHE_DEEPL_SOURCE_CULTURE = cultureIndex; - CACHE_DEEPL_SOURCE_CULTURE_IS_LOADED = true; - } - } - #endregion #endregion - #region Translation Settings + #region Culture Settings #region List of culture indices @@ -465,519 +353,57 @@ public static class AppSettings #region Generator Mode - private static SettingGeneratorMode CACHE_GENERATOR_MODE = SettingGeneratorMode.MANUAL; - private static bool CACHE_GENERATOR_MODE_IS_LOADED = false; + public static async Task GetGeneratorMode() => await AppSettings.GetSetting(SettingNames.GENERATOR_MODE, SettingGeneratorMode.MANUAL); - public static async Task GetGeneratorMode() - { - // When possible, use the cache: - if (CACHE_GENERATOR_MODE_IS_LOADED) - return CACHE_GENERATOR_MODE; + public static async Task SetGeneratorMode(SettingGeneratorMode mode) => await AppSettings.SetSetting(SettingNames.GENERATOR_MODE, mode); - var generatorMode = SettingGeneratorMode.MANUAL; - try - { - // Get the database: - await using var db = ProcessorMeta.ServiceProvider.GetRequiredService(); - - // Check, if the setting is already set: - if (await db.Settings.FirstOrDefaultAsync(n => n.Code == SettingNames.GENERATOR_MODE) is { } existingSetting) - { - generatorMode = (SettingGeneratorMode) existingSetting.IntegerValue; - return generatorMode; - } - - // Does not exist, so create it: - var setting = new Setting - { - Code = SettingNames.GENERATOR_MODE, - IntegerValue = (int) generatorMode, - }; - - await db.Settings.AddAsync(setting); - await db.SaveChangesAsync(); - return generatorMode; - } - finally - { - CACHE_GENERATOR_MODE_IS_LOADED = true; - CACHE_GENERATOR_MODE = generatorMode; - } - } - - public static async Task SetGeneratorMode(SettingGeneratorMode mode) - { - // Update the cache: - CACHE_GENERATOR_MODE = mode; - CACHE_GENERATOR_MODE_IS_LOADED = true; - - // Get the database: - await using var db = ProcessorMeta.ServiceProvider.GetRequiredService(); - - // Check, if the setting is already set: - if (await db.Settings.FirstOrDefaultAsync(n => n.Code == SettingNames.GENERATOR_MODE) is { } existingSetting) - { - existingSetting.IntegerValue = (int) mode; - await db.SaveChangesAsync(); - } - - // Does not exist, so create it: - else - { - var setting = new Setting - { - Code = SettingNames.GENERATOR_MODE, - IntegerValue = (int) mode, - }; - - await db.Settings.AddAsync(setting); - await db.SaveChangesAsync(); - } - } - #endregion #region .NET Generator Enabled/Disabled - private static bool CACHE_GENERATOR_DOTNET_ENABLED = true; - private static bool CACHE_GENERATOR_DOTNET_ENABLED_IS_LOADED = false; - - public static async Task GetGeneratorDotnetEnabled() - { - // When possible, use the cache: - if (CACHE_GENERATOR_DOTNET_ENABLED_IS_LOADED) - return CACHE_GENERATOR_DOTNET_ENABLED; + public static async Task GetGeneratorDotnetEnabled() => await AppSettings.GetSetting(SettingNames.GENERATOR_DOTNET_ENABLED, false); - var generatorDotnetEnabled = false; - try - { - // Get the database: - await using var db = ProcessorMeta.ServiceProvider.GetRequiredService(); - - // Check, if the setting is already set: - if (await db.Settings.FirstOrDefaultAsync(n => n.Code == SettingNames.GENERATOR_DOTNET_ENABLED) is { } existingSetting) - { - generatorDotnetEnabled = existingSetting.BoolValue; - return generatorDotnetEnabled; - } - - // Does not exist, so create it: - var setting = new Setting - { - Code = SettingNames.GENERATOR_DOTNET_ENABLED, - BoolValue = generatorDotnetEnabled, - }; - - await db.Settings.AddAsync(setting); - await db.SaveChangesAsync(); - return generatorDotnetEnabled; - } - finally - { - CACHE_GENERATOR_DOTNET_ENABLED_IS_LOADED = true; - CACHE_GENERATOR_DOTNET_ENABLED = generatorDotnetEnabled; - } - } - - public static async Task SetGeneratorDotnetEnabled(bool enabled) - { - // Update the cache: - CACHE_GENERATOR_DOTNET_ENABLED = enabled; - CACHE_GENERATOR_DOTNET_ENABLED_IS_LOADED = true; - - // Get the database: - await using var db = ProcessorMeta.ServiceProvider.GetRequiredService(); - - // Check, if the setting is already set: - if (await db.Settings.FirstOrDefaultAsync(n => n.Code == SettingNames.GENERATOR_DOTNET_ENABLED) is { } existingSetting) - { - existingSetting.BoolValue = enabled; - await db.SaveChangesAsync(); - } - - // Does not exist, so create it: - else - { - var setting = new Setting - { - Code = SettingNames.GENERATOR_DOTNET_ENABLED, - BoolValue = enabled, - }; - - await db.Settings.AddAsync(setting); - await db.SaveChangesAsync(); - } - } + public static async Task SetGeneratorDotnetEnabled(bool enabled) => await AppSettings.SetSetting(SettingNames.GENERATOR_DOTNET_ENABLED, enabled); #endregion #region .NET Generator Destination Path - - private static string CACHE_GENERATOR_DOTNET_DESTINATION_PATH = string.Empty; - private static bool CACHE_GENERATOR_DOTNET_DESTINATION_PATH_IS_LOADED = false; - - public static async Task GetGeneratorDotnetDestinationPath() - { - // When possible, use the cache: - if (CACHE_GENERATOR_DOTNET_DESTINATION_PATH_IS_LOADED) - return CACHE_GENERATOR_DOTNET_DESTINATION_PATH; - var generatorDotnetDestinationPath = string.Empty; - try - { - // Get the database: - await using var db = ProcessorMeta.ServiceProvider.GetRequiredService(); + public static async Task GetGeneratorDotnetDestinationPath() => await AppSettings.GetSetting(SettingNames.GENERATOR_DOTNET_DESTINATION_PATH, string.Empty); - // Check, if the setting is already set: - if (await db.Settings.FirstOrDefaultAsync(n => n.Code == SettingNames.GENERATOR_DOTNET_DESTINATION_PATH) is { } existingSetting) - { - generatorDotnetDestinationPath = existingSetting.TextValue; - return generatorDotnetDestinationPath; - } + public static async Task SetGeneratorDotnetDestinationPath(string path) => await AppSettings.SetSetting(SettingNames.GENERATOR_DOTNET_DESTINATION_PATH, path); - // Does not exist, so create it: - var setting = new Setting - { - Code = SettingNames.GENERATOR_DOTNET_DESTINATION_PATH, - TextValue = generatorDotnetDestinationPath, - }; - - await db.Settings.AddAsync(setting); - await db.SaveChangesAsync(); - return generatorDotnetDestinationPath; - } - finally - { - CACHE_GENERATOR_DOTNET_DESTINATION_PATH_IS_LOADED = true; - CACHE_GENERATOR_DOTNET_DESTINATION_PATH = generatorDotnetDestinationPath; - } - } - - public static async Task SetGeneratorDotnetDestinationPath(string path) - { - // Update the cache: - CACHE_GENERATOR_DOTNET_DESTINATION_PATH = path; - CACHE_GENERATOR_DOTNET_DESTINATION_PATH_IS_LOADED = true; - - // Get the database: - await using var db = ProcessorMeta.ServiceProvider.GetRequiredService(); - - // Check, if the setting is already set: - if (await db.Settings.FirstOrDefaultAsync(n => n.Code == SettingNames.GENERATOR_DOTNET_DESTINATION_PATH) is { } existingSetting) - { - existingSetting.TextValue = path; - await db.SaveChangesAsync(); - } - - // Does not exist, so create it: - else - { - var setting = new Setting - { - Code = SettingNames.GENERATOR_DOTNET_DESTINATION_PATH, - TextValue = path, - }; - - await db.Settings.AddAsync(setting); - await db.SaveChangesAsync(); - } - } - #endregion #region .NET Generator Namespace - - private static string CACHE_GENERATOR_DOTNET_NAMESPACE = string.Empty; - private static bool CACHE_GENERATOR_DOTNET_NAMESPACE_IS_LOADED = false; - - public static async Task GetGeneratorDotnetNamespace() - { - // When possible, use the cache: - if (CACHE_GENERATOR_DOTNET_NAMESPACE_IS_LOADED) - return CACHE_GENERATOR_DOTNET_NAMESPACE; - var generatorDotnetNamespace = "I18N"; - try - { - // Get the database: - await using var db = ProcessorMeta.ServiceProvider.GetRequiredService(); + public static async Task GetGeneratorDotnetNamespace() => await AppSettings.GetSetting(SettingNames.GENERATOR_DOTNET_NAMESPACE, "I18N"); - // Check, if the setting is already set: - if (await db.Settings.FirstOrDefaultAsync(n => n.Code == SettingNames.GENERATOR_DOTNET_NAMESPACE) is { } existingSetting) - { - generatorDotnetNamespace = existingSetting.TextValue; - return generatorDotnetNamespace; - } - - // Does not exist, so create it: - var setting = new Setting - { - Code = SettingNames.GENERATOR_DOTNET_NAMESPACE, - TextValue = generatorDotnetNamespace, - }; - - await db.Settings.AddAsync(setting); - await db.SaveChangesAsync(); - return generatorDotnetNamespace; - } - finally - { - CACHE_GENERATOR_DOTNET_NAMESPACE_IS_LOADED = true; - CACHE_GENERATOR_DOTNET_NAMESPACE = generatorDotnetNamespace; - } - } - - public static async Task SetGeneratorDotnetNamespace(string updatedNamespace) - { - // Update the cache: - CACHE_GENERATOR_DOTNET_NAMESPACE = updatedNamespace; - CACHE_GENERATOR_DOTNET_NAMESPACE_IS_LOADED = true; - - // Get the database: - await using var db = ProcessorMeta.ServiceProvider.GetRequiredService(); - - // Check, if the setting is already set: - if (await db.Settings.FirstOrDefaultAsync(n => n.Code == SettingNames.GENERATOR_DOTNET_NAMESPACE) is { } existingSetting) - { - existingSetting.TextValue = updatedNamespace; - await db.SaveChangesAsync(); - } - - // Does not exist, so create it: - else - { - var setting = new Setting - { - Code = SettingNames.GENERATOR_DOTNET_NAMESPACE, - TextValue = updatedNamespace, - }; - - await db.Settings.AddAsync(setting); - await db.SaveChangesAsync(); - } - } + public static async Task SetGeneratorDotnetNamespace(string updatedNamespace) => await AppSettings.SetSetting(SettingNames.GENERATOR_DOTNET_NAMESPACE, updatedNamespace); #endregion #region .NET Generator Default Culture - private static int CACHE_GENERATOR_DOTNET_DEFAULT_CULTURE = -1; - private static bool CACHE_GENERATOR_DOTNET_DEFAULT_CULTURE_IS_LOADED = false; - - public static async Task GetGeneratorDotnetDefaultCultureIndex() - { - // When possible, use the cache: - if (CACHE_GENERATOR_DOTNET_DEFAULT_CULTURE_IS_LOADED) - return CACHE_GENERATOR_DOTNET_DEFAULT_CULTURE; + public static async Task GetGeneratorDotnetDefaultCultureIndex() => await AppSettings.GetSetting(SettingNames.GENERATOR_DOTNET_DEFAULT_CULTURE, 0); - var generatorDotnetDefaultCulture = 0; - try - { - // Get the database: - await using var db = ProcessorMeta.ServiceProvider.GetRequiredService(); + public static async Task SetGeneratorDotnetDefaultCultureIndex(int updatedCulture) => await AppSettings.SetSetting(SettingNames.GENERATOR_DOTNET_DEFAULT_CULTURE, updatedCulture); - // Check, if the setting is already set: - if (await db.Settings.FirstOrDefaultAsync(n => n.Code == SettingNames.GENERATOR_DOTNET_DEFAULT_CULTURE) is { } existingSetting) - { - generatorDotnetDefaultCulture = existingSetting.IntegerValue; - return generatorDotnetDefaultCulture; - } - - // Does not exist, so create it: - var setting = new Setting - { - Code = SettingNames.GENERATOR_DOTNET_DEFAULT_CULTURE, - IntegerValue = generatorDotnetDefaultCulture, - }; - - await db.Settings.AddAsync(setting); - await db.SaveChangesAsync(); - return generatorDotnetDefaultCulture; - } - finally - { - CACHE_GENERATOR_DOTNET_DEFAULT_CULTURE_IS_LOADED = true; - CACHE_GENERATOR_DOTNET_DEFAULT_CULTURE = generatorDotnetDefaultCulture; - } - } - - public static async Task SetGeneratorDotnetDefaultCultureIndex(int updatedCulture) - { - // Update the cache: - CACHE_GENERATOR_DOTNET_DEFAULT_CULTURE = updatedCulture; - CACHE_GENERATOR_DOTNET_DEFAULT_CULTURE_IS_LOADED = true; - - // Get the database: - await using var db = ProcessorMeta.ServiceProvider.GetRequiredService(); - - // Check, if the setting is already set: - if (await db.Settings.FirstOrDefaultAsync(n => n.Code == SettingNames.GENERATOR_DOTNET_DEFAULT_CULTURE) is { } existingSetting) - { - existingSetting.IntegerValue = updatedCulture; - await db.SaveChangesAsync(); - } - - // Does not exist, so create it: - else - { - var setting = new Setting - { - Code = SettingNames.GENERATOR_DOTNET_DEFAULT_CULTURE, - IntegerValue = updatedCulture, - }; - - await db.Settings.AddAsync(setting); - await db.SaveChangesAsync(); - } - } - #endregion #region Godot Generator Enabled/Disabled - private static bool CACHE_GENERATOR_GODOT_ENABLED = true; - private static bool CACHE_GENERATOR_GODOT_ENABLED_IS_LOADED = false; - - public static async Task GetGeneratorGodotEnabled() - { - // When possible, use the cache: - if (CACHE_GENERATOR_GODOT_ENABLED_IS_LOADED) - return CACHE_GENERATOR_GODOT_ENABLED; + public static async Task GetGeneratorGodotEnabled() => await AppSettings.GetSetting(SettingNames.GENERATOR_GODOT_ENABLED, false); - var generatorGodotEnabled = false; - try - { - // Get the database: - await using var db = ProcessorMeta.ServiceProvider.GetRequiredService(); - - // Check, if the setting is already set: - if (await db.Settings.FirstOrDefaultAsync(n => n.Code == SettingNames.GENERATOR_GODOT_ENABLED) is { } existingSetting) - { - generatorGodotEnabled = existingSetting.BoolValue; - return generatorGodotEnabled; - } - - // Does not exist, so create it: - var setting = new Setting - { - Code = SettingNames.GENERATOR_GODOT_ENABLED, - BoolValue = generatorGodotEnabled, - }; - - await db.Settings.AddAsync(setting); - await db.SaveChangesAsync(); - return generatorGodotEnabled; - } - finally - { - CACHE_GENERATOR_GODOT_ENABLED_IS_LOADED = true; - CACHE_GENERATOR_GODOT_ENABLED = generatorGodotEnabled; - } - } - - public static async Task SetGeneratorGodotEnabled(bool enabled) - { - // Update the cache: - CACHE_GENERATOR_GODOT_ENABLED = enabled; - CACHE_GENERATOR_GODOT_ENABLED_IS_LOADED = true; - - // Get the database: - await using var db = ProcessorMeta.ServiceProvider.GetRequiredService(); - - // Check, if the setting is already set: - if (await db.Settings.FirstOrDefaultAsync(n => n.Code == SettingNames.GENERATOR_GODOT_ENABLED) is { } existingSetting) - { - existingSetting.BoolValue = enabled; - await db.SaveChangesAsync(); - } - - // Does not exist, so create it: - else - { - var setting = new Setting - { - Code = SettingNames.GENERATOR_GODOT_ENABLED, - BoolValue = enabled, - }; - - await db.Settings.AddAsync(setting); - await db.SaveChangesAsync(); - } - } + public static async Task SetGeneratorGodotEnabled(bool enabled) => await AppSettings.SetSetting(SettingNames.GENERATOR_GODOT_ENABLED, enabled); #endregion #region Godot Generator Destination Path - private static string CACHE_GENERATOR_GODOT_DESTINATION_PATH = string.Empty; - private static bool CACHE_GENERATOR_GODOT_DESTINATION_PATH_IS_LOADED = false; - - public static async Task GetGeneratorGodotDestinationPath() - { - // When possible, use the cache: - if (CACHE_GENERATOR_GODOT_DESTINATION_PATH_IS_LOADED) - return CACHE_GENERATOR_GODOT_DESTINATION_PATH; + public static async Task GetGeneratorGodotDestinationPath() => await AppSettings.GetSetting(SettingNames.GENERATOR_GODOT_DESTINATION_PATH, string.Empty); - var generatorGodotDestinationPath = string.Empty; - try - { - // Get the database: - await using var db = ProcessorMeta.ServiceProvider.GetRequiredService(); - - // Check, if the setting is already set: - if (await db.Settings.FirstOrDefaultAsync(n => n.Code == SettingNames.GENERATOR_GODOT_DESTINATION_PATH) is { } existingSetting) - { - generatorGodotDestinationPath = existingSetting.TextValue; - return generatorGodotDestinationPath; - } - - // Does not exist, so create it: - var setting = new Setting - { - Code = SettingNames.GENERATOR_GODOT_DESTINATION_PATH, - TextValue = generatorGodotDestinationPath, - }; - - await db.Settings.AddAsync(setting); - await db.SaveChangesAsync(); - return generatorGodotDestinationPath; - } - finally - { - CACHE_GENERATOR_GODOT_DESTINATION_PATH_IS_LOADED = true; - CACHE_GENERATOR_GODOT_DESTINATION_PATH = generatorGodotDestinationPath; - } - } - - public static async Task SetGeneratorGodotDestinationPath(string path) - { - // Update the cache: - CACHE_GENERATOR_GODOT_DESTINATION_PATH = path; - CACHE_GENERATOR_GODOT_DESTINATION_PATH_IS_LOADED = true; - - // Get the database: - await using var db = ProcessorMeta.ServiceProvider.GetRequiredService(); - - // Check, if the setting is already set: - if (await db.Settings.FirstOrDefaultAsync(n => n.Code == SettingNames.GENERATOR_GODOT_DESTINATION_PATH) is { } existingSetting) - { - existingSetting.TextValue = path; - await db.SaveChangesAsync(); - } - - // Does not exist, so create it: - else - { - var setting = new Setting - { - Code = SettingNames.GENERATOR_GODOT_DESTINATION_PATH, - TextValue = path, - }; - - await db.Settings.AddAsync(setting); - await db.SaveChangesAsync(); - } - } + public static async Task SetGeneratorGodotDestinationPath(string path) => await AppSettings.SetSetting(SettingNames.GENERATOR_GODOT_DESTINATION_PATH, path); #endregion From a19ad7dcdb45214c9fdacc6c051c2b945d61d248 Mon Sep 17 00:00:00 2001 From: Thorsten Sommer Date: Tue, 1 Nov 2022 19:24:27 +0100 Subject: [PATCH 16/16] Enhanced IO issue handling when generating output --- .../Processor/Generators/DotnetBigFile.cs | 50 ++++++++++++++++--- .../Processor/Generators/Generator.cs | 23 +++++++-- .../Processor/Generators/IGenerator.cs | 2 +- .../UI WinForms/Components/SectionTree.cs | 4 +- I18N Commander/UI WinForms/ExtensionsError.cs | 3 +- 5 files changed, 67 insertions(+), 15 deletions(-) diff --git a/I18N Commander/Processor/Generators/DotnetBigFile.cs b/I18N Commander/Processor/Generators/DotnetBigFile.cs index 15625fc..7b20abd 100644 --- a/I18N Commander/Processor/Generators/DotnetBigFile.cs +++ b/I18N Commander/Processor/Generators/DotnetBigFile.cs @@ -8,17 +8,27 @@ public class DotnetBigFile : IGenerator private static readonly List CULTURE_CODES = new(); private static int DEFAULT_CULTURE_INDEX = -1; - public async Task GenerateAsync() + public async Task> GenerateAsync() { const string filename = "I18N.cs"; var destPath = await AppSettings.GetGeneratorDotnetDestinationPath(); destPath = Environment.ExpandEnvironmentVariables(destPath); + long destBytesWritten = 0; var pathFinal = Path.Join(destPath, filename); var pathTemp = Path.Join(destPath, filename + ".gen"); - if(File.Exists(pathTemp)) - File.Delete(pathTemp); + var issueFinal = string.Empty; + + try + { + if(File.Exists(pathTemp)) + File.Delete(pathTemp); + } + catch (IOException e) + { + return new ProcessorResult(0, false, $"Cannot delete the temporary file: '{e.Message}'. Hint: Is the ransomware protection enabled in your Windows system? If so, please make sure that the I18N Commander has write permission."); + } CULTURE_CODES.Clear(); var cultures = await AppSettings.GetCultureInfos(); @@ -49,16 +59,40 @@ public class DotnetBigFile : IGenerator await this.TransformSection(writer, indention, section); }); } + catch (IOException e) + { + // Happens, e.g. when the ransomware protection on Windows is active and + // the I18N commander is not on the exclusion list. + return new ProcessorResult(0, false, $"Cannot write the generator's result file: '{e.Message}'. Hint: Is the ransomware protection enabled in your Windows system? If so, please make sure that the I18N Commander has write permission."); + } finally { - if(new FileInfo(pathTemp).Length > 0) + if (File.Exists(pathTemp)) { - if(File.Exists(pathFinal)) - File.Delete(pathFinal); - - File.Move(pathTemp, pathFinal); + destBytesWritten = new FileInfo(pathTemp).Length; + if (destBytesWritten > 0) + { + try + { + if (File.Exists(pathFinal)) + File.Delete(pathFinal); + + File.Move(pathTemp, pathFinal); + } + catch (IOException e) + { + // Happens when the file is still in use by the compiler, the IDE, etc. + // Depends on the timing, this happens sometimes. We ignore it, though. + issueFinal = e.Message; + } + } } } + + if(string.IsNullOrWhiteSpace(issueFinal)) + return new ProcessorResult(destBytesWritten, true, string.Empty); + else + return new ProcessorResult(0, false, $"Cannot move the generator's result file to the destination: '{issueFinal}'. Hint: Is the ransomware protection enabled in your Windows system? If so, please make sure that the I18N Commander has write permission."); } private string AddIndention(int indention) => new string(' ', indention * 3); diff --git a/I18N Commander/Processor/Generators/Generator.cs b/I18N Commander/Processor/Generators/Generator.cs index 045911a..29ed588 100644 --- a/I18N Commander/Processor/Generators/Generator.cs +++ b/I18N Commander/Processor/Generators/Generator.cs @@ -12,15 +12,30 @@ public static class Generator _ => VOID_GENERATOR, }; - public static async Task TriggerAllAsync() + public static async Task> TriggerAllAsync() { var dotnetEnabled = await AppSettings.GetGeneratorDotnetEnabled(); var godotEnabled = await AppSettings.GetGeneratorGodotEnabled(); + long bytesWritten = 0; if (dotnetEnabled) - await Generator.Get(Type.DOTNET).GenerateAsync(); - + { + var result = await Generator.Get(Type.DOTNET).GenerateAsync(); + if(!result.Successful) + return result; + + bytesWritten += result.Result; + } + if(godotEnabled) - await Generator.Get(Type.GODOT).GenerateAsync(); + { + var result = await Generator.Get(Type.GODOT).GenerateAsync(); + if(!result.Successful) + return result; + + bytesWritten += result.Result; + } + + return new ProcessorResult(bytesWritten); } } \ No newline at end of file diff --git a/I18N Commander/Processor/Generators/IGenerator.cs b/I18N Commander/Processor/Generators/IGenerator.cs index 14f87cc..39ded9e 100644 --- a/I18N Commander/Processor/Generators/IGenerator.cs +++ b/I18N Commander/Processor/Generators/IGenerator.cs @@ -2,5 +2,5 @@ public interface IGenerator { - public Task GenerateAsync(); + public Task> GenerateAsync(); } \ No newline at end of file diff --git a/I18N Commander/UI WinForms/Components/SectionTree.cs b/I18N Commander/UI WinForms/Components/SectionTree.cs index d0a8e99..878fe74 100644 --- a/I18N Commander/UI WinForms/Components/SectionTree.cs +++ b/I18N Commander/UI WinForms/Components/SectionTree.cs @@ -331,7 +331,9 @@ public partial class SectionTree : UserControl } this.buttonGenerate.Enabled = false; - await Generator.TriggerAllAsync(); + var result = await Generator.TriggerAllAsync(); + result.ProcessError(); + this.buttonGenerate.Enabled = true; } } \ No newline at end of file diff --git a/I18N Commander/UI WinForms/ExtensionsError.cs b/I18N Commander/UI WinForms/ExtensionsError.cs index e21b8ee..f265e66 100644 --- a/I18N Commander/UI WinForms/ExtensionsError.cs +++ b/I18N Commander/UI WinForms/ExtensionsError.cs @@ -6,7 +6,8 @@ public static class ExtensionsError { public static void ProcessError(this ProcessorResult result) { - if (result.Successful) return; + if (result.Successful) + return; MessageBox.Show(result.ErrorMessage, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); }