Board index » delphi » Sort files in TFileListBox by date

Sort files in TFileListBox by date

As I got no satisfying answer I'll have another try:
Is there a possibilty to sort files by date. A TFileOpenDialog doesn't help
because I want do use the files internally. The files don't have to be drawn
to the FileListBox, I only used it to receive the files of a specific
directory (so actually, it is not visible!) - I thought of using TSearchRec
but I find it quite difficult to understand.

Thanx & Ciao,
Stefan.
cerasus.development

 

Re:Sort files in TFileListBox by date


Hi Stefan,

Here is sample of what I once did.
It defines a new type of TStringlist which 'misuses' the
Objects part for holding the filetimestamp and has a
'special' sorting routine for ascending/descending sort
on these filetimestamps.

HTH
- Pieter

type
  { New type of TStringList which can sort ascending and
    descending.  It is specialized in sorting on filedates
    which are/must be stored in the objects part of the list}
  TNewStringList = class(TStringList)
  private
    FSortDir : integer;
  protected
    function  CompareFileDates(Idx1, Idx2: integer): integer;
    procedure BSort(L, R: integer);
  public
    procedure Sort; override;
    property SortDir: integer read FSortDir write FSortDir default 0;
  end;

{ TNewStringList }
function TNewStringList.CompareFileDates(Idx1, Idx2: integer): integer;
begin
  if integer(Objects[Idx1]) > integer(Objects[Idx2]) then
    Result := 1
  else if integer(Objects[Idx1]) < integer(Objects[Idx2]) then
    Result := -1
  else
    Result := 0;
end;

{It's not the fastest sorting algorithm but I couldn't get
the quicksort to function with it at the moment}
procedure TNewStringList.BSort(L, R: integer);
var
  I, P : integer;
begin
  if SortDir = 0 then // ascending
  begin
    repeat
      P := L;
      for I := L to R do
        if CompareFileDates( I, P ) > 0 then
          P := I;
      if P <> R then
        Exchange(P, R);
      Dec(R);
    until R <= L;
  end
  else                // descending
  begin
    repeat
      P := L;
      for I := L to R do
        if CompareFileDates( I, P ) < 0 then
          P := I;
      if P <> R then
        Exchange(P, R);
      Dec(R);
    until R <= L
  end;
end;

procedure TNewStringList.Sort;
begin
  if not Sorted and (Count > 1) then
  begin
    Changing;
    BSort(0, Count - 1);
    Changed;
  end;
end;

procedure GetAllFiles(Items: TStrings; SearchSpecs: string; Attr: word);
var
  SearchRec: TSearchRec;
begin
  if FindFirst(SearchSpecs, faAnyFile, SearchRec)=0 then
    repeat
      if (SearchRec.Name <> '.')  and
         (SearchRec.Name <> '..') and
         ((SearchRec.Attr and Attr) <> 0) then
        Items.AddObject(SearchRec.Name, TObject(SearchRec.Time));
    until FindNext(SearchRec) <> 0;
  FindClose(SearchRec);
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  Cnt       : integer;
  FileList  : TNewStringList;
  ADateTime : TDateTime;
begin
  FileList := TNewStringList.Create;
  try
    GetAllFiles(FileList, '*.*', faArchive);
    FileList.SortDir := 1; // 0=ascending 1=descending
    FileList.Sort;

    Memo1.Clear;
    for Cnt := 0 to FileList.Count-1 do
    begin
      ADateTime := FileDateToDateTime(integer(FileList.Objects[Cnt]));
      Memo1.Lines.Add(
        Format('%-20s %20s', [FileList[Cnt],
FormatDateTime('c',ADateTime)]));
    end;
  finally
    FileList.Free;
  end;
end;

Quote
Stefan Roeck wrote:
> As I got no satisfying answer I'll have another try:
> Is there a possibilty to sort files by date. A TFileOpenDialog doesn't
help
> because I want do use the files internally. The files don't have to be
drawn
> to the FileListBox, I only used it to receive the files of a specific
> directory (so actually, it is not visible!) - I thought of using
TSearchRec
> but I find it quite difficult to understand.

> Thanx & Ciao,
> Stefan.
> cerasus.development

Re:Sort files in TFileListBox by date


Hello Pieter,
thanks for your code, it really works fine. My problem now is that I want to
sort files not by date of creation but by last file access. I try to use
ftLastAccessTime-Property as following:

Items.AddObject(SearchRec.Name,
TObject(SearchRec.FindData.ftLastAccessTime));

But the raises a "invalid conversion"-error(or sth. like that, I have it in
German). Why can't I store the FileTime as an object in the list and how do
I have to convert it to make it working?

Thanks,
Ciao, Stefan.
cerasus.development

Pieter Zijlstra <pzijlstr...@freeler.nl> schrieb in im Newsbeitrag:
84bhon$de...@forums.borland.com...

Quote
> Hi Stefan,

> Here is sample of what I once did.
> It defines a new type of TStringlist which 'misuses' the
> Objects part for holding the filetimestamp and has a
> 'special' sorting routine for ascending/descending sort
> on these filetimestamps.

> HTH
> - Pieter

> type
>   { New type of TStringList which can sort ascending and
>     descending.  It is specialized in sorting on filedates
>     which are/must be stored in the objects part of the list}
>   TNewStringList = class(TStringList)
>   private
>     FSortDir : integer;
>   protected
>     function  CompareFileDates(Idx1, Idx2: integer): integer;
>     procedure BSort(L, R: integer);
>   public
>     procedure Sort; override;
>     property SortDir: integer read FSortDir write FSortDir default 0;
>   end;

