ソースを参照

Fix various sync bugs related to palettes

Equbuxu 3 年 前
コミット
d61e524424

+ 151 - 116
PixiEditor/Models/DataProviders/LocalPalettesFetcher.cs

@@ -2,56 +2,55 @@
 using PixiEditor.Models.DataHolders;
 using PixiEditor.Models.DataHolders.Palettes;
 using PixiEditor.Models.IO;
+using PixiEditor.Models.IO.JascPalFile;
+using PixiEditor.Models.UserPreferences;
+using SkiaSharp;
 using System;
 using System.Collections.Generic;
 using System.IO;
 using System.Linq;
 using System.Threading.Tasks;
-using PixiEditor.Models.IO.JascPalFile;
-using PixiEditor.Models.UserPreferences;
-using SkiaSharp;
 
 namespace PixiEditor.Models.DataProviders
 {
     public delegate void CacheUpdate(RefreshType refreshType, Palette itemAffected, string oldName);
-        
+
     public class LocalPalettesFetcher : PaletteListDataSource
     {
         public static string PathToPalettesFolder { get; } = Path.Join(
             Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
             "PixiEditor", "Palettes");
 
-        public List<Palette> CachedPalettes { get; private set; }
+        private List<Palette> cachedPalettes;
+
+        public event CacheUpdate CacheUpdated;
 
-        public event CacheUpdate CacheUpdated; 
-        
-        private List<string> _cachedFavoritePalettes;
+        private List<string> cachedFavoritePalettes;
 
-        private FileSystemWatcher _watcher;
-        
+        private FileSystemWatcher watcher;
 
         public override void Initialize()
         {
             InitDir();
-            _watcher = new FileSystemWatcher(PathToPalettesFolder);
-            _watcher.Filter = "*.pal";
-            _watcher.Changed += FileSystemChanged;
-            _watcher.Deleted += FileSystemChanged;
-            _watcher.Renamed += RenamedFile;
-            _watcher.Created += FileSystemChanged;
-
-            _watcher.EnableRaisingEvents = true;
-            _cachedFavoritePalettes = IPreferences.Current.GetLocalPreference<List<string>>(PreferencesConstants.FavouritePalettes);
-            
+            watcher = new FileSystemWatcher(PathToPalettesFolder);
+            watcher.Filter = "*.pal";
+            watcher.Changed += FileSystemChanged;
+            watcher.Deleted += FileSystemChanged;
+            watcher.Renamed += RenamedFile;
+            watcher.Created += FileSystemChanged;
+
+            watcher.EnableRaisingEvents = true;
+            cachedFavoritePalettes = IPreferences.Current.GetLocalPreference<List<string>>(PreferencesConstants.FavouritePalettes);
+
             IPreferences.Current.AddCallback(PreferencesConstants.FavouritePalettes, updated =>
             {
-                _cachedFavoritePalettes = (List<string>)updated;
+                cachedFavoritePalettes = (List<string>)updated;
             });
         }
 
         public override async Task<PaletteList> FetchPaletteList(int startIndex, int count, FilteringSettings filtering)
         {
-            if(CachedPalettes == null)
+            if (cachedPalettes == null)
             {
                 await RefreshCacheAll();
             }
@@ -61,7 +60,7 @@ namespace PixiEditor.Models.DataProviders
                 Palettes = new WpfObservableRangeCollection<Palette>()
             };
 
-            var filteredPalettes = CachedPalettes.Where(filtering.Filter).ToArray();
+            var filteredPalettes = cachedPalettes.Where(filtering.Filter).ToArray();
 
             if (startIndex >= filteredPalettes.Length) return result;
 
@@ -91,24 +90,122 @@ namespace PixiEditor.Models.DataProviders
         {
             string newName = Path.GetFileNameWithoutExtension(currentName);
 
-            while (File.Exists(Path.Join(PathToPalettesFolder, newName + ".pal")))
+            if (File.Exists(Path.Join(PathToPalettesFolder, newName + ".pal")))
             {
-                newName += "(1)";
+                int number = 1;
+                while (true)
+                {
+                    string potentialName = $"{newName} ({number})";
+                    number++;
+                    if (File.Exists(Path.Join(PathToPalettesFolder, potentialName + ".pal")))
+                        continue;
+                    newName = potentialName;
+                    break;
+                }
             }
 
             if (appendExtension)
-            {
                 newName += ".pal";
-            }
 
             return newName;
         }
 
-        public async Task RefreshCache(RefreshType refreshType, string file)
+        public async Task SavePalette(string fileName, SKColor[] colors)
+        {
+            watcher.EnableRaisingEvents = false;
+            string path = Path.Join(PathToPalettesFolder, fileName);
+            InitDir();
+            await JascFileParser.SaveFile(path, new PaletteFileData(colors));
+            watcher.EnableRaisingEvents = true;
+            
+            await RefreshCache(RefreshType.Created, path);
+        }
+
+        public async Task DeletePalette(string name)
+        {
+            if (!Directory.Exists(PathToPalettesFolder)) return;
+            string path = Path.Join(PathToPalettesFolder, name);
+            if (!File.Exists(path)) return;
+
+            watcher.EnableRaisingEvents = false;
+            File.Delete(path);
+            watcher.EnableRaisingEvents = true;
+            
+            await RefreshCache(RefreshType.Deleted, path);
+        }
+
+        public void RenamePalette(string oldFileName, string newFileName)
+        {
+            if (!Directory.Exists(PathToPalettesFolder)) 
+                return;
+            
+            string oldPath = Path.Join(PathToPalettesFolder, oldFileName);
+            string newPath = Path.Join(PathToPalettesFolder, newFileName);
+            if (!File.Exists(oldPath) || File.Exists(newPath)) 
+                return;
+
+            watcher.EnableRaisingEvents = false;
+            File.Move(oldPath, newPath);
+            watcher.EnableRaisingEvents = true;
+
+            RefreshCacheRenamed(newPath, oldPath);
+        }
+
+        public async Task RefreshCacheAll()
+        {
+            string[] files = DirectoryExtensions.GetFiles(
+                PathToPalettesFolder,
+                string.Join("|", AvailableParsers.SelectMany(x => x.SupportedFileExtensions)),
+                SearchOption.TopDirectoryOnly);
+            cachedPalettes = await ParseAll(files);
+            CacheUpdated?.Invoke(RefreshType.All, null, null);
+        }
+
+        private async void FileSystemChanged(object sender, FileSystemEventArgs e)
+        {
+            bool waitableExceptionOccured = false;
+            do
+            {
+                try
+                {
+                    switch (e.ChangeType)
+                    {
+                        case WatcherChangeTypes.Created:
+                            await RefreshCache(RefreshType.Created, e.FullPath);
+                            break;
+                        case WatcherChangeTypes.Deleted:
+                            await RefreshCache(RefreshType.Deleted, e.FullPath);
+                            break;
+                        case WatcherChangeTypes.Changed:
+                            await RefreshCache(RefreshType.Updated, e.FullPath);
+                            break;
+                        case WatcherChangeTypes.Renamed:
+                            // Handled by method below
+                            break;
+                        case WatcherChangeTypes.All:
+                            await RefreshCache(RefreshType.Created, e.FullPath);
+                            break;
+                        default:
+                            throw new ArgumentOutOfRangeException();
+                    }
+
+                    waitableExceptionOccured = false;
+                }
+                catch (IOException)
+                {
+                    waitableExceptionOccured = true;
+                    await Task.Delay(100);
+                }
+
+            }
+            while (waitableExceptionOccured);
+        }
+
+        private async Task RefreshCache(RefreshType refreshType, string file)
         {
             Palette updated = null;
             string affectedFileName = null;
-            
+
             switch (refreshType)
             {
                 case RefreshType.All:
@@ -130,26 +227,26 @@ namespace PixiEditor.Models.DataProviders
             CacheUpdated?.Invoke(refreshType, updated, affectedFileName);
         }
 
-        public void RefreshCacheRenamed(string newFilePath, string oldFilePath)
+        private void RefreshCacheRenamed(string newFilePath, string oldFilePath)
         {
             string oldFileName = Path.GetFileName(oldFilePath);
-            int index = CachedPalettes.FindIndex(p => p.FileName == oldFileName);
+            int index = cachedPalettes.FindIndex(p => p.FileName == oldFileName);
             if (index == -1) return;
-            
-            Palette palette = CachedPalettes[index];
+
+            Palette palette = cachedPalettes[index];
             palette.FileName = Path.GetFileName(newFilePath);
             palette.Name = Path.GetFileNameWithoutExtension(newFilePath);
-            
+
             CacheUpdated?.Invoke(RefreshType.Renamed, palette, oldFileName);
         }
 
         private string RefreshCacheDeleted(string filePath)
         {
             string fileName = Path.GetFileName(filePath);
-            int index = CachedPalettes.FindIndex(p => p.FileName == fileName);
+            int index = cachedPalettes.FindIndex(p => p.FileName == fileName);
             if (index == -1) return null;
-            
-            CachedPalettes.RemoveAt(index);
+
+            cachedPalettes.RemoveAt(index);
             return fileName;
         }
 
@@ -165,22 +262,22 @@ namespace PixiEditor.Models.DataProviders
                     if (newPalette is { IsCorrupted: false })
                     {
                         Palette pal = CreatePalette(newPalette, file,
-                            _cachedFavoritePalettes?.Contains(newPalette.Title) ?? false);
+                            cachedFavoritePalettes?.Contains(newPalette.Title) ?? false);
                         action(pal);
-                        
+
                         return pal;
                     }
                 }
             }
