UPD: change the functionality related to FileName from relying solely on TFile to TDisplayFile (it's crucial for issues related to Unicode normalization and some virtual filesource)

This commit is contained in:
rich2014 2026-06-25 08:20:25 +08:00
commit a787a8fd67
9 changed files with 85 additions and 90 deletions

View file

@ -1095,7 +1095,7 @@ begin
for ACol := 0 to ColumnsClass.Count - 1 do
begin
AFile.DisplayStrings.Add(ColumnsClass.GetColumnItemResultString(
ACol, AFile.FSFile, FileSource));
ACol, AFile, FileSource));
end;
end;

View file

@ -549,7 +549,7 @@ begin
AFile.DisplayStrings.BeginUpdate;
try
AFile.DisplayStrings.Clear;
AFile.DisplayStrings.Add(FormatFileFunction('DC().GETFILENAME{}', AFile.FSFile, FileSource));
AFile.DisplayStrings.Add(FormatFileFunction('DC().GETFILENAME{}', AFile, FileSource));
finally
AFile.DisplayStrings.EndUpdate;
end;
@ -781,7 +781,7 @@ end;
procedure TFileViewWithGrid.UpdateFooterDetails;
var
AFile: TFile;
AFile: TDisplayFile;
AFileName: String;
begin
if not Assigned(FAllDisplayFiles) or (FAllDisplayFiles.Count = 0)
@ -789,23 +789,26 @@ begin
lblDetails.Caption:= EmptyStr
else
begin
AFile:= CloneActiveFile;
AFile:= GetActiveDisplayFile;
if Assigned(AFile) then
try
// Get details info about file
AFileName:= #32#32 +FormatFileFunction('DC().GETFILEEXT{}', AFile, FileSource);
AFileName:= AFileName + #32#32 + FormatFileFunction('DC().GETFILESIZE{}', AFile, FileSource);
AFileName:= AFileName + #32#32 + FormatFileFunction('DC().GETFILETIME{}', AFile, FileSource);
AFileName:= AFileName + #32#32 + FormatFileFunction('DC().GETFILEATTR{}', AFile, FileSource);
lblDetails.Caption:= AFileName;
// Get file name
if not FlatView then
begin
AFileName:= FormatFileFunction('DC().GETFILENAMENOEXT{}', AFile, FileSource);
lblInfo.Caption:= FitFileName(AFileName, lblInfo.Canvas, AFile, lblInfo.ClientWidth);
begin
AFile:= AFile.Clone(True);
try
// Get details info about file
AFileName:= #32#32 +FormatFileFunction('DC().GETFILEEXT{}', AFile, FileSource);
AFileName:= AFileName + #32#32 + FormatFileFunction('DC().GETFILESIZE{}', AFile, FileSource);
AFileName:= AFileName + #32#32 + FormatFileFunction('DC().GETFILETIME{}', AFile, FileSource);
AFileName:= AFileName + #32#32 + FormatFileFunction('DC().GETFILEATTR{}', AFile, FileSource);
lblDetails.Caption:= AFileName;
// Get file name
if not FlatView then
begin
AFileName:= FormatFileFunction('DC().GETFILENAMENOEXT{}', AFile, FileSource);
lblInfo.Caption:= FitFileName(AFileName, lblInfo.Canvas, AFile.FSFile, lblInfo.ClientWidth);
end;
finally
AFile.Free;
end;
finally
AFile.Free;
end;
end;
end;

View file

@ -1307,7 +1307,7 @@ begin
if AFile.FSFile.Name = '..' then Exit;
HintInfo^.HintStr:= FileSource.GetFileName( AFile.FSFile );
sHint:= GetFileInfoToolTip(FileSource, AFile.FSFile);
sHint:= GetFileInfoToolTip(FileSource, AFile);
if (sHint <> EmptyStr) then
HintInfo^.HintStr:= HintInfo^.HintStr + LineEnding + sHint;

View file

