From ae7c3a3fff8fe5c53f11b630464b209a6ca76c5a Mon Sep 17 00:00:00 2001 From: adroslice Date: Thu, 21 Nov 2019 19:07:46 +0100 Subject: [PATCH] OperationMode overhaul, improved event handling, fixed properties - OperationMode now designed and implemented in a way that allows serialization - Modes are now defined using an enumeration with with attributes decorated values - MainWindow.xaml.cs's event handlers now only listen for changes to certain properties - Property naming conventions fixed - AvaloniaProperties are now public + static + readonly --- BFR/Helpers/Extensions.cs | 11 +++ BFR/MainWindow.xaml | 68 +++++++++---------- BFR/MainWindow.xaml.cs | 14 +++- BFR/MainWindowUIProperties.cs | 12 ++-- .../Attributes/DescriptionAttribute.cs | 12 ++++ .../Attributes/DisplayNameAttribute.cs | 12 ++++ BFR/Operations/Operation.cs | 7 +- BFR/Operations/OperationMode.cs | 19 +++--- BFR/Operations/Replace.cs | 45 +++++------- BFR/Operations/ReplaceMode.cs | 52 +++++++++++--- 10 files changed, 160 insertions(+), 92 deletions(-) create mode 100644 BFR/Operations/Attributes/DescriptionAttribute.cs create mode 100644 BFR/Operations/Attributes/DisplayNameAttribute.cs diff --git a/BFR/Helpers/Extensions.cs b/BFR/Helpers/Extensions.cs index 7b309e2..fc056db 100644 --- a/BFR/Helpers/Extensions.cs +++ b/BFR/Helpers/Extensions.cs @@ -27,4 +27,15 @@ namespace BFR.Helpers catch(Exception) { return false; } } } + + public static class EnumExtensions + { + public static T GetAttribute(this Enum enumVal) where T : Attribute + { + var type = enumVal.GetType(); + var memInfo = type.GetMember(enumVal.ToString()); + var attributes = memInfo[0].GetCustomAttributes(typeof(T), false); + return (attributes.Length > 0) ? (T)attributes[0] : null; + } + } } diff --git a/BFR/MainWindow.xaml b/BFR/MainWindow.xaml index fde5052..b5e5193 100644 --- a/BFR/MainWindow.xaml +++ b/BFR/MainWindow.xaml @@ -166,31 +166,31 @@ - + - - - - - - - - - - - - + + + + + + + + + + + + - - - - + + + + @@ -200,31 +200,31 @@ - + - - - - - - - - - - - - + + + + + + + + + + + + - - - - + + + + diff --git a/BFR/MainWindow.xaml.cs b/BFR/MainWindow.xaml.cs index bfcb4d1..fc65dba 100644 --- a/BFR/MainWindow.xaml.cs +++ b/BFR/MainWindow.xaml.cs @@ -16,6 +16,8 @@ namespace BFR { public partial class MainWindow : Window { + public readonly AvaloniaProperty[] HandledProperties = new AvaloniaProperty[] { TextBox.TextProperty, ComboBox.SelectedItemProperty, NumericUpDown.ValueProperty, CheckBox.IsCheckedProperty }; + public async Task OpenDirectoryButtonClick() => OpenDirectory(await new OpenFolderDialog() { Directory = WorkingDirectory }.ShowAsync(this)); public void OpenDirectory(string directory) { @@ -24,7 +26,11 @@ namespace BFR Filter(); } - public void FilterChanged(object sender, AvaloniaPropertyChangedEventArgs e) => Filter(); // Bind to PropertyChanged + public void FilterChanged(object sender, AvaloniaPropertyChangedEventArgs e) + { + if(HandledProperties.Contains(e.Property)) + Filter(); // Bind to PropertyChanged + } public void Filter() { // Filter all files in the directory for those satisfying the given filters @@ -35,7 +41,11 @@ namespace BFR Preview(); } - public void PreviewChanged(object sender, AvaloniaPropertyChangedEventArgs e) => Preview(); // Bind to PropertyChanged + public void PreviewChanged(object sender, AvaloniaPropertyChangedEventArgs e) + { + if (HandledProperties.Contains(e.Property)) + Preview(); // Bind to PropertyChanged + } public void Preview() { // Reset all files to how they currently exist diff --git a/BFR/MainWindowUIProperties.cs b/BFR/MainWindowUIProperties.cs index 43f4c73..4dd69f3 100644 --- a/BFR/MainWindowUIProperties.cs +++ b/BFR/MainWindowUIProperties.cs @@ -11,9 +11,9 @@ namespace BFR { public partial class MainWindow : Window { - private readonly AvaloniaProperty workingDirectoryProperty = + public static readonly AvaloniaProperty WorkingDirectoryProperty = AvaloniaProperty.Register(nameof(WorkingDirectory)); - public string WorkingDirectory { get => GetValue(workingDirectoryProperty); set => SetValue(workingDirectoryProperty, value); } + public string WorkingDirectory { get => GetValue(WorkingDirectoryProperty); set => SetValue(WorkingDirectoryProperty, value); } public AvaloniaList AllFiles { get; } = new AvaloniaList(); public AvaloniaList Files { get; } = new AvaloniaList(); @@ -28,12 +28,12 @@ namespace BFR OperationType.Make(), }; - public readonly AvaloniaProperty isCommitButtonEnabled = + public static readonly AvaloniaProperty IsCommitButtonEnabledProperty = AvaloniaProperty.Register("IsCommitButtonEnabled", defaultValue: false); - public bool IsCommitButtonEnabled { get => GetValue(isCommitButtonEnabled); set => SetValue(isCommitButtonEnabled, value); } - public readonly AvaloniaProperty undoCount = + public bool IsCommitButtonEnabled { get => GetValue(IsCommitButtonEnabledProperty); set => SetValue(IsCommitButtonEnabledProperty, value); } + public static readonly AvaloniaProperty UndoCountProperty = AvaloniaProperty.Register("UndoCount", defaultValue: 0); - public int UndoCount { get => GetValue(undoCount); set => SetValue(undoCount, value); } + public int UndoCount { get => GetValue(UndoCountProperty); set => SetValue(UndoCountProperty, value); } private readonly Stack> UndoStack = new Stack>(); // Filters diff --git a/BFR/Operations/Attributes/DescriptionAttribute.cs b/BFR/Operations/Attributes/DescriptionAttribute.cs new file mode 100644 index 0000000..57b32b6 --- /dev/null +++ b/BFR/Operations/Attributes/DescriptionAttribute.cs @@ -0,0 +1,12 @@ +using System; + +namespace BFR.Operations.Attributes +{ + class DescriptionAttribute : Attribute + { + public string Description { get; } + + public DescriptionAttribute(string description) => + Description = description; + } +} diff --git a/BFR/Operations/Attributes/DisplayNameAttribute.cs b/BFR/Operations/Attributes/DisplayNameAttribute.cs new file mode 100644 index 0000000..6982347 --- /dev/null +++ b/BFR/Operations/Attributes/DisplayNameAttribute.cs @@ -0,0 +1,12 @@ +using System; + +namespace BFR.Operations.Attributes +{ + class DisplayNameAttribute : Attribute + { + public string DisplayName { get; } + + public DisplayNameAttribute(string displayName) => + DisplayName = displayName; + } +} diff --git a/BFR/Operations/Operation.cs b/BFR/Operations/Operation.cs index 8b9426a..7487fcb 100644 --- a/BFR/Operations/Operation.cs +++ b/BFR/Operations/Operation.cs @@ -1,6 +1,7 @@ using System; using System.Linq; using System.Collections.Generic; +using System.Xml.Serialization; using Avalonia; @@ -11,8 +12,10 @@ namespace BFR.Operations public abstract class Operation : AvaloniaObject { // Needs to be avalonia property to update UI with any potential error. - private readonly AvaloniaProperty errorProperty = AvaloniaProperty.Register(nameof(Error), defaultValue: ""); - public string Error { get => GetValue(errorProperty); set => SetValue(errorProperty, value); } + [XmlIgnore] + public static readonly AvaloniaProperty ErrorProperty = AvaloniaProperty.Register(nameof(Error), defaultValue: ""); + [XmlIgnore] + public string Error { get => GetValue(ErrorProperty); set => SetValue(ErrorProperty, value); } public bool IsEnabled { get; set; } = true; public abstract string Name { get; } public abstract string Description { get; } diff --git a/BFR/Operations/OperationMode.cs b/BFR/Operations/OperationMode.cs index 2ba79fb..1b9baac 100644 --- a/BFR/Operations/OperationMode.cs +++ b/BFR/Operations/OperationMode.cs @@ -1,11 +1,14 @@ -namespace BFR.Operations -{ - public class OperationMode - { - public int Index { get; } - public string Name { get; } +using System; - public OperationMode(int index, string name) => - (Index, Name) = (index, name); +namespace BFR.Operations +{ + public abstract class OperationMode where T : Enum + { + public T Index { get; } + public string Name { get; } + public string Description { get; } + + protected OperationMode(T index, string name, string description) => + (Index, Name, Description) = (index, name, description); } } diff --git a/BFR/Operations/Replace.cs b/BFR/Operations/Replace.cs index 1f7351a..35b62ff 100644 --- a/BFR/Operations/Replace.cs +++ b/BFR/Operations/Replace.cs @@ -1,33 +1,27 @@ using System; using System.Linq; +using System.Xml.Serialization; using System.Collections.Generic; +using System.Text.RegularExpressions; using Avalonia; using BFR.Helpers; using BFR.DataModels; -using System.Text.RegularExpressions; namespace BFR.Operations { public class Replace : Operation { - private static readonly ReplaceMode DefaultMode = new ReplaceMode(0, "Pattern"); - - public override string Name => "Replace"; + public override string Name => nameof(Replace); public override string Description => "Replaces the selected part of the file name."; - public AvaloniaProperty modeProperty = - AvaloniaProperty.Register(nameof(Mode), defaultValue: DefaultMode); - public ReplaceMode Mode { get => GetValue(modeProperty); set => SetValue(modeProperty, value); } - public ReplaceMode[] Modes { get; } = new ReplaceMode[] - { - DefaultMode, - new ReplaceMode(1, "Characters"), - new ReplaceMode(2, "FromNToN"), - new ReplaceMode(3, "FirstN"), - new ReplaceMode(4, "LastN"), - }; + public static readonly AvaloniaProperty ModeProperty = + AvaloniaProperty.Register(nameof(Mode), ReplaceMode.Pattern); + public ReplaceMode Mode { get => GetValue(ModeProperty); set => SetValue(ModeProperty, value); } + + [XmlIgnore] + public ReplaceOperationMode[] Modes => ReplaceOperationMode.Modes; public string Replacement { get; set; } = ""; public bool FullName { get; set; } = false; @@ -51,7 +45,7 @@ namespace BFR.Operations if (FromN > ToN) throw new OperationException("From N can't be greater than To N."); // Regex Failure - if(Mode.IsPatternMode && UseRegex) + if(Modes[(int)Mode].IsPattern && UseRegex) try { "".Replace(Pattern, Replacement, true, false); } catch(Exception) { throw new OperationException("Invalid Regex Pattern."); } @@ -60,13 +54,13 @@ namespace BFR.Operations { var newName = FullName ? file.FullName : file.Name; - newName = Mode.Index switch + newName = Mode switch { - 0 => newName.Replace(Pattern, Replacement, UseRegex, UseRegexReplace), - 1 => Characters.Length > 0 ? Regex.Replace(newName, $"[{Characters}]", Replacement.Replace("$", "$$")) : newName, - 2 => Regex.Replace(newName, $"^(.{{{FromN}}}).+(.{{{newName.Length - ToN - 1}}})$", $"$1{Replacement.Replace("$", "$$")}$2"), - 3 => Regex.Replace(newName, $"^.{{{FirstN}}}", Replacement.Replace("$", "$$")), - 4 => Regex.Replace(newName, $".{{{LastN}}}$", Replacement.Replace("$", "$$")), + ReplaceMode.Pattern => newName.Replace(Pattern, Replacement, UseRegex, UseRegexReplace), + ReplaceMode.Characters => Characters.Length > 0 ? Regex.Replace(newName, $"[{Characters}]", Replacement.Replace("$", "$$")) : newName, + ReplaceMode.FromNToN => Regex.Replace(newName, $"^(.{{{FromN}}}).+(.{{{newName.Length - ToN - 1}}})$", $"$1{Replacement.Replace("$", "$$")}$2"), + ReplaceMode.FirstN => Regex.Replace(newName, $"^.{{{FirstN}}}", Replacement.Replace("$", "$$")), + ReplaceMode.LastN => Regex.Replace(newName, $".{{{LastN}}}$", Replacement.Replace("$", "$$")), _ => newName }; @@ -74,12 +68,5 @@ namespace BFR.Operations else file.Name = newName; } } - - private string ReplaceAllCharacters(string input, string characters, string replacement) - { - foreach (var character in characters) - input = input.Replace(character.ToString(), replacement); - return input; - } } } diff --git a/BFR/Operations/ReplaceMode.cs b/BFR/Operations/ReplaceMode.cs index b42f7db..877c1af 100644 --- a/BFR/Operations/ReplaceMode.cs +++ b/BFR/Operations/ReplaceMode.cs @@ -1,14 +1,44 @@ -namespace BFR.Operations -{ - public class ReplaceMode : OperationMode - { - public bool IsPatternMode => Index == 0; - public bool IsCharactersMode => Index == 1; - public bool IsFromNToNMode => Index == 2; - public bool IsFirstNMode => Index == 3; - public bool IsLastNMode => Index == 4; +using System; +using System.Linq; +using System.Collections.Generic; - public ReplaceMode(int index, string name) : - base(index, name) { } +using BFR.Helpers; +using BFR.Operations.Attributes; + +namespace BFR.Operations +{ + public class ReplaceOperationMode : OperationMode + { + public bool IsPattern => Index == ReplaceMode.Pattern; + public bool IsCharacters => Index == ReplaceMode.Characters; + public bool IsFromNToN => Index == ReplaceMode.FromNToN; + public bool IsFirstN => Index == ReplaceMode.FirstN; + public bool IsLastN => Index == ReplaceMode.LastN; + + public readonly static ReplaceOperationMode[] Modes = All(); + + protected ReplaceOperationMode(ReplaceMode index, string name, string description) : + base(index, name, description) { } + + protected static ReplaceOperationMode[] All() => ((IEnumerable)Enum.GetValues(typeof(ReplaceMode))).Select(x => + new ReplaceOperationMode( + x, + x.GetAttribute().DisplayName, + x.GetAttribute().Description + )).ToArray(); + } + + public enum ReplaceMode + { + [DisplayName(nameof(Pattern)), Description("Replaces the specified pattern.")] + Pattern, + [DisplayName(nameof(Characters)), Description("Replaces each of the specified characters.")] + Characters, + [DisplayName("From N to N"), Description("Replaces everything in the specified range (zero-based indeces).")] + FromNToN, + [DisplayName("First N"), Description("Replaces the first N characters.")] + FirstN, + [DisplayName("Last N"), Description("Replaces the last N characters.")] + LastN, } }