diff --git a/src/Artemis.Core/Models/Profile/Profile.cs b/src/Artemis.Core/Models/Profile/Profile.cs index d8f143462..6d2645f07 100644 --- a/src/Artemis.Core/Models/Profile/Profile.cs +++ b/src/Artemis.Core/Models/Profile/Profile.cs @@ -29,8 +29,6 @@ namespace Artemis.Core ProfileEntity = profileEntity; EntityId = profileEntity.Id; - UndoStack = new MaxStack(20); - RedoStack = new MaxStack(20); Exceptions = new List(); Scripts = new ReadOnlyObservableCollection(_scripts); ScriptConfigurations = new ReadOnlyObservableCollection(_scriptConfigurations); @@ -81,8 +79,6 @@ namespace Artemis.Core /// public ProfileEntity ProfileEntity { get; internal set; } - internal MaxStack UndoStack { get; set; } - internal MaxStack RedoStack { get; set; } internal List Exceptions { get; } /// diff --git a/src/Artemis.Core/Services/Storage/Interfaces/IProfileService.cs b/src/Artemis.Core/Services/Storage/Interfaces/IProfileService.cs index 13a9fb3be..d73a904e8 100644 --- a/src/Artemis.Core/Services/Storage/Interfaces/IProfileService.cs +++ b/src/Artemis.Core/Services/Storage/Interfaces/IProfileService.cs @@ -10,11 +10,6 @@ namespace Artemis.Core.Services /// public interface IProfileService : IArtemisService { - /// - /// Gets the JSON serializer settings used to create profile mementos - /// - public static JsonSerializerSettings MementoSettings { get; } = new() {TypeNameHandling = TypeNameHandling.All}; - /// /// Gets the JSON serializer settings used to import/export profiles /// @@ -101,26 +96,14 @@ namespace Artemis.Core.Services /// Saves the current icon of this profile /// void SaveProfileConfigurationIcon(ProfileConfiguration profileConfiguration); - + /// /// Writes the profile to persistent storage /// /// /// void SaveProfile(Profile profile, bool includeChildren); - - /// - /// Attempts to restore the profile to the state it had before the last call. - /// - /// - bool UndoSaveProfile(Profile profile); - - /// - /// Attempts to restore the profile to the state it had before the last call. - /// - /// - bool RedoSaveProfile(Profile profile); - + /// /// Exports the profile described in the given into an export model /// diff --git a/src/Artemis.Core/Services/Storage/ProfileService.cs b/src/Artemis.Core/Services/Storage/ProfileService.cs index c6a4a21b0..12b212cd4 100644 --- a/src/Artemis.Core/Services/Storage/ProfileService.cs +++ b/src/Artemis.Core/Services/Storage/ProfileService.cs @@ -442,88 +442,24 @@ namespace Artemis.Core.Services _profileCategories.Sort((a, b) => a.Order - b.Order); } } - + public void SaveProfile(Profile profile, bool includeChildren) { - string memento = JsonConvert.SerializeObject(profile.ProfileEntity, IProfileService.MementoSettings); + _logger.Debug("Updating profile - Saving {Profile}", profile); profile.Save(); if (includeChildren) { foreach (RenderProfileElement child in profile.GetAllRenderElements()) child.Save(); } - - // If there are no changes, don't bother saving - string updatedMemento = JsonConvert.SerializeObject(profile.ProfileEntity, IProfileService.MementoSettings); - if (memento.Equals(updatedMemento)) - { - _logger.Debug("Updating profile - Skipping save, no changes"); - return; - } - - _logger.Debug("Updating profile - Saving " + profile); - profile.RedoStack.Clear(); - profile.UndoStack.Push(memento); - + // At this point the user made actual changes, save that profile.IsFreshImport = false; profile.ProfileEntity.IsFreshImport = false; _profileRepository.Save(profile.ProfileEntity); } - - public bool UndoSaveProfile(Profile profile) - { - // Keep the profile from being rendered by locking it - lock (profile) - { - if (profile.UndoStack.Count == 0) - { - _logger.Debug("Undo profile update - Failed, undo stack empty"); - return false; - } - - string top = profile.UndoStack.Pop(); - string memento = JsonConvert.SerializeObject(profile.ProfileEntity, IProfileService.MementoSettings); - profile.RedoStack.Push(memento); - profile.ProfileEntity = - JsonConvert.DeserializeObject(top, IProfileService.MementoSettings) - ?? throw new InvalidOperationException("Failed to deserialize memento"); - - profile.Load(); - profile.PopulateLeds(_rgbService.EnabledDevices); - } - - _logger.Debug("Undo profile update - Success"); - return true; - } - - public bool RedoSaveProfile(Profile profile) - { - // Keep the profile from being rendered by locking it - lock (profile) - { - if (profile.RedoStack.Count == 0) - { - _logger.Debug("Redo profile update - Failed, redo empty"); - return false; - } - - string top = profile.RedoStack.Pop(); - string memento = JsonConvert.SerializeObject(profile.ProfileEntity, IProfileService.MementoSettings); - profile.UndoStack.Push(memento); - profile.ProfileEntity = - JsonConvert.DeserializeObject(top, IProfileService.MementoSettings) - ?? throw new InvalidOperationException("Failed to deserialize memento"); - - profile.Load(); - profile.PopulateLeds(_rgbService.EnabledDevices); - - _logger.Debug("Redo profile update - Success"); - return true; - } - } - + public ProfileConfigurationExportModel ExportProfile(ProfileConfiguration profileConfiguration) { // The profile may not be active and in that case lets activate it real quick @@ -596,8 +532,6 @@ namespace Artemis.Core.Services /// public void AdaptProfile(Profile profile) { - string memento = JsonConvert.SerializeObject(profile.ProfileEntity, IProfileService.MementoSettings); - List devices = _rgbService.EnabledDevices.ToList(); foreach (Layer layer in profile.GetAllLayers()) layer.Adapter.Adapt(devices); @@ -608,9 +542,6 @@ namespace Artemis.Core.Services renderProfileElement.Save(); _logger.Debug("Adapt profile - Saving " + profile); - profile.RedoStack.Clear(); - profile.UndoStack.Push(memento); - _profileRepository.Save(profile.ProfileEntity); } diff --git a/src/Artemis.Core/Utilities/MaxStack.cs b/src/Artemis.Core/Utilities/MaxStack.cs deleted file mode 100644 index 7b80c060c..000000000 --- a/src/Artemis.Core/Utilities/MaxStack.cs +++ /dev/null @@ -1,118 +0,0 @@ -// Source: https://ntsblog.homedev.com.au/index.php/2010/05/06/c-stack-with-maximum-limit/ -using System; -using System.Collections; -using System.Collections.Generic; - -namespace Artemis.Core -{ - /// - /// Generic stack implementation with a maximum limit - /// When something is pushed on the last item is removed from the list - /// - [Serializable] - internal class MaxStack - { - #region Fields - - private int _limit; - private LinkedList _list; - - #endregion - - #region Constructors - - public MaxStack(int maxSize) - { - _limit = maxSize; - _list = new LinkedList(); - - } - - #endregion - - #region Public Stack Implementation - - public void Push(T value) - { - if (_list.Count == _limit) - { - _list.RemoveLast(); - } - _list.AddFirst(value); - } - - public T Pop() - { - if (_list.Count > 0) - { - T value = _list.First!.Value; - _list.RemoveFirst(); - return value; - } - else - { - throw new InvalidOperationException("The Stack is empty"); - } - - - } - - public T Peek() - { - if (_list.Count > 0) - { - T value = _list.First!.Value; - return value; - } - else - { - throw new InvalidOperationException("The Stack is empty"); - } - - } - - public void Clear() - { - _list.Clear(); - - } - - public int Count - { - get { return _list.Count; } - } - - /// - /// Checks if the top object on the stack matches the value passed in - /// - /// - /// - public bool IsTop(T value) - { - bool result = false; - if (Count > 0) - { - result = Peek()!.Equals(value); - } - return result; - } - - public bool Contains(T value) - { - bool result = false; - if (Count > 0) - { - result = _list.Contains(value); - } - return result; - } - - public IEnumerator GetEnumerator() - { - return _list.GetEnumerator(); - } - - #endregion - - } -} \ No newline at end of file diff --git a/src/Artemis.UI/Screens/Sidebar/Dialogs/ProfileConfigurationEditViewModel.cs b/src/Artemis.UI/Screens/Sidebar/Dialogs/ProfileConfigurationEditViewModel.cs index 5fd5d40ff..d91864ba7 100644 --- a/src/Artemis.UI/Screens/Sidebar/Dialogs/ProfileConfigurationEditViewModel.cs +++ b/src/Artemis.UI/Screens/Sidebar/Dialogs/ProfileConfigurationEditViewModel.cs @@ -68,6 +68,7 @@ namespace Artemis.UI.Screens.Sidebar pluginManagementService.GetFeaturesOfType().Where(m => !m.IsAlwaysAvailable).Select(m => new ProfileModuleViewModel(m)) ); Modules.Insert(0, null); + _selectedModule = Modules.FirstOrDefault(m => m?.Module == _profileConfiguration.Module); VisualEditorViewModel = nodeVmFactory.NodeScriptViewModel(_profileConfiguration.ActivationCondition, true); diff --git a/src/Artemis.UI/Screens/Sidebar/SidebarCategoryView.axaml b/src/Artemis.UI/Screens/Sidebar/SidebarCategoryView.axaml index 172b34d08..774ecabbe 100644 --- a/src/Artemis.UI/Screens/Sidebar/SidebarCategoryView.axaml +++ b/src/Artemis.UI/Screens/Sidebar/SidebarCategoryView.axaml @@ -89,7 +89,32 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + { @@ -86,6 +89,9 @@ namespace Artemis.UI.Screens.Sidebar public ReactiveCommand ToggleSuspended { get; } public ReactiveCommand AddProfile { get; } public ReactiveCommand EditCategory { get; } + public ReactiveCommand MoveUp { get; } + public ReactiveCommand MoveDown { get; } + public ProfileCategory ProfileCategory { get; } public ReadOnlyObservableCollection ProfileConfigurations { get; } @@ -137,6 +143,36 @@ namespace Artemis.UI.Screens.Sidebar _profileService.SaveProfileCategory(ProfileCategory); } + private void ExecuteMoveUp() + { + List categories = _profileService.ProfileCategories.OrderBy(p => p.Order).ToList(); + int index = categories.IndexOf(ProfileCategory); + if (index <= 0) + return; + + categories[index - 1].Order++; + ProfileCategory.Order--; + _profileService.SaveProfileCategory(categories[index - 1]); + _profileService.SaveProfileCategory(ProfileCategory); + + _sidebarViewModel.UpdateProfileCategories(); + } + + private void ExecuteMoveDown() + { + List categories = _profileService.ProfileCategories.OrderBy(p => p.Order).ToList(); + int index = categories.IndexOf(ProfileCategory); + if (index >= categories.Count - 1) + return; + + categories[index + 1].Order--; + ProfileCategory.Order++; + _profileService.SaveProfileCategory(categories[index + 1]); + _profileService.SaveProfileCategory(ProfileCategory); + + _sidebarViewModel.UpdateProfileCategories(); + } + public void AddProfileConfiguration(ProfileConfiguration profileConfiguration, int? index) { ProfileCategory oldCategory = profileConfiguration.Category; diff --git a/src/Artemis.UI/Screens/Sidebar/SidebarProfileConfigurationViewModel.cs b/src/Artemis.UI/Screens/Sidebar/SidebarProfileConfigurationViewModel.cs index b89eb75c9..0aa2b0f9e 100644 --- a/src/Artemis.UI/Screens/Sidebar/SidebarProfileConfigurationViewModel.cs +++ b/src/Artemis.UI/Screens/Sidebar/SidebarProfileConfigurationViewModel.cs @@ -43,7 +43,8 @@ namespace Artemis.UI.Screens.Sidebar ExportProfile = ReactiveCommand.CreateFromTask(ExecuteExportProfile); DuplicateProfile = ReactiveCommand.Create(ExecuteDuplicateProfile); - this.WhenActivated(d => _isDisabled = ProfileConfiguration.WhenAnyValue(c => c.IsSuspended, c => c.ActivationConditionMet, (suspended, met) => suspended || !met) + this.WhenActivated(d => _isDisabled = ProfileConfiguration.WhenAnyValue(c => c.Profile) + .Select(p => p == null) .ToProperty(this, vm => vm.IsDisabled) .DisposeWith(d)); _profileService.LoadProfileConfigurationIcon(ProfileConfiguration);