@ -772,9 +772,8 @@ begin
for i := 0 to aFileSourceFiles.Count - 1 do
begin
AFile := TDisplayFile.Create(aFileSourceFiles[i]);
AFile.DisplayName:= fs.GetDisplayFileName(aFileSourceFiles[i]);
AFile.TextColor:= gColorExt.GetColorBy(AFile.FSFile);
if HaveIcons then
begin
AFile.IconID := PixMapManager.GetIconByFile(fs,

View file

@ -47,8 +47,8 @@ uses
//DC
DCXmlConfig, uOSForms, uRegExprW, uFileProperty, uFormCommands,
uFileSourceSetFilePropertyOperation, DCStringHashListUtf8, uClassesEx, uFile,
uFileSource, DCClassesUtf8, uHotkeyManager;
uFileSourceSetFilePropertyOperation, DCStringHashListUtf8, uClassesEx,
uFile, uDisplayFile, uFileSource, DCClassesUtf8, uHotkeyManager;
const
HotkeysCategoryMultiRename = 'MultiRename'; // <--Not displayed to user, stored in .scf (Shortcut Configuration File)
@ -2190,6 +2190,7 @@ end;
function TfrmMultiRename.sHandleFormatString(const sFormatStr: string; ItemNr: integer): string;
var
aFile: TFile;
aDisplayFile: TDisplayFile;
Index: int64;
Counter: int64;
Dirs: TStringArray;
@ -2254,7 +2255,11 @@ begin
'=':
begin
Result := sReplaceBadChars(FormatFileFunction(UTF8Copy(sFormatStr, 2, UTF8Length(sFormatStr) - 1), FFiles.Items[ItemNr], FFileSource, True));
aDisplayFile:= TDisplayFile.Create(aFile);
aDisplayFile.DisplayName:= EmptyStr;
Result := sReplaceBadChars(FormatFileFunction(UTF8Copy(sFormatStr, 2, UTF8Length(sFormatStr) - 1), aDisplayFile, FFileSource, True));
aDisplayFile.FSFile:= nil;
aDisplayFile.Free;
end;
else

View file

@ -28,7 +28,7 @@ interface
uses
Classes, SysUtils, fgl, DCXmlConfig,
uFile, uFileSource;
uFile, uDisplayFile, uFileSource;
type
{ THintItem }
@ -57,7 +57,7 @@ type
procedure Clear;
function GetFileInfoToolTip(aFileSource: IFileSource; const aFile: TFile): String;
function GetFileInfoToolTip(aFileSource: IFileSource; const aFile: TDisplayFile): String;
procedure Load(AConfig: TXmlConfig; ANode: TXmlNode);
procedure LoadFromFile(const FileName: String);
@ -69,7 +69,7 @@ type
property HintItemList: THintItemList read FHintItemList;
end;
function GetFileInfoToolTip(aFileSource: IFileSource; const aFile: TFile): String;
function GetFileInfoToolTip(aFileSource: IFileSource; const aDisplayFile: TDisplayFile): String;
implementation
@ -81,10 +81,13 @@ uses
{$ENDIF}
,DCClassesUtf8;
function GetFileInfoToolTip(aFileSource: IFileSource; const aFile: TFile): String;
function GetFileInfoToolTip(aFileSource: IFileSource; const aDisplayFile: TDisplayFile): String;
function GetDefaultToolTip(const Hint: String): String;
var
aFile: TFile;
begin
aFile:= aDisplayFile.FSFile;
Result:= Hint;
if (fpModificationTime in aFile.SupportedProperties) and aFile.ModificationTimeProperty.IsValid then
with (aFile.Properties[fpModificationTime] as TFileModificationDateTimeProperty) do
@ -103,7 +106,7 @@ begin
if fspDirectAccess in aFileSource.Properties then
begin
case gShowToolTipMode of
tttmCombineDcSystem, tttmDcSystemCombine, tttmDcIfPossThenSystem, tttmDcOnly: Result := StringReplace(gFileInfoToolTip.GetFileInfoToolTip(aFileSource, aFile), '\n', LineEnding, [rfReplaceAll]);
tttmCombineDcSystem, tttmDcSystemCombine, tttmDcIfPossThenSystem, tttmDcOnly: Result := StringReplace(gFileInfoToolTip.GetFileInfoToolTip(aFileSource, aDisplayFile), '\n', LineEnding, [rfReplaceAll]);
tttmSystemOnly: Result := EmptyStr;
end;
@ -177,7 +180,7 @@ begin
end;
function TFileInfoToolTip.GetFileInfoToolTip(aFileSource: IFileSource;
const aFile: TFile): String;
const aFile: TDisplayFile): String;
var
I, J: Integer;
HintItem: THintItem;
@ -194,7 +197,7 @@ begin
with gSearchTemplateList do
begin
if (Templates[J].TemplateName = PChar(HintItem.Mask)+1) and
Templates[J].CheckFile(AFile) then
Templates[J].CheckFile(AFile.FSFile) then
begin
Result:= FormatFileFunctions(HintItem.Hint, aFile, aFileSource);
Exit;
@ -202,7 +205,7 @@ begin
end;
// Get hint by file mask
if MatchesMaskList(AFile.Name, HintItem.Mask) then
if MatchesMaskList(AFile.FSFile.Name, HintItem.Mask) then
begin
Result:= FormatFileFunctions(HintItem.Hint, aFile, aFileSource);
Exit;

