doublecmd/src/filesources/ufilesource.pas

1258 lines
39 KiB
ObjectPascal

unit uFileSource;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils,
syncobjs, URIParser,
LCLProc, Menus, Graphics, ShellCtrls,
DCStrUtils,
uFile, uDisplayFile, uFileProperty,
uFileSourceWatcher,
uFileSourceOperation, uFileSourceOperationTypes, uFileSourceProperty;
type
TFileSource = class;
TFileSourceConnection = class;
IFileSource = interface;
TFileSourceConsultResult = ( fscrSuccess, fscrNotImplemented, fscrNotSupported, fscrCancel );
{$scopedEnums on}
TFileSourceConsultPhase = ( source, target );
TFileSourceConsultParams = Record
handled: Boolean;
phase: TFileSourceConsultPhase;
operationType: TFileSourceOperationType;
files: TFiles;
targetPath: String;
sourceFS: IFileSource;
targetFS: IFileSource;
currentFS: IFileSource;
partnerFS: IFileSource;
consultResult: TFileSourceConsultResult;
resultOperationType: TFileSourceOperationType;
resultFS: IFileSource;
resultTargetPath: String;
operationTemp: Boolean;
end;
TFileSourceProcessor = class
procedure consultOperation( var params: TFileSourceConsultParams ); virtual; abstract;
procedure confirmOperation( var params: TFileSourceConsultParams ); virtual; abstract;
end;
TFileSourceUIParams = record
sender: TObject;
fs: IFileSource;
displayFile: TDisplayFile;
multiColumns: Boolean; // True for ColumnsView, False for BriefView
col: Integer;
row: Integer;
drawingRect: TRect;
decorationRect: TRect;
case Byte of
0: (
iconRect: TRect;
focused: Boolean
);
1: (
shift: TShiftState;
x: Integer;
y: Integer
);
end;
TFileSourceUIHandler = class
procedure draw( var params: TFileSourceUIParams ); virtual; abstract;
function click( var params: TFileSourceUIParams ): Boolean; virtual; abstract;
end;
TFileSourceField = record
Content: String;
Header: String;
Width: Integer;
Option: String;
Align: TAlignment;
end;
TFileSourceFields = array of TFileSourceField;
TFileSourceOperationsClasses = array[TFileSourceOperationType] of TFileSourceOperationClass;
TPathsArray = array of string;
{$scopedEnums on}
TFileSourceEventType = ( reload, relocation, queryActive );
TFileSourceEventParams = record
eventType: TFileSourceEventType;
fs: IFileSource;
// reload input
paths: TPathsArray;
// relocation input
newPath: String;
// queryActive output
resultDisplayFile: TDisplayFile;
end;
TFileSourceEventListener = procedure(var params: TFileSourceEventParams) of object;
{ IFileSource }
IFileSource = interface(IInterface)
['{B7F0C4C8-59F6-4A35-A54C-E8242F4AD809}']
function GetWatcher: TFileSourceWatcher;
function GetProcessor: TFileSourceProcessor;
function GetUIHandler: TFileSourceUIHandler;
function Equals(aFileSource: IFileSource): Boolean;
function IsInterface(InterfaceGuid: TGuid): Boolean;
function IsClass(ClassType: TClass): Boolean;
function GetClass: TFileSource;
function GetURI: TURI;
function GetClassName: String;
function GetRefCount: Integer;
function GetFileSystem: String;
function GetCurrentAddress: String;
function GetCurrentWorkingDirectory: String;
function SetCurrentWorkingDirectory(NewDir: String): Boolean;
function GetSupportedFileProperties: TFilePropertiesTypes;
function GetRetrievableFileProperties: TFilePropertiesTypes;
function GetOperationsTypes: TFileSourceOperationTypes;
function GetProperties: TFileSourceProperties;
function GetFiles(TargetPath: String): TFiles;
function GetFilesForPathAndType(
const TargetPath: String;
const types: TObjectTypes;
const sort: TFileSortType ): TStringList;
function GetParentFileSource: IFileSource;
procedure SetParentFileSource(NewValue: IFileSource);
function CreateFileObject(const APath: String): TFile;
function CanRetrieveProperties(AFile: TFile; PropertiesToSet: TFilePropertiesTypes): Boolean;
procedure RetrieveProperties(AFile: TFile; PropertiesToSet: TFilePropertiesTypes; const AVariantProperties: array of String);
function CreateListOperation(TargetPath: String): TFileSourceOperation;
function CreateCopyOperation(var SourceFiles: TFiles;
TargetPath: String): TFileSourceOperation;
function CreateCopyInOperation(SourceFileSource: IFileSource;
var SourceFiles: TFiles;
TargetPath: String): TFileSourceOperation;
function CreateCopyOutOperation(TargetFileSource: IFileSource;
var SourceFiles: TFiles;
TargetPath: String): TFileSourceOperation;
function CreateMoveOperation(var SourceFiles: TFiles;
TargetPath: String): TFileSourceOperation;
function CreateDeleteOperation(var FilesToDelete: TFiles): TFileSourceOperation;
function CreateWipeOperation(var FilesToWipe: TFiles): TFileSourceOperation;
function CreateSplitOperation(var aSourceFile: TFile;
aTargetPath: String): TFileSourceOperation;
function CreateCombineOperation(var theSourceFiles: TFiles;
aTargetFile: String): TFileSourceOperation;
function CreateCreateDirectoryOperation(BasePath: String; DirectoryPath: String): TFileSourceOperation;
function CreateExecuteOperation(var ExecutableFile: TFile; BasePath, Verb: String): TFileSourceOperation;
function CreateTestArchiveOperation(var theSourceFiles: TFiles): TFileSourceOperation;
function CreateCalcChecksumOperation(var theFiles: TFiles;
aTargetPath: String;
aTargetMask: String): TFileSourceOperation;
function CreateCalcStatisticsOperation(var theFiles: TFiles): TFileSourceOperation;
function CreateSetFilePropertyOperation(var theTargetFiles: TFiles;
var theNewProperties: TFileProperties): TFileSourceOperation;
function GetOperationClass(OperationType: TFileSourceOperationType): TFileSourceOperationClass;
function IsSystemFile(aFile: TFile): Boolean;
function IsHiddenFile(aFile: TFile): Boolean;
function GetFileName(aFile: TFile): String;
function GetDisplayFileName(aFile: TFile): String;
function IsPathAtRoot(Path: String): Boolean;
function GetParentDir(sPath : String): String;
function GetRootDir(sPath : String): String; overload;
function GetRootDir: String; overload;
function GetPathType(sPath : String): TPathType;
function GetCustomIcon(aFile: TFile; AIconSize: Integer; out AIcon: TBitmap): IntPtr; overload;
function GetCustomIcon(const path: String; const iconSize: Integer): TBitmap; overload;
function GetFreeSpace(Path: String; out FreeSize, TotalSize : Int64) : Boolean;
function GetRealPath(const path: String): String;
function GetLocalName(var aFile: TFile): Boolean;
procedure AddSearchPath( const startPath: String; paths: TStringList );
function CreateDirectory(const Path: String): Boolean;
function FileSystemEntryExists(const Path: String): Boolean;
function GetDefaultView(out DefaultView: TFileSourceFields): Boolean;
function QueryContextMenu(AFiles: TFiles; var AMenu: TPopupMenu): Boolean;
function GetConnection(Operation: TFileSourceOperation): TFileSourceConnection;
procedure RemoveOperationFromQueue(Operation: TFileSourceOperation);
procedure AddChild(AFileSource: IFileSource);
procedure eventNotify( var params: TFileSourceEventParams );
procedure Reload(const PathsToReload: TPathsArray);
procedure Reload(const PathToReload: String);
function needReload(const PathToReload: String; const PathToCheck: String): Boolean;
procedure AddEventListener(FunctionToCall: TFileSourceEventListener);
procedure RemoveEventListener(FunctionToCall: TFileSourceEventListener);
property URI: TURI read GetURI;
property ClassName: String read GetClassName;
property FileSystem: String read GetFileSystem;
property CurrentAddress: String read GetCurrentAddress;
property ParentFileSource: IFileSource read GetParentFileSource write SetParentFileSource;
property Properties: TFileSourceProperties read GetProperties;
property SupportedFileProperties: TFilePropertiesTypes read GetSupportedFileProperties;
property RetrievableFileProperties: TFilePropertiesTypes read GetRetrievableFileProperties;
end;
{ TFileSource }
TFileSource = class(TInterfacedObject, IFileSource)
private
FEventListeners: TMethodList;
{en
File source on which this file source is dependent on
(files that it accesses are on the parent file source).
}
FParentFileSource: IFileSource;
{en
Callback called when an operation assigned to a connection finishes.
It just redirects to a virtual function.
}
procedure OperationFinishedCallback(Operation: TFileSourceOperation;
State: TFileSourceOperationState);
protected
FURI: TURI;
FCurrentAddress: String;
FOperationsClasses: TFileSourceOperationsClasses;
{en
Children file source list
}
FChildrenFileSource: TInterfaceList;
function GetURI: TURI;
{en
Retrieves the full address of the file source
(the CurrentPath is relative to this).
This may be used for specifying address:
- archive : path to archive
- network : address of server
etc.
}
function GetCurrentAddress: String; virtual;
{en
Retrieves the current directory of the file source.
}
function GetCurrentWorkingDirectory: String; virtual;
{en
Sets the current directory for the file source.
@returns(@true if path change was successful,
@false otherwise)
}
function SetCurrentWorkingDirectory(NewDir: String): Boolean; virtual;
{en
Returns all the properties supported by the file type of the given file source.
}
function GetSupportedFileProperties: TFilePropertiesTypes; virtual;
{en
Returns all the file properties that can be retrieved by the file source.
}
function GetRetrievableFileProperties: TFilePropertiesTypes; virtual;
function GetParentFileSource: IFileSource;
procedure SetParentFileSource(NewValue: IFileSource);
{en
Checks if the connection is available and, if it is, assigns it to the operation.
@returns(Connection object if the connection is available,
@nil otherwise.)
}
function TryAcquireConnection(connection: TFileSourceConnection;
operation: TFileSourceOperation): TFileSourceConnection; virtual;
procedure OperationFinished(Operation: TFileSourceOperation); virtual;
{en
Reloads any internal file lists/caches.
@param(PathsToReload
Describes paths in file source from which file lists should be reloaded.
The function may also reload any subpaths, though that is
dependent on the specific file source implementation.)
}
procedure DoReload(const PathsToReload: TPathsArray); virtual;
function CreateFileObject(const APath: String): TFile;
public
constructor Create; virtual; overload;
constructor Create(const URI: TURI); virtual; overload;
destructor Destroy; override;
class function GetFileSource: IFileSource; virtual;
function GetWatcher: TFileSourceWatcher; virtual;
function GetProcessor: TFileSourceProcessor; virtual;
function GetUIHandler: TFileSourceUIHandler; virtual;
function Equals(aFileSource: IFileSource): Boolean; overload;
function IsInterface(InterfaceGuid: TGuid): Boolean;
function IsClass(aClassType: TClass): Boolean;
function GetClass: TFileSource;
function GetClassName: String; // For debugging purposes.
function GetRefCount: Integer; // For debugging purposes.
// Retrieve operations permitted on the source. = capabilities?
function GetOperationsTypes: TFileSourceOperationTypes; virtual;
// Retrieve some properties of the file source.
function GetProperties: TFileSourceProperties; virtual;
// Retrieves a list of files.
// This is the same as GetOperation(fsoList), executing it
// and returning the result of Operation.ReleaseFiles.
// Caller is responsible for freeing the result list.
function GetFiles(TargetPath: String): TFiles; virtual;
// Retrieves a list of files.
// the default implementation is based on GetFiles(TargetPath: String): TFiles;
// a more efficient implementation is possible for specific FileSources.
function GetFilesForPathAndType(
const path: String;
const types: TObjectTypes;
const sort: TFileSortType ): TStringList;
// Create an empty TFile object with appropriate properties for the file.
class function CreateFile(const APath: String): TFile; virtual;
procedure RetrieveProperties(AFile: TFile; PropertiesToSet: TFilePropertiesTypes; const AVariantProperties: array of String); virtual;
function CanRetrieveProperties(AFile: TFile; PropertiesToSet: TFilePropertiesTypes): Boolean; virtual;
// These functions create an operation object specific to the file source.
function CreateListOperation(TargetPath: String): TFileSourceOperation; virtual;
function CreateCopyOperation(var SourceFiles: TFiles;
TargetPath: String): TFileSourceOperation; virtual;
function CreateCopyInOperation(SourceFileSource: IFileSource;
var SourceFiles: TFiles;
TargetPath: String): TFileSourceOperation; virtual;
function CreateCopyOutOperation(TargetFileSource: IFileSource;
var SourceFiles: TFiles;
TargetPath: String): TFileSourceOperation; virtual;
function CreateMoveOperation(var SourceFiles: TFiles;
TargetPath: String): TFileSourceOperation; virtual;
function CreateDeleteOperation(var FilesToDelete: TFiles): TFileSourceOperation; virtual;
function CreateWipeOperation(var FilesToWipe: TFiles): TFileSourceOperation; virtual;
function CreateSplitOperation(var aSourceFile: TFile;
aTargetPath: String): TFileSourceOperation; virtual;
function CreateCombineOperation(var theSourceFiles: TFiles;
aTargetFile: String): TFileSourceOperation; virtual;
function CreateCreateDirectoryOperation(BasePath: String; DirectoryPath: String): TFileSourceOperation; virtual;
function CreateExecuteOperation(var ExecutableFile: TFile; BasePath, Verb: String): TFileSourceOperation; virtual;
function CreateTestArchiveOperation(var theSourceFiles: TFiles): TFileSourceOperation; virtual;
function CreateCalcChecksumOperation(var theFiles: TFiles;
aTargetPath: String;
aTargetMask: String): TFileSourceOperation; virtual;
function CreateCalcStatisticsOperation(var theFiles: TFiles): TFileSourceOperation; virtual;
function CreateSetFilePropertyOperation(var theTargetFiles: TFiles;
var theNewProperties: TFileProperties): TFileSourceOperation; virtual;
function GetOperationClass(OperationType: TFileSourceOperationType): TFileSourceOperationClass;
class function GetMainIcon(out Path: String): Boolean; virtual;
{en
Returns @true if the given path is supported by the file source,
@false otherwise.
}
class function IsSupportedPath(const Path: String): Boolean; virtual;
{en
Returns @true if the given path is the root path of the file source,
@false otherwise.
}
function IsSystemFile(aFile: TFile): Boolean; virtual;
function IsHiddenFile(aFile: TFile): Boolean; virtual;
function GetDisplayFileName(aFile: TFile): String; virtual;
function GetFileName(aFile: TFile): String; virtual;
function IsPathAtRoot(Path: String): Boolean; virtual;
function GetParentDir(sPath : String): String; virtual;
function GetRootDir(sPath : String): String; virtual; overload;
function GetRootDir: String; virtual; overload;
function GetPathType(sPath : String): TPathType; virtual;
function GetFileSystem: String; virtual;
function CreateDirectory(const Path: String): Boolean; virtual;
function FileSystemEntryExists(const Path: String): Boolean; virtual;
function GetCustomIcon(aFile: TFile; AIconSize: Integer; out AIcon: TBitmap): PtrInt; virtual; overload;
function GetCustomIcon(const path: String; const iconSize: Integer): TBitmap; virtual; overload;
function GetFreeSpace(Path: String; out FreeSize, TotalSize : Int64) : Boolean; virtual;
function QueryContextMenu(AFiles: TFiles; var AMenu: TPopupMenu): Boolean; virtual;
function GetDefaultView(out DefaultView: TFileSourceFields): Boolean; virtual;
function GetRealPath(const path: String): String; virtual;
function GetLocalName(var aFile: TFile): Boolean; virtual;
procedure AddSearchPath( const startPath: String; paths: TStringList ); virtual;
function GetConnection(Operation: TFileSourceOperation): TFileSourceConnection; virtual;
{en
This function is to ensure the operation does not stay in the queue
when it's being destroyed.
}
procedure RemoveOperationFromQueue(Operation: TFileSourceOperation); virtual;
procedure AddChild(AFileSource: IFileSource);
{en
Reloads the file list from the file source.
This is used if a file source has any internal cache or file list.
Overwrite DoReload in descendant classes.
}
procedure eventNotify( var params: TFileSourceEventParams );
procedure Reload(const PathsToReload: TPathsArray); virtual; overload;
procedure Reload(const PathToReload: String); overload;
function needReload(const PathToReload: String; const PathToCheck: String): Boolean; virtual;
procedure AddEventListener(FunctionToCall: TFileSourceEventListener);
procedure RemoveEventListener(FunctionToCall: TFileSourceEventListener);
property CurrentAddress: String read GetCurrentAddress;
property ParentFileSource: IFileSource read GetParentFileSource write SetParentFileSource;
property Properties: TFileSourceProperties read GetProperties;
property SupportedFileProperties: TFilePropertiesTypes read GetSupportedFileProperties;
property RetrievableFileProperties: TFilePropertiesTypes read GetRetrievableFileProperties;
end;
{ TFileSourceConnection }
TFileSourceConnection = class
private
FAssignedOperation: TFileSourceOperation;
FOperationLock: TCriticalSection;
function GetAssignedOperation: TFileSourceOperation;
protected
FCurrentPath: String; // Always includes trailing path delimiter.
function GetCurrentPath: String; virtual;
procedure SetCurrentPath(NewPath: String); virtual;
public
constructor Create; virtual;
destructor Destroy; override;
function IsAvailable: Boolean;
function Acquire(Operation: TFileSourceOperation): Boolean;
procedure Release;
property CurrentPath: String read GetCurrentPath write SetCurrentPath;
property AssignedOperation: TFileSourceOperation read GetAssignedOperation;
end;
{ TFileSources }
TFileSources = class(TInterfaceList)
private
function Get(I: Integer): IFileSource;
procedure Put(i : Integer;item : IFileSource);
public
procedure Assign(otherFileSources: TFileSources);
property Items[I: Integer]: IFileSource read Get write Put; default;
end;
EFileSourceException = class(Exception);
EFileNotFound = class(EFileSourceException)
private
FFilePath: String;
public
constructor Create(const AFilePath: string); reintroduce;
property FilePath: String read FFilePath;
end;
var
defaultFileSourceProcessor: TFileSourceProcessor;
implementation
uses
uDebug, uFileSourceManager, uFileSourceListOperation, uLng, LazFileUtils;
var
defaultFileSourceWatcher: TFileSourceWatcher;
{ TFileSource }
constructor TFileSource.Create;
begin
if ClassType = TFileSource then
raise Exception.Create('Cannot construct abstract class');
inherited Create;
FEventListeners := TMethodList.Create;
FileSourceManager.Add(Self); // Increases RefCount
// We don't want to count the reference in Manager, because we want to detect
// when there are no more references other than this single one in the Manager.
// So, we remove this reference here.
// When RefCount reaches 0 Destroy gets called and the last remaining reference
// (in the Manager) is removed there.
InterLockedDecrement(frefcount);
DCDebug('Creating ', ClassName);
end;
constructor TFileSource.Create(const URI: TURI);
var
AddressURI: TURI;
begin
Create;
FURI:= URI;
FillChar(AddressURI, SizeOf(TURI), 0);
AddressURI.Protocol:= FURI.Protocol;
AddressURI.Username:= FURI.Username;
AddressURI.Host:= FURI.Host;
AddressURI.Port:= FURI.Port;
AddressURI.HasAuthority:= FURI.HasAuthority;
FCurrentAddress:= EncodeURI(AddressURI);
end;
destructor TFileSource.Destroy;
begin
DCDebug('Destroying ', ClassName, ' when refcount=', DbgS(refcount));
if RefCount <> 0 then
begin
// There could have been an exception raised in the constructor
// in which case RefCount will be 1, so only issue warning if there was no exception.
// This will check for any exception, but it's enough for a warning.
if not Assigned(ExceptObject) then
DCDebug('Error: RefCount <> 0 for ', Self.ClassName);
end;
if Assigned(FileSourceManager) then
begin
// Restore reference removed in Create and
// remove the instance remaining in Manager.
// Increase refcount by 2, because we don't want removing the last instance
// from Manager to trigger another Destroy.
// RefCount = 0
InterLockedIncrement(frefcount);
InterLockedIncrement(frefcount);
// RefCount = 2
FileSourceManager.Remove(Self);
// RefCount = 1
InterLockedDecrement(frefcount);
// RefCount = 0 (back at the final value)
end
else
DCDebug('Error: Cannot remove file source - manager already destroyed!');
FreeAndNil(FChildrenFileSource);
FreeAndNil(FEventListeners);
inherited Destroy;
end;
class function TFileSource.GetFileSource: IFileSource;
begin
Result:= Create;
end;
function TFileSource.GetWatcher: TFileSourceWatcher;
begin
Result:= defaultFileSourceWatcher;
end;
function TFileSource.GetProcessor: TFileSourceProcessor;
begin
Result:= defaultFileSourceProcessor;
end;
function TFileSource.GetUIHandler: TFileSourceUIHandler;
begin
Result:= nil;
end;
function TFileSource.Equals(aFileSource: IFileSource): Boolean;
begin
// Both interface variables must be brought to the same interface.
Result := (Self as IFileSource) = (aFileSource as IFileSource);
end;
function TFileSource.IsInterface(InterfaceGuid: TGuid): Boolean;
var
t: TObject;
begin
Result := (Self.QueryInterface(InterfaceGuid, t) = S_OK);
if Result then
_Release; // QueryInterface increases refcount.
end;
function TFileSource.IsClass(aClassType: TClass): Boolean;
begin
Result := Self is aClassType;
end;
function TFileSource.GetClass: TFileSource;
begin
Result := Self
end;
function TFileSource.GetClassName: String;
begin
Result := ClassName;
end;
function TFileSource.GetRefCount: Integer;
begin
Result := RefCount;
end;
function TFileSource.GetOperationsTypes: TFileSourceOperationTypes;
begin
Result := [];
end;
function TFileSource.GetProperties: TFileSourceProperties;
begin
Result := [];
end;
function TFileSource.GetURI: TURI;
begin
Result := FURI;
end;
function TFileSource.GetCurrentAddress: String;
begin
Result := FCurrentAddress;
end;
function TFileSource.GetCurrentWorkingDirectory: String;
begin
Result := '';
end;
function TFileSource.SetCurrentWorkingDirectory(NewDir: String): Boolean;
begin
// By default every path setting succeeds.
Result := True;
end;
function TFileSource.GetSupportedFileProperties: TFilePropertiesTypes;
begin
Result := [fpName];
end;
function TFileSource.GetRetrievableFileProperties: TFilePropertiesTypes;
begin
Result := [];
end;
function TFileSource.GetParentFileSource: IFileSource;
begin
Result := FParentFileSource;
end;
procedure TFileSource.SetParentFileSource(NewValue: IFileSource);
begin
FParentFileSource := NewValue;
end;
function TFileSource.IsPathAtRoot(Path: String): Boolean;
begin
Result := (Path = GetRootDir(Path));
end;
function TFileSource.GetParentDir(sPath : String): String;
begin
Result := DCStrUtils.GetParentDir(sPath);
end;
function TFileSource.GetRootDir(sPath : String): String;
begin
// Default root is '/'. Override in descendant classes for other.
Result := PathDelim;
end;
function TFileSource.GetRootDir: String;
begin
Result := GetRootDir('');
end;
function TFileSource.GetPathType(sPath : String): TPathType;
begin
Result := ptNone;
if sPath <> '' then
begin
// Default root is '/'. Override in descendant classes for other.
if (sPath[1] = PathDelim) then
Result := ptAbsolute
else if ( Pos( PathDelim, sPath ) > 0 ) then
Result := ptRelative;
end;
end;
function TFileSource.GetFileSystem: String;
begin
Result:= EmptyStr;
end;
function TFileSource.CreateDirectory(const Path: String): Boolean;
begin
Result := False;
end;
function TFileSource.FileSystemEntryExists(const Path: String): Boolean;
begin
Result := True;
end;
function TFileSource.GetCustomIcon(aFile: TFile; AIconSize: Integer; out
AIcon: TBitmap): PtrInt;
begin
AIcon:= nil;
Result:= -1;
end;
function TFileSource.GetCustomIcon(const path: String; const iconSize: Integer): TBitmap;
begin
Result:= nil;
end;
function TFileSource.GetFreeSpace(Path: String; out FreeSize, TotalSize : Int64) : Boolean;
begin
Result := False; // not supported by default
end;
function TFileSource.QueryContextMenu(AFiles: TFiles; var AMenu: TPopupMenu): Boolean;
begin
Result:= False;
end;
function TFileSource.GetDefaultView(out DefaultView: TFileSourceFields): Boolean;
begin
Result:= False;
end;
function TFileSource.GetRealPath(const path: String): String;
begin
Result:= path;
end;
function TFileSource.GetLocalName(var aFile: TFile): Boolean;
begin
Result:= False;
end;
procedure TFileSource.AddSearchPath(const startPath: String; paths: TStringList);
begin
end;
// Operations.
function TFileSource.GetFiles(TargetPath: String): TFiles;
var
Operation: TFileSourceOperation;
ListOperation: TFileSourceListOperation;
begin
Result := nil;
if fsoList in GetOperationsTypes then
begin
Operation := CreateListOperation(TargetPath);
if Assigned(Operation) then
try
ListOperation := Operation as TFileSourceListOperation;
ListOperation.Execute;
Result := ListOperation.ReleaseFiles;
finally
FreeAndNil(Operation);
end;
end;
end;
function FilesSortAlphabet(List: TStringList; Index1, Index2: Integer): Integer;
begin
Result:= CompareFilenames(List[Index1], List[Index2]);
end;
function FilesSortFoldersFirst(List: TStringList; Index1, Index2: Integer): Integer;
var
attr1: TFileAttributesProperty;
attr2: TFileAttributesProperty;
isDir1: Boolean;
isDir2: Boolean;
begin
attr1:= TFileAttributesProperty(List.Objects[Index1]);
isDir1:= Assigned(attr1) and attr1.IsDirectory;
attr2:= TFileAttributesProperty(List.Objects[Index2]);
isDir2:= Assigned(attr2) and attr2.IsDirectory;
if isDir1 xor isDir2 then begin
if isDir1 then
Result:= -1
else begin
Result:= 1;
end;
end else begin
Result:= CompareFilenames(List[Index1], List[Index2])
end;
end;
function TFileSource.GetFilesForPathAndType(
const path: String;
const types: TObjectTypes;
const sort: TFileSortType ): TStringList;
var
files: TFiles;
strings: TStringList;
i: Integer;
f: TFile;
attr: TFileAttributesProperty;
begin
Result:= nil;
files:= self.GetFiles( path );
if files = nil then
Exit;
strings:= TStringList.Create;
for i:= 0 to files.Count-1 do begin
f:= files[i];
if (f.Name = '.') or (f.Name = '..') then
Continue;
attr:= f.AttributesProperty;
if Assigned(attr) then begin
if self.IsSystemFile(f) and not (otHidden in types) then
continue;
if attr.IsDirectory and not (otFolders in types) then
Continue;
if not attr.IsDirectory and not (otNonFolders in types) then
Continue;
end;
strings.AddObject( self.GetFileName(f), attr );
end;
if strings.Count > 1 then begin
case sort of
fstAlphabet: strings.CustomSort(@FilesSortAlphabet);
fstFoldersFirst: strings.CustomSort(@FilesSortFoldersFirst);
end;
end;
files.Free;
Result:= strings;
end;
class function TFileSource.CreateFile(const APath: String): TFile;
begin
Result := TFile.Create(APath);
end;
function TFileSource.CreateFileObject(const APath: String): TFile;
begin
Result := CreateFile(APath);
end;
procedure TFileSource.RetrieveProperties(AFile: TFile; PropertiesToSet: TFilePropertiesTypes; const AVariantProperties: array of String);
begin
// Does not set any properties by default.
end;
function TFileSource.CanRetrieveProperties(AFile: TFile; PropertiesToSet: TFilePropertiesTypes): Boolean;
begin
Result := ((PropertiesToSet - AFile.AssignedProperties) * RetrievableFileProperties) <> [];
end;
function TFileSource.CreateListOperation(TargetPath: String): TFileSourceOperation;
begin
Result := nil;
end;
function TFileSource.CreateCopyOperation(var SourceFiles: TFiles;
TargetPath: String): TFileSourceOperation;
begin
Result := nil;
end;
function TFileSource.CreateCopyInOperation(SourceFileSource: IFileSource;
var SourceFiles: TFiles;
TargetPath: String): TFileSourceOperation;
begin
Result := nil;
end;
function TFileSource.CreateCopyOutOperation(TargetFileSource: IFileSource;
var SourceFiles: TFiles;
TargetPath: String): TFileSourceOperation;
begin
Result := nil;
end;
function TFileSource.CreateMoveOperation(var SourceFiles: TFiles;
TargetPath: String): TFileSourceOperation;
begin
Result := nil;
end;
function TFileSource.CreateDeleteOperation(var FilesToDelete: TFiles): TFileSourceOperation;
begin
Result := nil;
end;
function TFileSource.CreateWipeOperation(var FilesToWipe: TFiles): TFileSourceOperation;
begin
Result := nil;
end;
function TFileSource.CreateSplitOperation(var aSourceFile: TFile;
aTargetPath: String): TFileSourceOperation;
begin
Result := nil;
end;
function TFileSource.CreateCombineOperation(var theSourceFiles: TFiles;
aTargetFile: String): TFileSourceOperation;
begin
Result := nil;
end;
function TFileSource.CreateCreateDirectoryOperation(BasePath: String; DirectoryPath: String): TFileSourceOperation;
begin
Result := nil;
end;
function TFileSource.CreateExecuteOperation(var ExecutableFile: TFile; BasePath, Verb: String): TFileSourceOperation;
begin
Result := nil;
end;
function TFileSource.CreateTestArchiveOperation(var theSourceFiles: TFiles): TFileSourceOperation;
begin
Result:= nil;
end;
function TFileSource.CreateCalcChecksumOperation(var theFiles: TFiles;
aTargetPath: String;
aTargetMask: String): TFileSourceOperation;
begin
Result := nil;
end;
function TFileSource.CreateCalcStatisticsOperation(var theFiles: TFiles): TFileSourceOperation;
begin
Result := nil;
end;
function TFileSource.CreateSetFilePropertyOperation(var theTargetFiles: TFiles;
var theNewProperties: TFileProperties): TFileSourceOperation;
begin
Result := nil;
end;
function TFileSource.GetOperationClass(OperationType: TFileSourceOperationType): TFileSourceOperationClass;
begin
Result := FOperationsClasses[OperationType];
end;
class function TFileSource.GetMainIcon(out Path: String): Boolean;
begin
Result := False;
end;
class function TFileSource.IsSupportedPath(const Path: String): Boolean;
begin
Result:= True;
end;
function TFileSource.IsSystemFile(aFile: TFile): Boolean;
begin
{$IF DEFINED(MSWINDOWS)}
if fpAttributes in aFile.SupportedProperties then
Result := TFileAttributesProperty(aFile.Properties[fpAttributes]).IsSysFile
else
Result := False;
{$ELSEIF DEFINED(DARWIN)}
if (Length(aFile.Name) > 1) and (aFile.Name[1] = '.') and (aFile.Name <> '..') then exit(true);
if aFile.Name='Icon'#$0D then exit(true);
exit(false);
{$ELSE}
// Files beginning with '.' are treated as system/hidden files on Unix.
Result := (Length(aFile.Name) > 1) and (aFile.Name[1] = '.') and (aFile.Name <> '..');
{$ENDIF}
end;
function TFileSource.IsHiddenFile(aFile: TFile): Boolean;
begin
if not (fpAttributes in aFile.SupportedProperties) then
Result := False
else begin
if aFile.Properties[fpAttributes] is TNtfsFileAttributesProperty then
Result := TNtfsFileAttributesProperty(aFile.Properties[fpAttributes]).IsHidden
else begin
// Files beginning with '.' are treated as system/hidden files on Unix.
Result := (Length(aFile.Name) > 1) and (aFile.Name[1] = '.') and (aFile.Name <> '..');
end;
end;
end;
function TFileSource.GetDisplayFileName(aFile: TFile): String;
begin
Result:= EmptyStr;
end;
function TFileSource.GetFileName(aFile: TFile): String;
begin
Result:= aFile.Name;
end;
function TFileSource.GetConnection(Operation: TFileSourceOperation): TFileSourceConnection;
begin
// By default connections are not supported.
Result := nil;
end;
function TFileSource.TryAcquireConnection(connection: TFileSourceConnection;
operation: TFileSourceOperation): TFileSourceConnection;
begin
if connection.Acquire(operation) then
begin
// We must know when the operation is finished,
// that is when the connection is free again.
operation.AddStateChangedListener([fsosStopped], @OperationFinishedCallback);
Result := connection;
end
else
begin
Result := nil;
end;
end;
procedure TFileSource.RemoveOperationFromQueue(Operation: TFileSourceOperation);
begin
// Nothing by default.
end;
procedure TFileSource.AddChild(AFileSource: IFileSource);
begin
if (FChildrenFileSource = nil) then
begin
FChildrenFileSource:= TInterfaceList.Create;
end
else if FChildrenFileSource.Count > 32 then
begin
FChildrenFileSource.Delete(0);
end;
FChildrenFileSource.Add(AFileSource);
end;
procedure TFileSource.OperationFinishedCallback(Operation: TFileSourceOperation;
State: TFileSourceOperationState);
begin
if State = fsosStopped then
begin
Operation.RemoveStateChangedListener([fsosStopped], @OperationFinishedCallback);
OperationFinished(Operation);
end;
end;
procedure TFileSource.OperationFinished(Operation: TFileSourceOperation);
begin
// Nothing by default.
end;
procedure TFileSource.DoReload(const PathsToReload: TPathsArray);
begin
// Nothing by default.
end;
procedure TFileSource.eventNotify( var params: TFileSourceEventParams );
var
i: Integer;
FunctionToCall: TFileSourceEventListener;
begin
if FEventListeners = nil then
Exit;
for i := 0 to FEventListeners.Count - 1 do begin
FunctionToCall:= TFileSourceEventListener(FEventListeners.Items[i]);
FunctionToCall( params );
end;
end;
procedure TFileSource.Reload(const PathsToReload: TPathsArray);
var
params: TFileSourceEventParams;
begin
DoReload(PathsToReload);
params.fs:= Self;
params.eventType:= TFileSourceEventType.reload;
params.paths:= PathsToReload;
eventNotify( params );
end;
procedure TFileSource.Reload(const PathToReload: String);
var
PathsToReload: TPathsArray = nil;
begin
if PathToReload <> EmptyStr then begin
SetLength(PathsToReload, 1);
PathsToReload[0] := PathToReload;
end;
Reload(PathsToReload);
end;
function TFileSource.needReload(
const PathToReload: String;
const PathToCheck: String): Boolean;
begin
Result := IsInPath(PathToReload, PathToCheck, True, True);
end;
procedure TFileSource.AddEventListener(FunctionToCall: TFileSourceEventListener);
begin
FEventListeners.Add(TMethod(FunctionToCall));
end;
procedure TFileSource.RemoveEventListener(FunctionToCall: TFileSourceEventListener);
begin
FEventListeners.Remove(TMethod(FunctionToCall));
end;
{ TFileSourceConnection }
constructor TFileSourceConnection.Create;
begin
FOperationLock := TCriticalSection.Create;
inherited Create;
DCDebug('Creating connection ', ClassName);
end;
destructor TFileSourceConnection.Destroy;
begin
if Assigned(FAssignedOperation) and (FAssignedOperation.State <> fsosStopped) then
DCDebug('Error: Destroying connection ', ClassName, ' with active operation ', FAssignedOperation.ClassName);
inherited Destroy;
DCDebug('Destroying connection ', ClassName);
FreeAndNil(FOperationLock);
end;
function TFileSourceConnection.GetAssignedOperation: TFileSourceOperation;
begin
// For just reading lock is probably not needed here.
Result := FAssignedOperation;
end;
function TFileSourceConnection.GetCurrentPath: String;
begin
Result := FCurrentPath;
end;
procedure TFileSourceConnection.SetCurrentPath(NewPath: String);
begin
if NewPath <> '' then
NewPath := IncludeTrailingPathDelimiter(NewPath);
FCurrentPath := NewPath;
end;
function TFileSourceConnection.IsAvailable: Boolean;
begin
Result := (GetAssignedOperation() = nil);
end;
function TFileSourceConnection.Acquire(Operation: TFileSourceOperation): Boolean;
begin
FOperationLock.Acquire;
try
Result := (FAssignedOperation = nil);
if Result then
FAssignedOperation := Operation;
finally
FOperationLock.Release;
end;
end;
procedure TFileSourceConnection.Release;
begin
FOperationLock.Acquire;
try
FAssignedOperation := nil;
finally
FOperationLock.Release;
end;
end;
{ TFileSources }
function TFileSources.Get(I: Integer): IFileSource;
begin
if (I >= 0) and (I < Count) then
Result := inherited Items[I] as IFileSource
else
Result := nil;
end;
procedure TFileSources.Put(i: Integer; item: IFileSource);
begin
inherited Put(i, item);
end;
procedure TFileSources.Assign(otherFileSources: TFileSources);
var
i: Integer;
begin
Clear;
for i := 0 to otherFileSources.Count - 1 do
Add(otherFileSources.Items[i]);
end;
constructor EFileNotFound.Create(const AFilePath: string);
begin
FFilePath := AFilePath;
inherited Create(Format(rsMsgFileNotFound, [aFilePath]));
end;
initialization
defaultFileSourceWatcher:= TDefaultFileSourceWatcher.Create;
finalization
FreeAndNil( defaultFileSourceWatcher );
end.