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:
parent
2aa1fa6529
commit
c519147302
|
@ -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>
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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 =
|
||||||
|
|
11
BFR/Operations/OperationMode.cs
Normal file
11
BFR/Operations/OperationMode.cs
Normal 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
85
BFR/Operations/Replace.cs
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
14
BFR/Operations/ReplaceMode.cs
Normal file
14
BFR/Operations/ReplaceMode.cs
Normal 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) { }
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user