View file

@ -29,7 +29,7 @@ unit uColumns;
interface
uses
Classes, SysUtils, Graphics, uFile, uFileSource,
Classes, SysUtils, Graphics, uFile, uDisplayFile, uFileSource,
DCXmlConfig, FpJson, DCBasicTypes, uFileFunctions, uColors;
const
@ -68,7 +68,7 @@ type
procedure SetUnique(const AValue: String);
procedure SetFuncString(NewValue: String);
function GetColumnResultString(AFile: TFile; const AFileSource: IFileSource): String;
function GetColumnResultString(AFile: TDisplayFile; const AFileSource: IFileSource): String;
public
//---------------------
@ -167,7 +167,7 @@ type
}
function GetColumnFunctions(const Index: Integer): TFileFunctions;
function GetColumnItemResultString(const Index: Integer;
const AFile: TFile; const AFileSource: IFileSource): String;
const AFile: TDisplayFile; const AFileSource: IFileSource): String;
//--------------------------------------------------------------------------
function GetColumnItem(const Index: Integer): TPanelColumn;
function GetCount: Integer;
@ -565,7 +565,7 @@ begin
end;
function TPanelColumnsClass.GetColumnItemResultString(const Index: Integer;
const AFile: TFile; const AFileSource: IFileSource): String;
const AFile: TDisplayFile; const AFileSource: IFileSource): String;
begin
if Index >= Flist.Count then
Exit(EmptyStr);
@ -1231,7 +1231,7 @@ begin
inherited Destroy;
end;
function TPanelColumn.GetColumnResultString(AFile: TFile; const AFileSource: IFileSource): String;
function TPanelColumn.GetColumnResultString(AFile: TDisplayFile; const AFileSource: IFileSource): String;
var
i: Integer;
s: String;

View file

