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. FOwner : TComponent;
  130. Procedure SetOperation( Value :TFileOperation );
  131. Function GetOperation :TFileOperation;
  132. Function GetWantMappingHandle :Boolean;
  133. Procedure SetWantMappingHandle ( Value :Boolean );
  134. Procedure SetFlags( Value :TFileOperationFlags );
  135. Function GetFlags :TFileOperationFlags;
  136. Function GetOperFlag( F :Cardinal ) :Boolean;
  137. Procedure SetOperFlag( F :Cardinal; V :Boolean );
  138. Procedure ReadData( Reader :TReader );
  139. Procedure WriteData( Writer :TWriter );
  140. Procedure SwapStringList(Var FromL, ToL : TStringList);
  141. Function GetOperationAborted: Bool;
  142. {==============================================================}
  143. protected
  144. {==============================================================}
  145. Procedure DefineProperties( Filer :TFiler ); override;
  146. {==============================================================}
  147. public
  148. {==============================================================}
  149. Property OperationAborted : Bool Read GetOperationAborted;
  150. Property OperandFrom : TStringList Read fFrom Write fFrom;
  151. Property OperandTo : TStringList Read FTo Write fTo;
  152. Property CanUndo : Boolean Read fCanUndo;
  153. Property LastOperation : TFileOperation Read fLastOperation;
  154. Property LastOperandFrom : TStringList Read fLastFrom;
  155. Property LastOperandTo : TStringList Read fLastTo;
  156. Constructor Create(aOwner :TComponent); Override;
  157. Destructor Destroy; override;
  158. Function Execute : Boolean;
  159. Function UndoExecute : Boolean;
  160. Procedure ClearUndo;
  161. {==============================================================}
  162. published
  163. {==============================================================}
  164. Property Operation : TFileOperation Read GetOperation Write SetOperation Stored false;
  165. Property Flags : TFileOperationFlags Read GetFlags Write SetFlags Stored false;
  166. Property WantMappingHandle :Boolean Read GetWantMappingHandle Write SetWantMappingHandle Stored false;
  167. end;
  168. procedure Register;
  169. resourcestring
  170. SFileOperation = 'File Operation';
  171. {==============================================================}
  172. implementation
  173. {==============================================================}
  174. uses
  175. PasTools;
  176. { TFileOperator }
  177. procedure TFileOperator.SetOperation( Value :TFileOperation );
  178. begin
  179. with FData do
  180. case Value of
  181. foCopy : wFunc := FO_COPY;
  182. foDelete : wFunc := FO_DELETE;
  183. foRename : wFunc := FO_RENAME;
  184. foMove : wFunc := FO_MOVE;
  185. end;
  186. end; {SetOperation}
  187. function TFileOperator.GetOperation :TFileOperation;
  188. begin
  189. result := foCopy;
  190. case FData.wFunc of
  191. FO_COPY : result := foCopy;
  192. FO_DELETE : result := foDelete;
  193. FO_RENAME : result := foRename;
  194. FO_MOVE : result := foMove;
  195. end;
  196. end; {GetOperation}
  197. function TFileOperator.GetWantMappingHandle :Boolean;
  198. begin
  199. result := GetOperFlag( FOF_WANTMAPPINGHANDLE );
  200. end;
  201. procedure TFileOperator.SetWantMappingHandle ( Value :Boolean );
  202. begin
  203. SetOperFlag( FOF_WANTMAPPINGHANDLE, Value );
  204. end;
  205. procedure TFileOperator.SetFlags( Value :TFileOperationFlags );
  206. begin
  207. SetOperFlag( FOF_ALLOWUNDO, foAllowUndo in Value );
  208. SetOperFlag( FOF_CONFIRMMOUSE, foConfirmMouse in Value );
  209. SetOperFlag( FOF_FILESONLY, foFilesOnly in Value );
  210. SetOperFlag( FOF_MULTIDESTFILES, foMultiDestFiles in Value );
  211. SetOperFlag( FOF_NOCONFIRMATION, foNoConfirmation in Value );
  212. SetOperFlag( FOF_NOCONFIRMMKDIR, foNoConfirmMkDir in Value );
  213. SetOperFlag( FOF_RENAMEONCOLLISION, foRenameOnCollision in Value );
  214. SetOperFlag( FOF_SILENT, foSilent in Value );
  215. SetOperFlag( FOF_SIMPLEPROGRESS, foSimpleProgress in Value );
  216. end; {SetFlags}
  217. function TFileOperator.GetFlags :TFileOperationFlags;
  218. begin
  219. result := [];
  220. if GetOperFlag( FOF_ALLOWUNDO ) then include( result, foAllowUndo );
  221. if GetOperFlag( FOF_CONFIRMMOUSE ) then include( result, foConfirmMouse );
  222. if GetOperFlag( FOF_FILESONLY ) then include( result, foFilesOnly );
  223. if GetOperFlag( FOF_MULTIDESTFILES ) then include( result, foMultiDestFiles );
  224. if GetOperFlag( FOF_NOCONFIRMATION ) then include( result, foNoConfirmation );
  225. if GetOperFlag( FOF_NOCONFIRMMKDIR ) then include( result, foNoConfirmMkDir );
  226. if GetOperFlag( FOF_RENAMEONCOLLISION ) then include( result, foRenameOnCollision );
  227. if GetOperFlag( FOF_SILENT ) then include( result, foSilent );
  228. if GetOperFlag( FOF_SIMPLEPROGRESS ) then include( result, foSimpleProgress );
  229. end; {GetFlags}
  230. function TFileOperator.GetOperFlag( F :Cardinal ):boolean;
  231. begin
  232. result := ( FData.fFlags and F ) <> 0;
  233. end;
  234. procedure TFileOperator.SetOperFlag( F :Cardinal; V :Boolean );
  235. begin
  236. with FData do
  237. if V then
  238. fFlags := fFlags or F
  239. else fFlags := fFlags and ( not F );
  240. end;
  241. procedure TFileOperator.DefineProperties( Filer :TFiler );
  242. begin
  243. Inherited DefineProperties( Filer );
  244. Filer.DefineProperty( 'data', ReadData, WriteData, true );
  245. end;
  246. procedure TFileOperator.ReadData( Reader :TReader );
  247. begin
  248. Reader.Read( FData, SizeOf( FData ) );
  249. end;
  250. procedure TFileOperator.WriteData( Writer :TWriter );
  251. begin
  252. writer.write( FData, SizeOf( FData ) );
  253. end;
  254. Constructor TFileOperator.Create(aOwner :TComponent);
  255. begin
  256. inherited Create(aOwner);
  257. FOwner := aOwner;
  258. fFrom := TStringList.Create;
  259. fTo := TStringList.Create;
  260. fLastFrom := TStringList.Create;
  261. fLastTo := TStringList.Create;
  262. fCanUndo := False;
  263. FData.fFlags := FOF_ALLOWUNDO OR FOF_NOCONFIRMMKDIR;
  264. end; {Create}
  265. function TFileOperator.Execute : Boolean;
  266. Var SFrom : String;
  267. sTo : String;
  268. Function ConvertOperand(List : TStringList) : String;
  269. Var i : Integer;
  270. Begin
  271. Result := '';
  272. For i := 0 to Pred(List.Count) Do
  273. Begin
  274. // SHFileOperation does not support long paths anyway
  275. Result := Result + ApiPath(List[i]);
  276. SetLength(Result, Succ(Length(Result)));
  277. Result[Length(Result)] := #0;
  278. End;
  279. SetLength(Result, Succ(Length(Result)));
  280. Result[Length(Result)] := #0;
  281. End; {ConvertOperand}
  282. begin {Execute}
  283. SFrom := ConvertOperand(FFrom);
  284. STo := ConvertOperand(FTo);
  285. FData.pFrom := PChar( SFrom );
  286. FData.pTo := PChar( STo );
  287. IF (FOwner is TWinControl) And TWinControl(FOwner).HandleAllocated Then
  288. FData.Wnd := GetParentForm(TWinControl(FOwner)).Handle
  289. Else
  290. FData.Wnd := Application.Handle;
  291. Try
  292. IF Assigned(FData.hNameMappings) Then
  293. shFreeNameMappings(THandle(FData.hNameMappings));
  294. Finally
  295. FData.hNameMappings := NIL;
  296. End;
  297. Try
  298. Try
  299. IF Operation = foRename Then
  300. Result := RenameFile(FFrom[0], FTo[0])
  301. Else
  302. Result := ShFileOperation( FData ) = 0;
  303. Finally
  304. IF GetOperFlag(FOF_ALLOWUNDO) And
  305. Not GetOperFlag(FOF_MULTIDESTFILES) And
  306. Not GetOperFlag(FOF_RENAMEONCOLLISION) And
  307. (Operation <> foDelete) Then
  308. Begin
  309. SwapStringList(fLastFrom, fFrom);
  310. SwapStringList(fLastTo, fTo);
  311. fLastFlags := Flags;
  312. fCanUndo := True;
  313. fLastOperation := Operation;
  314. End
  315. Else
  316. Begin
  317. FLastFrom.Clear;
  318. FLastTo.Clear;
  319. fCanUndo := False;
  320. End;
  321. FFrom.Clear;
  322. FTo.Clear;
  323. End;
  324. Except
  325. Result := False;
  326. End;
  327. end; {Execute}
  328. destructor TFileOperator.Destroy;
  329. begin
  330. IF Assigned(FFrom) Then
  331. FFrom.Free;
  332. IF Assigned(FTo) Then
  333. FTo.Free;
  334. IF Assigned(FLastFrom) Then
  335. FLastFrom.Free;
  336. IF Assigned(FLastTo) Then
  337. FLastTo.Free;
  338. IF Assigned(FData.hNameMappings) Then
  339. shFreeNameMappings(THandle(FData.hNameMappings));
  340. inherited Destroy;
  341. end; {Destroy}
  342. Procedure TFileOperator.SwapStringList(Var FromL, ToL : TStringList);
  343. Var StrL : TStringList;
  344. Begin
  345. StrL := FromL;
  346. FromL := ToL;
  347. ToL := StrL;
  348. End; {SwapStringList}
  349. Function TFileOperator.GetOperationAborted: Bool;
  350. Begin
  351. Result := FData.fAnyOperationsAborted;
  352. End;
  353. Function TFileOperator.UndoExecute : Boolean;
  354. Var SaveFlags : TFileOperationFlags;
  355. SaveOperation : TFileOperation;
  356. i : Integer;
  357. Begin
  358. Result := False;
  359. IF Not fCanUndo Or
  360. (fLastFrom.Count = 0) Or
  361. (FLastTo.Count <> 1) Then
  362. Exit;
  363. SaveFlags := Flags;
  364. SaveOperation := Operation;
  365. Flags := fLastFlags;
  366. Case SaveOperation OF
  367. foCopy : IF fLastTo.Count = 1 Then
  368. Begin
  369. Operation := foDelete;
  370. Flags := Flags - [foAllowUndo] + [foNoConfirmation];
  371. OperandFrom.Clear;
  372. For i := 0 To fLastFrom.Count - 1 Do
  373. OperandFrom.Add(IncludeTrailingPathDelimiter(fLastTo[0]) + ExtractFilename(fLastFrom[i]));
  374. Result := Execute;
  375. End;
  376. foMove : IF fLastTo.Count = 1 Then
  377. Begin
  378. Operation := foMove;
  379. Flags := Flags + [foAllowUndo, foNoConfirmation];
  380. OperandFrom.Clear;
  381. OperandTo.Clear;
  382. OperandTo.Add(ExtractFilePath(fLastFrom[0]));
  383. For i := 0 To fLastFrom.Count - 1 Do
  384. OperandFrom.Add(IncludeTrailingPathDelimiter(fLastTo[0]) + ExtractFilename(fLastFrom[i]));
  385. Result := Execute;
  386. End;
  387. foRename: IF (FLastFrom.Count = 1) And (FLastTo.Count = 1) Then
  388. Begin
  389. Operation := foRename;
  390. Flags := Flags + [foAllowUndo, foNoConfirmation];
  391. OperandFrom.Clear;
  392. OperandTo.Clear;
  393. OperandFrom.Add(fLastTo[0]);
  394. OperandTo.Add(fLastFrom[0]);
  395. Result := Execute;
  396. End;
  397. End; {Case}
  398. Flags := SaveFlags;
  399. Operation := SaveOperation;
  400. End; {UndoExecute}
  401. Procedure TFileOperator.ClearUndo;
  402. Begin
  403. fCanUndo := False;
  404. fLastFrom.Clear;
  405. fLastTo.Clear;
  406. End; {ClearUndo}
  407. procedure Register;
  408. begin
  409. {MP}RegisterComponents( {'Tools'}'DriveDir', [ TFileOperator ] );
  410. end;
  411. end.