mirror of
https://gitlab.com/freepascal.org/lazarus/lazarus.git
synced 2025-06-22 13:08:14 +02:00
lcl/shellctrls: Add a custom compare function for sorting the nodes in the ShellTreeView while collecting the files/dirs.
This commit is contained in:
parent
24d0ade4bc
commit
cbf44a384b
@ -41,7 +41,7 @@ type
|
|||||||
|
|
||||||
TObjectTypes = set of TObjectType;
|
TObjectTypes = set of TObjectType;
|
||||||
|
|
||||||
TFileSortType = (fstNone, fstAlphabet, fstFoldersFirst);
|
TFileSortType = (fstNone, fstAlphabet, fstFoldersFirst, fstCustom);
|
||||||
|
|
||||||
TMaskCaseSensitivity = (mcsPlatformDefault, mcsCaseInsensitive, mcsCaseSensitive);
|
TMaskCaseSensitivity = (mcsPlatformDefault, mcsCaseInsensitive, mcsCaseSensitive);
|
||||||
|
|
||||||
@ -56,6 +56,22 @@ type
|
|||||||
TCustomShellTreeView = class;
|
TCustomShellTreeView = class;
|
||||||
TCustomShellListView = class;
|
TCustomShellListView = class;
|
||||||
|
|
||||||
|
{ TFileItem }
|
||||||
|
|
||||||
|
TFileItem = class(TObject)
|
||||||
|
private
|
||||||
|
FFileInfo: TSearchRec;
|
||||||
|
FBasePath: String;
|
||||||
|
public
|
||||||
|
//more data to sort by size, date... etc
|
||||||
|
isFolder: Boolean;
|
||||||
|
constructor Create(const DirInfo: TSearchRec; ABasePath: String);
|
||||||
|
property BasePath: String read FBasePath;
|
||||||
|
property FileInfo: TSearchRec read FFileInfo write FFileInfo;
|
||||||
|
end;
|
||||||
|
|
||||||
|
TFileItemCompareEvent = function(Item1, Item2: TFileItem): integer of object;
|
||||||
|
|
||||||
{ TCustomShellTreeView }
|
{ TCustomShellTreeView }
|
||||||
|
|
||||||
TAddItemEvent = procedure(Sender: TObject; const ABasePath: String;
|
TAddItemEvent = procedure(Sender: TObject; const ABasePath: String;
|
||||||
@ -71,10 +87,12 @@ type
|
|||||||
FInitialRoot: String;
|
FInitialRoot: String;
|
||||||
FUseBuiltinIcons: Boolean;
|
FUseBuiltinIcons: Boolean;
|
||||||
FOnAddItem: TAddItemEvent;
|
FOnAddItem: TAddItemEvent;
|
||||||
|
FOnSortCompare: TFileItemCompareEvent;
|
||||||
{ Setters and getters }
|
{ Setters and getters }
|
||||||
function GetPath: string;
|
function GetPath: string;
|
||||||
procedure SetFileSortType(const AValue: TFileSortType);
|
procedure SetFileSortType(const AValue: TFileSortType);
|
||||||
procedure SetObjectTypes(AValue: TObjectTypes);
|
procedure SetObjectTypes(AValue: TObjectTypes);
|
||||||
|
procedure SetOnSortCompare(AValue: TFileItemCompareEvent);
|
||||||
procedure SetPath(AValue: string);
|
procedure SetPath(AValue: string);
|
||||||
procedure SetRoot(const AValue: string);
|
procedure SetRoot(const AValue: string);
|
||||||
procedure SetShellListView(const Value: TCustomShellListView);
|
procedure SetShellListView(const Value: TCustomShellListView);
|
||||||
@ -116,6 +134,7 @@ type
|
|||||||
property Root: string read FRoot write SetRoot;
|
property Root: string read FRoot write SetRoot;
|
||||||
property Path: string read GetPath write SetPath;
|
property Path: string read GetPath write SetPath;
|
||||||
property OnAddItem: TAddItemEvent read FOnAddItem write FOnAddItem;
|
property OnAddItem: TAddItemEvent read FOnAddItem write FOnAddItem;
|
||||||
|
property OnSortCompare: TFileItemCompareEvent read FOnSortCompare write SetOnSortCompare;
|
||||||
{ Protected properties which users may want to access, see bug 15374 }
|
{ Protected properties which users may want to access, see bug 15374 }
|
||||||
property Items;
|
property Items;
|
||||||
end;
|
end;
|
||||||
@ -202,6 +221,7 @@ type
|
|||||||
property OnMouseWheelRight;
|
property OnMouseWheelRight;
|
||||||
property OnSelectionChanged;
|
property OnSelectionChanged;
|
||||||
property OnShowHint;
|
property OnShowHint;
|
||||||
|
property OnSortCompare;
|
||||||
property OnUTF8KeyPress;
|
property OnUTF8KeyPress;
|
||||||
property Options;
|
property Options;
|
||||||
property TreeLineColor;
|
property TreeLineColor;
|
||||||
@ -416,20 +436,12 @@ begin
|
|||||||
Result := MaskCaseSensitivityStrings[CS];
|
Result := MaskCaseSensitivityStrings[CS];
|
||||||
end;
|
end;
|
||||||
|
|
||||||
{ TFileItem : internal helper class used for temporarily storing info in an internal TStrings component}
|
operator = (const A, B: TMethod): Boolean;
|
||||||
type
|
begin
|
||||||
{ TFileItem }
|
Result := (A.Code = B.Code) and (A.Data = B.Data);
|
||||||
TFileItem = class(TObject)
|
end;
|
||||||
private
|
|
||||||
FFileInfo: TSearchRec;
|
|
||||||
FBasePath: String;
|
|
||||||
public
|
|
||||||
//more data to sort by size, date... etc
|
|
||||||
isFolder: Boolean;
|
|
||||||
constructor Create(const DirInfo: TSearchRec; ABasePath: String);
|
|
||||||
property FileInfo: TSearchRec read FFileInfo write FFileInfo;
|
|
||||||
end;
|
|
||||||
|
|
||||||
|
{ TFileItem : internal helper class used for temporarily storing info in an internal TStrings component}
|
||||||
|
|
||||||
constructor TFileItem.Create(const DirInfo:TSearchRec; ABasePath: String);
|
constructor TFileItem.Create(const DirInfo:TSearchRec; ABasePath: String);
|
||||||
begin
|
begin
|
||||||
@ -439,6 +451,32 @@ begin
|
|||||||
end;
|
end;
|
||||||
|
|
||||||
|
|
||||||
|
{ TFileItemAVLTree
|
||||||
|
|
||||||
|
Specialized TAVLTree descendant for sorting the TFileItems found by the
|
||||||
|
helper function GetFilesInDir such that a user-friendly compare function
|
||||||
|
can be applied. }
|
||||||
|
|
||||||
|
type
|
||||||
|
TFileItemAVLTree = class(TAVLTree)
|
||||||
|
private
|
||||||
|
FFileItemCompare: TFileItemCompareEvent;
|
||||||
|
function InternalFileItemCompare(ATree: TAvlTree; Item1, Item2: Pointer): Integer;
|
||||||
|
public
|
||||||
|
constructor CreateFileItemCompare(ACompare: TFileItemCompareEvent);
|
||||||
|
end;
|
||||||
|
|
||||||
|
constructor TFileItemAVLTree.CreateFileItemCompare(ACompare: TFileItemCompareEvent);
|
||||||
|
begin
|
||||||
|
FFileItemCompare := ACompare;
|
||||||
|
inherited CreateObjectCompare(@InternalFileItemCompare);
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TFileItemAVLTree.InternalFileItemCompare(ATree: TAvlTree; Item1, Item2: Pointer): Integer;
|
||||||
|
begin
|
||||||
|
Result := FFileItemCompare(TFileItem(Item1), TFileItem(Item2));
|
||||||
|
end;
|
||||||
|
|
||||||
{ TShellTreeNode }
|
{ TShellTreeNode }
|
||||||
|
|
||||||
procedure TShellTreeNode.SetBasePath(ABasePath: String);
|
procedure TShellTreeNode.SetBasePath(ABasePath: String);
|
||||||
@ -613,7 +651,7 @@ begin
|
|||||||
BeginUpdate;
|
BeginUpdate;
|
||||||
Refresh(nil);
|
Refresh(nil);
|
||||||
try
|
try
|
||||||
SetPath(CurrPath);
|
SetPath(CurrPath);
|
||||||
except
|
except
|
||||||
// CurrPath may have been removed in the mean time by another process, just ignore
|
// CurrPath may have been removed in the mean time by another process, just ignore
|
||||||
on E: EInvalidPath do ;//
|
on E: EInvalidPath do ;//
|
||||||
@ -623,6 +661,42 @@ begin
|
|||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
procedure TCustomShellTreeView.SetOnSortCompare(AValue: TFileItemCompareEvent);
|
||||||
|
var
|
||||||
|
RootNode: TTreeNode;
|
||||||
|
CurrPath: String;
|
||||||
|
begin
|
||||||
|
if TMethod(AValue) = TMethod(FOnSortCompare) then
|
||||||
|
Exit;
|
||||||
|
|
||||||
|
FOnSortCompare := AValue;
|
||||||
|
|
||||||
|
if (([csLoading,csDesigning] * ComponentState) <> []) or (FFileSortType <> fstCustom) then
|
||||||
|
Exit;
|
||||||
|
|
||||||
|
CurrPath := GetPath;
|
||||||
|
try
|
||||||
|
BeginUpdate;
|
||||||
|
Items.Clear;
|
||||||
|
if FRoot = '' then
|
||||||
|
PopulateWithBaseFiles()
|
||||||
|
else
|
||||||
|
begin
|
||||||
|
RootNode := Items.AddChild(nil, FRoot);
|
||||||
|
RootNode.HasChildren := True;
|
||||||
|
RootNode.Expand(False);
|
||||||
|
try
|
||||||
|
SetPath(CurrPath);
|
||||||
|
except
|
||||||
|
// CurrPath may have been removed in the mean time by another process, just ignore
|
||||||
|
on E: EInvalidPath do ;//
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
finally
|
||||||
|
EndUpdate;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
function TCustomShellTreeView.CanExpand(Node: TTreeNode): Boolean;
|
function TCustomShellTreeView.CanExpand(Node: TTreeNode): Boolean;
|
||||||
var
|
var
|
||||||
OldAutoExpand: Boolean;
|
OldAutoExpand: Boolean;
|
||||||
@ -718,7 +792,6 @@ begin
|
|||||||
if f1.isFolder then Result:=-1
|
if f1.isFolder then Result:=-1
|
||||||
else Result:=1;
|
else Result:=1;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
end;
|
end;
|
||||||
|
|
||||||
{ Helper routine.
|
{ Helper routine.
|
||||||
@ -731,15 +804,17 @@ end;
|
|||||||
}
|
}
|
||||||
procedure GetFilesInDir(const ABaseDir: string; AMask: string;
|
procedure GetFilesInDir(const ABaseDir: string; AMask: string;
|
||||||
AObjectTypes: TObjectTypes; AResult: TStrings; AFileSortType: TFileSortType;
|
AObjectTypes: TObjectTypes; AResult: TStrings; AFileSortType: TFileSortType;
|
||||||
ACaseSensitivity: TMaskCaseSensitivity = mcsPlatformDefault);
|
ACaseSensitivity: TMaskCaseSensitivity = mcsPlatformDefault;
|
||||||
|
ASortCompare: TFileItemCompareEvent = nil);
|
||||||
var
|
var
|
||||||
DirInfo: TSearchRec;
|
DirInfo: TSearchRec;
|
||||||
FindResult, i: Integer;
|
FindResult, i: Integer;
|
||||||
IsDirectory, IsValidDirectory, IsHidden, AddFile, UseMaskList, CaseSens: Boolean;
|
IsDirectory, IsValidDirectory, IsHidden, AddFile, UseMaskList, CaseSens: Boolean;
|
||||||
SearchStr, ShortFilename: string;
|
SearchStr, ShortFilename: string;
|
||||||
MaskList: TMaskList = nil;
|
MaskList: TMaskList = nil;
|
||||||
Files: TList;
|
Files: TFileItemAVLTree;
|
||||||
FileItem: TFileItem;
|
FileItem: TFileItem;
|
||||||
|
avlNode: TAVLTreeNode;
|
||||||
{$if defined(windows) and not defined(wince)}
|
{$if defined(windows) and not defined(wince)}
|
||||||
ErrMode : LongWord;
|
ErrMode : LongWord;
|
||||||
{$endif}
|
{$endif}
|
||||||
@ -755,7 +830,7 @@ begin
|
|||||||
Delete(AMask, Length(AMask), 1);
|
Delete(AMask, Length(AMask), 1);
|
||||||
if Trim(AMask) = '' then
|
if Trim(AMask) = '' then
|
||||||
AMask := AllFilesMask;
|
AMask := AllFilesMask;
|
||||||
//Use a TMaksList if more than 1 mask is specified or if MaskCaseSensitivity differs from the platform default behaviour
|
//Use a TMaskList if more than 1 mask is specified or if MaskCaseSensitivity differs from the platform default behaviour
|
||||||
UseMaskList := (Pos(';', AMask) > 0) or
|
UseMaskList := (Pos(';', AMask) > 0) or
|
||||||
{$ifdef NotLiteralFilenames}
|
{$ifdef NotLiteralFilenames}
|
||||||
(ACaseSensitivity = mcsCaseSensitive)
|
(ACaseSensitivity = mcsCaseSensitive)
|
||||||
@ -778,10 +853,16 @@ begin
|
|||||||
end;
|
end;
|
||||||
|
|
||||||
try
|
try
|
||||||
if AFileSortType = fstNone then
|
Files := nil;
|
||||||
Files:=nil
|
case AFileSortType of
|
||||||
else
|
fstAlphabet:
|
||||||
Files := TList.Create;
|
Files := TFileItemAVLTree.Create(@FilesSortAlphabet);
|
||||||
|
fstFoldersFirst:
|
||||||
|
Files := TFileItemAVLTree.Create(@FilesSortFoldersFirst);
|
||||||
|
fstCustom:
|
||||||
|
if ASortCompare <> nil then
|
||||||
|
Files := TFileItemAVLTree.CreateFileItemCompare(ASortCompare);
|
||||||
|
end;
|
||||||
|
|
||||||
i := 0;
|
i := 0;
|
||||||
if UseMaskList then
|
if UseMaskList then
|
||||||
@ -837,16 +918,14 @@ begin
|
|||||||
|
|
||||||
if Assigned(Files) then
|
if Assigned(Files) then
|
||||||
begin
|
begin
|
||||||
case AFileSortType of
|
avlNode := Files.FindLowest;
|
||||||
fstAlphabet: Files.Sort(@FilesSortAlphabet);
|
while Assigned(avlNode) do
|
||||||
fstFoldersFirst: Files.Sort(@FilesSortFoldersFirst);
|
begin
|
||||||
|
FileItem := TFileItem(avlNode.Data);
|
||||||
|
AResult.AddObject(FileItem.FileInfo.Name, FileItem);
|
||||||
|
avlNode := Files.FindSuccessor(avlNode);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
for i:=0 to Files.Count-1 do
|
|
||||||
begin
|
|
||||||
FileItem:=TFileItem(Files[i]);
|
|
||||||
AResult.AddObject(FileItem.FileInfo.Name, FileItem);
|
|
||||||
end;
|
|
||||||
//don't free the TFileItems here, they will freed by the calling routine
|
//don't free the TFileItems here, they will freed by the calling routine
|
||||||
Files.Free;
|
Files.Free;
|
||||||
end;
|
end;
|
||||||
@ -949,7 +1028,7 @@ begin
|
|||||||
Items.BeginUpdate;
|
Items.BeginUpdate;
|
||||||
try
|
try
|
||||||
Files.OwnsObjects := True;
|
Files.OwnsObjects := True;
|
||||||
GetFilesInDir(ANodePath, AllFilesMask, FObjectTypes, Files, FFileSortType);
|
GetFilesInDir(ANodePath, AllFilesMask, FObjectTypes, Files, FFileSortType, mcsPlatformDefault, FOnSortCompare);
|
||||||
Result := Files.Count > 0;
|
Result := Files.Count > 0;
|
||||||
|
|
||||||
for i := 0 to Files.Count - 1 do
|
for i := 0 to Files.Count - 1 do
|
||||||
|
Loading…
Reference in New Issue
Block a user