@ -27,7 +27,7 @@ unit uFileFunctions;
interface
uses
Classes, SysUtils, Menus, uFile, uFileProperty, uFileSource;
Classes, SysUtils, Menus, uFile, uDisplayFile, uFileProperty, uFileSource;
type
TFileFunction = (fsfName = 0,
@ -75,8 +75,8 @@ const
'' // fsfInvalid
);
function FormatFileFunction(FuncS: string; AFile: TFile; const AFileSource: IFileSource; RetrieveProperties: Boolean = False): string;
function FormatFileFunctions(FuncS: String; AFile: TFile; const AFileSource: IFileSource): String;
function FormatFileFunction(FuncS: string; ADisplayFile: TDisplayFile; const AFileSource: IFileSource; RetrieveProperties: Boolean = False): string;
function FormatFileFunctions(FuncS: String; ADisplayFile: TDisplayFile; const AFileSource: IFileSource): String;
function GetVariantFileProperty(const FuncS: String; AFile: TFile; const AFileSource: IFileSource): Variant;
function GetFileFunctionByName(FuncS: string): TFileFunction;
function GetFilePropertyType(FileFunction: TFileFunction): TFilePropertiesTypes; inline;
@ -180,33 +180,18 @@ begin
Result := Copy(S, 1, I - 1);
end;
function FormatFileFunction(FuncS: string; AFile: TFile;
function FormatFileFunction(FuncS: string; ADisplayFile: TDisplayFile;
const AFileSource: IFileSource; RetrieveProperties: Boolean): string;
var
AIndex: Integer;
AValue: Variant;
FileFunction: TFileFunction;
AType, AFunc, AParam: String;
AFile: TFile;
AFileProperty: TFileVariantProperty;
FilePropertiesNeeded: TFilePropertiesTypes;
ADisplayName: String;
ADisplayNameNoExtension: String;
ADisplayExtension: String;
procedure prepareDisplayFileName;
begin
ADisplayName := AFileSource.GetDisplayFileName(AFile);
if ADisplayName = EmptyStr then begin
ADisplayName := AFile.Name;
ADisplayNameNoExtension := AFile.NameNoExt;
ADisplayExtension := AFile.Extension;
end else begin
TFile.SplitIntoNameAndExtension(ADisplayName, ADisplayNameNoExtension, ADisplayExtension);
end;
end;
begin
AFile:= ADisplayFile.FSFile;
Result := EmptyStr;
//---------------------
AType := upcase(GetModType(FuncS));
@ -227,21 +212,20 @@ begin
if aFileSource.CanRetrieveProperties(AFile, FilePropertiesNeeded) then
aFileSource.RetrieveProperties(AFile, FilePropertiesNeeded, []);
end;
prepareDisplayFileName;
case FileFunction of
fsfName:
begin
// Show square brackets around directories
if gDirBrackets and (AFile.IsDirectory or
AFile.IsLinkToDirectory) then
Result := gFolderPrefix + ADisplayName + gFolderPostfix
Result := gFolderPrefix + ADisplayFile.DisplayName + gFolderPostfix
else
Result := ADisplayName;
Result := ADisplayFile.DisplayName;
end;
fsfExtension:
begin
Result := ADisplayExtension;
Result := ADisplayFile.DisplayExt;
end;
fsfSize:
@ -357,9 +341,9 @@ begin
// Show square brackets around directories
if gDirBrackets and (AFile.IsDirectory or
AFile.IsLinkToDirectory) then
Result := gFolderPrefix + ADisplayNameNoExtension + gFolderPostfix
Result := gFolderPrefix + ADisplayFile.DisplayNameNoExt + gFolderPostfix
else
Result := ADisplayNameNoExtension;
Result := ADisplayFile.DisplayNameNoExt;
end;
fsfType:
@ -416,7 +400,7 @@ begin
//------------------------------------------------------
end;
function FormatFileFunctions(FuncS: String; AFile: TFile; const AFileSource: IFileSource): String;
function FormatFileFunctions(FuncS: String; ADisplayFile: TDisplayFile; const AFileSource: IFileSource): String;
var
P: Integer;
begin
@ -435,7 +419,7 @@ begin
if P = 0 then
Break
else if P > 1 then
Result:= Result + FormatFileFunction(Copy(FuncS, 1, P - 1), AFile, AFileSource, True);
Result:= Result + FormatFileFunction(Copy(FuncS, 1, P - 1), ADisplayFile, AFileSource, True);
Delete(FuncS, 1, P);
end;

View file

@ -41,7 +41,7 @@ type
@br 0 equal
@br 1 greater)
}
class function Compare(const FileSorting: TFileSorting; File1, File2: TFile): Integer;
class function Compare(const FileSorting: TFileSorting; displayFile1, displayFile2: TDisplayFile): Integer;
public
constructor Create(const Sortings: TFileSortings); reintroduce;
@ -165,8 +165,8 @@ type
function CloneSortings(const Sortings: TFileSortings): TFileSortings;
function ICompareByDirectory(item1, item2: TFile; bSortNegative: Boolean):Integer;
function ICompareByName(item1, item2: TFile; bSortNegative: Boolean):Integer;
function ICompareByNameNoExt(item1, item2: TFile; bSortNegative: Boolean):Integer;
function ICompareByName(item1, item2: TDisplayFile; bSortNegative: Boolean):Integer;
function ICompareByNameNoExt(displayFile1, displayFile2: TDisplayFile; bSortNegative: Boolean):Integer;
function ICompareByExt (item1, item2: TFile; bSortNegative: Boolean):Integer;
function ICompareBySize(item1, item2: TFile; bSortNegative: Boolean):Integer;
function ICompareByDate(date1, date2: TDateTime; bSortNegative: Boolean):Integer;
@ -456,36 +456,31 @@ begin
Result := CompareStrings(item1.Name, item2.Name, gSortNatural, gSortSpecial, gSortCaseSensitivity);
end;
function ICompareByName(item1, item2: TFile; bSortNegative: Boolean):Integer;
function ICompareByName(item1, item2: TDisplayFile; bSortNegative: Boolean):Integer;
var
name1: String;
name2: String;
begin
name1:= item1.Name;
name2:= item2.Name;
if (name1=EmptyStr) and (name2=EmptyStr) then begin
name1:= item1.FullPath;
name2:= item2.FullPath;
end;
name1:= item1.DisplayName;
name2:= item2.DisplayName;
Result := CompareStrings(name1, name2, gSortNatural, gSortSpecial, gSortCaseSensitivity);
if bSortNegative then
Result := -Result;
end;
function ICompareByNameNoExt(item1, item2: TFile; bSortNegative: Boolean):Integer;
function ICompareByNameNoExt(displayFile1, displayFile2: TDisplayFile; bSortNegative: Boolean):Integer;
begin
// Don't sort directories only by name.
if item1.IsDirectory or item1.IsLinkToDirectory or
item2.IsDirectory or item2.IsLinkToDirectory then
if displayFile1.FSFile.IsDirectory or displayFile1.FSFile.IsLinkToDirectory or
displayFile2.FSFile.IsDirectory or displayFile2.FSFile.IsLinkToDirectory then
begin
// Sort by full name.
Result := ICompareByName(item1, item2, bSortNegative);
Result := ICompareByName(displayFile1, displayFile2, bSortNegative);
end
else
begin
Result := CompareStrings(item1.NameNoExt, item2.NameNoExt, gSortNatural, gSortSpecial, gSortCaseSensitivity);
Result := CompareStrings(displayFile1.DisplayNameNoExt, displayFile2.DisplayNameNoExt, gSortNatural, gSortSpecial, gSortCaseSensitivity);
if bSortNegative then
Result := -Result;
@ -637,11 +632,14 @@ begin
end;
end;
class function TBaseSorter.Compare(const FileSorting: TFileSorting; File1, File2: TFile): Integer;
class function TBaseSorter.Compare(
const FileSorting: TFileSorting;
displayFile1, displayFile2: TDisplayFile ): Integer;
var
i: Integer;
bNegative: Boolean;
AFileProp: TFilePropertyType;
File1, File2: TFile;
begin
Result := 0;
@ -656,12 +654,15 @@ begin
Exit;
end;
File1:= displayFile1.FSFile;
File2:= displayFile2.FSFile;
for i := 0 to Length(FileSorting.SortFunctions) - 1 do
begin
//------------------------------------------------------
case FileSorting.SortFunctions[i] of
fsfName:
Result := ICompareByName(File1, File2, bNegative);
Result := ICompareByName( displayFile1, displayFile2, bNegative);
fsfExtension:
Result := ICompareByExt(File1, File2, bNegative);
fsfSize:
@ -710,7 +711,7 @@ begin
if bNegative then Result := -Result;
end;
fsfNameNoExtension:
Result := ICompareByNameNoExt(File1, File2, bNegative);
Result := ICompareByNameNoExt(displayFile1, displayFile2, bNegative);
fsfType:
begin
Result := mbCompareText(File1.TypeProperty.Value,
@ -1029,7 +1030,7 @@ begin
for i := 0 to Length(FSortings) - 1 do
begin
Result := Compare(FSortings[i], TDisplayFile(item1).FSFile, TDisplayFile(item2).FSFile);
Result := Compare(FSortings[i], TDisplayFile(item1), TDisplayFile(item2) );
if Result <> 0 then Exit;
end;
end;
@ -1176,18 +1177,18 @@ begin
// Put directories first.
if (gSortFolderMode <> sfmSortLikeFile) then
begin
Result := ICompareByDirectory(TFile(item1), TFile(item2), False); // Ascending
Result := ICompareByDirectory(TDisplayFile(item1).FSFile, TDisplayFile(item2).FSFile, False); // Ascending
if Result <> 0 then Exit;
end
else begin
// Put '..' first.
if TFile(item1).Name = '..' then Exit(-1);
if TFile(item2).Name = '..' then Exit(+1);
if TDisplayFile(item1).FSFile.Name = '..' then Exit(-1);
if TDisplayFile(item2).FSFile.Name = '..' then Exit(+1);
end;
for i := 0 to Length(FSortings) - 1 do
begin
Result := Compare(FSortings[i], TFile(item1), TFile(item2));
Result := Compare(FSortings[i], TDisplayFile(item1), TDisplayFile(item2) );
if Result <> 0 then Exit;
end;
end;