> { TNewStringList }
> function TNewStringList.CompareFileDates(Idx1, Idx2: integer): integer;
> begin
>   if integer(Objects[Idx1]) > integer(Objects[Idx2]) then
>     Result := 1
>   else if integer(Objects[Idx1]) < integer(Objects[Idx2]) then
>     Result := -1
>   else
>     Result := 0;
> end;

> {It's not the fastest sorting algorithm but I couldn't get
> the quicksort to function with it at the moment}
> procedure TNewStringList.BSort(L, R: integer);
> var
>   I, P : integer;
> begin
>   if SortDir = 0 then // ascending
>   begin
>     repeat
>       P := L;
>       for I := L to R do
>         if CompareFileDates( I, P ) > 0 then
>           P := I;
>       if P <> R then
>         Exchange(P, R);
>       Dec(R);
>     until R <= L;
>   end
>   else                // descending
>   begin
>     repeat
>       P := L;
>       for I := L to R do
>         if CompareFileDates( I, P ) < 0 then
>           P := I;
>       if P <> R then
>         Exchange(P, R);
>       Dec(R);
>     until R <= L
>   end;
> end;

> procedure TNewStringList.Sort;
> begin
>   if not Sorted and (Count > 1) then
>   begin
>     Changing;
>     BSort(0, Count - 1);
>     Changed;
>   end;
> end;

> procedure GetAllFiles(Items: TStrings; SearchSpecs: string; Attr: word);
> var
>   SearchRec: TSearchRec;
> begin
>   if FindFirst(SearchSpecs, faAnyFile, SearchRec)=0 then
>     repeat
>       if (SearchRec.Name <> '.')  and
>          (SearchRec.Name <> '..') and
>          ((SearchRec.Attr and Attr) <> 0) then
>         Items.AddObject(SearchRec.Name, TObject(SearchRec.Time));
>     until FindNext(SearchRec) <> 0;
>   FindClose(SearchRec);
> end;

> procedure TForm1.Button1Click(Sender: TObject);
> var
>   Cnt       : integer;
>   FileList  : TNewStringList;
>   ADateTime : TDateTime;
> begin
>   FileList := TNewStringList.Create;
>   try
>     GetAllFiles(FileList, '*.*', faArchive);
>     FileList.SortDir := 1; // 0=ascending 1=descending
>     FileList.Sort;

>     Memo1.Clear;
>     for Cnt := 0 to FileList.Count-1 do
>     begin
>       ADateTime := FileDateToDateTime(integer(FileList.Objects[Cnt]));
>       Memo1.Lines.Add(
>         Format('%-20s %20s', [FileList[Cnt],
> FormatDateTime('c',ADateTime)]));
>     end;
>   finally
>     FileList.Free;
>   end;
> end;

> Stefan Roeck wrote:
> > As I got no satisfying answer I'll have another try:
> > Is there a possibilty to sort files by date. A TFileOpenDialog doesn't
> help
> > because I want do use the files internally. The files don't have to be
> drawn
> > to the FileListBox, I only used it to receive the files of a specific
> > directory (so actually, it is not visible!) - I thought of using
> TSearchRec
> > but I find it quite difficult to understand.

> > Thanx & Ciao,
> > Stefan.
> > cerasus.development

Re:Sort files in TFileListBox by date


SearchRec.FindData.ftLastAccessTime is actually a structure containing
two dwords this will not fit in the Objects part of the stringlist.
There are two things you can do write your own type of (string)list
capable of storing other info as Objects (for a sample see VCL source
files) or convert the ftLastAccessTime to the DOS type of  filetimestamp.

This can be done by replacing the GetAllFiles procedure with this:

procedure GetAllFiles(Items: TStrings; SearchSpecs: string; Attr: word);
var
  SearchRec: TSearchRec;
  wDate    : word;
  wTime    : word;
begin
  if FindFirst(SearchSpecs, faAnyFile, SearchRec)=0 then
    repeat
      if (SearchRec.Name <> '.')  and
         (SearchRec.Name <> '..') and
         ((SearchRec.Attr and Attr) <> 0) then
        if not FileTimeToDosDateTime( SearchRec.FindData.ftLastAccessTime,
Date, wTime) then
          MessageDlg('Ooops', mtWarning, [mbOk], 0)
        else
          Items.AddObject(SearchRec.Name, TObject(wTime or (wDate shl 16)));
    until FindNext(SearchRec) <> 0;
  FindClose(SearchRec);
end;

Note:
On my system (currently win95) it does only give the date
of the last file access (time always 23:00). I'm not sure what
it does on other OS's.

- Pieter

Stefan Roeck wrote

Quote
> Hello Pieter,
> thanks for your code, it really works fine. My problem now is that I want
to
> sort files not by date of creation but by last file access. I try to use
> ftLastAccessTime-Property as following:

> Items.AddObject(SearchRec.Name,

TObject(SearchRec.FindData.ftLastAccessTime));
Quote

> But the raises a "invalid conversion"-error(or sth. like that, I have it
in
> German). Why can't I store the FileTime as an object in the list and how
do
> I have to convert it to make it working?

> Thanks,
> Ciao, Stefan.
> cerasus.development

Other Threads