Added Replace-Operation, structuring and reworked event bindings

- Added Replace-Operation + UI
- Added OperationMode to make it easier to give operations different modes
- Replaced all specific-control-event-handlers with one general PropertyChanged-event-handler for each preview and filter
- TODO: Add Remove-Operation as a subclass of Replace
This commit is contained in:
adroslice 2019-11-17 16:22:19 +01:00
parent 2aa1fa6529
commit c519147302
6 changed files with 164 additions and 19 deletions

View File

@ -6,6 +6,7 @@
mc:Ignorable="d" MinWidth="720" MinHeight="480" mc:Ignorable="d" MinWidth="720" MinHeight="480"
x:Class="BFR.MainWindow" x:Class="BFR.MainWindow"
Title="BFR"> Title="BFR">
<Grid RowDefinitions="auto,auto,*" ColumnDefinitions="*,*,*" Classes="StyleBorders"> <Grid RowDefinitions="auto,auto,*" ColumnDefinitions="*,*,*" Classes="StyleBorders">
<!-- Directory Selection + Filters --> <!-- Directory Selection + Filters -->
<Border Grid.Row="0" Grid.ColumnSpan="3"> <Border Grid.Row="0" Grid.ColumnSpan="3">
@ -17,13 +18,13 @@
<!-- Filters --> <!-- Filters -->
<Grid Grid.Row="1" Grid.ColumnSpan="3" ColumnDefinitions="auto,*,auto,*,auto,auto,auto,auto" IsVisible="{Binding #FilterExpander.IsExpanded}"> <Grid Grid.Row="1" Grid.ColumnSpan="3" ColumnDefinitions="auto,*,auto,*,auto,auto,auto,auto" IsVisible="{Binding #FilterExpander.IsExpanded}">
<TextBlock Grid.Column="0" Text="Extension:"/> <TextBlock Grid.Column="0" Text="Extension:"/>
<TextBox Grid.Column="1" Text="{Binding FilterExtension}" KeyUp="FilterChanged"/> <TextBox Grid.Column="1" Text="{Binding FilterExtension}" PropertyChanged="FilterChanged"/>
<TextBlock Grid.Column="2" Text="Name:"/> <TextBlock Grid.Column="2" Text="Name:"/>
<TextBox Grid.Column="3" Text="{Binding FilterPattern}" KeyUp="FilterChanged"/> <TextBox Grid.Column="3" Text="{Binding FilterPattern}" PropertyChanged="FilterChanged"/>
<TextBlock Grid.Column="4" Text="Full Name:"/> <TextBlock Grid.Column="4" Text="Full Name:"/>
<CheckBox Grid.Column="5" IsChecked="{Binding FilterFullName}" Click="FilterChanged"/> <CheckBox Grid.Column="5" IsChecked="{Binding FilterFullName}" PropertyChanged="FilterChanged"/>
<TextBlock Grid.Column="6" Text="Regex:"/> <TextBlock Grid.Column="6" Text="Regex:"/>
<CheckBox Grid.Column="7" IsChecked="{Binding FilterRegex}" Click="FilterChanged" Margin="4,4,5,4"/> <CheckBox Grid.Column="7" IsChecked="{Binding FilterRegex}" PropertyChanged="FilterChanged" Margin="4,4,5,4"/>
</Grid> </Grid>
</Grid> </Grid>
</Border> </Border>
@ -99,6 +100,10 @@
<Setter Property="Margin" Value="4"/> <Setter Property="Margin" Value="4"/>
<Setter Property="Grid.Column" Value="1"/> <Setter Property="Grid.Column" Value="1"/>
</Style> </Style>
<Style Selector="Grid > NumericUpDown">
<Setter Property="Margin" Value="4"/>
<Setter Property="Grid.Column" Value="1"/>
</Style>
<Style Selector="Grid > CheckBox"> <Style Selector="Grid > CheckBox">
<Setter Property="Margin" Value="4"/> <Setter Property="Margin" Value="4"/>
<Setter Property="Grid.Column" Value="1"/> <Setter Property="Grid.Column" Value="1"/>
@ -151,12 +156,45 @@
<Expander Classes="OperationExpander"> <Expander Classes="OperationExpander">
<Grid ColumnDefinitions="auto,*" RowDefinitions="*,*"> <Grid ColumnDefinitions="auto,*" RowDefinitions="*,*">
<TextBlock Grid.Row="0" Text="Replacement:"/> <TextBlock Grid.Row="0" Text="Replacement:"/>
<TextBox Grid.Row="0" Text="{Binding Replacement}" KeyUp="PreviewChanged"/> <TextBox Grid.Row="0" Text="{Binding Replacement}" PropertyChanged="PreviewChanged"/>
<TextBlock Grid.Row="1" Text="Full Name:"/> <TextBlock Grid.Row="1" Text="Full Name:"/>
<CheckBox Grid.Row="1" IsChecked="{Binding FullName}" Click="PreviewChanged"/> <CheckBox Grid.Row="1" IsChecked="{Binding FullName}" PropertyChanged="PreviewChanged"/>
</Grid>
</Expander>
</DataTemplate>
<DataTemplate DataType="{x:Type ops:Replace}">
<Expander Classes="OperationExpander">
<Grid ColumnDefinitions="auto,*" RowDefinitions="auto,auto,auto,auto,auto,auto,auto,auto,auto,auto,auto">
<TextBlock Grid.Row="0" Text="Mode:"/>
<ComboBox Grid.Row="0" Items="{Binding Modes}" SelectedItem="{Binding Mode}" PropertyChanged="PreviewChanged">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}"/>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
<TextBlock Grid.Row="1" Text="Pattern:" IsVisible="{Binding Mode.IsPatternMode}"/>
<TextBox Grid.Row="1" Text="{Binding Pattern}" IsVisible="{Binding Mode.IsPatternMode}" PropertyChanged="PreviewChanged"/>
<TextBlock Grid.Row="2" Text="Characters:" IsVisible="{Binding Mode.IsCharactersMode}"/>
<TextBox Grid.Row="2" Text="{Binding Characters}" IsVisible="{Binding Mode.IsCharactersMode}" PropertyChanged="PreviewChanged"/>
<TextBlock Grid.Row="3" Text="From N:" IsVisible="{Binding Mode.IsFromNToNMode}"/>
<NumericUpDown Grid.Row="3" Value="{Binding FromN}" IsVisible="{Binding Mode.IsFromNToNMode}" PropertyChanged="PreviewChanged"/>
<TextBlock Grid.Row="4" Text="To N:" IsVisible="{Binding Mode.IsFromNToNMode}"/>
<NumericUpDown Grid.Row="4" Value="{Binding ToN}" IsVisible="{Binding Mode.IsFromNToNMode}" PropertyChanged="PreviewChanged"/>
<TextBlock Grid.Row="5" Text="First N:" IsVisible="{Binding Mode.IsFirstNMode}"/>
<NumericUpDown Grid.Row="5" Value="{Binding FirstN}" IsVisible="{Binding Mode.IsFirstNMode}" PropertyChanged="PreviewChanged"/>
<TextBlock Grid.Row="6" Text="Last N:" IsVisible="{Binding Mode.IsLastNMode}"/>
<NumericUpDown Grid.Row="6" Value="{Binding LastN}" IsVisible="{Binding Mode.IsLastNMode}" PropertyChanged="PreviewChanged"/>
<TextBlock Grid.Row="7" Text="Replacement:"/>
<TextBox Grid.Row="7" Text="{Binding Replacement}" PropertyChanged="PreviewChanged"/>
<TextBlock Grid.Row="8" Text="Regex:" IsVisible="{Binding Mode.IsPatternMode}"/>
<CheckBox Grid.Row="8" IsChecked="{Binding UseRegex}" IsVisible="{Binding Mode.IsPatternMode}" PropertyChanged="PreviewChanged"/>
<TextBlock Grid.Row="9" Text="Replace Regex:" IsVisible="{Binding Mode.IsPatternMode}"/>
<CheckBox Grid.Row="9" IsChecked="{Binding UseRegexReplace}" IsVisible="{Binding Mode.IsPatternMode}" PropertyChanged="PreviewChanged"/>
<TextBlock Grid.Row="10" Text="Full Name:"/>
<CheckBox Grid.Row="10" IsChecked="{Binding FullName}" PropertyChanged="PreviewChanged"/>
</Grid> </Grid>
</Expander> </Expander>
</DataTemplate> </DataTemplate>
</Window.DataTemplates> </Window.DataTemplates>
</Window> </Window>

