forked from doublecmd/doublecmd
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathufilesource.pas
1025 lines (844 loc) · 31.8 KB
/
ufilesource.pas
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
unit uFileSource;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils, DCStrUtils, syncobjs, LCLProc, URIParser, Menus,
uFileSourceOperation,
uFileSourceOperationTypes,
uFileSourceProperty,
uFileProperty,
uFile;
type
TFileSource = class;
TFileSourceConnection = class;
IFileSource = interface;
TFileSourceField = record
Content: String;
Header: String;
Width: Integer;
Option: String;
Align: TAlignment;
end;
TFileSourceFields = array of TFileSourceField;
TPathsArray = array of string;
TFileSourceOperationsClasses = array[TFileSourceOperationType] of TFileSourceOperationClass;
TFileSourceReloadEventNotify = procedure(const aFileSource: IFileSource;
const ReloadedPaths: TPathsArray) of object;
{ IFileSource }
IFileSource = interface(IInterface)
['{B7F0C4C8-59F6-4A35-A54C-E8242F4AD809}']
function Equals(aFileSource: IFileSource): Boolean;
function IsInterface(InterfaceGuid: TGuid): Boolean;
function IsClass(ClassType: TClass): Boolean;
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 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; 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 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 GetFreeSpace(Path: String; out FreeSize, TotalSize : Int64) : Boolean;
function GetLocalName(var aFile: TFile): Boolean;
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 Reload(const PathsToReload: TPathsArray);
procedure Reload(const PathToReload: String);
procedure AddReloadEventListener(FunctionToCall: TFileSourceReloadEventNotify);
procedure RemoveReloadEventListener(FunctionToCall: TFileSourceReloadEventNotify);
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
FReloadEventListeners: 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;
function Equals(aFileSource: IFileSource): Boolean; overload;
function IsInterface(InterfaceGuid: TGuid): Boolean;
function IsClass(aClassType: TClass): Boolean;
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;
// 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; 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 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 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 GetLocalName(var aFile: TFile): Boolean; 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 Reload(const PathsToReload: TPathsArray); virtual; overload;
procedure Reload(const PathToReload: String); overload;
procedure AddReloadEventListener(FunctionToCall: TFileSourceReloadEventNotify);
procedure RemoveReloadEventListener(FunctionToCall: TFileSourceReloadEventNotify);
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;
public
procedure Assign(otherFileSources: TFileSources);
property Items[I: Integer]: IFileSource read Get; default;
end;
{ TFileSourceManager }
TFileSourceManager = class
private
FFileSources: TFileSources;
// Only allow adding and removing to/from Manager by TFileSource constructor and destructor.
procedure Add(aFileSource: IFileSource);
procedure Remove(aFileSource: IFileSource);
public
constructor Create;
destructor Destroy; override;
function Find(FileSourceClass: TClass; Address: String; CaseSensitive: Boolean = True): IFileSource;
end;
EFileSourceException = class(Exception);
EFileNotFound = class(EFileSourceException)
private
FFilePath: String;
public
constructor Create(const AFilePath: string); reintroduce;
property FilePath: String read FFilePath;
end;
var
FileSourceManager: TFileSourceManager;
implementation
uses
uDebug, uFileSourceListOperation, uLng;
{ TFileSource }
constructor TFileSource.Create;
begin
if ClassType = TFileSource then
raise Exception.Create('Cannot construct abstract class');
inherited Create;
FReloadEventListeners := 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(FReloadEventListeners);
inherited Destroy;
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.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.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.GetLocalName(var aFile: TFile): Boolean;
begin
Result:= False;
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;
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; 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.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.Reload(const PathsToReload: TPathsArray);
var
i: Integer;
FunctionToCall: TFileSourceReloadEventNotify;
begin
DoReload(PathsToReload);
if Assigned(FReloadEventListeners) then
for i := 0 to FReloadEventListeners.Count - 1 do
begin
FunctionToCall := TFileSourceReloadEventNotify(FReloadEventListeners.Items[i]);
FunctionToCall(Self, PathsToReload);
end;
end;
procedure TFileSource.Reload(const PathToReload: String);
var
PathsToReload: TPathsArray;
begin
SetLength(PathsToReload, 1);
PathsToReload[0] := PathToReload;
Reload(PathsToReload);
end;
procedure TFileSource.AddReloadEventListener(FunctionToCall: TFileSourceReloadEventNotify);
begin
FReloadEventListeners.Add(TMethod(FunctionToCall));
end;
procedure TFileSource.RemoveReloadEventListener(FunctionToCall: TFileSourceReloadEventNotify);
begin
FReloadEventListeners.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.Assign(otherFileSources: TFileSources);
var
i: Integer;
begin
Clear;
for i := 0 to otherFileSources.Count - 1 do
Add(otherFileSources.Items[i]);
end;
{ TFileSourceManager }
constructor TFileSourceManager.Create;
begin
FFileSources := TFileSources.Create;
end;
destructor TFileSourceManager.Destroy;
var
i: Integer;
begin
if FFileSources.Count > 0 then
begin
DCDebug('Warning: Destroying manager with existing file sources!');
for i := 0 to FFileSources.Count - 1 do
begin
// Restore the reference taken in TFileSource.Create before removing
// all file sources from the list.
FFileSources[i]._AddRef;
// Free instance.
FFileSources.put(i, nil);
end;
end;
FreeAndNil(FFileSources);
inherited Destroy;
end;
procedure TFileSourceManager.Add(aFileSource: IFileSource);
begin
if FFileSources.IndexOf(aFileSource) < 0 then
begin
FFileSources.Add(aFileSource);
end
else
DCDebug('Error: File source already exists in manager!');
end;
procedure TFileSourceManager.Remove(aFileSource: IFileSource);
begin
FFileSources.Remove(aFileSource);
end;
function TFileSourceManager.Find(FileSourceClass: TClass; Address: String;
CaseSensitive: Boolean): IFileSource;
var
I: Integer;
StrCmp: function(const S1, S2: String): Integer;
begin
if CaseSensitive then
StrCmp:= @CompareStr
else begin
StrCmp:= @CompareText;
end;
for I := 0 to FFileSources.Count - 1 do