Browse Source

Added instance mutex and fixed filtering

Krzysztof Krysiński 3 years ago
parent
commit
f582aee5a1

+ 79 - 8
PixiEditor/App.xaml.cs

@@ -4,10 +4,15 @@ using PixiEditor.Models.Enums;
 using PixiEditor.ViewModels;
 using PixiEditor.Views.Dialogs;
 using System;
+using System.Collections.Generic;
 using System.Diagnostics;
+using System.IO;
 using System.Linq;
 using System.Text.RegularExpressions;
+using System.Threading;
 using System.Windows;
+using PixiEditor.Models.Controllers;
+using PixiEditor.Models.UserPreferences;
 
 namespace PixiEditor
 {
@@ -16,21 +21,87 @@ namespace PixiEditor
     /// </summary>
     public partial class App : Application
     {
+        /// <summary>The event mutex name.</summary>
+        private const string UniqueEventName = "33f1410b-2ad7-412a-a468-34fe0a85747c";
+
+        /// <summary>The unique mutex name.</summary>
+        private const string UniqueMutexName = "ab2afe27-b9ee-4f03-a1e4-c18da16a349c";
+
+        /// <summary>The event wait handle.</summary>
+        private EventWaitHandle _eventWaitHandle;
+
+        private string passedArgsFile = Path.Join(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "PixiEditor", ".passedArgs");
+
+        /// <summary>The mutex.</summary>
+        private Mutex _mutex;
+
         protected override void OnStartup(StartupEventArgs e)
         {
-            string arguments = string.Join(' ', e.Args);
-
-            if (ParseArgument("--crash (\"?)([A-z0-9:\\/\\ -_.]+)\\1", arguments, out Group[] groups))
+            if (HandleNewInstance())
             {
-                CrashReport report = CrashReport.Parse(groups[2].Value);
-                MainWindow = new CrashReportDialog(report);
+                StartupArgs.Args = e.Args.ToList();
+                string arguments = string.Join(' ', e.Args);
+
+                if (ParseArgument("--crash (\"?)([A-z0-9:\\/\\ -_.]+)\\1", arguments, out Group[] groups))
+                {
+                    CrashReport report = CrashReport.Parse(groups[2].Value);
+                    MainWindow = new CrashReportDialog(report);
+                }
+                else
+                {
+                    MainWindow = new MainWindow();
+                }
+
+                MainWindow.Show();
             }
-            else
+        }
+
+        private bool HandleNewInstance()
+        {
+            bool isOwned;
+            _mutex = new Mutex(true, UniqueMutexName, out isOwned);
+            _eventWaitHandle = new EventWaitHandle(false, EventResetMode.AutoReset, UniqueEventName);
+
+            GC.KeepAlive(_mutex);
+
+            if (isOwned)
             {
-                MainWindow = new MainWindow();
+                var thread = new Thread(
+                    () =>
+                    {
+                        while (_eventWaitHandle.WaitOne())
+                        {
+                            Current.Dispatcher.BeginInvoke(
+                                (Action)(() =>
+                                {
+                                    MainWindow mainWindow = ((MainWindow)Current.MainWindow);
+                                    if (mainWindow != null)
+                                    {
+                                        mainWindow.BringToForeground();
+                                        StartupArgs.Args = File.ReadAllText(passedArgsFile).Split(' ').ToList();
+                                        File.Delete(passedArgsFile);
+                                        StartupArgs.Args.Add("--openedInExisting");
+                                        mainWindow.DataContext.OnStartupCommand.Execute(null);
+                                    }
+                                }));
+                        }
+                    })
+                {
+                    // It is important mark it as background otherwise it will prevent app from exiting.
+                    IsBackground = true
+                };
+
+                thread.Start();
+                return true;
             }
 
-            MainWindow.Show();
+            // Notify other instance so it could bring itself to foreground.
+            File.WriteAllText(passedArgsFile, string.Join(' ', Environment.GetCommandLineArgs()));
+            _eventWaitHandle.Set();
+
+            // Terminate this instance.
+            Shutdown();
+            return false;
         }
 
         protected override void OnSessionEnding(SessionEndingCancelEventArgs e)

+ 11 - 0
PixiEditor/Models/Controllers/StartupArgs.cs

@@ -0,0 +1,11 @@
+using System.Collections.Generic;
+
+namespace PixiEditor.Models.Controllers;
+
+/// <summary>
+///     A class that holds startup command line arguments + custom passed ones.
+/// </summary>
+public static class StartupArgs
+{
+    public static List<string> Args { get; set; }
+}

+ 10 - 0
PixiEditor/Models/DataHolders/Palettes/FilteringSettings.cs

@@ -25,8 +25,18 @@ namespace PixiEditor.Models.DataHolders.Palettes
             // Lexical comparison
             bool result = string.IsNullOrWhiteSpace(Name) || palette.Name.Contains(Name, StringComparison.OrdinalIgnoreCase);
 
+            if(!result)
+            {
+                return false;
+            }
+
             result = (ShowOnlyFavourites && palette.IsFavourite) || !ShowOnlyFavourites;
 
+            if(!result)
+            {
+                return false;
+            }
+
             switch (ColorsNumberMode)
             {
                 case ColorsNumberMode.Any:

+ 15 - 1
PixiEditor/Models/DataHolders/Palettes/Palette.cs

@@ -2,6 +2,8 @@
 using PixiEditor.Helpers;
 using System.Collections.Generic;
 using System.Collections.ObjectModel;
+using System.IO;
+using System.Text.RegularExpressions;
 
 namespace PixiEditor.Models.DataHolders.Palettes
 {
@@ -9,7 +11,14 @@ namespace PixiEditor.Models.DataHolders.Palettes
     {
         public string Name { get; set; }
         public List<string> Colors { get; set; }
-        public string FileName { get; set; }
+
+        private string? fileName;
+
+        public string? FileName
+        {
+            get => fileName;
+            set => fileName = ReplaceInvalidChars(value);
+        }
 
         public bool IsFavourite { get; set; }
 
@@ -19,5 +28,10 @@ namespace PixiEditor.Models.DataHolders.Palettes
             Colors = colors;
             FileName = fileName;
         }
+
+        private string? ReplaceInvalidChars(string? filename)
+        {
+            return filename == null ? null : string.Join("_", filename.Split(Path.GetInvalidFileNameChars()));
+        }
     }
 }

+ 2 - 2
PixiEditor/Models/DataProviders/LocalPalettesFetcher.cs

@@ -80,9 +80,9 @@ namespace PixiEditor.Models.DataProviders
             return result;
         }
 
-        public static async Task SavePalette(string name, SKColor[] colors)
+        public static async Task SavePalette(string fileName, SKColor[] colors)
         {
-            string path = Path.Join(PathToPalettesFolder, name + ".pal");
+            string path = Path.Join(PathToPalettesFolder, fileName);
             InitDir();
             await JascFileParser.SaveFile(path, new PaletteFileData(colors));
         }

+ 15 - 6
PixiEditor/ViewModels/SubViewModels/Main/ColorsViewModel.cs

@@ -14,6 +14,7 @@ using System.IO;
 using System.Linq;
 using System.Threading.Tasks;
 using System.Windows;
+using PixiEditor.Models.Controllers;
 using PixiEditor.Models.ExternalServices;
 using PixiEditor.Views.Dialogs;
 
@@ -83,7 +84,7 @@ namespace PixiEditor.ViewModels.SubViewModels.Main
 
         private async Task ImportLospecPalette()
         {
-            var args = Environment.GetCommandLineArgs();
+            var args = StartupArgs.Args;
             var lospecPaletteArg = args.FirstOrDefault(x => x.StartsWith("lospec-palette://"));
 
             if (lospecPaletteArg != null)
@@ -95,15 +96,23 @@ namespace PixiEditor.ViewModels.SubViewModels.Main
                 var palette = await LospecPaletteFetcher.FetchPalette(lospecPaletteArg.Split(@"://")[1].Replace("/", ""));
                 if (palette != null)
                 {
+                    palette.FileName = $"{palette.Name}.pal";
+
                     await LocalPalettesFetcher.SavePalette(
-                        palette.Name,
+                        palette.FileName,
                         palette.Colors.Select(SKColor.Parse).ToArray());
 
-                    palette.FileName = $"{palette.Name}.pal";
-
+                    await browser.RefreshLocalCache();
                     await browser.UpdatePaletteList();
-                    int indexOfImported = browser.SortedResults.IndexOf(browser.SortedResults.First(x => x.FileName == palette.FileName));
-                    browser.SortedResults.Move(indexOfImported, 0);
+                    if(browser.SortedResults.Any(x => x.FileName == palette.FileName))
+                    {
+                        int indexOfImported = browser.SortedResults.IndexOf(browser.SortedResults.First(x => x.FileName == palette.FileName));
+                        browser.SortedResults.Move(indexOfImported, 0);
+                    }
+                    else
+                    {
+                        browser.SortedResults.Insert(0, palette);
+                    }
                 }
                 else
                 {

+ 4 - 2
PixiEditor/ViewModels/SubViewModels/Main/FileViewModel.cs

@@ -16,6 +16,7 @@ using System.IO;
 using System.Linq;
 using System.Windows;
 using System.Windows.Media.Imaging;
+using PixiEditor.Models.Controllers;
 
 namespace PixiEditor.ViewModels.SubViewModels.Main
 {
@@ -189,13 +190,14 @@ namespace PixiEditor.ViewModels.SubViewModels.Main
 
         private void Owner_OnStartupEvent(object sender, System.EventArgs e)
         {
-            var args = Environment.GetCommandLineArgs();
+            var args = StartupArgs.Args;
             var file = args.FirstOrDefault(x => Importer.IsSupportedFile(x) && File.Exists(x));
             if (file != null)
             {
                 Open(file);
             }
-            else if (Owner.BitmapManager.Documents.Count == 0 || !args.Contains("--crash"))
+            else if ((Owner.BitmapManager.Documents.Count == 0
+                     || !args.Contains("--crash")) && !args.Contains("--openedInExisting"))
             {
                 if (IPreferences.Current.GetPreference("ShowStartupWindow", true))
                 {

+ 18 - 11
PixiEditor/Views/Dialogs/PalettesBrowser.xaml.cs

@@ -2,7 +2,6 @@
 using PixiEditor.Models.DataHolders.Palettes;
 using PixiEditor.Models.DataProviders;
 using PixiEditor.Models.Enums;
-using PixiEditor.Models.Events;
 using System;
 using System.Collections.Generic;
 using System.Diagnostics;
@@ -12,8 +11,6 @@ using System.Threading.Tasks;
 using System.Windows;
 using System.Windows.Controls;
 using System.Windows.Input;
-using PixiEditor.Models.IO;
-using PixiEditor.Models.IO.JascPalFile;
 using PixiEditor.Views.UserControls.Palettes;
 using SkiaSharp;
 using PixiEditor.Helpers;
@@ -149,6 +146,8 @@ namespace PixiEditor.Views.Dialogs
 
         private static PaletteList _cachedPaletteList;
 
+        private LocalPalettesFetcher _localPalettesFetcher;
+
         public PalettesBrowser()
         {
             InitializeComponent();
@@ -214,8 +213,7 @@ namespace PixiEditor.Views.Dialogs
                     LocalPalettesFetcher.DeletePalette(palette.FileName);
                     RemoveFavouritePalette(palette);
 
-                    LocalPalettesFetcher paletteListDataSource = (LocalPalettesFetcher)PaletteListDataSources.First(x => x is LocalPalettesFetcher);
-                    await paletteListDataSource.RefreshCache();
+                    await RefreshLocalCache();
                     await UpdatePaletteList();
                 }
             }
@@ -262,6 +260,17 @@ namespace PixiEditor.Views.Dialogs
             browser.Sort();
         }
 
+        public async Task RefreshLocalCache()
+        {
+            if (_localPalettesFetcher == null)
+            {
+                _localPalettesFetcher = (LocalPalettesFetcher)PaletteListDataSources.First(x => x is LocalPalettesFetcher);
+
+            }
+
+            await _localPalettesFetcher.RefreshCache();
+        }
+
 
         public async Task UpdatePaletteList()
         {
@@ -426,11 +435,10 @@ namespace PixiEditor.Views.Dialogs
             }
 
             await LocalPalettesFetcher.SavePalette(finalFileName, CurrentEditingPalette.ToArray());
-            LocalPalettesFetcher paletteListDataSource = (LocalPalettesFetcher)PaletteListDataSources.First(x => x is LocalPalettesFetcher);
-            await paletteListDataSource.RefreshCache();
+            await RefreshLocalCache();
             await UpdatePaletteList();
 
-            var palette = paletteListDataSource.CachedPalettes.FirstOrDefault(x => x.FileName == finalFileName);
+            var palette = _localPalettesFetcher.CachedPalettes.FirstOrDefault(x => x.FileName == finalFileName);
             if (palette != null)
             {
                 if (SortedResults.Contains(palette))
@@ -447,7 +455,7 @@ namespace PixiEditor.Views.Dialogs
         private async void PaletteItem_OnRename(object sender, EditableTextBlock.TextChangedEventArgs e)
         {
             PaletteItem item = (PaletteItem)sender;
-            if (string.IsNullOrWhiteSpace(e.NewText) || e.NewText.IndexOfAny(Path.GetInvalidFileNameChars()) >= 0 || e.NewText == item.Palette.Name)
+            if (string.IsNullOrWhiteSpace(e.NewText) || e.NewText == item.Palette.Name)
             {
                 return;
             }
@@ -459,8 +467,7 @@ namespace PixiEditor.Views.Dialogs
                 Path.Join(LocalPalettesFetcher.PathToPalettesFolder, item.Palette.FileName));
 
 
-            LocalPalettesFetcher paletteListDataSource = (LocalPalettesFetcher)PaletteListDataSources.First(x => x is LocalPalettesFetcher);
-            await paletteListDataSource.RefreshCache();
+            await RefreshLocalCache();
             await UpdatePaletteList();
         }
     }

+ 15 - 1
PixiEditor/Views/MainWindow.xaml.cs

@@ -66,7 +66,6 @@ namespace PixiEditor
         public static MainWindow CreateWithDocuments(IEnumerable<Document> documents)
         {
             MainWindow window = new();
-
             BitmapManager bitmapManager = window.services.GetRequiredService<BitmapManager>();
 
             foreach (Document document in documents)
@@ -79,6 +78,21 @@ namespace PixiEditor
             return window;
         }
 
+        /// <summary>Brings main window to foreground.</summary>
+        public void BringToForeground()
+        {
+            if (WindowState == WindowState.Minimized || this.Visibility == Visibility.Hidden)
+            {
+                Show();
+                WindowState = WindowState.Normal;
+            }
+
+            Activate();
+            Topmost = true;
+            Topmost = false;
+            Focus();
+        }
+
         protected override void OnClosing(CancelEventArgs e)
         {
             DataContext.CloseWindow(e);