Added Sort-Operation + UI
This commit is contained in:
parent
a1d76d1920
commit
4b50f44985
|
@ -280,5 +280,23 @@
|
||||||
</Grid>
|
</Grid>
|
||||||
</Expander>
|
</Expander>
|
||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
|
<DataTemplate DataType="{x:Type ops:Sort}">
|
||||||
|
<Expander Classes="OperationExpander">
|
||||||
|
<Grid ColumnDefinitions="auto,*" RowDefinitions="auto,auto,auto">
|
||||||
|
<TextBlock Grid.Row="0" Text="Mode:"/>
|
||||||
|
<ComboBox Grid.Row="0" Items="{Binding Modes}" Name="ModeSelector" SelectedIndex="{Binding Mode}" PropertyChanged="PreviewChanged">
|
||||||
|
<ComboBox.ItemTemplate>
|
||||||
|
<DataTemplate>
|
||||||
|
<TextBlock Text="{Binding Name}"/>
|
||||||
|
</DataTemplate>
|
||||||
|
</ComboBox.ItemTemplate>
|
||||||
|
</ComboBox>
|
||||||
|
<TextBlock Grid.Row="1" Text="Direction:" IsVisible="{Binding !#ModeSelector.SelectedItem.IsReverse}"/>
|
||||||
|
<ComboBox Grid.Row="1" Items="{Binding Directions}" SelectedIndex="{Binding Direction}" IsVisible="{Binding !#ModeSelector.SelectedItem.IsReverse}" PropertyChanged="PreviewChanged"/>
|
||||||
|
<TextBlock Grid.Row="2" Text="Full Name:" IsVisible="{Binding !#ModeSelector.SelectedItem.IsReverse}"/>
|
||||||
|
<CheckBox Grid.Row="2" IsChecked="{Binding FullName}" IsVisible="{Binding !#ModeSelector.SelectedItem.IsReverse}" PropertyChanged="PreviewChanged"/>
|
||||||
|
</Grid>
|
||||||
|
</Expander>
|
||||||
|
</DataTemplate>
|
||||||
</Window.DataTemplates>
|
</Window.DataTemplates>
|
||||||
</Window>
|
</Window>
|
||||||
|
|
|
@ -28,6 +28,7 @@ namespace BFR
|
||||||
OperationType.Make<Remove>(),
|
OperationType.Make<Remove>(),
|
||||||
OperationType.Make<Number>(),
|
OperationType.Make<Number>(),
|
||||||
OperationType.Make<Add>(),
|
OperationType.Make<Add>(),
|
||||||
|
OperationType.Make<Sort>(),
|
||||||
};
|
};
|
||||||
|
|
||||||
public static readonly AvaloniaProperty<bool> IsCommitButtonEnabledProperty =
|
public static readonly AvaloniaProperty<bool> IsCommitButtonEnabledProperty =
|
||||||
|
|
134
BFR/Operations/Sort.cs
Normal file
134
BFR/Operations/Sort.cs
Normal file
|
@ -0,0 +1,134 @@
|
||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Globalization;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
using BFR.Helpers;
|
||||||
|
using BFR.DataModels;
|
||||||
|
|
||||||
|
using Avalonia;
|
||||||
|
|
||||||
|
namespace BFR.Operations
|
||||||
|
{
|
||||||
|
public class Sort : Operation
|
||||||
|
{
|
||||||
|
public override string Name => nameof(Sort);
|
||||||
|
public override string Description => "Sorts the file names according to how they are when they arrive at this operation.";
|
||||||
|
|
||||||
|
public static readonly AvaloniaProperty<SortMode> ModeProperty =
|
||||||
|
AvaloniaProperty.Register<MainWindow, SortMode>(nameof(Mode), SortMode.Normal);
|
||||||
|
public SortMode Mode { get => GetValue(ModeProperty); set => SetValue(ModeProperty, value); }
|
||||||
|
public SortDirection Direction { get; set; }
|
||||||
|
|
||||||
|
public SortOperationMode[] Modes => SortOperationMode.Modes;
|
||||||
|
public string[] Directions => Enum.GetNames(typeof(SortDirection));
|
||||||
|
|
||||||
|
public bool FullName { get; set; } = false;
|
||||||
|
|
||||||
|
private Func<FileModel, string> GetName => FullName
|
||||||
|
? (Func<FileModel, string>)(x => x.FullName)
|
||||||
|
: (Func<FileModel, string>)(x => x.Name);
|
||||||
|
|
||||||
|
protected override void ApplyToInternal(IList<FileModel> files)
|
||||||
|
{
|
||||||
|
// Fail conditions: No fail conditions.
|
||||||
|
// Apply operation
|
||||||
|
files.ReplaceAll(new List<FileModel> (Mode switch
|
||||||
|
{
|
||||||
|
SortMode.Normal => files.OrderBy(GetName, StringComparer.CurrentCulture),
|
||||||
|
SortMode.Natural => files.OrderBy(GetName, Comparer<string>.Create(CompareNatural)),
|
||||||
|
SortMode.Ordinal => files.OrderBy(GetName, StringComparer.Ordinal),
|
||||||
|
SortMode.Length => files.OrderBy(x => GetName(x).Length),
|
||||||
|
SortMode.Reverse => files.Reverse(),
|
||||||
|
_ => files
|
||||||
|
}));
|
||||||
|
if (Mode != SortMode.Reverse && Direction == SortDirection.Descending)
|
||||||
|
files.ReplaceAll(new List<FileModel>(files.Reverse()));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Code taken and slightly modified from https://git.lastassault.de/speatzle/BulkFileRenamer
|
||||||
|
public static int CompareNatural(string strA, string strB) =>
|
||||||
|
CompareNatural(strA, strB, CultureInfo.CurrentCulture, CompareOptions.IgnoreCase);
|
||||||
|
|
||||||
|
public static int CompareNatural(string strA, string strB, CultureInfo culture, CompareOptions options)
|
||||||
|
{
|
||||||
|
var cmp = culture.CompareInfo;
|
||||||
|
var (iA, iB) = (0, 0);
|
||||||
|
var softResult = 0;
|
||||||
|
var softResultWeight = 0;
|
||||||
|
while (iA < strA.Length && iB < strB.Length)
|
||||||
|
{
|
||||||
|
var isDigitA = char.IsDigit(strA[iA]);
|
||||||
|
var isDigitB = char.IsDigit(strB[iB]);
|
||||||
|
if (isDigitA != isDigitB)
|
||||||
|
return cmp.Compare(strA, iA, strB, iB, options);
|
||||||
|
else if (!isDigitA && !isDigitB)
|
||||||
|
{
|
||||||
|
var jA = iA + 1;
|
||||||
|
var jB = iB + 1;
|
||||||
|
while (jA < strA.Length && !char.IsDigit(strA[jA])) jA++;
|
||||||
|
while (jB < strB.Length && !char.IsDigit(strB[jB])) jB++;
|
||||||
|
var cmpResult = cmp.Compare(strA, iA, jA - iA, strB, iB, jB - iB, options);
|
||||||
|
if (cmpResult != 0)
|
||||||
|
{
|
||||||
|
var (secA, secB) = (strA[iA..jA], strB[iB..jB]);
|
||||||
|
if (cmp.Compare(secA + "1", secB + "2", options) ==
|
||||||
|
cmp.Compare(secA + "2", secB + "1", options))
|
||||||
|
return cmp.Compare(strA, iA, strB, iB, options);
|
||||||
|
else if (softResultWeight < 1)
|
||||||
|
{
|
||||||
|
softResult = cmpResult;
|
||||||
|
softResultWeight = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
(iA, jA) = (iB, jB);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var zeroA = (char)(strA[iA] - (int)char.GetNumericValue(strA[iA]));
|
||||||
|
var zeroB = (char)(strB[iB] - (int)char.GetNumericValue(strB[iB]));
|
||||||
|
var (jA, jB) = (iA, iB);
|
||||||
|
while (jA < strA.Length && strA[jA] == zeroA) jA++;
|
||||||
|
while (jB < strB.Length && strB[jB] == zeroB) jB++;
|
||||||
|
int resultIfSameLength = 0;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
isDigitA = jA < strA.Length && char.IsDigit(strA[jA]);
|
||||||
|
isDigitB = jB < strB.Length && char.IsDigit(strB[jB]);
|
||||||
|
var numA = isDigitA ? (int)char.GetNumericValue(strA[jA]) : 0;
|
||||||
|
var numB = isDigitB ? (int)char.GetNumericValue(strB[jB]) : 0;
|
||||||
|
if (isDigitA && (char)(strA[jA] - numA) != zeroA) isDigitA = false;
|
||||||
|
if (isDigitB && (char)(strB[jB] - numB) != zeroB) isDigitB = false;
|
||||||
|
if (isDigitA && isDigitB)
|
||||||
|
{
|
||||||
|
if (numA != numB && resultIfSameLength == 0)
|
||||||
|
resultIfSameLength = numA < numB ? -1 : 1;
|
||||||
|
jA++;
|
||||||
|
jB++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
while (isDigitA && isDigitB);
|
||||||
|
if (isDigitA != isDigitB)
|
||||||
|
return isDigitA ? 1 : -1;
|
||||||
|
else if (resultIfSameLength != 0)
|
||||||
|
return resultIfSameLength;
|
||||||
|
var (lA, lB) = (jA - iA, jB - iB);
|
||||||
|
if (lA != lB)
|
||||||
|
return lA > lB ? -1 : 1;
|
||||||
|
else if (zeroA != zeroB && softResultWeight < 2)
|
||||||
|
{
|
||||||
|
softResult = cmp.Compare(strA, iA, 1, strB, iB, 1, options);
|
||||||
|
softResultWeight = 2;
|
||||||
|
}
|
||||||
|
(iA, iB) = (jA, jB);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (iA < strA.Length || iB < strB.Length)
|
||||||
|
return iA < strA.Length ? 1 : -1;
|
||||||
|
else if (softResult != 0)
|
||||||
|
return softResult;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
49
BFR/Operations/SortMode.cs
Normal file
49
BFR/Operations/SortMode.cs
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
using BFR.Helpers;
|
||||||
|
|
||||||
|
namespace BFR.Operations
|
||||||
|
{
|
||||||
|
public class SortOperationMode : OperationMode<SortMode>
|
||||||
|
{
|
||||||
|
public bool IsAlphanumerical => Index == SortMode.Normal;
|
||||||
|
public bool IsNatural => Index == SortMode.Natural;
|
||||||
|
public bool IsLength => Index == SortMode.Length;
|
||||||
|
public bool IsReverse => Index == SortMode.Reverse;
|
||||||
|
|
||||||
|
public static readonly SortOperationMode[] Modes = All();
|
||||||
|
|
||||||
|
protected static SortOperationMode[] All() => ((IEnumerable<SortMode>)Enum.GetValues(typeof(SortMode))).Select(x =>
|
||||||
|
new SortOperationMode(
|
||||||
|
x,
|
||||||
|
x.GetAttribute<OperationModeAttribute>().DisplayName,
|
||||||
|
x.GetAttribute<OperationModeAttribute>().Description
|
||||||
|
)).ToArray();
|
||||||
|
|
||||||
|
public SortOperationMode(SortMode index, string name, string description) :
|
||||||
|
base(index, name, description)
|
||||||
|
{ }
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum SortMode
|
||||||
|
{
|
||||||
|
[OperationMode(nameof(Normal), "Compares file names based on current culture.")]
|
||||||
|
Normal,
|
||||||
|
[OperationMode(nameof(Ordinal), "Compares each successive ordinal character in a string (each character as its ASCII number value).")]
|
||||||
|
Ordinal,
|
||||||
|
[OperationMode(nameof(Natural), "Sorts by natural sort order (numeric values grouped).")]
|
||||||
|
Natural,
|
||||||
|
[OperationMode(nameof(Length), "Sorts by file name length.")]
|
||||||
|
Length,
|
||||||
|
[OperationMode(nameof(Reverse), "Reverses the list order.")]
|
||||||
|
Reverse,
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum SortDirection
|
||||||
|
{
|
||||||
|
Ascending,
|
||||||
|
Descending
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user