View File

@ -5,10 +5,9 @@ using System.Threading.Tasks;
using System.Collections.Generic; using System.Collections.Generic;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using Avalonia.Input; using Avalonia;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Markup.Xaml; using Avalonia.Markup.Xaml;
using Avalonia.Interactivity;
using BFR.Helpers; using BFR.Helpers;
using BFR.DataModels; using BFR.DataModels;
@ -25,22 +24,18 @@ namespace BFR
Filter(); Filter();
} }
public void FilterChanged(object sender, KeyEventArgs e) => Filter(); public void FilterChanged(object sender, AvaloniaPropertyChangedEventArgs e) => Filter(); // Bind to PropertyChanged
public void FilterChanged(object sender, RoutedEventArgs e) => Filter();
public void Filter() public void Filter()
{ {
// Filter all files in the directory for those satisfying the given filters // Filter all files in the directory for those satisfying the given filters
Files.ReplaceAll(AllFiles.Where(x => Files.ReplaceAll(AllFiles.Where(x =>
(FilterExtension == "" || x.Extension == FilterExtension) (FilterExtension == "" || x.OldExtension == FilterExtension)
&& (FilterFullName ? x.OldFullName : x.OldName).RegexContains(FilterRegex ? FilterPattern : Regex.Escape(FilterPattern)))); && (FilterFullName ? x.OldFullName : x.OldName).RegexContains(FilterRegex ? FilterPattern : Regex.Escape(FilterPattern))));
Preview(); Preview();
} }
public void PreviewChanged(object sender, KeyEventArgs e) => Preview(); // TextBox.KeyUp public void PreviewChanged(object sender, AvaloniaPropertyChangedEventArgs e) => Preview(); // Bind to PropertyChanged
public void PreviewChanged(object sender, RoutedEventArgs e) => Preview(); // CheckBox.Click
public void PreviewChanged(object sender, SelectionChangedEventArgs e) => Preview(); // ComboBox.SelectionChanged
public void PreviewChanged(object sender, NumericUpDownValueChangedEventArgs e) => Preview(); // NumericUpDown.ValueChanged
public void Preview() public void Preview()
{ {
// Reset all files to how they currently exist // Reset all files to how they currently exist

View File

@ -1,10 +1,11 @@
using Avalonia; using System.Collections.Generic;
using Avalonia;
using Avalonia.Collections; using Avalonia.Collections;
using Avalonia.Controls; using Avalonia.Controls;
using BFR.DataModels; using BFR.DataModels;
using BFR.Operations; using BFR.Operations;
using System.Collections.Generic;
namespace BFR namespace BFR
{ {
@ -22,7 +23,8 @@ namespace BFR
public int SelectedOperationType { get; set; } = 0; public int SelectedOperationType { get; set; } = 0;
public OperationType[] OperationTypes { get; } = new[] public OperationType[] OperationTypes { get; } = new[]
{ {
OperationType.Make<Overwrite>() OperationType.Make<Overwrite>(),
OperationType.Make<Replace>()
}; };
public readonly AvaloniaProperty<bool> isCommitButtonEnabled = public readonly AvaloniaProperty<bool> isCommitButtonEnabled =

View File

@ -0,0 +1,11 @@
namespace BFR.Operations
{
public class OperationMode
{
public int Index { get; }
public string Name { get; }
public OperationMode(int index, string name) =>
(Index, Name) = (index, name);
}
}

85
BFR/Operations/Replace.cs Normal file
View File

@ -0,0 +1,85 @@
using System;
using System.Linq;
using System.Collections.Generic;
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 Description => "Replaces the selected part of the file name.";
public AvaloniaProperty<ReplaceMode> modeProperty =
AvaloniaProperty.Register<MainWindow, ReplaceMode>(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 string Replacement { get; set; } = "";
public bool FullName { get; set; } = false;
public string Pattern { get; set; } = "";
public bool UseRegex { get; set; } = false;
public bool UseRegexReplace { get; set; } = false;
public string Characters { get; set; } = "";
public int FirstN { get; set; } = 0;
public int LastN { get; set; } = 0;
public int FromN { get; set; } = 0;
public int ToN { get; set; } = 0;
protected override void ApplyToInternal(IList<FileModel> files)
{
// Fail conditions: Various out-of-bounds
var shortestLength = files.Min(x => FullName ? x.FullName.Length : x.Name.Length);
if (FirstN > shortestLength) throw new OperationException($"First N can't be greater than the shortest file name length ({shortestLength}).");
if (LastN > shortestLength) throw new OperationException($"Last N can't be greater than the shortest file name length ({shortestLength}).");
if (FromN > shortestLength) throw new OperationException($"From N can't be greater than the shortest file name length ({shortestLength}).");
if (ToN > shortestLength) throw new OperationException($"To N can't be greater than the shortest file name length ({shortestLength}).");
if (FromN > ToN) throw new OperationException("From N can't be greater than To N.");
// Regex Failure
if(Mode.IsPatternMode && UseRegex)
try { "".Replace(Pattern, Replacement, true, false); }
catch(Exception) { throw new OperationException("Invalid Regex Pattern."); }
// Apply operation
foreach(var file in files)
{
var newName = FullName ? file.FullName : file.Name;
newName = Mode.Index 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("$", "$$")),
_ => newName
};
if (FullName) file.FullName = newName;
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;
}
}
}

View File

@ -0,0 +1,14 @@
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;
public ReplaceMode(int index, string name) :
base(index, name) { }
}
}