mirror of
https://github.com/doublecmd/doublecmd.git
synced 2026-06-21 09:58:13 +00:00
477 lines
12 KiB
ObjectPascal
477 lines
12 KiB
ObjectPascal
unit uFileSorting;
|
|
|
|
{$mode objfpc}{$H+}
|
|
|
|
interface
|
|
|
|
uses
|
|
Classes, SysUtils, uTypes, uColumns;
|
|
|
|
type
|
|
|
|
TSortDirection = (sdNone, sdAscending, sdDescending);
|
|
|
|
TFileSorting = record
|
|
SortFunctions: TFileFunctions;
|
|
SortDirection: TSortDirection;
|
|
end;
|
|
|
|
TFileSortings = array of TFileSorting;
|
|
|
|
{ TListSorter }
|
|
|
|
TListSorter = class
|
|
private
|
|
FSortList: TList;
|
|
FSortings: TFileSortings;
|
|
|
|
function MultiCompare(item1, item2:Pointer):Integer;
|
|
|
|
{en
|
|
Compares two file records using file functions.
|
|
@param(ptr1
|
|
First file)
|
|
@param(ptr2
|
|
Second file)
|
|
@returns(-1 lesser
|
|
@br 0 equal
|
|
@br 1 greater)
|
|
}
|
|
function Compare(FileSorting: TFileSorting; ptr1, ptr2: PFileRecItem): Integer;
|
|
|
|
Procedure QuickSort(FList: PPointerList; L, R : Longint);
|
|
|
|
public
|
|
{en
|
|
Creates the sorter.
|
|
@param(List
|
|
List to be sorted.)
|
|
@param(FileSorting
|
|
Sorting which will be used to sort file records.)
|
|
}
|
|
constructor Create(List: TList; Sortings: TFileSortings);
|
|
|
|
procedure Sort;
|
|
end;
|
|
|
|
{en
|
|
Returns true if the file functions will sort by the given sort function.
|
|
}
|
|
function HasSortFunction(FileFunctions: TFileFunctions;
|
|
SortFunction: TFileFunction): Boolean;
|
|
{en
|
|
Adds a function to the given list of functions.
|
|
}
|
|
procedure AddSortFunction(var FileFunctions: TFileFunctions;
|
|
SortFunction: TFileFunction);
|
|
|
|
{en
|
|
Adds sorting by a function with a given sorting direction to a file sortings.
|
|
}
|
|
procedure AddSorting(var FileSortings: TFileSortings;
|
|
SortFunction: TFileFunction; SortDirection: TSortDirection);
|
|
|
|
function ICompareByDirectory(item1, item2:PFileRecItem; bSortNegative: Boolean):Integer;
|
|
function ICompareByName(item1, item2:PFileRecItem; bSortNegative: Boolean):Integer;
|
|
function ICompareByNameNoExt(item1, item2:PFileRecItem; bSortNegative: Boolean):Integer;
|
|
function ICompareByExt (item1, item2:PFileRecItem; bSortNegative: Boolean):Integer;
|
|
function ICompareBySize(item1, item2:PFileRecItem; bSortNegative: Boolean):Integer;
|
|
function ICompareByDate(item1, item2:PFileRecItem; bSortNegative: Boolean):Integer;
|
|
function ICompareByAttr(item1, item2:PFileRecItem; bSortNegative: Boolean):Integer;
|
|
|
|
function ReverseSortDirection(SortDirection: TSortDirection): TSortDirection;
|
|
|
|
implementation
|
|
|
|
uses
|
|
uOSUtils, uGlobs, uDCUtils;
|
|
|
|
|
|
function HasSortFunction(FileFunctions: TFileFunctions;
|
|
SortFunction: TFileFunction): Boolean;
|
|
var
|
|
i: Integer;
|
|
begin
|
|
for i := 0 to Length(FileFunctions) - 1 do
|
|
begin
|
|
if SortFunction = FileFunctions[i] then
|
|
Exit(True);
|
|
end;
|
|
Result := False;
|
|
end;
|
|
|
|
procedure AddSortFunction(var FileFunctions: TFileFunctions;
|
|
SortFunction: TFileFunction);
|
|
begin
|
|
SetLength(FileFunctions, Length(FileFunctions) + 1);
|
|
FileFunctions[Length(FileFunctions) - 1] := SortFunction;
|
|
end;
|
|
|
|
procedure AddSorting(var FileSortings: TFileSortings;
|
|
SortFunction: TFileFunction; SortDirection: TSortDirection);
|
|
begin
|
|
SetLength(FileSortings, Length(FileSortings) + 1);
|
|
|
|
SetLength(FileSortings[Length(FileSortings) - 1].SortFunctions, 0);
|
|
AddSortFunction(FileSortings[Length(FileSortings) - 1].SortFunctions, SortFunction);
|
|
FileSortings[Length(FileSortings) - 1].SortDirection := SortDirection;
|
|
end;
|
|
|
|
|
|
function ICompareByDirectory(item1, item2:PFileRecItem; bSortNegative: Boolean):Integer;
|
|
begin
|
|
{> 0 (positive) Item1 is less than Item2
|
|
0 Item1 is equal to Item2
|
|
< 0 (negative) Item1 is greater than Item2}
|
|
|
|
Result:=0;
|
|
|
|
if (not (FPS_ISDIR(item1^.iMode) or item1^.bLinkIsDir)) and (not (FPS_ISDIR(item2^.iMode) or item2^.bLinkIsDir)) then Exit;
|
|
if (not (FPS_ISDIR(item1^.iMode) or item1^.bLinkIsDir)) and (FPS_ISDIR(item2^.iMode) or item2^.bLinkIsDir) then
|
|
begin
|
|
Result:=+1;
|
|
Exit;
|
|
end;
|
|
if (FPS_ISDIR(item1^.iMode) or item1^.bLinkIsDir) and (not (FPS_ISDIR(item2^.iMode) or item2^.bLinkIsDir)) then
|
|
begin
|
|
Result:=-1;
|
|
Exit;
|
|
end;
|
|
// both is directory, compare it
|
|
// if item1.fName=item2.fName then Exit;
|
|
// handle .. first
|
|
if item1^.sName='..' then
|
|
begin
|
|
Result:=-1;
|
|
Exit;
|
|
end;
|
|
if item2^.sName='..' then
|
|
begin
|
|
Result:=+1;
|
|
Exit;
|
|
end;
|
|
end;
|
|
|
|
function ICompareByName(item1, item2:PFileRecItem; bSortNegative: Boolean):Integer;
|
|
begin
|
|
{> 0 (positive) Item1 is less than Item2
|
|
0 Item1 is equal to Item2
|
|
< 0 (negative) Item1 is greater than Item2}
|
|
Result := 0;
|
|
|
|
if gCaseSensitiveSort then
|
|
Result := StrComp(PChar(item1^.sName), PChar(item2^.sName))
|
|
else
|
|
Result := mbCompareText(item1^.sName, item2^.sName);
|
|
|
|
if bSortNegative then
|
|
Result := -Result;
|
|
end;
|
|
|
|
function ICompareByNameNoExt(item1, item2:PFileRecItem; bSortNegative: Boolean):Integer;
|
|
var
|
|
name1, name2: string;
|
|
begin
|
|
{> 0 (positive) Item1 is less than Item2
|
|
0 Item1 is equal to Item2
|
|
< 0 (negative) Item1 is greater than Item2}
|
|
Result := 0;
|
|
|
|
// Don't sort directories only by name.
|
|
if FPS_ISDIR(item1^.iMode) or (FPS_ISLNK(item1^.iMode) and item1^.bLinkIsDir) or
|
|
FPS_ISDIR(item2^.iMode) or (FPS_ISLNK(item2^.iMode) and item1^.bLinkIsDir) then
|
|
begin
|
|
// Sort by full name.
|
|
Result := ICompareByName(item1, item2, bSortNegative);
|
|
end
|
|
else
|
|
begin
|
|
name1 := ExtractOnlyFileName(item1^.sName);
|
|
name2 := ExtractOnlyFileName(item2^.sName);
|
|
|
|
if gCaseSensitiveSort then
|
|
Result := StrComp(PChar(name1), PChar(name2))
|
|
else
|
|
Result := mbCompareText(name1, name2);
|
|
|
|
if bSortNegative then
|
|
Result := -Result;
|
|
end;
|
|
end;
|
|
|
|
function ICompareByExt(item1, item2:PFileRecItem; bSortNegative: Boolean):Integer;
|
|
begin
|
|
{> 0 (positive) Item1 is less than Item2
|
|
0 Item1 is equal to Item2
|
|
< 0 (negative) Item1 is greater than Item2}
|
|
|
|
Result:=0;
|
|
|
|
if item1^.sExt = item2^.sExt then
|
|
Exit;
|
|
|
|
if gCaseSensitiveSort then
|
|
Result := StrComp(PChar(item1^.sExt), PChar(item2^.sExt))
|
|
else
|
|
Result := mbCompareText(item1^.sExt, item2^.sExt);
|
|
|
|
if bSortNegative then
|
|
Result := -Result;
|
|
end;
|
|
|
|
function ICompareByDate(item1, item2:PFileRecItem; bSortNegative: Boolean):Integer;
|
|
begin
|
|
{> 0 (positive) Item1 is less than Item2
|
|
0 Item1 is equal to Item2
|
|
< 0 (negative) Item1 is greater than Item2}
|
|
|
|
Result:=0;
|
|
|
|
if item1^.fTimeI = item2^.fTimeI then
|
|
Exit;
|
|
|
|
if item1^.fTimeI < item2^.fTimeI then
|
|
Result := -1
|
|
else
|
|
Result := +1;
|
|
|
|
if bSortNegative then
|
|
Result := -Result;
|
|
end;
|
|
|
|
function ICompareByAttr(item1, item2:PFileRecItem; bSortNegative: Boolean):Integer;
|
|
begin
|
|
{> 0 (positive) Item1 is less than Item2
|
|
0 Item1 is equal to Item2
|
|
< 0 (negative) Item1 is greater than Item2}
|
|
|
|
Result:=0;
|
|
|
|
if item1^.iMode = item2^.iMode then
|
|
Exit;
|
|
|
|
if item1^.iMode > item2^.iMode then
|
|
Result := -1
|
|
else
|
|
Result := +1;
|
|
|
|
if bSortNegative then
|
|
Result := -Result;
|
|
end;
|
|
|
|
function ICompareBySize(item1, item2:PFileRecItem; bSortNegative: Boolean):Integer;
|
|
|
|
function GetSize(pFile: PFileRecItem): Cardinal;
|
|
begin
|
|
if FPS_ISDIR(pFile^.iMode) or pFile^.bLinkIsDir then
|
|
begin
|
|
if pFile^.iDirSize <> 0 then
|
|
Result := pFile^.iDirSize
|
|
else
|
|
Result := 0;
|
|
end
|
|
else
|
|
Result := pFile^.iSize;
|
|
end;
|
|
|
|
var
|
|
iSize1 : Cardinal;
|
|
iSize2 : Cardinal;
|
|
begin
|
|
{> 0 (positive) Item1 is less than Item2
|
|
0 Item1 is equal to Item2
|
|
< 0 (negative) Item1 is greater than Item2}
|
|
|
|
Result := 0;
|
|
|
|
iSize1 := GetSize(item1);
|
|
iSize2 := GetSize(item2);
|
|
|
|
if iSize1 = iSize2 then
|
|
Exit;
|
|
|
|
if iSize1 < iSize2 then
|
|
Result := -1
|
|
else
|
|
Result := +1;
|
|
|
|
if bSortNegative then
|
|
Result := -Result;
|
|
end;
|
|
|
|
function ReverseSortDirection(SortDirection: TSortDirection): TSortDirection;
|
|
begin
|
|
case SortDirection of
|
|
sdAscending:
|
|
Result := sdDescending;
|
|
sdDescending:
|
|
Result := sdAscending;
|
|
end;
|
|
end;
|
|
|
|
{ TListSorter }
|
|
|
|
constructor TListSorter.Create(List: TList; Sortings: TFileSortings);
|
|
begin
|
|
FSortList := List;
|
|
FSortings := Sortings;
|
|
|
|
inherited Create;
|
|
end;
|
|
|
|
procedure TListSorter.Sort;
|
|
begin
|
|
if Assigned(FSortList) and Assigned(FSortList.List) and
|
|
(FSortList.Count > 1) then
|
|
begin
|
|
QuickSort(FSortList.List, 0, FSortList.Count-1);
|
|
end;
|
|
end;
|
|
|
|
{ Return Values for ICompareByxxxx function
|
|
|
|
> 0 (positive) Item1 is less than Item2
|
|
0 Item1 is equal to Item2
|
|
< 0 (negative) Item1 is greater than Item2
|
|
}
|
|
|
|
{
|
|
This function is simples support of sorting
|
|
directory (handle uglobs.gDirSortFirst)
|
|
|
|
Result is 0 if both parametres is directory and equal
|
|
or not a directory (both).
|
|
|
|
Else return +/- as ICompare****
|
|
> 0 (positive) Item1 is less than Item2
|
|
< 0 (negative) Item1 is greater than Item2
|
|
}
|
|
|
|
function TListSorter.MultiCompare(item1, item2:Pointer):Integer;
|
|
var
|
|
i : Integer;
|
|
begin
|
|
{> 0 (positive) Item1 is less than Item2
|
|
0 Item1 is equal to Item2
|
|
< 0 (negative) Item1 is greater than Item2}
|
|
|
|
Result := 0;
|
|
if item1 = item2 then Exit;
|
|
|
|
// Put directories first.
|
|
if gDirSortFirst then
|
|
begin
|
|
Result := ICompareByDirectory(item1, item2, False); // Ascending
|
|
if Result <> 0 then Exit;
|
|
end;
|
|
|
|
for i := 0 to Length(FSortings) - 1 do
|
|
begin
|
|
Result := Compare(FSortings[i], item1, item2);
|
|
if Result <> 0 then Exit;
|
|
end;
|
|
end;
|
|
|
|
function TListSorter.Compare(FileSorting: TFileSorting; ptr1, ptr2: PFileRecItem): Integer;
|
|
var
|
|
i: Integer;
|
|
bNegative: Boolean;
|
|
begin
|
|
case FileSorting.SortDirection of
|
|
sdAscending:
|
|
bNegative := False;
|
|
|
|
sdDescending:
|
|
bNegative := True;
|
|
|
|
else
|
|
Exit;
|
|
end;
|
|
|
|
if Length(FileSorting.SortFunctions) > 0 then
|
|
begin
|
|
Result := 0;
|
|
|
|
for i := 0 to Length(FileSorting.SortFunctions) - 1 do
|
|
begin
|
|
//------------------------------------------------------
|
|
// Only DC internal functions supported.
|
|
case FileSorting.SortFunctions[i] of
|
|
fsfName:
|
|
Result := ICompareByName(ptr1, ptr2, bNegative);
|
|
fsfExtension:
|
|
Result := ICompareByExt(ptr1, ptr2, bNegative);
|
|
fsfSize:
|
|
Result := ICompareBySize(ptr1, ptr2, bNegative);
|
|
fsfAttr:
|
|
Result := ICompareByAttr(ptr1, ptr2, bNegative);
|
|
fsfPath:
|
|
begin
|
|
Result := mbCompareText(ptr1^.sPath, ptr2^.sPath);
|
|
if bNegative then
|
|
Result := -Result;
|
|
end;
|
|
fsfGroup:
|
|
begin
|
|
Result := mbCompareText(ptr1^.sGroup, ptr2^.sGroup);
|
|
if bNegative then
|
|
Result := -Result;
|
|
end;
|
|
fsfOwner:
|
|
begin
|
|
Result := mbCompareText(ptr1^.sOwner, ptr2^.sOwner);
|
|
if bNegative then
|
|
Result := -Result;
|
|
end;
|
|
fsfTime:
|
|
Result := ICompareByDate(ptr1, ptr2, bNegative);
|
|
fsfLinkTo:
|
|
begin
|
|
Result := mbCompareText(ptr1^.sLinkTo, ptr2^.sLinkTo);
|
|
if bNegative then
|
|
Result := -Result;
|
|
end;
|
|
fsfNameNoExtension:
|
|
Result := ICompareByNameNoExt(ptr1, ptr2, bNegative);
|
|
end;
|
|
|
|
if Result <> 0 then
|
|
Exit;
|
|
end;
|
|
end
|
|
else
|
|
Result := -1;
|
|
end;
|
|
|
|
// From FPC: lists.inc.
|
|
Procedure TListSorter.QuickSort(FList: PPointerList; L, R : Longint);
|
|
var
|
|
I, J : Longint;
|
|
P, Q : Pointer;
|
|
begin
|
|
repeat
|
|
I := L;
|
|
J := R;
|
|
P := FList^[ (L + R) div 2 ];
|
|
repeat
|
|
while MultiCompare(P, FList^[i]) > 0 do
|
|
I := I + 1;
|
|
while MultiCompare(P, FList^[J]) < 0 do
|
|
J := J - 1;
|
|
If I <= J then
|
|
begin
|
|
Q := FList^[I];
|
|
Flist^[I] := FList^[J];
|
|
FList^[J] := Q;
|
|
I := I + 1;
|
|
J := J - 1;
|
|
end;
|
|
until I > J;
|
|
if L < J then
|
|
QuickSort(FList, L, J);
|
|
L := I;
|
|
until I >= R;
|
|
end;
|
|
|
|
end.
|
|
|