From 45eb6a58ae7fd4e0f84d9e64eb78def0eebfe274 Mon Sep 17 00:00:00 2001 From: sr55 Date: Sat, 4 Mar 2017 17:22:26 +0000 Subject: WinGui: Rework of the preset system - Better support for categories. - Switched from a List to Treeview Control. - Remember the expansion state of each group - Put User Presets on top after next save. Closes #445 --- .../Services/Presets/Interfaces/IPresetObject.cs | 17 ++ .../Services/Presets/Interfaces/IPresetService.cs | 23 +- .../HandBrakeWPF/Services/Presets/Model/Preset.cs | 23 +- .../Presets/Model/PresetDisplayCategory.cs | 78 ++++++ .../HandBrakeWPF/Services/Presets/PresetService.cs | 285 ++++++++++++++++----- 5 files changed, 360 insertions(+), 66 deletions(-) create mode 100644 win/CS/HandBrakeWPF/Services/Presets/Interfaces/IPresetObject.cs create mode 100644 win/CS/HandBrakeWPF/Services/Presets/Model/PresetDisplayCategory.cs (limited to 'win/CS/HandBrakeWPF/Services/Presets') diff --git a/win/CS/HandBrakeWPF/Services/Presets/Interfaces/IPresetObject.cs b/win/CS/HandBrakeWPF/Services/Presets/Interfaces/IPresetObject.cs new file mode 100644 index 000000000..07c3d4d1b --- /dev/null +++ b/win/CS/HandBrakeWPF/Services/Presets/Interfaces/IPresetObject.cs @@ -0,0 +1,17 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// This file is part of the HandBrake source code - It may be used under the terms of the GNU General Public License. +// +// +// The Preset Service Interface +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace HandBrakeWPF.Services.Presets.Interfaces +{ + public interface IPresetObject + { + bool IsSelected { get; set; } + string Category { get; } + } +} \ No newline at end of file diff --git a/win/CS/HandBrakeWPF/Services/Presets/Interfaces/IPresetService.cs b/win/CS/HandBrakeWPF/Services/Presets/Interfaces/IPresetService.cs index fb46b932f..f75c054c5 100644 --- a/win/CS/HandBrakeWPF/Services/Presets/Interfaces/IPresetService.cs +++ b/win/CS/HandBrakeWPF/Services/Presets/Interfaces/IPresetService.cs @@ -23,7 +23,7 @@ namespace HandBrakeWPF.Services.Presets.Interfaces /// /// Gets a Collection of presets. /// - ObservableCollection Presets { get; } + ObservableCollection Presets { get; } /// /// Gets DefaultPreset. @@ -35,6 +35,16 @@ namespace HandBrakeWPF.Services.Presets.Interfaces /// void Load(); + /// + /// Save the state of the Preset Treview + /// + void SaveCategoryStates(); + + /// + /// Load the state of the Preset Treeview. + /// + void LoadCategoryStates(); + /// /// Add a new preset to the system /// @@ -83,7 +93,10 @@ namespace HandBrakeWPF.Services.Presets.Interfaces /// /// The Preset to remove /// - void Remove(Preset preset); + /// + /// True if it was removed successfully, false otherwise. + /// + bool Remove(Preset preset); /// /// Remove a group of presets by category @@ -155,5 +168,11 @@ namespace HandBrakeWPF.Services.Presets.Interfaces /// The replacement. /// void Replace(Preset existing, Preset replacement); + + /// + /// Set the selected preset + /// + /// The preset we want to select. + void SetSelected(Preset selectedPreset); } } \ No newline at end of file diff --git a/win/CS/HandBrakeWPF/Services/Presets/Model/Preset.cs b/win/CS/HandBrakeWPF/Services/Presets/Model/Preset.cs index 8b6aee917..5f179f67e 100644 --- a/win/CS/HandBrakeWPF/Services/Presets/Model/Preset.cs +++ b/win/CS/HandBrakeWPF/Services/Presets/Model/Preset.cs @@ -11,6 +11,7 @@ namespace HandBrakeWPF.Services.Presets.Model { using HandBrakeWPF.Model.Audio; using HandBrakeWPF.Model.Subtitles; + using HandBrakeWPF.Services.Presets.Interfaces; using HandBrakeWPF.Utilities; using EncodeTask = HandBrakeWPF.Services.Encode.Model.EncodeTask; @@ -24,7 +25,7 @@ namespace HandBrakeWPF.Services.Presets.Model /// https://github.com/Caliburn-Micro/Caliburn.Micro/issues/89 /// https://github.com/Caliburn-Micro/Caliburn.Micro/issues/96 /// - public class Preset : PropertyChangedBase // Delibery not + public class Preset : PropertyChangedBase, IPresetObject // Delibery not { #region Constants and Fields @@ -33,6 +34,8 @@ namespace HandBrakeWPF.Services.Presets.Model /// private bool isDefault; + private bool isSelected; + #endregion /// @@ -72,6 +75,24 @@ namespace HandBrakeWPF.Services.Presets.Model /// public string Description { get; set; } + /// + /// Reflects the visual state of this preset. + /// + public bool IsExpanded { get; set; } + + public bool IsSelected + { + get + { + return this.isSelected; + } + set + { + this.isSelected = value; + this.NotifyOfPropertyChange(() => this.IsSelected); + } + } + /// /// Gets or sets a value indicating whether this is a built in preset /// diff --git a/win/CS/HandBrakeWPF/Services/Presets/Model/PresetDisplayCategory.cs b/win/CS/HandBrakeWPF/Services/Presets/Model/PresetDisplayCategory.cs new file mode 100644 index 000000000..95d2213fb --- /dev/null +++ b/win/CS/HandBrakeWPF/Services/Presets/Model/PresetDisplayCategory.cs @@ -0,0 +1,78 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// This file is part of the HandBrake source code - It may be used under the terms of the GNU General Public License. +// +// +// A Preset Category encoding with. +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace HandBrakeWPF.Services.Presets.Model +{ + using System.ComponentModel; + using Caliburn.Micro; + using HandBrakeWPF.Services.Presets.Interfaces; + + public class PresetDisplayCategory : PropertyChangedBase, IPresetObject + { + private bool isSelected; + private bool isExpanded; + + public PresetDisplayCategory(string category, BindingList presets) + { + this.Category = category; + this.Presets = presets; + } + + public string Category { get; private set; } + public BindingList Presets { get; private set; } + + public string Description => this.Category; + + public bool IsExpanded + { + get + { + return this.isExpanded; + } + set + { + if (value == this.isExpanded) return; + this.isExpanded = value; + this.NotifyOfPropertyChange(() => this.IsExpanded); + } + } + + public bool IsSelected + { + get + { + return this.isSelected; + } + set + { + if (value == this.isSelected) return; + this.isSelected = value; + this.NotifyOfPropertyChange(() => this.IsSelected); + } + } + + protected bool Equals(PresetDisplayCategory other) + { + return string.Equals(this.Category, other.Category); + } + + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) return false; + if (ReferenceEquals(this, obj)) return true; + if (obj.GetType() != this.GetType()) return false; + return Equals((PresetDisplayCategory)obj); + } + + public override int GetHashCode() + { + return (this.Category != null ? this.Category.GetHashCode() : 0); + } + } +} diff --git a/win/CS/HandBrakeWPF/Services/Presets/PresetService.cs b/win/CS/HandBrakeWPF/Services/Presets/PresetService.cs index 78387e54e..a90bf1543 100644 --- a/win/CS/HandBrakeWPF/Services/Presets/PresetService.cs +++ b/win/CS/HandBrakeWPF/Services/Presets/PresetService.cs @@ -12,6 +12,8 @@ namespace HandBrakeWPF.Services.Presets using System; using System.Collections.Generic; using System.Collections.ObjectModel; + using System.Collections.Specialized; + using System.ComponentModel; using System.Diagnostics; using System.IO; using System.Linq; @@ -46,7 +48,8 @@ namespace HandBrakeWPF.Services.Presets public const int ForcePresetReset = 3; public static string UserPresetCatgoryName = "User Presets"; private readonly string presetFile = Path.Combine(DirectoryUtilities.GetUserStoragePath(VersionHelper.IsNightly()), "presets.json"); - private readonly ObservableCollection presets = new ObservableCollection(); + private readonly ObservableCollection presets = new ObservableCollection(); // Can store Presets and PresetDisplayCategory objects. + private readonly Dictionary flatPresetList = new Dictionary(); private readonly IErrorService errorService; private readonly IUserSettingService userSettingService; @@ -70,7 +73,7 @@ namespace HandBrakeWPF.Services.Presets /// /// Gets a Collection of presets. /// - public ObservableCollection Presets + public ObservableCollection Presets { get { @@ -85,7 +88,7 @@ namespace HandBrakeWPF.Services.Presets { get { - return this.presets.FirstOrDefault(p => p.IsDefault); + return this.flatPresetList.Values.FirstOrDefault(p => p.IsDefault); } } @@ -100,12 +103,18 @@ namespace HandBrakeWPF.Services.Presets if (!File.Exists(this.presetFile)) { this.UpdateBuiltInPresets(); + return; } // Load the presets from file this.LoadPresets(); } + public bool Add(Preset preset) + { + return this.Add(preset, false); + } + /// /// Add a new preset to the system. /// Performs an Update if it already exists @@ -113,23 +122,45 @@ namespace HandBrakeWPF.Services.Presets /// /// A Preset to add /// + /// + /// Prevents Saving of presets. + /// /// /// True if added, /// False if name already exists /// - public bool Add(Preset preset) + public bool Add(Preset preset, bool isLoading) { if (!this.CheckIfPresetExists(preset.Name)) { - this.presets.Add(preset); + // Check to see if the category already exists. + PresetDisplayCategory category = this.presets.FirstOrDefault(a => a.Category == preset.Category) as PresetDisplayCategory; // TODO build Dict for this. + if (category != null) + { + category.Presets.Add(preset); + } + else if (!string.IsNullOrEmpty(preset.Category)) + { + // Otherwise, if we have category but it doesn't exist, create it. + this.presets.Add(new PresetDisplayCategory(preset.Category, new BindingList { preset })); + } + else + { + // Preset has no category. + this.presets.Add(preset); + } + + this.flatPresetList.Add(preset.Name, preset); // Update the presets file - this.SavePresetFiles(); + if (!isLoading) + { + this.SavePresetFiles(); + } return true; } - this.Update(preset); - return true; + return false; } /// @@ -170,7 +201,7 @@ namespace HandBrakeWPF.Services.Presets try { preset = JsonPresetFactory.ImportPreset(hbPreset); - preset.Category = UserPresetCatgoryName; + preset.Category = UserPresetCatgoryName; // TODO can we get this from the preset? // IF we are using Source Max, Set the Max Width / Height values. if (preset.PictureSettingsMode == PresetPictureSettingsMode.SourceMaximum) @@ -206,13 +237,10 @@ namespace HandBrakeWPF.Services.Presets } else { - this.Add(preset); + this.Add(preset, false); } } } - - // Category Handling. - // TODO maybe for a future release. } } @@ -243,22 +271,18 @@ namespace HandBrakeWPF.Services.Presets /// public void Update(Preset update) { - // TODO - Change this to be a lookup - foreach (Preset preset in this.presets) + Preset preset; + if (this.flatPresetList.TryGetValue(update.Name, out preset)) { - if (preset.Name == update.Name) - { - preset.Task = update.Task; - preset.PictureSettingsMode = update.PictureSettingsMode; - preset.Category = update.Category; - preset.Description = update.Description; - preset.AudioTrackBehaviours = update.AudioTrackBehaviours; - preset.SubtitleTrackBehaviours = update.SubtitleTrackBehaviours; - - // Update the presets file - this.SavePresetFiles(); - break; - } + preset.Task = update.Task; + preset.PictureSettingsMode = update.PictureSettingsMode; + preset.Category = update.Category; + preset.Description = update.Description; + preset.AudioTrackBehaviours = update.AudioTrackBehaviours; + preset.SubtitleTrackBehaviours = update.SubtitleTrackBehaviours; + + // Update the presets file + this.SavePresetFiles(); } } @@ -274,7 +298,7 @@ namespace HandBrakeWPF.Services.Presets public void Replace(Preset existing, Preset replacement) { this.Remove(existing); - this.Add(replacement); + this.Add(replacement, false); } /// @@ -283,54 +307,86 @@ namespace HandBrakeWPF.Services.Presets /// /// The Preset to remove /// - public void Remove(Preset preset) + /// True if successfully removed, false otherwise. + public bool Remove(Preset preset) { if (preset == null || preset.IsDefault) { - return; + return false; + } + + PresetDisplayCategory cateogry = this.presets.FirstOrDefault(p => p.Category == preset.Category) as PresetDisplayCategory; + if (cateogry != null) + { + // Remove the preset, and cleanup the category if it's not got any presets in it. + cateogry.Presets.Remove(preset); + this.flatPresetList.Remove(preset.Name); + if (cateogry.Presets.Count == 0) + { + this.presets.Remove(cateogry); + } + } + else + { + this.presets.Remove(preset); + this.flatPresetList.Remove(preset.Name); } - this.presets.Remove(preset); this.SavePresetFiles(); + + return true; } /// /// Remove a group of presets by category /// - /// + /// /// The Category to remove /// - public void RemoveGroup(string category) + public void RemoveGroup(string categoryName) { - List removeList = this.presets.Where(p => p.Category == category).ToList(); - foreach (Preset preset in removeList) + PresetDisplayCategory category = this.presets.FirstOrDefault(p => p.Category == categoryName) as PresetDisplayCategory; + if (category != null) { - if (preset.IsDefault) + foreach (Preset preset in category.Presets) { - // Skip default preset - continue; + if (preset.IsDefault) + { + // Skip default preset + continue; + } + + this.presets.Remove(preset); + this.flatPresetList.Remove(preset.Name); } - this.presets.Remove(preset); - } + // Cleanup the category if we can. + if (category.Presets.Count == 0) + { + this.presets.Remove(category); + } - this.SavePresetFiles(); + this.SavePresetFiles(); + } } /// /// Set Default Preset /// - /// + /// /// The name. /// - public void SetDefault(Preset name) + public void SetDefault(Preset preset) { - foreach (Preset preset in this.presets) + // Set IsDefault false for everything. + foreach (Preset item in this.flatPresetList.Values) { - preset.IsDefault = false; + item.IsDefault = false; } - name.IsDefault = true; + // Set the new preset to default. + preset.IsDefault = true; + this.SavePresetFiles(); } @@ -345,7 +401,13 @@ namespace HandBrakeWPF.Services.Presets /// public Preset GetPreset(string name) { - return this.presets.FirstOrDefault(item => item.Name == name); + Preset preset; + if (this.flatPresetList.TryGetValue(name, out preset)) + { + return preset; + } + + return null; } /// @@ -353,10 +415,54 @@ namespace HandBrakeWPF.Services.Presets /// public void ClearBuiltIn() { - List remove = this.presets.Where(p => p.IsBuildIn).ToList(); - foreach (Preset preset in remove) + List topLevel = new List(); + foreach (IPresetObject item in this.presets) { - this.presets.Remove(preset); + // We either have a Preset + Preset foundPreset = item as Preset; + if (foundPreset != null && foundPreset.IsBuildIn) + { + topLevel.Add(item); + } + + // Or a Category. + PresetDisplayCategory foundCategory = item as PresetDisplayCategory; + if (foundCategory != null) + { + // Find all the presets in this category to remove + List presetsToRemove = new List(); + foreach (Preset categoryPreset in foundCategory.Presets) + { + if (categoryPreset.IsBuildIn) + { + presetsToRemove.Add(categoryPreset); + } + } + + // Then remove them. + foreach (Preset toRemove in presetsToRemove) + { + foundCategory.Presets.Remove(toRemove); + this.flatPresetList.Remove(toRemove.Name); + } + + // Check if we can remove this category. + if (foundCategory.Presets.Count == 0) + { + topLevel.Add(foundCategory); + } + } + } + + // Remove any top level items we need to remove. + foreach (var item in topLevel) + { + this.presets.Remove(item); + + if (item.GetType() == typeof(Preset)) + { + this.flatPresetList.Remove(((Preset)item).Name); + } } } @@ -366,6 +472,7 @@ namespace HandBrakeWPF.Services.Presets public void ClearAll() { this.presets.Clear(); + this.flatPresetList.Clear(); } /// @@ -385,15 +492,9 @@ namespace HandBrakeWPF.Services.Presets Preset preset = JsonPresetFactory.ImportPreset(hbpreset); preset.IsBuildIn = true; preset.Category = category.PresetName; - - if (preset.Name == "iPod") - { - preset.Task.KeepDisplayAspect = true; - } - preset.Task.AllowedPassthruOptions = new AllowedPassthru(true); // We don't want to override the built-in preset - this.presets.Add(preset); + this.Add(preset, true); } } @@ -418,7 +519,12 @@ namespace HandBrakeWPF.Services.Presets /// public bool CheckIfPresetExists(string name) { - return name == string.Empty || this.presets.Any(item => item.Name == name); + if (this.flatPresetList.ContainsKey(name)) + { + return true; + } + + return false; } /// @@ -432,7 +538,60 @@ namespace HandBrakeWPF.Services.Presets /// public bool CanUpdatePreset(string name) { - return this.presets.Where(preset => preset.Name == name).Any(preset => preset.IsBuildIn == false); + Preset preset; + if (this.flatPresetList.TryGetValue(name, out preset)) + { + return !preset.IsBuildIn; + } + + return true; + } + + /// + /// Set the selected preset + /// + /// The preset we want to select. + public void SetSelected(Preset selectedPreset) + { + foreach (var item in this.flatPresetList.Values) + { + item.IsSelected = false; + } + + selectedPreset.IsSelected = true; + } + + public void SaveCategoryStates() + { + StringCollection expandedPresets = new StringCollection(); + foreach (IPresetObject presetObject in this.presets) + { + PresetDisplayCategory category = presetObject as PresetDisplayCategory; + if (category != null && category.IsExpanded) + { + expandedPresets.Add(category.Category); + } + } + + this.userSettingService.SetUserSetting(UserSettingConstants.PresetExpandedStateList, expandedPresets); + } + + public void LoadCategoryStates() + { + StringCollection expandedPresets = this.userSettingService.GetUserSetting(UserSettingConstants.PresetExpandedStateList); + if (expandedPresets == null || expandedPresets.Count == 0) + { + return; + } + + foreach (IPresetObject presetObject in this.presets) + { + PresetDisplayCategory category = presetObject as PresetDisplayCategory; + if (category != null && expandedPresets.Contains(category.Category)) + { + category.IsExpanded = true; + } + } } #endregion @@ -609,7 +768,7 @@ namespace HandBrakeWPF.Services.Presets preset.Task.MaxHeight = preset.Task.Width; } - this.presets.Add(preset); + this.Add(preset, true); } } @@ -629,7 +788,7 @@ namespace HandBrakeWPF.Services.Presets preset.Task.MaxHeight = preset.Task.Width; } - this.presets.Add(preset); + this.Add(preset, true); } } } @@ -658,7 +817,7 @@ namespace HandBrakeWPF.Services.Presets // Orgamise the Presets list into Json Equivilent objects. Dictionary presetCategories = new Dictionary(); List uncategorisedPresets = new List(); - foreach (Preset item in this.presets) + foreach (Preset item in this.flatPresetList.Values.OrderBy(o => o.IsBuildIn)) // Handle User Presets first. { if (string.IsNullOrEmpty(item.Category)) { -- cgit v1.2.3