FileOperator.pas 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471
  1. unit FileOperator;
  2. {
  3. +------------------------------------------------------------------------------+
  4. | TFileOperator Component Version 1.02 / 1999 |
  5. +------------------------------------------------------------------------------+
  6. | Author: Ingo Eckel |
  7. | |
  8. | Based on the TFileOperator component written by Glen Why 1996 |
  9. | Enhancements: uses TStringList as operands, undo functionality implemented. |
  10. +------------------------------------------------------------------------------+
  11. | Description: |
  12. | This component encapsulates the ShFileOperation API of Microsoft Windows. |
  13. | Performs a copy, move, rename, or delete operation on a file system object. |
  14. | It also allows to undo the last operation, if a copy or move operation |
  15. | was performed. |
  16. +------------------------------------------------------------------------------+
  17. | Properties: |
  18. | |
  19. | OperandFrom: |
  20. | Stringlist, that contains the names of the source files, |
  21. | wildcard filename (*.*) is accepted. |
  22. | |
  23. | OperandTo: |
  24. | Stringlist that specifies the destination for the moved, |
  25. | copied or renamed file. Should be the target directory, when |
  26. | performing a copy or move operation. |
  27. | |
  28. | WantMappingHandle: True, if shFileOperation should create a filename- |
  29. | mapping of the processed files. This mapping is allocated by |
  30. | shFileOperation and will be automatically deallocated by the |
  31. | component. |
  32. | |
  33. | Operation: |
  34. | -foCopy Copies the files specified by OperandFrom to the location |
  35. | specified by OperandTo. |
  36. | -foDelete Deletes the files specified by OperandFrom (OperandTo |
  37. | ignored). |
  38. | -foMove Moves the files specified by OperandFrom to the location |
  39. | specified by OperandTo. |
  40. | -foRename Renames the files specified by OperandFrom. |
  41. | |
  42. | Options: |
  43. | -foAllowUndo Preserves undo information, if possible. |
  44. | -foConfirmMouse Not implemented. |
  45. | -foFilesOnly Performs the operation only on files if a wildcard |
  46. | filename (*.*) is specified. |
  47. | -foMultiDestFiles Indicates that the OperandTo member specifies multiple|
  48. | destination files (one for each source file) rather |
  49. | than one directory where all source files are |
  50. | to be deposited. |
  51. | Note: not compatible with undo operation. |
  52. | -foNoConfirmation Responds with "yes to all" for any dialog box that |
  53. | is displayed. |
  54. | -fofNoConfirmMkDir Does not confirm the creation of a new directory |
  55. | if the operation requires one to be created. |
  56. | -foRenameOnCollision Gives the file being operated on a new name |
  57. | (such as "Copy #1 of...") in a move, copy, |
  58. | or rename operation if a file of the target name |
  59. | already exists. |
  60. | Note: not compatible with undo operation. |
  61. | -foSilent Does not display a progress dialog box. |
  62. | -foSimpleProgress Displays a progress dialog box, but does not show |
  63. | the filenames. |
  64. | |
  65. | Title: String to use as the title for a progress dialog box. |
  66. | This member is used only if Options includes fofSimpleProgress. |
  67. | |
  68. | OperationAborted: Value that receives True if the user aborted any file |
  69. | operations before they were completed or FALSE otherwise. |
  70. | |
  71. | LastOperandFrom Stringlist of last performed value of Operandfrom. |
  72. | |
  73. | LastOperandTo Stringlist of last performed value of OperandTo. |
  74. | |
  75. | LastOperation Value of last performed operation. |
  76. | |
  77. | WantMappingHandle After execution should a file mapping be returned. |
  78. | Works only, when option foMultiDestFiles used. |
  79. | Note: don't know, how to use this. |
  80. | |
  81. | NameMappings Pointer to namemappings, if property wantmapping- |
  82. | handle set. |
  83. +------------------------------------------------------------------------------+
  84. | Methods: |
  85. | |
  86. | Function Execute : Boolean; |
  87. | Performs the copy, move, rename, or delete operation. |
  88. | Returns zero if successful or nonzero value if an error occurs. |
  89. | |
  90. | Function UndoExecute : Boolean; |
  91. | Reverses the last copy, move or rename operation. |
  92. | Note: works currently only, if only a single OperandTo is used. This |
  93. | OperandTo must be the target directory. |
  94. | |
  95. | Function CanUndo : Boolean; |
  96. | Returns TRUE, if undo of last operation is possible. |
  97. | |
  98. | Procedure ClearUndo; |
  99. | Clears the preserved undo informations. After that, CanUndo allways |
  100. | returns false. |
  101. +------------------------------------------------------------------------------+
  102. | Events: |
  103. +------------------------------------------------------------------------------+
  104. }
  105. {==============================================================}
  106. interface
  107. {==============================================================}
  108. uses
  109. Windows, Classes, Forms, Controls, ShellAPI, BaseUtils, SysUtils;
  110. Type
  111. TFileOperation = ( foCopy, foDelete, foMove, foRename );
  112. TFileOperationFlag = ( foAllowUndo, foConfirmMouse, foFilesOnly,
  113. foMultiDestFiles, foNoConfirmation, foNoConfirmMkDir,
  114. foRenameOnCollision, foSilent, foSimpleProgress);
  115. TFileOperationFlags = set of TFileOperationFlag;
  116. {==============================================================}
  117. TFileOperator = class( TComponent )
  118. {==============================================================}
  119. private
  120. {==============================================================}
  121. FData : TShFileOpStruct;
  122. FFrom : TStringList;
  123. FTo : TStringList;
  124. FLastFrom : TStringList;
  125. FLastTo : TStringList;
  126. FLastOperation : TFileOperation;
  127. fLastFlags : TFileOperationFlags;
  128. fCanUndo : Boolean;
  129. Procedure SetOperation( Value :TFileOperation );
  130. Function GetOperation :TFileOperation;
  131. Function GetWantMappingHandle :Boolean;
  132. Procedure SetWantMappingHandle ( Value :Boolean );
  133. Procedure SetFlags( Value :TFileOperationFlags );
  134. Function GetFlags :TFileOperationFlags;
  135. Function GetOperFlag( F :Cardinal ) :Boolean;
  136. Procedure SetOperFlag( F :Cardinal; V :Boolean );
  137. Procedure ReadData( Reader :TReader );
  138. Procedure WriteData( Writer :TWriter );
  139. Procedure SwapStringList(Var FromL, ToL : TStringList);
  140. Function GetOperationAborted: Bool;
  141. {==============================================================}
  142. protected
  143. {==============================================================}
  144. Procedure DefineProperties( Filer :TFiler ); override;
  145. {==============================================================}
  146. public
  147. {==============================================================}
  148. Property OperationAborted : Bool Read GetOperationAborted;
  149. Property OperandFrom : TStringList Read fFrom Write fFrom;
  150. Property OperandTo : TStringList Read FTo Write fTo;
  151. Property CanUndo : Boolean Read fCanUndo;
  152. Property LastOperation : TFileOperation Read fLastOperation;
  153. Property LastOperandFrom : TStringList Read fLastFrom;
  154. Property LastOperandTo : TStringList Read fLastTo;
  155. Constructor Create(aOwner :TComponent); Override;
  156. Destructor Destroy; override;
  157. Function Execute : Boolean;
  158. Function UndoExecute : Boolean;
  159. Procedure ClearUndo;
  160. {==============================================================}
  161. published
  162. {==============================================================}
  163. Property Operation : TFileOperation Read GetOperation Write SetOperation Stored false;
  164. Property Flags : TFileOperationFlags Read GetFlags Write SetFlags Stored false;
  165. Property WantMappingHandle :Boolean Read GetWantMappingHandle Write SetWantMappingHandle Stored false;
  166. end;
  167. const
  168. FileOperatorDefaultFlags = [foAllowUndo, foNoConfirmMkDir];
  169. procedure Register;
  170. {==============================================================}
  171. implementation
  172. {==============================================================}
  173. uses
  174. PasTools;
  175. { TFileOperator }
  176. procedure TFileOperator.SetOperation( Value :TFileOperation );
  177. begin
  178. with FData do
  179. case Value of
  180. foCopy : wFunc := FO_COPY;
  181. foDelete : wFunc := FO_DELETE;
  182. foRename : wFunc := FO_RENAME;
  183. foMove : wFunc := FO_MOVE;
  184. end;
  185. end; {SetOperation}
  186. function TFileOperator.GetOperation :TFileOperation;
  187. begin
  188. result := foCopy;
  189. case FData.wFunc of
  190. FO_COPY : result := foCopy;
  191. FO_DELETE : result := foDelete;
  192. FO_RENAME : result := foRename;
  193. FO_MOVE : result := foMove;
  194. end;
  195. end; {GetOperation}
  196. function TFileOperator.GetWantMappingHandle :Boolean;
  197. begin
  198. result := GetOperFlag( FOF_WANTMAPPINGHANDLE );
  199. end;
  200. procedure TFileOperator.SetWantMappingHandle ( Value :Boolean );
  201. begin
  202. SetOperFlag( FOF_WANTMAPPINGHANDLE, Value );
  203. end;
  204. procedure TFileOperator.SetFlags( Value :TFileOperationFlags );
  205. begin
  206. SetOperFlag( FOF_ALLOWUNDO, foAllowUndo in Value );
  207. SetOperFlag( FOF_CONFIRMMOUSE, foConfirmMouse in Value );
  208. SetOperFlag( FOF_FILESONLY, foFilesOnly in Value );
  209. SetOperFlag( FOF_MULTIDESTFILES, foMultiDestFiles in Value );
  210. SetOperFlag( FOF_NOCONFIRMATION, foNoConfirmation in Value );
  211. SetOperFlag( FOF_NOCONFIRMMKDIR, foNoConfirmMkDir in Value );
  212. SetOperFlag( FOF_RENAMEONCOLLISION, foRenameOnCollision in Value );
  213. SetOperFlag( FOF_SILENT, foSilent in Value );
  214. SetOperFlag( FOF_SIMPLEPROGRESS, foSimpleProgress in Value );
  215. end; {SetFlags}
  216. function TFileOperator.GetFlags :TFileOperationFlags;
  217. begin
  218. result := [];
  219. if GetOperFlag( FOF_ALLOWUNDO ) then include( result, foAllowUndo );
  220. if GetOperFlag( FOF_CONFIRMMOUSE ) then include( result, foConfirmMouse );
  221. if GetOperFlag( FOF_FILESONLY ) then include( result, foFilesOnly );
  222. if GetOperFlag( FOF_MULTIDESTFILES ) then include( result, foMultiDestFiles );
  223. if GetOperFlag( FOF_NOCONFIRMATION ) then include( result, foNoConfirmation );
  224. if GetOperFlag( FOF_NOCONFIRMMKDIR ) then include( result, foNoConfirmMkDir );
  225. if GetOperFlag( FOF_RENAMEONCOLLISION ) then include( result, foRenameOnCollision );
  226. if GetOperFlag( FOF_SILENT ) then include( result, foSilent );
  227. if GetOperFlag( FOF_SIMPLEPROGRESS ) then include( result, foSimpleProgress );
  228. end; {GetFlags}
  229. function TFileOperator.GetOperFlag( F :Cardinal ):boolean;
  230. begin
  231. result := ( FData.fFlags and F ) <> 0;
  232. end;
  233. procedure TFileOperator.SetOperFlag( F :Cardinal; V :Boolean );
  234. begin
  235. with FData do
  236. if V then
  237. fFlags := fFlags or F
  238. else fFlags := fFlags and ( not F );
  239. end;
  240. procedure TFileOperator.DefineProperties( Filer :TFiler );
  241. begin
  242. Inherited DefineProperties( Filer );
  243. Filer.DefineProperty( 'data', ReadData, WriteData, true );
  244. end;
  245. procedure TFileOperator.ReadData( Reader :TReader );
  246. begin
  247. Reader.Read( FData, SizeOf( FData ) );
  248. end;
  249. procedure TFileOperator.WriteData( Writer :TWriter );
  250. begin
  251. writer.write( FData, SizeOf( FData ) );
  252. end;
  253. Constructor TFileOperator.Create(aOwner :TComponent);
  254. begin
  255. inherited Create(aOwner);
  256. fFrom := TStringList.Create;
  257. fTo := TStringList.Create;
  258. fLastFrom := TStringList.Create;
  259. fLastTo := TStringList.Create;
  260. fCanUndo := False;
  261. FData.fFlags := 0;
  262. Flags := FileOperatorDefaultFlags;
  263. end; {Create}
  264. function TFileOperator.Execute : Boolean;
  265. Var SFrom : String;
  266. sTo : String;
  267. Function ConvertOperand(List : TStringList) : String;
  268. Var i : Integer;
  269. Begin
  270. Result := '';
  271. For i := 0 to Pred(List.Count) Do
  272. Begin
  273. // SHFileOperation does not support long paths anyway
  274. Result := Result + ApiPath(List[i]);
  275. SetLength(Result, Succ(Length(Result)));
  276. Result[Length(Result)] := #0;
  277. End;
  278. SetLength(Result, Succ(Length(Result)));
  279. Result[Length(Result)] := #0;
  280. End; {ConvertOperand}
  281. begin {Execute}
  282. SFrom := ConvertOperand(FFrom);
  283. STo := ConvertOperand(FTo);
  284. FData.pFrom := PChar( SFrom );
  285. FData.pTo := PChar( STo );
  286. IF (Owner is TWinControl) And TWinControl(Owner).HandleAllocated Then
  287. FData.Wnd := GetParentForm(TWinControl(Owner)).Handle
  288. Else
  289. FData.Wnd := Application.Handle;
  290. Try
  291. IF Assigned(FData.hNameMappings) Then
  292. shFreeNameMappings(THandle(FData.hNameMappings));
  293. Finally
  294. FData.hNameMappings := NIL;
  295. End;
  296. Try
  297. Try
  298. IF Operation = foRename Then
  299. Result := RenameFile(FFrom[0], FTo[0])
  300. Else
  301. Result := ShFileOperation( FData ) = 0;
  302. Finally
  303. IF GetOperFlag(FOF_ALLOWUNDO) And
  304. Not GetOperFlag(FOF_MULTIDESTFILES) And
  305. Not GetOperFlag(FOF_RENAMEONCOLLISION) And
  306. (Operation <> foDelete) Then
  307. Begin
  308. SwapStringList(fLastFrom, fFrom);
  309. SwapStringList(fLastTo, fTo);
  310. fLastFlags := Flags;
  311. fCanUndo := True;
  312. fLastOperation := Operation;
  313. End
  314. Else
  315. Begin
  316. FLastFrom.Clear;
  317. FLastTo.Clear;
  318. fCanUndo := False;
  319. End;
  320. FFrom.Clear;
  321. FTo.Clear;
  322. End;
  323. Except
  324. Result := False;
  325. End;
  326. end; {Execute}
  327. destructor TFileOperator.Destroy;
  328. begin
  329. IF Assigned(FFrom) Then
  330. FFrom.Free;
  331. IF Assigned(FTo) Then
  332. FTo.Free;
  333. IF Assigned(FLastFrom) Then
  334. FLastFrom.Free;
  335. IF Assigned(FLastTo) Then
  336. FLastTo.Free;
  337. IF Assigned(FData.hNameMappings) Then
  338. shFreeNameMappings(THandle(FData.hNameMappings));
  339. inherited Destroy;
  340. end; {Destroy}
  341. Procedure TFileOperator.SwapStringList(Var FromL, ToL : TStringList);
  342. Var StrL : TStringList;
  343. Begin
  344. StrL := FromL;
  345. FromL := ToL;
  346. ToL := StrL;
  347. End; {SwapStringList}
  348. Function TFileOperator.GetOperationAborted: Bool;
  349. Begin
  350. Result := FData.fAnyOperationsAborted;
  351. End;
  352. Function TFileOperator.UndoExecute : Boolean;
  353. Var SaveFlags : TFileOperationFlags;
  354. SaveOperation : TFileOperation;
  355. i : Integer;
  356. Begin
  357. Result := False;
  358. IF Not fCanUndo Or
  359. (fLastFrom.Count = 0) Or
  360. (FLastTo.Count <> 1) Then
  361. Exit;
  362. SaveFlags := Flags;
  363. SaveOperation := Operation;
  364. Flags := fLastFlags;
  365. Case SaveOperation OF
  366. foCopy : IF fLastTo.Count = 1 Then
  367. Begin
  368. Operation := foDelete;
  369. Flags := Flags - [foAllowUndo] + [foNoConfirmation];
  370. OperandFrom.Clear;
  371. For i := 0 To fLastFrom.Count - 1 Do
  372. OperandFrom.Add(IncludeTrailingPathDelimiter(fLastTo[0]) + ExtractFilename(fLastFrom[i]));
  373. Result := Execute;
  374. End;
  375. foMove : IF fLastTo.Count = 1 Then
  376. Begin
  377. Operation := foMove;
  378. Flags := Flags + [foAllowUndo, foNoConfirmation];
  379. OperandFrom.Clear;
  380. OperandTo.Clear;
  381. OperandTo.Add(ExtractFilePath(fLastFrom[0]));
  382. For i := 0 To fLastFrom.Count - 1 Do
  383. OperandFrom.Add(IncludeTrailingPathDelimiter(fLastTo[0]) + ExtractFilename(fLastFrom[i]));
  384. Result := Execute;
  385. End;
  386. foRename: IF (FLastFrom.Count = 1) And (FLastTo.Count = 1) Then
  387. Begin
  388. Operation := foRename;
  389. Flags := Flags + [foAllowUndo, foNoConfirmation];
  390. OperandFrom.Clear;
  391. OperandTo.Clear;
  392. OperandFrom.Add(fLastTo[0]);
  393. OperandTo.Add(fLastFrom[0]);
  394. Result := Execute;
  395. End;
  396. End; {Case}
  397. Flags := SaveFlags;
  398. Operation := SaveOperation;
  399. End; {UndoExecute}
  400. Procedure TFileOperator.ClearUndo;
  401. Begin
  402. fCanUndo := False;
  403. fLastFrom.Clear;
  404. fLastTo.Clear;
  405. End; {ClearUndo}
  406. procedure Register;
  407. begin
  408. {MP}RegisterComponents( {'Tools'}'DriveDir', [ TFileOperator ] );
  409. end;
  410. end.