From ffd92d22595684edb298e5796cf844506e39c8a1 Mon Sep 17 00:00:00 2001 From: Stone_Red <56473591+Stone-Red-Code@users.noreply.github.com> Date: Sun, 23 Feb 2025 15:32:56 +0100 Subject: [PATCH] Add create new plugin button to plugin manager --- src/DesktopMagic/Helpers/FileUtilities.cs | 93 +++++++++++++ src/DesktopMagic/Plugins/PluginManager.xaml | 10 +- .../Plugins/PluginManager.xaml.cs | 126 ++++++++++++++++++ .../Resources/Strings/StringResources.de.xaml | 2 + .../Resources/Strings/StringResources.en.xaml | 2 + 5 files changed, 232 insertions(+), 1 deletion(-) create mode 100644 src/DesktopMagic/Helpers/FileUtilities.cs diff --git a/src/DesktopMagic/Helpers/FileUtilities.cs b/src/DesktopMagic/Helpers/FileUtilities.cs new file mode 100644 index 0000000..0428fbc --- /dev/null +++ b/src/DesktopMagic/Helpers/FileUtilities.cs @@ -0,0 +1,93 @@ +using System; +using System.IO; +using System.Runtime.InteropServices; +using System.Text; + +namespace DesktopMagic.Helpers; + +internal static class FileUtilities +{ + public static bool ExistsOnPath(string fileName) + { + return GetFullPath(fileName) is not null; + } + + public static string? GetFullPath(string fileName) + { + if (File.Exists(fileName)) + { + return Path.GetFullPath(fileName); + } + + string? values = Environment.GetEnvironmentVariable("PATH"); + + if (string.IsNullOrEmpty(values)) + { + return null; + } + + foreach (string path in values.Split(Path.PathSeparator)) + { + string fullPath = Path.Combine(path, fileName); + if (File.Exists(fullPath)) + { + return fullPath; + } + } + return null; + } + + public static bool HasAssociatedProgram(string extension) + { + return GetAssociatedProgram(extension) is not null; + } + + public static string? GetAssociatedProgram(string extension) + { + uint pcchOut = 0; + _ = AssocQueryString(AssocF.Verify, AssocStr.Executable, extension, null, null, ref pcchOut); + + StringBuilder pszOut = new StringBuilder((int)pcchOut); + _ = AssocQueryString(AssocF.Verify, AssocStr.Executable, extension, null, pszOut, ref pcchOut); + + if (File.Exists(pszOut.ToString())) + { + return pszOut.ToString(); + } + + return null; + } + + [DllImport("Shlwapi.dll", SetLastError = true, CharSet = CharSet.Unicode)] + private static extern uint AssocQueryString(AssocF flags, AssocStr str, string pszAssoc, string? pszExtra, [Out] StringBuilder? pszOut, [In][Out] ref uint pcchOut); + + [Flags] + public enum AssocF + { + Init_NoRemapCLSID = 0x1, + Init_ByExeName = 0x2, + Open_ByExeName = 0x2, + Init_DefaultToStar = 0x4, + Init_DefaultToFolder = 0x8, + NoUserSettings = 0x10, + NoTruncate = 0x20, + Verify = 0x40, + RemapRunDll = 0x80, + NoFixUps = 0x100, + IgnoreBaseClass = 0x200 + } + + public enum AssocStr + { + Command = 1, + Executable, + FriendlyDocName, + FriendlyAppName, + NoOpen, + ShellNewValue, + DDECommand, + DDEIfExec, + DDEApplication, + DDETopic + } +} \ No newline at end of file diff --git a/src/DesktopMagic/Plugins/PluginManager.xaml b/src/DesktopMagic/Plugins/PluginManager.xaml index d2834b5..baba24a 100644 --- a/src/DesktopMagic/Plugins/PluginManager.xaml +++ b/src/DesktopMagic/Plugins/PluginManager.xaml @@ -96,7 +96,15 @@ - + + + + diff --git a/src/DesktopMagic/Plugins/PluginManager.xaml.cs b/src/DesktopMagic/Plugins/PluginManager.xaml.cs index 78d3cc2..a6d61ff 100644 --- a/src/DesktopMagic/Plugins/PluginManager.xaml.cs +++ b/src/DesktopMagic/Plugins/PluginManager.xaml.cs @@ -1,4 +1,5 @@ using DesktopMagic.DataContexts; +using DesktopMagic.Dialogs; using DesktopMagic.Helpers; using Modio; @@ -9,11 +10,13 @@ using System; using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics; +using System.Globalization; using System.IO; using System.IO.Compression; using System.Linq; using System.Net.Http; using System.Text.Json; +using System.Text.RegularExpressions; using System.Threading.Tasks; using System.Windows; using System.Windows.Threading; @@ -34,6 +37,7 @@ public partial class PluginManager : Window private readonly PluginManagerDataContext pluginManagerDataContext = new(); private readonly string pluginsPath = Path.Combine(App.ApplicationDataPath, "Plugins"); + private readonly string pluginDevelopmentPath = Path.Combine(App.ApplicationDataPath, "PluginDevelopment"); private readonly DispatcherTimer searchTimer = new() { @@ -125,6 +129,9 @@ public partial class PluginManager : Window e.Cancel = pluginManagerDataContext.IsLoading; } + [GeneratedRegex(@"[^a-zA-Z0-9]")] + private static partial Regex IdentifierNameRegex(); + private async Task Install(Mod mod) { pluginManagerDataContext.IsLoading = true; @@ -235,4 +242,123 @@ public partial class PluginManager : Window pluginManagerDataContext.IsSearching = false; } + + private void CreatePluginButton_Click(object sender, RoutedEventArgs e) + { + try + { + CreateNewPlugin(); + } + catch (Exception ex) + { + App.Logger.LogError(ex.Message, source: "PluginManager"); + _ = MessageBox.Show(ex.Message, "Plugin Manager", MessageBoxButton.OK, MessageBoxImage.Error); + } + } + + private void CreateNewPlugin() + { + if (!FileUtilities.ExistsOnPath("dotnet.exe")) + { + _ = MessageBox.Show("The .NET SDK is required to create a plugin. Please install it and try again.", "Plugin Manager", MessageBoxButton.OK, MessageBoxImage.Error); + return; + } + + string pluginGuid = Guid.NewGuid().ToString(); + string pluginPath = Path.Combine(pluginsPath, pluginGuid); + uint pluginId = (uint)Random.Shared.Next(1000, 9999); + + InputDialog inputDialog = new((string)FindResource("enterPluginName"), "Plugin Manager") + { + Owner = this, + }; + + if (inputDialog.ShowDialog() is not true) + { + return; + } + + string pluginName = inputDialog.ResponseText; + string pluginSafeName = pluginName.ToLower().Replace("_", " "); + + TextInfo info = CultureInfo.CurrentCulture.TextInfo; + pluginSafeName = info.ToTitleCase(pluginSafeName); + pluginSafeName = IdentifierNameRegex().Replace(pluginSafeName, ""); + + if (!Directory.Exists(pluginDevelopmentPath)) + { + _ = Directory.CreateDirectory(pluginDevelopmentPath); + } + + string pluginProjectPath = Path.Combine(pluginDevelopmentPath, pluginSafeName); + + if (Directory.Exists(pluginProjectPath)) + { + _ = MessageBox.Show("A plugin with the same name already exists. Please choose a different name.", "Plugin Manager", MessageBoxButton.OK, MessageBoxImage.Error); + return; + } + + PluginMetadata pluginMetadata = new(pluginName, pluginId); + + _ = Directory.CreateDirectory(pluginPath); + _ = Directory.CreateDirectory(pluginProjectPath); + + string pluginMetadataPath = Path.Combine(pluginPath, "metadata.json"); + File.WriteAllText(pluginMetadataPath, JsonSerializer.Serialize(pluginMetadata)); + + string cmd = $"new classlib -n {pluginSafeName} -o {pluginProjectPath} -f net8.0 --target-framework-override net8.0-windows7"; + Process process = Process.Start("dotnet", cmd); + process.WaitForExit(); + + // Install the required NuGet packages + process = Process.Start("dotnet", $"add {pluginProjectPath} package DesktopMagic.Api"); + process.WaitForExit(); + + File.Move(Path.Combine(pluginProjectPath, "Class1.cs"), Path.Combine(pluginProjectPath, $"{pluginSafeName}.cs")); + + string code = $@"using DesktopMagic.Api; +using System.Drawing; + +namespace {pluginSafeName}; + +public class {pluginSafeName}Plugin : Plugin +{{ + public override Bitmap Main() + {{ + Bitmap bmp = new Bitmap(1000, 1000); + + using (Graphics g = Graphics.FromImage(bmp)) + {{ + g.Clear(Application.Theme.PrimaryColor); // Set the background color to the color specified in the DesktopMagic application. + + g.DrawString(""Hello World"", new Font(Application.Theme.Font, 100), Brushes.Black, new PointF(0, 0)); // Draw ""Hello World"" to the image. + }} + + bmp.SetResolution(300, 300); // Set DPI to avoid scaling issues. + + return bmp; // Return the image. + }} +}} +"; + + File.WriteAllText(Path.Combine(pluginProjectPath, $"{pluginSafeName}.cs"), code); + + // Open the project in the default IDE + + string? associatedProgram = FileUtilities.GetAssociatedProgram(".csproj"); + + if (associatedProgram is null) + { + _ = Process.Start("explorer.exe", pluginProjectPath); + return; + } + + ProcessStartInfo psi = new ProcessStartInfo + { + FileName = associatedProgram, + Arguments = Path.Combine(pluginProjectPath, $"{pluginSafeName}.csproj"), + }; + + _ = Process.Start(psi); + } } \ No newline at end of file diff --git a/src/DesktopMagic/Resources/Strings/StringResources.de.xaml b/src/DesktopMagic/Resources/Strings/StringResources.de.xaml index 786fdde..a3ff1e3 100644 --- a/src/DesktopMagic/Resources/Strings/StringResources.de.xaml +++ b/src/DesktopMagic/Resources/Strings/StringResources.de.xaml @@ -15,6 +15,8 @@ Neu Laden Plugins Ordner Plugins Manager + Neues Plugin erstellen + Pluginnamen eingeben Farbe: Standard Wollen sie das Programm wirklich schließen? diff --git a/src/DesktopMagic/Resources/Strings/StringResources.en.xaml b/src/DesktopMagic/Resources/Strings/StringResources.en.xaml index fa6d822..fec2c8b 100644 --- a/src/DesktopMagic/Resources/Strings/StringResources.en.xaml +++ b/src/DesktopMagic/Resources/Strings/StringResources.en.xaml @@ -15,6 +15,8 @@ Reload Plugins Folder Plugins Manager + Create New Plugin + Enter plugin name Color: Default Do you really want to close the program?