Added Sort-Operation + UI
This commit is contained in:
parent
a1d76d1920
commit
4b50f44985
|
@ -280,5 +280,23 @@
|
|||
</Grid>
|
||||
</Expander>
|
||||
</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>
|
||||
|
|
|
@ -28,6 +28,7 @@ namespace BFR
|
|||
OperationType.Make<Remove>(),
|
||||
OperationType.Make<Number>(),
|
||||
OperationType.Make<Add>(),
|
||||
OperationType.Make<Sort>(),
|
||||
};
|
||||
|
||||
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