-            
+
             return null;
         }
-        
+
         private async Task<Palette> RefreshCacheUpdated(string file)
         {
             return await RefreshCacheItem(file, palette =>
             {
-                Palette existingPalette = CachedPalettes.FirstOrDefault(x => x.FileName == palette.FileName);
+                Palette existingPalette = cachedPalettes.FirstOrDefault(x => x.FileName == palette.FileName);
                 if (existingPalette != null)
                 {
                     existingPalette.Colors = palette.Colors.ToList();
@@ -192,17 +289,16 @@ namespace PixiEditor.Models.DataProviders
 
         private async Task<Palette> RefreshCacheAdded(string file)
         {
-            return await RefreshCacheItem(file, CachedPalettes.Add);
-        }
-
-        public async Task RefreshCacheAll()
-        {
-            string[] files = DirectoryExtensions.GetFiles(
-                PathToPalettesFolder,
-                string.Join("|", AvailableParsers.SelectMany(x => x.SupportedFileExtensions)),
-                SearchOption.TopDirectoryOnly);
-            CachedPalettes = await ParseAll(files);
-            CacheUpdated?.Invoke(RefreshType.All, null, null);
+            return await RefreshCacheItem(file, palette =>
+            {
+                string fileName = Path.GetFileName(file);
+                int index = cachedPalettes.FindIndex(p => p.FileName == fileName);
+                if (index != -1)
+                {
+                    cachedPalettes.RemoveAt(index);
+                }
+                cachedPalettes.Add(palette);
+            });
         }
 
         private async Task<List<Palette>> ParseAll(string[] files)
@@ -216,8 +312,8 @@ namespace PixiEditor.Models.DataProviders
                 var foundParser = AvailableParsers.First(x => x.SupportedFileExtensions.Contains(extension));
                 {
                     PaletteFileData fileData = await foundParser.Parse(file);
-                    if(fileData.IsCorrupted) continue;
-                    var palette = CreatePalette(fileData, file, _cachedFavoritePalettes?.Contains(fileData.Title) ?? false);
+                    if (fileData.IsCorrupted) continue;
+                    var palette = CreatePalette(fileData, file, cachedFavoritePalettes?.Contains(fileData.Title) ?? false);
 
                     result.Add(palette);
                 }
@@ -239,67 +335,6 @@ namespace PixiEditor.Models.DataProviders
             return palette;
         }
 
-        public async Task SavePalette(string fileName, SKColor[] colors)
-        {
-            _watcher.EnableRaisingEvents = false;
-            string path = Path.Join(PathToPalettesFolder, fileName);
-            InitDir();
-            await JascFileParser.SaveFile(path, new PaletteFileData(colors));
-
-            
-            _watcher.EnableRaisingEvents = true;
-            await RefreshCache(RefreshType.Created, path);
-        }
-
-        public void DeletePalette(string name)
-        {
-            if (!Directory.Exists(PathToPalettesFolder)) return;
-            string path = Path.Join(PathToPalettesFolder, name);
-            if (!File.Exists(path)) return;
-
-            File.Delete(path);
-        }
-
-        private async void FileSystemChanged(object sender, FileSystemEventArgs e)
-        {
-            bool waitableExceptionOccured = false;
-            do
-            {
-                try
-                {
-                    switch (e.ChangeType)
-                    {
-                        case WatcherChangeTypes.Created:
-                            await RefreshCache(RefreshType.Created, e.FullPath);
-                            break;
-                        case WatcherChangeTypes.Deleted:
-                            await RefreshCache(RefreshType.Deleted, e.FullPath);
-                            break;
-                        case WatcherChangeTypes.Changed:
-                            await RefreshCache(RefreshType.Updated, e.FullPath);
-                            break;
-                        case WatcherChangeTypes.Renamed:
-                            // Handled by method below
-                            break;
-                        case WatcherChangeTypes.All:
-                            await RefreshCache(RefreshType.Created, e.FullPath);
-                            break;
-                        default:
-                            throw new ArgumentOutOfRangeException();
-                    }
-                    
-                    waitableExceptionOccured = false;
-                }
-                catch(IOException)
-                {
-                    waitableExceptionOccured = true;
-                    await Task.Delay(100);
-                }
-                
-            }
-            while (waitableExceptionOccured);
-        }
-        
         private void RenamedFile(object sender, RenamedEventArgs e)
         {
             RefreshCacheRenamed(e.FullPath, e.OldFullPath);

+ 53 - 107
PixiEditor/Views/Dialogs/PalettesBrowser.xaml.cs

@@ -23,112 +23,101 @@ using System.Windows.Navigation;
 
 namespace PixiEditor.Views.Dialogs
 {
-    public delegate void ListFetched(PaletteList list, int cacheId);
-
-    /// <summary>
-    /// Interaction logic for LospecPalettesBrowser.xaml
-    /// </summary>
     public partial class PalettesBrowser : Window
     {
-        public const int ItemsPerLoad = 10;
+        private const int ItemsPerLoad = 10;
 
         public PaletteList PaletteList
         {
-            get { return (PaletteList)GetValue(PaletteListProperty); }
-            set { SetValue(PaletteListProperty, value); }
+            get => (PaletteList)GetValue(PaletteListProperty);
+            set => SetValue(PaletteListProperty, value);
         }
 
-        // Using a DependencyProperty as the backing store for PaletteList.  This enables animation, styling, binding, etc...
         public static readonly DependencyProperty PaletteListProperty =
-            DependencyProperty.Register("PaletteList", typeof(PaletteList), typeof(PalettesBrowser));
+            DependencyProperty.Register(nameof(PaletteList), typeof(PaletteList), typeof(PalettesBrowser));
 
         public ICommand ImportPaletteCommand
         {
-            get { return (ICommand)GetValue(ImportPaletteCommandProperty); }
-            set { SetValue(ImportPaletteCommandProperty, value); }
+            get => (ICommand)GetValue(ImportPaletteCommandProperty);
+            set => SetValue(ImportPaletteCommandProperty, value);
         }
 
-        // Using a DependencyProperty as the backing store for ImportPaletteCommand.  This enables animation, styling, binding, etc...
         public static readonly DependencyProperty ImportPaletteCommandProperty =
-            DependencyProperty.Register("ImportPaletteCommand", typeof(ICommand), typeof(PalettesBrowser));
+            DependencyProperty.Register(nameof(ImportPaletteCommand), typeof(ICommand), typeof(PalettesBrowser));
 
         public static readonly DependencyProperty DeletePaletteCommandProperty = DependencyProperty.Register(
-            "DeletePaletteCommand", typeof(ICommand), typeof(PalettesBrowser), new PropertyMetadata(default(ICommand)));
+            nameof(DeletePaletteCommand), typeof(ICommand), typeof(PalettesBrowser), new PropertyMetadata(default(ICommand)));
 
         public ICommand DeletePaletteCommand
         {
-            get { return (ICommand)GetValue(DeletePaletteCommandProperty); }
-            set { SetValue(DeletePaletteCommandProperty, value); }
+            get => (ICommand)GetValue(DeletePaletteCommandProperty);
+            set => SetValue(DeletePaletteCommandProperty, value);
         }
 
         public bool IsFetching
         {
-            get { return (bool)GetValue(IsFetchingProperty); }
-            set { SetValue(IsFetchingProperty, value); }
+            get => (bool)GetValue(IsFetchingProperty);
+            set => SetValue(IsFetchingProperty, value);
         }
 
-        // Using a DependencyProperty as the backing store for IsFetching.  This enables animation, styling, binding, etc...
         public static readonly DependencyProperty IsFetchingProperty =
-            DependencyProperty.Register("IsFetching", typeof(bool), typeof(PalettesBrowser), new PropertyMetadata(false));
+            DependencyProperty.Register(nameof(IsFetching), typeof(bool), typeof(PalettesBrowser), new PropertyMetadata(false));
 
         public int ColorsNumber
         {
-            get { return (int)GetValue(ColorsNumberProperty); }
-            set { SetValue(ColorsNumberProperty, value); }
+            get => (int)GetValue(ColorsNumberProperty);
+            set => SetValue(ColorsNumberProperty, value);
         }
 
-        // Using a DependencyProperty as the backing store for ColorsNumber.  This enables animation, styling, binding, etc...
         public static readonly DependencyProperty ColorsNumberProperty =
-            DependencyProperty.Register("ColorsNumber", typeof(int), typeof(PalettesBrowser),
+            DependencyProperty.Register(nameof(ColorsNumber), typeof(int), typeof(PalettesBrowser),
                 new PropertyMetadata(8, ColorsNumberChanged));
 
         public WpfObservableRangeCollection<PaletteListDataSource> PaletteListDataSources
         {
-            get { return (WpfObservableRangeCollection<PaletteListDataSource>)GetValue(PaletteListDataSourcesProperty); }
-            set { SetValue(PaletteListDataSourcesProperty, value); }
+            get => (WpfObservableRangeCollection<PaletteListDataSource>)GetValue(PaletteListDataSourcesProperty);
+            set => SetValue(PaletteListDataSourcesProperty, value);
         }
 
-        // Using a DependencyProperty as the backing store for PaletteListDataSources.  This enables animation, styling, binding, etc...
         public static readonly DependencyProperty PaletteListDataSourcesProperty =
-            DependencyProperty.Register("PaletteListDataSources", typeof(WpfObservableRangeCollection<PaletteListDataSource>), typeof(PalettesBrowser), new PropertyMetadata(new WpfObservableRangeCollection<PaletteListDataSource>()));
+            DependencyProperty.Register(nameof(PaletteListDataSources), typeof(WpfObservableRangeCollection<PaletteListDataSource>), typeof(PalettesBrowser), new PropertyMetadata(new WpfObservableRangeCollection<PaletteListDataSource>()));
         public bool SortAscending
         {
-            get { return (bool)GetValue(SortAscendingProperty); }
-            set { SetValue(SortAscendingProperty, value); }
+            get => (bool)GetValue(SortAscendingProperty);
+            set => SetValue(SortAscendingProperty, value);
         }
 
-        // Using a DependencyProperty as the backing store for SortDescending.  This enables animation, styling, binding, etc...
         public static readonly DependencyProperty SortAscendingProperty =
-            DependencyProperty.Register("SortAscending", typeof(bool), typeof(PalettesBrowser), new PropertyMetadata(true, OnSortAscendingChanged));
+            DependencyProperty.Register(nameof(SortAscending), typeof(bool), typeof(PalettesBrowser), new PropertyMetadata(true, OnSortAscendingChanged));
 
 
         public static readonly DependencyProperty SortedResultsProperty = DependencyProperty.Register(
-            "SortedResults", typeof(WpfObservableRangeCollection<Palette>), typeof(PalettesBrowser), new PropertyMetadata(default(WpfObservableRangeCollection<Palette>)));
+            nameof(SortedResults), typeof(WpfObservableRangeCollection<Palette>), typeof(PalettesBrowser), new PropertyMetadata(default(WpfObservableRangeCollection<Palette>)));
 
         public WpfObservableRangeCollection<Palette> SortedResults
         {
-            get { return (WpfObservableRangeCollection<Palette>)GetValue(SortedResultsProperty); }
-            set { SetValue(SortedResultsProperty, value); }
+            get => (WpfObservableRangeCollection<Palette>)GetValue(SortedResultsProperty);
+            set => SetValue(SortedResultsProperty, value);
         }
 
         public static readonly DependencyProperty NameFilterProperty = DependencyProperty.Register(
-            "NameFilter", typeof(string), typeof(PalettesBrowser),
+            nameof(NameFilter), typeof(string), typeof(PalettesBrowser),
             new PropertyMetadata(default(string), OnNameFilterChanged));
 
         public string NameFilter
         {
-            get { return (string)GetValue(NameFilterProperty); }
-            set { SetValue(NameFilterProperty, value); }
+            get => (string)GetValue(NameFilterProperty);
+            set => SetValue(NameFilterProperty, value);
         }
 
         public static readonly DependencyProperty ShowOnlyFavouritesProperty = DependencyProperty.Register(
-            "ShowOnlyFavourites", typeof(bool), typeof(PalettesBrowser),
+            nameof(ShowOnlyFavourites), typeof(bool), typeof(PalettesBrowser),
             new PropertyMetadata(false, OnShowOnlyFavouritesChanged));
 
         public bool ShowOnlyFavourites
         {
-            get { return (bool)GetValue(ShowOnlyFavouritesProperty); }
-            set { SetValue(ShowOnlyFavouritesProperty, value); }
+            get => (bool)GetValue(ShowOnlyFavouritesProperty);
+            set => SetValue(ShowOnlyFavouritesProperty, value);
         }
 
         public RelayCommand<Palette> ToggleFavouriteCommand { get; set; }
@@ -151,13 +140,13 @@ namespace PixiEditor.Views.Dialogs
         {
             get
             {
-                return _localPalettesFetcher ??= (LocalPalettesFetcher)PaletteListDataSources.First(x => x is LocalPalettesFetcher);
+                return localPalettesFetcher ??= (LocalPalettesFetcher)PaletteListDataSources.First(x => x is LocalPalettesFetcher);
             }
         }
 
-        private LocalPalettesFetcher _localPalettesFetcher;
+        private LocalPalettesFetcher localPalettesFetcher;
 
-        private string[] _stopItTexts = new[]
+        private readonly string[] stopItTexts = new[]
         {
             "That's enough. Tidy up your file names.",
             "Can you stop copying these names please?", "No, really, stop it.", "Don't you have anything better to do?"
@@ -169,12 +158,12 @@ namespace PixiEditor.Views.Dialogs
             Instance = this;
             DeletePaletteCommand = new RelayCommand<Palette>(DeletePalette);
             ToggleFavouriteCommand = new RelayCommand<Palette>(ToggleFavourite, CanToggleFavourite);
-            Loaded += async (sender, args) =>
+            Loaded += async (_, _) =>
             {
                 LocalPalettesFetcher.CacheUpdated += LocalCacheRefreshed;
                 await LocalPalettesFetcher.RefreshCacheAll();
             };
-            Closed += (s, e) =>
+            Closed += (_, _) =>
             {
                 Instance = null;
                 LocalPalettesFetcher.CacheUpdated -= LocalCacheRefreshed;
@@ -248,7 +237,8 @@ namespace PixiEditor.Views.Dialogs
         private void HandleCacheItemUpdated(Palette updatedItem)
         {
             var item = SortedResults.FirstOrDefault(x => x.FileName == updatedItem.FileName);
-            if (item == null) return;
+            if (item is null) 
+                return;
 
             item.Name = updatedItem.Name;
             item.IsFavourite = updatedItem.IsFavourite;
@@ -379,8 +369,8 @@ namespace PixiEditor.Views.Dialogs
         private async void ScrollViewer_ScrollChanged(object sender, ScrollChangedEventArgs e)
         {
             if (PaletteList?.Palettes == null) return;
-            var scrollViewer = (ScrollViewer)sender;
-            if (scrollViewer.VerticalOffset == scrollViewer.ScrollableHeight)
+            var viewer = (ScrollViewer)sender;
+            if (viewer.VerticalOffset == viewer.ScrollableHeight)
             {
                 IsFetching = true;
                 var newPalettes = await FetchPaletteList(0, Filtering);
@@ -408,7 +398,7 @@ namespace PixiEditor.Views.Dialogs
 
         private async void ColorsComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
         {
-            if (e.AddedItems is { Count: > 0 } && e.AddedItems[0] is ComboBoxItem item && item.Content is string value)
+            if (e.AddedItems is { Count: > 0 } && e.AddedItems[0] is ComboBoxItem { Content: string value })
             {
                 ColorsNumberMode = Enum.Parse<ColorsNumberMode>(value);
                 Filtering.ColorsNumberMode = ColorsNumberMode;
@@ -489,77 +479,33 @@ namespace PixiEditor.Views.Dialogs
 
         private async void AddFromPalette_OnClick(object sender, RoutedEventArgs e)
         {
-            if (CurrentEditingPalette?.Count == 0) return;
-
-            string finalFileName = "Unnamed Palette.pal";
-
-            string path = Path.Join(LocalPalettesFetcher.PathToPalettesFolder, finalFileName);
-            int i = 1;
-            while (File.Exists(path))
-            {
-                finalFileName = $"Unnamed Palette {i}.pal";
-                path = Path.Join(LocalPalettesFetcher.PathToPalettesFolder, finalFileName);
-                i++;
-            }
+            if (CurrentEditingPalette?.Count == 0) 
+                return;
 
+            string finalFileName = LocalPalettesFetcher.GetNonExistingName("Unnamed Palette.pal", true);
             await LocalPalettesFetcher.SavePalette(finalFileName, CurrentEditingPalette.ToArray());
-
-            var palette = _localPalettesFetcher.CachedPalettes.FirstOrDefault(x => x.FileName == finalFileName);
-            if (palette != null)
-            {
-                if (SortedResults == null)
-                {
-                    SortedResults = new WpfObservableRangeCollection<Palette>();
-                }
-
-                if (SortedResults.Contains(palette))
-                {
-                    SortedResults.Move(SortedResults.IndexOf(palette), 0);
-                }
-                else
-                {
-                    SortedResults.Insert(0, palette);
-                }
-            }
         }
 
         private void PaletteItem_OnRename(object sender, EditableTextBlock.TextChangedEventArgs e)
         {
             PaletteItem item = (PaletteItem)sender;
-            string oldFileName = $"{e.OldText}.pal";
-
+            item.Palette.Name = e.OldText;
+            
             if (string.IsNullOrWhiteSpace(e.NewText) || e.NewText == item.Palette.Name || e.NewText.Length > 50)
-            {
-                item.Palette.FileName = oldFileName;
-                item.Palette.Name = e.OldText;
                 return;
-            }
 
-            string oldPath = Path.Join(LocalPalettesFetcher.PathToPalettesFolder, oldFileName);
-
-            string finalNewName = $"{Palette.ReplaceInvalidChars(e.NewText)}.pal";
-            string newPath = Path.Join(LocalPalettesFetcher.PathToPalettesFolder, LocalPalettesFetcher.GetNonExistingName(finalNewName, true));
+            string oldFileName = $"{e.OldText}.pal";
+            
+            string finalNewName = LocalPalettesFetcher.GetNonExistingName($"{Palette.ReplaceInvalidChars(e.NewText)}.pal", true);
+            string newPath = Path.Join(LocalPalettesFetcher.PathToPalettesFolder, finalNewName);
 
             if (newPath.Length > 250)
             {
-                NoticeDialog.Show(_stopItTexts[Random.Shared.Next(_stopItTexts.Length - 1)], "The name is too long.");
+                NoticeDialog.Show(stopItTexts[Random.Shared.Next(stopItTexts.Length - 1)], "The name is too long.");
                 return;
             }
-
-            if (!File.Exists(oldPath))
-            {
-                item.Palette.FileName = oldFileName;
-                item.Palette.Name = e.OldText;
-                return;
-            }
-
-
-            File.Move(oldPath, newPath);
-
-            item.Palette.FileName = finalNewName;
-            item.Palette.Name = e.NewText;
-
-            HandleCacheItemRenamed(item.Palette, oldFileName);
+            
+            LocalPalettesFetcher.RenamePalette(oldFileName, finalNewName);
         }
 
         private static void UpdateRenamedFavourite(string old, string newName)