home *** CD-ROM | disk | FTP | other *** search
/ Chip 2003 February / Chip_2003-02_cd1.bin / zkuste / delphi / kompon / d13456 / FINDFILE.ZIP / FindFile.pas < prev    next >
Pascal/Delphi Source File  |  2002-08-25  |  34KB  |  1,198 lines

  1. {------------------------------------------------------------------------------}
  2. {                                                                              }
  3. {  TFindFile v3.02                                                             }
  4. {  by Kambiz R. Khojasteh                                                      }
  5. {                                                                              }
  6. {  kambiz@delphiarea.com                                                       }
  7. {  http://www.delphiarea.com                                                   }
  8. {                                                                              }
  9. {  Special thanks to:                                                          }
  10. {    Frederik Decoster <essevee@yahoo.com> for fixing folder look up bug.      }
  11. {                                                                              }
  12. {------------------------------------------------------------------------------}
  13.  
  14. unit FindFile;
  15.  
  16. interface
  17.  
  18. uses
  19.   Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  20.   Masks;
  21.  
  22. type
  23.  
  24.   EFindFileError = class(Exception);
  25.  
  26.   TFileMatchEvent = procedure (Sender: TObject; const Folder: String;
  27.     const FileInfo: TSearchRec) of object;
  28.  
  29.   TFolderChangeEvent = procedure (Sender: TObject; const Folder: String;
  30.     var IgnoreFolder: Boolean) of object;
  31.  
  32.   TFileCriteria = class(TPersistent)
  33.   private
  34.     fFilename: String;
  35.     fLocation: String;
  36.     fIncluded: TStringList;
  37.     fExcluded: TStringList;
  38.     fSubfolders: Boolean;
  39.     procedure SetIncluded(Value: TStringList);
  40.     procedure SetExcluded(Value: TStringList);
  41.   public
  42.     constructor Create;
  43.     destructor Destroy; override;
  44.     procedure Assign(Source: TPersistent); override;
  45.   published
  46.     property FileName: String read fFilename write fFilename;
  47.     property Location: String read fLocation write fLocation;
  48.     property Included: TStringList read fIncluded write SetIncluded;
  49.     property Excluded: TStringList read fExcluded write SetExcluded;
  50.     property Subfolders: Boolean read fSubfolders write fSubfolders default True;
  51.   end;
  52.  
  53.   TFileAttributes = set of (ffArchive, ffReadonly, ffHidden, ffSystem, ffDirectory);
  54.  
  55.   TAttributeCriteria = class(TPersistent)
  56.   private
  57.      fFlags: Integer;
  58.      fExactMatch: Boolean;
  59.      function GetAttributes: TFileAttributes;
  60.      procedure SetAttributes(Value: TFileAttributes);
  61.   public
  62.     constructor Create;
  63.     procedure Assign(Source: TPersistent); override;
  64.     property Flags: Integer read fFlags write fFlags;
  65.     function Matches(Attr: Integer): Boolean;
  66.   published
  67.     property Attributes: TFileAttributes read GetAttributes write SetAttributes
  68.       default [ffArchive, ffReadonly, ffHidden, ffSystem];
  69.     property ExactMatch: Boolean read fExactMatch write fExactMatch default False;
  70.   end;
  71.  
  72.   TDateTimeCriteria = class(TPersistent)
  73.   private
  74.      fCreatedBefore: TDateTime;
  75.      fCreatedAfter: TDateTime;
  76.      fModifiedBefore: TDateTime;
  77.      fModifiedAfter: TDateTime;
  78.      fAccessedBefore: TDateTime;
  79.      fAccessedAfter: TDateTime;
  80.   public
  81.     procedure Assign(Source: TPersistent); override;
  82.     function Matches(const Created, Modified, Accessed: TFileTime): Boolean; 
  83.   published
  84.     property CreatedBefore: TDateTime read fCreatedBefore write fCreatedBefore;
  85.     property CreatedAfter: TDateTime read fCreatedAfter write fCreatedAfter;
  86.     property ModifiedBefore: TDateTime read fModifiedBefore write fModifiedBefore;
  87.     property ModifiedAfter: TDateTime read fModifiedAfter write fModifiedAfter;
  88.     property AccessedBefore: TDateTime read fAccessedBefore write fAccessedBefore;
  89.     property AccessedAfter: TDateTime read fAccessedAfter write fAccessedAfter;
  90.   end;
  91.  
  92.   TSizeCriteria = class(TPersistent)
  93.   private
  94.     fMin: DWORD;
  95.     fMax: DWORD;
  96.   public
  97.     procedure Assign(Source: TPersistent); override;
  98.     function Matches(Size: DWORD): Boolean; 
  99.   published
  100.     property Min: DWORD read fMin write fMin default 0;
  101.     property Max: DWORD read fMax write fMax default 0;
  102.   end;
  103.  
  104.   TContentCriteria = class(TPersistent)
  105.   private
  106.     fPhrase: String;
  107.     fPhraseLen: Integer;
  108.     fIgnoreCase: Boolean;
  109.     fTargetPhrase: String;
  110.     procedure SetPhrase(const Value: String);
  111.     procedure SetIgnoreCase(Value: Boolean);
  112.   protected
  113.     property TargetPhrase: String read fTargetPhrase;
  114.   public
  115.     constructor Create;
  116.     procedure Assign(Source: TPersistent); override;
  117.     property PhraseLen: Integer read fPhraseLen;
  118.     function Matches(const FileName: String): Boolean;
  119.   published
  120.     property Phrase: String read fPhrase write SetPhrase;
  121.     property IgnoreCase: Boolean read fIgnoreCase write SetIgnoreCase default True;
  122.   end;
  123.  
  124.   TSearchCriteria = class(TPersistent)
  125.   private
  126.     fFiles: TFileCriteria;
  127.     fAttribute: TAttributeCriteria;
  128.     fTimeStamp: TDateTimeCriteria;
  129.     fSize: TSizeCriteria;
  130.     fContent: TContentCriteria;
  131.     procedure SetFiles(Value: TFileCriteria);
  132.     procedure SetAttribute(Value: TAttributeCriteria);
  133.     procedure SetTimeStamp(Value: TDateTimeCriteria);
  134.     procedure SetSize(Value: TSizeCriteria);
  135.     procedure SetContent(Value: TContentCriteria);
  136.   public
  137.     constructor Create;
  138.     destructor Destroy; override;
  139.     procedure Assign(Source: TPersistent); override;
  140.   published
  141.     property Files: TFileCriteria read fFiles write SetFiles;
  142.     property Attribute: TAttributeCriteria read fAttribute write SetAttribute;
  143.     property TimeStamp: TDateTimeCriteria read fTimeStamp write SetTimeStamp;
  144.     property Size: TSizeCriteria read fSize write SetSize;
  145.     property Content: TContentCriteria read fContent write SetContent;
  146.   end;
  147.  
  148.   TTargetFolder = class(TObject)
  149.   private
  150.     fFolder: String;
  151.     fSubfolders: Boolean;
  152.     fFileMasks: TStringList;
  153.   public
  154.     constructor Create;
  155.     destructor Destroy; override;
  156.     property Folder: String read fFolder write fFolder;
  157.     property Subfolders: Boolean read fSubfolders write fSubfolders;
  158.     property FileMasks: TStringList read fFileMasks;
  159.   end;
  160.  
  161.   TTargetFolderList = class(TList)
  162.   private
  163.     fExcludedFiles: TStringList;
  164.     function GetItems(Index: Integer): TTargetFolder;
  165.   public
  166.     constructor Create;
  167.     destructor Destroy; override;
  168.     function IndexOfFolder(const Folder: String): Integer;
  169.     function AddFolder(const Folder: String): TTargetFolder;
  170.     function IsExcluded(const Folder, FileName: String): Boolean;
  171.     property Items[Index: Integer]: TTargetFolder read GetItems; default;
  172.     property ExcludedFiles: TStringList read fExcludedFiles;
  173.   end;
  174.  
  175.   // TTargetSearch holds all running search parameters. This ables us to change
  176.   // the component's properties without affecting the running search.
  177.   TTargetSearch = class(TObject)
  178.   protected
  179.      TargetFolders: TTargetFolderList;
  180.      Attribute: TAttributeCriteria;
  181.      TimeStamp: TDateTimeCriteria;
  182.      Size: TSizeCriteria;
  183.      Content: TContentCriteria;
  184.      procedure PrepareTargetFolders(FileCriteria: TFileCriteria);
  185.   public
  186.     constructor Create(Criteria: TSearchCriteria);
  187.     destructor Destroy; override;
  188.     function Matches(const Folder: String; const SR: TSearchRec): Boolean;
  189.   end;
  190.  
  191.   TFindFile = class(TComponent)
  192.   private
  193.     fCriteria: TSearchCriteria;
  194.     fThreaded: Boolean;
  195.     fThreadPriority: TThreadPriority;
  196.     fAborted: Boolean;
  197.     fBusy: Boolean;
  198.     fOnFileMatch: TFileMatchEvent;
  199.     fOnFolderChange: TFolderChangeEvent;
  200.     fOnSearchBegin: TNotifyEvent;
  201.     fOnSearchFinish: TNotifyEvent;
  202.     fOnSearchAbort: TNotifyEvent;
  203.     SearchThread: TThread;
  204.     TargetSearch: TTargetSearch;
  205.     ActiveTargetFolder: TTargetFolder;
  206.     procedure SetCriteria(Value: TSearchCriteria);
  207.     procedure ThreadTerminated(Sender: TObject);
  208.   protected
  209.     procedure DoSearchBegin; virtual;
  210.     procedure DoSearchFinish; virtual;
  211.     procedure DoSearchAbort; virtual;
  212.     function DoFolderChange(const Folder: String): Boolean; virtual;
  213.     procedure DoFileMatch(const Folder: String; const FileInfo: TSearchRec); virtual;
  214.     function IsAcceptable(const Folder: String; const SR: TSearchRec): Boolean;
  215.     procedure InitializeSearch;
  216.     procedure FinalizeSearch;
  217.     procedure SearchForFiles;
  218.   public
  219.     constructor Create(AOwner: TComponent); override;
  220.     destructor Destroy; override;
  221.     procedure Execute;
  222.     procedure Abort;
  223.     property Busy: Boolean read fBusy;
  224.     property Aborted: Boolean read fAborted;
  225.   published
  226.     property Criteria: TSearchCriteria read fCriteria write SetCriteria;
  227.     property Threaded: Boolean read fThreaded write fThreaded default False;
  228.     property ThreadPriority: TThreadPriority
  229.       read fThreadPriority write fThreadPriority default tpNormal;
  230.     property OnFileMatch: TFileMatchEvent read fOnFileMatch write fOnFileMatch;
  231.     property OnFolderChange: TFolderChangeEvent read fOnFolderChange write fOnFolderChange;
  232.     property OnSearchBegin: TNotifyEvent read fOnSearchBegin write fOnSearchBegin;
  233.     property OnSearchFinish: TNotifyEvent read fOnSearchFinish write fOnSearchFinish;
  234.     property OnSearchAbort: TNotifyEvent read fOnSearchAbort write fOnSearchAbort;
  235.   end;
  236.  
  237. procedure Register;
  238.  
  239. function AddTrailingBackslash(const Path: String): String;
  240. function RemoveTrailingBackslash(const Path: String): String;
  241. function FileTimeToDateTime(const FileTime: TFileTime): TDateTime;
  242. function IsDateBetween(const aDate, Before, After: TDateTime): Boolean;
  243. function FileContains(const FileName: String; const Phrase: String;
  244.   IgnoreCase: Boolean): Boolean;
  245.  
  246. implementation
  247.  
  248. uses
  249.   FileCtrl;
  250.  
  251. const
  252.   Delimiter = ';';
  253.   IncSubfolders = '>';
  254.   ExcSubfolders = '<';
  255.   ValidFileAttr = faAnyFile and not faVolumeID;
  256.  
  257. procedure Register;
  258. begin
  259.   RegisterComponents('Delphi Area', [TFindFile]);
  260. end;
  261.  
  262. { Helper Functions }
  263.  
  264. function AddTrailingBackslash(const Path: String): String;
  265. var
  266.   PathLen: Integer;
  267. begin
  268.   PathLen := Length(Path);
  269.   if (PathLen > 0) and not (Path[PathLen] in ['\', ':']) then
  270.     Result := Path + '\'
  271.   else
  272.     Result := Path;
  273. end;
  274.  
  275. function RemoveTrailingBackslash(const Path: String): String;
  276. var
  277.   PathLen: Integer;
  278. begin
  279.   PathLen := Length(Path);
  280.   if (PathLen > 1) and (Path[PathLen] = '\') and (Path[PathLen-1] <> ':') then
  281.     Result := Copy(Path, 1, PathLen - 1)
  282.   else
  283.     Result := Path;
  284. end;
  285.  
  286. function FileTimeToDateTime(const FileTime: TFileTime): TDateTime;
  287. var
  288.   LocalFileTime: TFileTime;
  289.   SystemTime: TSystemTime;
  290. begin
  291.   FileTimeToLocalFileTime(FileTime, LocalFileTime);
  292.   FileTimeToSystemTime(LocalFileTime, SystemTime);
  293.   Result := SystemTimeToDateTime(SystemTime);
  294. end;
  295.  
  296. function IsDateBetween(const aDate, Before, After: TDateTime): Boolean;
  297. begin
  298.   Result := True;
  299.   if Before <> 0 then
  300.     if Frac(Before) = 0 then      { Checks date only }
  301.       Result := Result and (Int(aDate) <= Before)
  302.     else if Int(Before) = 0 then  { Checks time only }
  303.       Result := Result and (Frac(aDate) <= Before)
  304.     else                          { Checks date and time }
  305.       Result := Result and (aDate <= Before);
  306.   if After <> 0 then
  307.     if Frac(After) = 0 then       { Checks date only }
  308.       Result := Result and (Int(aDate) >= After)
  309.     else if Int(After) = 0 then   { Checks time only }
  310.       Result := Result and (Frac(aDate) >= After)
  311.     else                          { Checks date and time }
  312.       Result := Result and (aDate >= After);
  313. end;
  314.  
  315. function FileContainsPhrase(const FileName: String; const Phrase: PChar;
  316.   PhraseLen: Integer; MatchLowerCase: Boolean): Boolean;
  317. const
  318.   MaxBufferSize = $F000; // Must be larger than PhraseLen
  319. var
  320.   Stream: TFileStream;
  321.   DataSize: Integer;
  322.   BufferSize: Integer;
  323.   Buffer, B, P: PChar;
  324.   N, Offset: Integer;
  325. begin
  326.   Result := False;
  327.   Stream := TFileStream.Create(FileName, fmOpenRead or fmShareDenyNone);
  328.   try
  329.     DataSize := Stream.Size;
  330.     if DataSize >= PhraseLen then
  331.     begin
  332.       if DataSize > MaxBufferSize then
  333.         if PhraseLen <= MaxBufferSize then
  334.           BufferSize := MaxBufferSize
  335.         else
  336.           BufferSize := PhraseLen
  337.       else
  338.         BufferSize := DataSize;
  339.       GetMem(Buffer, BufferSize);
  340.       try
  341.         P := Phrase;
  342.         Offset := 0;
  343.         while (DataSize + Offset) >= PhraseLen do
  344.         begin
  345.           B := Buffer + Offset;
  346.           N := BufferSize - Offset;
  347.           if N > DataSize then N := DataSize;
  348.           Stream.Read(B^, N);
  349.           if MatchLowerCase then AnsiLowerBuff(B, N);
  350.           Dec(DataSize, N);
  351.           repeat
  352.             if B^ = P^ then
  353.             begin
  354.               Inc(P);
  355.               Inc(Offset);
  356.               Result := (Offset = PhraseLen);
  357.             end
  358.             else if P <> Phrase then
  359.             begin
  360.               P := Phrase;
  361.               Dec(B, Offset);
  362.               Inc(N, Offset);
  363.               Offset := 0;
  364.             end;
  365.             Inc(B);
  366.             Dec(N);
  367.           until Result or (N = 0);
  368.         end;
  369.       finally
  370.         FreeMem(Buffer, BufferSize);
  371.       end;
  372.     end;
  373.   finally
  374.     Stream.Free;
  375.   end;
  376. end;
  377.  
  378. function FileContains(const FileName: String; const Phrase: String;
  379.   IgnoreCase: Boolean): Boolean;
  380. begin
  381.   if IgnoreCase then
  382.     Result := FileContainsPhrase(FileName, PChar(LowerCase(Phrase)),
  383.       Length(Phrase), True)
  384.   else
  385.     Result := FileContainsPhrase(FileName, PChar(Phrase),
  386.       Length(Phrase), False);
  387. end;
  388.  
  389. function StrMatches(const Str, Mask: String): Boolean;
  390. var
  391.   SIdx, SLen: Integer;
  392.   MIdx, MLen: Integer;
  393.   M: Char;
  394. begin
  395.   SLen := Length(Str); SIdx := 1;
  396.   MLen := Length(Mask); MIdx := 1;
  397.   while (SIdx <= SLen) and (MIdx <= MLen) do
  398.   begin
  399.     M := Mask[MIdx];
  400.     case M of
  401.       '*': SIdx := SLen;
  402.       '?': ;
  403.     else
  404.       if UpCase(M) <> UpCase(Str[MIdx]) then
  405.         Break;
  406.     end;
  407.     Inc(SIdx);
  408.     Inc(MIdx);
  409.   end;
  410.   Result := (SIdx > SLen);
  411. end;
  412.  
  413. function FileMatches(const FileDir, FileName, Mask: String): Boolean;
  414. var
  415.   MaskDrive, MaskDir, MaskName: String;
  416.   FileDrive, InnerFileDir: String;
  417. begin
  418.   Result := False;
  419.   MaskDir := ExtractFilePath(Mask);
  420.   // Checkes file path part, if mask contains path
  421.   if Length(MaskDir) > 0 then
  422.   begin
  423.     FileDrive := ExtractFileDrive(FileDir);
  424.     MaskDrive := ExtractFileDrive(MaskDir);
  425.     // Checkes drive, if mask contains drive
  426.     if Length(MaskDrive) > 0 then
  427.     begin
  428.       if not StrMatches(FileDrive, MaskDrive) then
  429.         Exit; // Not Matched, drives are different 
  430.       // Removes drive part from the Mask 
  431.       Delete(MaskDir, 1, Length(MaskDrive));
  432.     end;
  433.     // Checkes directory
  434.     if Length(MaskDir) > Length(FileDir) - Length(FileDrive) then
  435.       Exit // Not Matched, Mask's length is longer than folder's length
  436.     else
  437.     begin
  438.       // Checkes most inner directories
  439.       InnerFileDir := Copy(FileDir, Length(FileDir) - Length(MaskDir) + 1, Length(MaskDir));
  440.       if CompareText(InnerFileDir, MaskDir) <> 0 then
  441.         Exit; // Not Matched
  442.     end;
  443.   end;
  444.   // Checkes file name part if mask contains filename
  445.   MaskName := ExtractFileName(Mask);
  446.   if Length(MaskName) > 0 then
  447.     Result := MatchesMask(FileName, MaskName)
  448.   else
  449.     Result := True; // Matched
  450. end;
  451.  
  452. { TFileCriteria }
  453.  
  454. constructor TFileCriteria.Create;
  455. begin
  456.   inherited Create;
  457.   fIncluded := TStringList.Create;
  458.   fExcluded := TStringList.Create;
  459.   fSubfolders := True;
  460. end;
  461.  
  462. destructor TFileCriteria.Destroy;
  463. begin
  464.   fIncluded.Free;
  465.   fExcluded.Free;
  466.   inherited Destroy;
  467. end;
  468.  
  469. procedure TFileCriteria.Assign(Source: TPersistent);
  470. begin
  471.   if Source is TFileCriteria then
  472.   begin
  473.     Filename := TFileCriteria(Source).FileName;
  474.     Location := TFileCriteria(Source).Location;
  475.     Included := TFileCriteria(Source).Included;
  476.     Excluded := TFileCriteria(Source).Excluded;
  477.     Subfolders := TFileCriteria(Source).Subfolders;
  478.   end
  479.   else
  480.     inherited Assign(Source);
  481. end;
  482.  
  483. procedure TFileCriteria.SetIncluded(Value: TStringList);
  484. begin
  485.   fIncluded.Assign(Value);
  486. end;
  487.  
  488. procedure TFileCriteria.SetExcluded(Value: TStringList);
  489. begin
  490.   fExcluded.Assign(Value);
  491. end;
  492.  
  493. { TAttributeCriteria }
  494.  
  495. constructor TAttributeCriteria.Create;
  496. begin
  497.   inherited Create;
  498.   fFlags := faArchive or faReadonly or faHidden or faSysFile;
  499.   fExactMatch := False;
  500. end;
  501.  
  502. procedure TAttributeCriteria.Assign(Source: TPersistent);
  503. begin
  504.   if Source is TAttributeCriteria then
  505.   begin
  506.     Flags := TAttributeCriteria(Source).Flags;
  507.     ExactMatch := TAttributeCriteria(Source).ExactMatch;
  508.   end
  509.   else
  510.     inherited Assign(Source);
  511. end;
  512.  
  513. function TAttributeCriteria.GetAttributes: TFileAttributes;
  514. begin
  515.   Result := [];
  516.   if (Flags and faArchive) = faArchive then
  517.     Include(Result, ffArchive);
  518.   if (Flags and faReadonly) = faReadonly then
  519.     Include(Result, ffReadonly);
  520.   if (Flags and faHidden) = faHidden then
  521.     Include(Result, ffHidden);
  522.   if (Flags and faSysFile) = faSysFile then
  523.     Include(Result, ffSystem);
  524.   if (Flags and faDirectory) = faDirectory then
  525.     Include(Result, ffDirectory);
  526. end;
  527.  
  528. procedure TAttributeCriteria.SetAttributes(Value: TFileAttributes);
  529. var
  530.   NewFlags: Integer;
  531. begin
  532.   NewFlags := 0;
  533.   if ffArchive in Value then
  534.     NewFlags := NewFlags or faArchive;
  535.   if ffReadonly in Value then
  536.     NewFlags := NewFlags or faReadonly;
  537.   if ffHidden in Value then
  538.     NewFlags := NewFlags or faHidden;
  539.   if ffSystem in Value then
  540.     NewFlags := NewFlags or faSysFile;
  541.   if ffDirectory in Value then
  542.     NewFlags := NewFlags or faDirectory;
  543.   Flags := NewFlags;
  544. end;
  545.  
  546. function TAttributeCriteria.Matches(Attr: Integer): Boolean;
  547. begin
  548.   Attr := Attr and ValidFileAttr;
  549.   Result := (not ExactMatch or (Flags = Attr)) and
  550.             (ExactMatch or ((not Flags and Attr) = 0));
  551. end;
  552.  
  553. { TDateTimeCriteria }
  554.  
  555. procedure TDateTimeCriteria.Assign(Source: TPersistent);
  556. begin
  557.   if Source is TDateTimeCriteria then
  558.   begin
  559.     CreatedBefore := TDateTimeCriteria(Source).CreatedBefore;
  560.     CreatedAfter := TDateTimeCriteria(Source).CreatedAfter;
  561.     ModifiedBefore := TDateTimeCriteria(Source).ModifiedBefore;
  562.     ModifiedAfter := TDateTimeCriteria(Source).ModifiedAfter;
  563.     AccessedBefore := TDateTimeCriteria(Source).AccessedBefore;
  564.     AccessedAfter := TDateTimeCriteria(Source).AccessedAfter;
  565.   end
  566.   else
  567.     inherited Assign(Source);
  568. end;
  569.  
  570. function TDateTimeCriteria.Matches(const Created, Modified, Accessed: TFileTime): Boolean;
  571. var
  572.   DateTime: TDateTime;
  573. begin
  574.   Result := False;
  575.   if (CreatedBefore <> 0) or (CreatedAfter <> 0) then
  576.   begin
  577.     DateTime := FileTimeToDateTime(Created);
  578.     if not IsDateBetween(DateTime, CreatedBefore, CreatedAfter) then Exit;
  579.   end;
  580.   if (ModifiedBefore <> 0) or (ModifiedAfter <> 0) then
  581.   begin
  582.     DateTime := FileTimeToDateTime(Modified);
  583.     if not IsDateBetween(DateTime, ModifiedBefore, ModifiedAfter) then Exit;
  584.   end;
  585.   if (AccessedBefore <> 0) or (AccessedAfter <> 0) then
  586.   begin
  587.     DateTime := FileTimeToDateTime(Accessed);
  588.     if not IsDateBetween(DateTime, AccessedBefore, AccessedAfter) then Exit;
  589.   end;
  590.   Result := True;
  591. end;
  592.  
  593. { TSizeCriteria }
  594.  
  595. procedure TSizeCriteria.Assign(Source: TPersistent);
  596. begin
  597.   if Source is TSizeCriteria then
  598.   begin
  599.     Min := TSizeCriteria(Source).Min;
  600.     Max := TSizeCriteria(Source).Max;
  601.   end
  602.   else
  603.     inherited Assign(Source);
  604. end;
  605.  
  606. function TSizeCriteria.Matches(Size: DWORD): Boolean;
  607. begin
  608.   Result := ((Min = 0) or (Size >= Min)) and ((Max = 0) or (Size <= Max));
  609. end;
  610.  
  611. { TContentCriteria }
  612.  
  613. constructor TContentCriteria.Create;
  614. begin
  615.   inherited Create;
  616.   fIgnoreCase := True;
  617. end;
  618.  
  619. procedure TContentCriteria.Assign(Source: TPersistent);
  620. begin
  621.   if Source is TContentCriteria then
  622.   begin
  623.     Phrase := TContentCriteria(Source).Phrase;
  624.     IgnoreCase := TContentCriteria(Source).IgnoreCase;
  625.   end
  626.   else
  627.     inherited Assign(Source);
  628. end;
  629.  
  630. procedure TContentCriteria.SetPhrase(const Value: String);
  631. begin
  632.   if Phrase <> Value then
  633.   begin
  634.     fPhrase := Value;
  635.     fPhraseLen := Length(Value);
  636.     if IgnoreCase then
  637.       fTargetPhrase := LowerCase(Phrase)
  638.     else
  639.       fTargetPhrase := Phrase;
  640.   end;
  641. end;
  642.  
  643. procedure TContentCriteria.SetIgnoreCase(Value: Boolean);
  644. begin
  645.   if IgnoreCase <> Value then
  646.   begin
  647.     fIgnoreCase := Value;
  648.     if IgnoreCase then
  649.       fTargetPhrase := LowerCase(Phrase)
  650.     else
  651.       fTargetPhrase := Phrase;
  652.   end;
  653. end;
  654.  
  655. function TContentCriteria.Matches(const FileName: String): Boolean;
  656. begin
  657.   if PhraseLen > 0 then
  658.     try
  659.       Result := FileContainsPhrase(FileName, PChar(TargetPhrase), PhraseLen, IgnoreCase)
  660.     except
  661.       Result := False;
  662.     end
  663.   else
  664.     Result := True;
  665. end;
  666.  
  667. { TSearchCriteria }
  668.  
  669. constructor TSearchCriteria.Create;
  670. begin
  671.   inherited Create;
  672.   fFiles := TFileCriteria.Create;
  673.   fAttribute := TAttributeCriteria.Create;
  674.   fTimeStamp := TDateTimeCriteria.Create;
  675.   fSize := TSizeCriteria.Create;
  676.   fContent := TContentCriteria.Create;
  677. end;
  678.  
  679. destructor TSearchCriteria.Destroy;
  680. begin
  681.   fFiles.Free;
  682.   fAttribute.Free;
  683.   fTimeStamp.Free;
  684.   fSize.Free;
  685.   fContent.Free;
  686.   inherited Destroy;
  687. end;
  688.  
  689. procedure TSearchCriteria.Assign(Source: TPersistent);
  690. begin
  691.   if Source is TSearchCriteria then
  692.   begin
  693.     Files := TSearchCriteria(Source).Files;
  694.     Attribute := TSearchCriteria(Source).Attribute;
  695.     TimeStamp := TSearchCriteria(Source).TimeStamp;
  696.     Size := TSearchCriteria(Source).Size;
  697.     Content := TSearchCriteria(Source).Content;
  698.   end
  699.   else
  700.     inherited Assign(Source);
  701. end;
  702.  
  703. procedure TSearchCriteria.SetFiles(Value: TFileCriteria);
  704. begin
  705.   Files.Assign(Value);
  706. end;
  707.  
  708. procedure TSearchCriteria.SetAttribute(Value: TAttributeCriteria);
  709. begin
  710.   Attribute.Assign(Value);
  711. end;
  712.  
  713. procedure TSearchCriteria.SetTimeStamp(Value: TDateTimeCriteria);
  714. begin
  715.   TimeStamp.Assign(Value);
  716. end;
  717.  
  718. procedure TSearchCriteria.SetSize(Value: TSizeCriteria);
  719. begin
  720.   Size.Assign(Value);
  721. end;
  722.  
  723. procedure TSearchCriteria.SetContent(Value: TContentCriteria);
  724. begin
  725.   Content.Assign(Value);
  726. end;
  727.  
  728. { TTargetFolder }
  729.  
  730. constructor TTargetFolder.Create;
  731. begin
  732.   inherited Create;
  733.   fFileMasks := TStringList.Create;
  734. end;
  735.  
  736. destructor TTargetFolder.Destroy;
  737. begin
  738.   fFileMasks.Free;
  739.   inherited Destroy;
  740. end;
  741.  
  742. { TTargetFolderList }
  743.  
  744. constructor TTargetFolderList.Create;
  745. begin
  746.   inherited Create;
  747.   fExcludedFiles := TStringList.Create;
  748. end;
  749.  
  750. destructor TTargetFolderList.Destroy;
  751. var
  752.   Index: Integer;
  753. begin
  754.   fExcludedFiles.Free;
  755.   for Index := Count - 1 downto 0 do
  756.     Items[Index].Free;
  757.   inherited Destroy;
  758. end;
  759.  
  760. function TTargetFolderList.IndexOfFolder(const Folder: String): Integer;
  761. var
  762.   Index: Integer;
  763. begin
  764.   Result := -1;
  765.   for Index := 0 to Count - 1 do
  766.     if CompareText(Folder, Items[Index].Folder) = 0 then
  767.     begin
  768.       Result := -1;
  769.       Break;
  770.     end;
  771. end;
  772.  
  773. function TTargetFolderList.AddFolder(const Folder: String): TTargetFolder;
  774. var
  775.   Index: Integer;
  776.   FullPath: String;
  777. begin
  778.   FullPath := AddTrailingBackslash(ExpandFileName(Folder));
  779.   Index := IndexOfFolder(FullPath);
  780.   if Index >= 0 then
  781.     Result := Items[Index]
  782.   else
  783.   begin
  784.     Result := TTargetFolder.Create;
  785.     Result.Folder := FullPath;
  786.     Insert(0, Result);
  787.   end;
  788. end;
  789.  
  790. function TTargetFolderList.IsExcluded(const Folder, FileName: String): Boolean;
  791. var
  792.   I: Integer;
  793. begin
  794.   Result := False;
  795.   for I := ExcludedFiles.Count - 1 downto 0 do
  796.     if FileMatches(Folder, FileName, ExcludedFiles[I]) then
  797.     begin
  798.       Result := True;
  799.       Exit;
  800.     end;
  801. end;
  802.  
  803. function TTargetFolderList.GetItems(Index: Integer): TTargetFolder;
  804. begin
  805.   Result := TTargetFolder(inherited Items[Index]);
  806. end;
  807.  
  808. { TTargetSearch }
  809.  
  810. constructor TTargetSearch.Create(Criteria: TSearchCriteria);
  811. begin
  812.   inherited Create;
  813.   TargetFolders := TTargetFolderList.Create;
  814.   Attribute := TAttributeCriteria.Create;
  815.   TimeStamp := TDateTimeCriteria.Create;
  816.   Size := TSizeCriteria.Create;
  817.   Content := TContentCriteria.Create;
  818.   PrepareTargetFolders(Criteria.Files);
  819.   Attribute.Assign(Criteria.Attribute);
  820.   TimeStamp.Assign(Criteria.TimeStamp);
  821.   Size.Assign(Criteria.Size);
  822.   Content.Assign(Criteria.Content);
  823.   if Content.PhraseLen > 0 then
  824.     Attribute.Attributes := Attribute.Attributes - [ffDirectory];
  825. end;
  826.  
  827. destructor TTargetSearch.Destroy;
  828. begin
  829.   TargetFolders.Free;
  830.   Attribute.Free;
  831.   TimeStamp.Free;
  832.   Size.Free;
  833.   Content.Free;
  834.   inherited Destroy;
  835. end;
  836.  
  837. procedure TTargetSearch.PrepareTargetFolders(FileCriteria: TFileCriteria);
  838.  
  839.   function CreateItemsList(ItemsText: String): TStringList;
  840.   var
  841.     DelimiterPos: Integer;
  842.   begin
  843.     Result := TStringList.Create;
  844.     Result.Duplicates := dupIgnore;
  845.     while ItemsText <> '' do
  846.     begin
  847.       DelimiterPos := Pos(Delimiter, ItemsText);
  848.       if DelimiterPos = 0 then
  849.       begin
  850.         Result.Add(ItemsText);
  851.         Break;
  852.       end
  853.       else
  854.       begin
  855.         Result.Add(Copy(ItemsText, 1, DelimiterPos - 1));
  856.         Delete(ItemsText, 1, DelimiterPos);
  857.       end;
  858.     end;
  859.   end;
  860.  
  861.   function CheckSubfolders(var Folder: String): Boolean;
  862.   begin
  863.     Result := FileCriteria.Subfolders;
  864.     if Folder <> '' then
  865.     begin
  866.       case Folder[1] of
  867.         IncSubfolders:
  868.         begin
  869.           Result := True;
  870.           Delete(Folder, 1, 1);
  871.         end;
  872.         ExcSubfolders:
  873.         begin
  874.           Result := False;
  875.           Delete(Folder, 1, 1);
  876.         end;
  877.       end;
  878.     end;
  879.   end;
  880.  
  881. var
  882.   I: Integer;
  883.   Item: String;
  884.   FileList: TStringList;
  885.   FolderList: TStringList;
  886.   ThisFolder: TTargetFolder;
  887.   Subfolders: Boolean;
  888. begin
  889.   TargetFolders.ExcludedFiles.Assign(FileCriteria.Excluded);
  890.   // Processes Included property
  891.   for I := 0 to FileCriteria.Included.Count - 1 do
  892.   begin
  893.     Item := FileCriteria.Included[I];
  894.     Subfolders := CheckSubfolders(Item);
  895.     ThisFolder := TargetFolders.AddFolder(ExtractFilePath(Item));
  896.     ThisFolder.FileMasks.Add(ExtractFileName(Item));
  897.     ThisFolder.Subfolders := Subfolders;
  898.   end;
  899.   // Processes FileName and Location properties
  900.   FileList := CreateItemsList(FileCriteria.FileName);
  901.   try
  902.     FolderList := CreateItemsList(FileCriteria.Location);
  903.     try
  904.       for I := 0 to FolderList.Count - 1 do
  905.       begin
  906.         Item := FolderList[I];
  907.         Subfolders := CheckSubfolders(Item);
  908.         ThisFolder := TargetFolders.AddFolder(Item);
  909.         ThisFolder.FileMasks.AddStrings(FileList);
  910.         ThisFolder.Subfolders := Subfolders;
  911.       end;
  912.     finally
  913.       FolderList.Free;
  914.     end;
  915.   finally
  916.     FileList.Free;
  917.   end;
  918. end;
  919.  
  920. function TTargetSearch.Matches(const Folder: String;
  921.   const SR: TSearchRec): Boolean;
  922. begin
  923.   with SR.FindData do
  924.     Result := Attribute.Matches(SR.Attr) and Size.Matches(SR.Size) and
  925.     TimeStamp.Matches(ftCreationTime, ftLastWriteTime, ftLastAccessTime) and
  926.     not TargetFolders.IsExcluded(Folder, SR.Name) and
  927.     Content.Matches(Folder + SR.Name);
  928. end;
  929.  
  930. { TSearchThread }
  931.  
  932. type
  933.   PSearchRec = ^TSearchRec;
  934.   TSearchThread = class(TThread)
  935.   private
  936.     Owner: TFindFile;
  937.     ThisFolder: String;
  938.     ThisFolderIgnored: Boolean;
  939.     MatchedSR: PSearchRec;
  940.     procedure NotifyFolderChanged;
  941.     procedure NotifyFileMatched;
  942.   protected
  943.     constructor Create(AOwner: TFindFile);
  944.     procedure Execute; override;
  945.   end;
  946.  
  947. constructor TSearchThread.Create(AOwner: TFindFile);
  948. begin
  949.   inherited Create(True);
  950.   Owner := AOwner;
  951.   FreeOnTerminate := True;
  952.   Priority := Owner.ThreadPriority;
  953.   OnTerminate := Owner.ThreadTerminated;
  954.   Resume;
  955. end;
  956.  
  957. procedure TSearchThread.NotifyFileMatched;
  958. begin
  959.   Owner.DoFileMatch(ThisFolder, MatchedSR^);
  960. end;
  961.  
  962. procedure TSearchThread.NotifyFolderChanged;
  963. begin
  964.   ThisFolderIgnored := not Owner.DoFolderChange(ThisFolder);
  965. end;
  966.  
  967. procedure TSearchThread.Execute;
  968.  
  969.   procedure SearchIn(const Path: String);
  970.   var
  971.     SR: TSearchRec;
  972.     MaskIndex: Integer;
  973.   begin
  974.     ThisFolder := Path;
  975.     Synchronize(NotifyFolderChanged);
  976.     if ThisFolderIgnored then Exit;
  977.     with Owner.ActiveTargetFolder do
  978.     begin
  979.       // Searches in the current folder for all file masks 
  980.       MaskIndex := FileMasks.Count;
  981.       while not Terminated and (MaskIndex > 0) do
  982.       begin
  983.         Dec(MaskIndex);
  984.         if not Terminated and (FindFirst(Path + FileMasks[MaskIndex], ValidFileAttr, SR) = 0) then
  985.         begin
  986.           repeat
  987.             if (SR.Name <> '.') and (SR.Name <> '..') and Owner.IsAcceptable(Path, SR) then
  988.             begin
  989.               MatchedSR := @SR;
  990.               Synchronize(NotifyFileMatched);
  991.             end;
  992.           until Terminated or (FindNext(SR) <> 0);
  993.           FindClose(SR);
  994.         end;
  995.       end;
  996.       // Searches in subfolders
  997.       if Subfolders then
  998.       begin
  999.         if not Terminated and (FindFirst(Path + '*.*', ValidFileAttr, SR) = 0) then
  1000.         begin
  1001.           repeat
  1002.             if ((SR.Attr and faDirectory) = faDirectory) and
  1003.                (SR.Name <> '.') and (SR.Name <> '..')
  1004.             then
  1005.               SearchIn(Path + SR.Name + '\');
  1006.           until Terminated or (FindNext(SR) <> 0);
  1007.           FindClose(SR);
  1008.         end;
  1009.       end;
  1010.     end;
  1011.   end;
  1012.  
  1013. var
  1014.   Index: Integer;
  1015. begin
  1016.   Index := Owner.TargetSearch.TargetFolders.Count;
  1017.   while not Terminated and (Index > 0) do
  1018.   begin
  1019.     Dec(Index);
  1020.     Owner.ActiveTargetFolder := Owner.TargetSearch.TargetFolders[Index];
  1021.     SearchIn(Owner.ActiveTargetFolder.Folder);
  1022.   end;
  1023. end;
  1024.  
  1025. { TFindFile }
  1026.  
  1027. constructor TFindFile.Create(AOwner: TComponent);
  1028. begin
  1029.   inherited Create(AOwner);
  1030.   fCriteria := TSearchCriteria.Create;
  1031.   fThreaded := False;
  1032.   fThreadPriority := tpNormal;
  1033.   fAborted := False;
  1034.   fBusy := False;
  1035. end;
  1036.  
  1037. destructor TFindFile.Destroy;
  1038. begin
  1039.   if Busy then
  1040.   begin
  1041.     Abort;
  1042.     repeat
  1043.       Application.ProcessMessages
  1044.     until Busy;
  1045.   end;
  1046.   fCriteria.Free;
  1047.   inherited Destroy;
  1048. end;
  1049.  
  1050. procedure TFindFile.Abort;
  1051. begin
  1052.   if fBusy then
  1053.   begin
  1054.     fAborted := True;
  1055.     DoSearchAbort;
  1056.     if Assigned(SearchThread) then
  1057.       SearchThread.Terminate;
  1058.   end;
  1059. end;
  1060.  
  1061. procedure TFindFile.DoFileMatch(const Folder: String;
  1062.   const FileInfo: TSearchRec);
  1063. begin
  1064.   if not Aborted and Assigned(fOnFileMatch) then
  1065.     fOnFileMatch(Self, Folder, FileInfo);
  1066. end;
  1067.  
  1068. function TFindFile.DoFolderChange(const Folder: String): Boolean;
  1069. var
  1070.   IgnoreIt: Boolean;
  1071. begin
  1072.   IgnoreIt := Aborted;
  1073.   if not Aborted and Assigned(fOnFolderChange) then
  1074.     fOnFolderChange(Self, Folder, IgnoreIt);
  1075.   Result := not IgnoreIt;
  1076. end;
  1077.  
  1078. procedure TFindFile.DoSearchBegin;
  1079. begin
  1080.   if Assigned(fOnSearchBegin) then
  1081.     fOnSearchBegin(Self);
  1082. end;
  1083.  
  1084. procedure TFindFile.DoSearchFinish;
  1085. begin
  1086.   if Assigned(fOnSearchFinish) and not (csDestroying in ComponentState) then
  1087.     fOnSearchFinish(Self);
  1088. end;
  1089.  
  1090. procedure TFindFile.DoSearchAbort;
  1091. begin
  1092.   if Assigned(fOnSearchAbort) and not (csDestroying in ComponentState) then
  1093.     fOnSearchAbort(Self);
  1094. end;
  1095.  
  1096. procedure TFindFile.SearchForFiles;
  1097.  
  1098.   procedure SearchIn(const Path: String);
  1099.   var
  1100.     SR: TSearchRec;
  1101.     MaskIndex: Integer;
  1102.   begin
  1103.     if not DoFolderChange(Path) then Exit;
  1104.     with ActiveTargetFolder do
  1105.     begin
  1106.       // Searches in the current folder for all file masks
  1107.       MaskIndex := FileMasks.Count;
  1108.       while not Aborted and (MaskIndex > 0) do
  1109.       begin
  1110.         Dec(MaskIndex);
  1111.         if not Aborted and (FindFirst(Path + FileMasks[MaskIndex], ValidFileAttr, SR) = 0) then
  1112.         begin
  1113.           repeat
  1114.             if (SR.Name <> '.') and (SR.Name <> '..') and IsAcceptable(Path, SR) then
  1115.               DoFileMatch(Path, SR);
  1116.           until Aborted or (FindNext(SR) <> 0);
  1117.           FindClose(SR);
  1118.         end;
  1119.       end;
  1120.       // Searches in subfolders
  1121.       if Subfolders then
  1122.       begin
  1123.         if not Aborted and (FindFirst(Path + '*.*', ValidFileAttr, SR) = 0) then
  1124.         begin
  1125.           repeat
  1126.             if ((SR.Attr and faDirectory) = faDirectory) and
  1127.                (SR.Name <> '.') and (SR.Name <> '..')
  1128.             then
  1129.               SearchIn(Path + SR.Name + '\');
  1130.           until Aborted or (FindNext(SR) <> 0);
  1131.           FindClose(SR);
  1132.         end;
  1133.       end;
  1134.     end;
  1135.   end;
  1136.  
  1137. var
  1138.   Index: Integer;
  1139. begin
  1140.   Index := TargetSearch.TargetFolders.Count;
  1141.   while not Aborted and (Index > 0) do
  1142.   begin
  1143.     Dec(Index);
  1144.     ActiveTargetFolder := TargetSearch.TargetFolders[Index];
  1145.     SearchIn(ActiveTargetFolder.Folder);
  1146.   end;
  1147. end;
  1148.  
  1149. procedure TFindFile.InitializeSearch;
  1150. begin
  1151.   fBusy := True;
  1152.   fAborted := False;
  1153.   TargetSearch := TTargetSearch.Create(Criteria);
  1154.   DoSearchBegin;
  1155. end;
  1156.  
  1157. procedure TFindFile.FinalizeSearch;
  1158. begin
  1159.   DoSearchFinish;
  1160.   TargetSearch.Free;
  1161.   fBusy := False;
  1162. end;
  1163.  
  1164. procedure TFindFile.Execute;
  1165. begin
  1166.   if not Busy then
  1167.   begin
  1168.     InitializeSearch;
  1169.     if Threaded then
  1170.       SearchThread := TSearchThread.Create(Self)
  1171.     else
  1172.     begin
  1173.       SearchForFiles;
  1174.       FinalizeSearch;
  1175.     end;
  1176.   end;
  1177. end;
  1178.  
  1179. function TFindFile.IsAcceptable(const Folder: String; const SR: TSearchRec): Boolean;
  1180. begin
  1181.   Result := TargetSearch.Matches(Folder, SR)
  1182. end;
  1183.  
  1184. procedure TFindFile.ThreadTerminated(Sender: TObject);
  1185. begin
  1186.   SearchThread := nil;
  1187.   FinalizeSearch;
  1188. end;
  1189.  
  1190. procedure TFindFile.SetCriteria(Value: TSearchCriteria);
  1191. begin
  1192.   Criteria.Assign(Value);
  1193. end;
  1194.  
  1195. end.
  1196.  
  1197.  
  1198.