DBF - acessar dbf sem bde

Top  Previous  Next

sunit DBF;

(* ===========================================================================

 * dbf.dcu - tDBF : A custom data set which uses a flat binary

 *             structured datafile for single client usage only.

 *

 * Author:  Horacio Jamilis

 * Copyright (C) 1998, Terabyte Computacion

 *

 * ===========================================================================

 * v 0.91

 * - Fixed error on deleting records

 * - Added filtering capabilities (work wrong when there are no records within

 *   the filter expresion - Only support expresion with one field like

 *   "NUMFIELD>10or "TEXTFIELD<='TEST'or "DATEFIELD=19980626"

 *   (in yyyymmdd format)).

 * ===========================================================================

 * FOR C++ Builder users:

 * Use the file named DBF_C instead of this one.

 * Especial thanks to Michael Beauregard (Michael_Beauregard@mck.com).

 * ===========================================================================

 *)

 

interface

 

uses

  SysUtils, Classes, Db, DsgnIntf;

 

type

  TFilenameProperty = class(TStringProperty)

  public

    procedure Edit; override;

    function GetAttributes: TPropertyAttributes; override;

  end;

 

  EDBFError = class (Exception);

 

  pDateTime = ^TDateTime;

  pBoolean = ^Boolean;

  pInteger = ^Integer;

 

  PRecInfo = ^TRecInfo;

  TRecInfo = record

    Bookmark: Longint;

    BookmarkFlag: TBookmarkFlag;

  end;

 

  TdbfHeader = record  { Dbase III + header definition        }

     VersionNumber    :byte;  { version number (03h or 83h ) }

     LastUpdateYear   :byte;  { last update YY MM DD         }

     LastUpdateMonth  :byte;

     LastUpdateDay    :byte;

     NumberOfRecords  :longint; { number of record in database }

     BytesInHeader    :smallint;{ number of bytes in header }

     BytesInRecords   :smallint;{ number of bytes in records }

     ReservedInHeader :array[1..20of char;   { reserved bytes in header }

  end;

 

  TdbfField = record

     FieldName   :array[1..11of char; { Name of this record             }

     FieldType   :char;           { type of record - C,N,D,L,etc.         }

     fld_addr    :longint;        { not used }

     Width       :byte;           { total field width of this record      }

     Decimals    :byte;           { number of digits to right of decimal  }

     MultiUser   :smallint;       { reserved for multi user }

     WorkAreaID  :byte;           { Work area ID }

     MUser       :smallint;       { reserved for multi_user }

     SetFields   :byte;           { SET_FIELDS flag }

     Reserved    :array[1..4of byte;      { 8 bytes reserved }

  end;                           { record starts                         }

 

Type

  pRecordHeader = ^tRecordHeader;

  tRecordHeader = record

    DeletedFlag : char;

  end;

 

type

  TDBF = class(TDataSet)

  protected

    FStream: TStream; // the physical table

    FTableName: string; // table path and file name

    fDBFHeader : TdbfHeader;

    // record data

    fRecordHeaderSize : Integer;   // The size of the record header

    FRecordCount,                  // current number of record

    FRecordSize,                   // the size of the actual data

    FRecordBufferSize,             // data + housekeeping (TRecInfo)

    FRecordInfoOffset,             // offset of RecInfo in record buffer

    FCurrentRecord,                // current record (0 to FRecordCount - 1)

    BofCrack,                      // before the first record (crack)

    EofCrack: Integer;             // after the last record (crack)

    FIsTableOpen: Boolean;         // status

    FFileWidth,                    // field widths in record

    FFileDecimals,                 // field decimals in record

    FFileOffset: TList;            // field offsets in record

    fReadOnly : Boolean;           // Enhancements

    fStartData : Integer;          // Position in file where data starts

    function FFieldType(F : char):TFieldType;

    function FFieldSize(FType:char;FWidth:integer):integer;

  protected

    // TDataSet virtual abstract method

    function AllocRecordBuffer: PChar; override;

    procedure FreeRecordBuffer(var Buffer: PChar); override;

    procedure GetBookmarkData(Buffer: PChar; Data: Pointer); override;

    function GetBookmarkFlag(Buffer: PChar): TBookmarkFlag; override;

    function GetFieldData(Field: TField; Buffer: Pointer): Boolean; override;

    function GetRecord(Buffer: PChar; GetMode: TGetMode; DoCheck: Boolean): TGetResult; override;

    function GetRecordSize: Word; override;

    procedure InternalAddRecord(Buffer: Pointer; Append: Boolean); override;

    procedure InternalClose; override;

    procedure InternalDelete; override;

    procedure InternalFirst; override;

    procedure InternalGotoBookmark(Bookmark: Pointer); override;

    procedure InternalHandleException; override;

    procedure InternalInitFieldDefs; override;

    procedure InternalInitRecord(Buffer: PChar); override;

    procedure InternalLast; override;

    procedure InternalOpen; override;

    procedure InternalPost; override;

    procedure InternalSetToRecord(Buffer: PChar); override;

    function IsCursorOpen: Boolean; override;

    procedure SetBookmarkFlag(Buffer: PChar; Value: TBookmarkFlag); override;

    procedure SetBookmarkData(Buffer: PChar; Data: Pointer); override;

    procedure SetFieldData(Field: TField; Buffer: Pointer); override;

    // TDataSet virtual method (optional)

    function GetRecordCount: Integer; override;

    procedure SetRecNo(Value: Integer); override;

    function GetRecNo: Integer; override;

    Procedure WriteHeader;

  private

    Procedure _ReadRecord(Buffer:PChar;IntRecNum:Integer);

    Procedure _WriteRecord(Buffer:PChar;IntRecNum:Integer);

    Procedure _AppendRecord(Buffer:PChar);

    Procedure _SwapRecords(Rec1,REc2:Integer);

    Function _CompareRecords(SortFields:Array of String;Rec1,Rec2:Integer):Integer;

    Function _ProcessFilter(Buffer:PChar):boolean;

  public

    constructor Create(AOwner:tComponent); override;

    procedure CreateTable;

    Procedure PackTable;

    Procedure SortTable(SortFields : Array of String);

    Procedure UnsortTable;

  published

    property TableName: string read FTableName write FTableName;

    property ReadOnly : Boolean read fReadOnly write fReadonly default False;

    property DBFHeader : tDBFHeader read fDBFHeader;

    // redeclared data set properties

    property Active;

    property Filter;

    property Filtered;

    property BeforeOpen;

    property AfterOpen;

    property BeforeClose;

    property AfterClose;

    property BeforeInsert;

    property AfterInsert;

    property BeforeEdit;

    property AfterEdit;

    property BeforePost;

    property AfterPost;

    property BeforeCancel;

    property AfterCancel;

    property BeforeDelete;

    property AfterDelete;

    property BeforeScroll;

    property AfterScroll;

    property OnCalcFields;

    property OnDeleteError;

    property OnEditError;

    property OnNewRecord;

    property OnPostError;

  end;

 

procedure Register;

 

implementation

 

uses

  TypInfo, Dialogs, Windows, Forms, Controls, IniFiles;

 

Const

  dfhVersionNumber = 13;

 

TYPE

  PBufArray = ^BufArray;

  BufArray = Array[0..0of Char;

 

// ****************************************************************************

// Low Level Routines for accessing an internal record

 

// ____________________________________________________________________________

// TDBF._ReadRecord

Procedure TDBF._ReadRecord(Buffer:PChar;IntRecNum:Integer);

  {-Read a record based on the internal record number (absolute)}

BEGIN

  FStream.Position := FStartData + (FRecordSize * IntRecNum);

 try

  FStream.ReadBuffer(Buffer^, FRecordSize);

 except

 end;

END;

 

// ____________________________________________________________________________

// TDBF._WriteRecord

Procedure TDBF._WriteRecord(Buffer:PChar;IntRecNum:Integer);

  {-Write a record based on the internal record number (absolute)}

BEGIN

  FStream.Position := FStartData + (FRecordSize * IntRecNum);

  FStream.WriteBuffer (Buffer^, FRecordSize);

END;

 

// ____________________________________________________________________________

// TDBF._AppendRecord

Procedure TDBF._AppendRecord(Buffer:PChar);

BEGIN

  FStream.Position := FStartData + (FRecordSize * (FRecordCount{+FDeletedCount}));

  FStream.WriteBuffer (Buffer^, FRecordSize);

END;

 

/////////////////////////////////////////////////

////// Part I:

////// Initialization, opening, and closing

/////////////////////////////////////////////////

 

// ____________________________________________________________________________

// TDBF.InternalOpen

// I: open the table/file

procedure TDBF.InternalOpen;

var

  Field : TField;

  i,j : integer;

  d : string;

begin

  // check if the file exists

  if not FileExists (FTableName) then

    raise eDBFError.Create ('Open: Table file not found');

 

  // create a stream for the file

  if fReadOnly then

    fStream := tFileStream.Create( fTableName, fmOpenRead + fmShareDenyWrite)

  else

    FStream := TFileStream.Create (FTableName, fmOpenReadWrite + fmShareExclusive);

  fStream.ReadBuffer(fDBFHeader,SizeOf(TDBFHeader));

 

  // sets cracks and record position

  BofCrack := -1;

  EofCrack := fRecordCount{+fDeletedCount};

  FCurrentRecord := BofCrack;

 

  // set the bookmark size

  BookmarkSize := sizeOf (Integer);

 

  if not (assigned(FFileOffset)) then

    FFileOffset := TList.Create;

  if not (assigned(FFileWidth)) then

    FFileWidth := TList.Create;

  if not (assigned(FFileDecimals)) then

    FFileDecimals := TList.Create;

 

  // initialize the field definitions

  // (another virtual abstract method of TDataSet)

  InternalInitFieldDefs;

 

  FRecordInfoOffset := FRecordSize;

  FRecordBufferSize := FRecordSize + sizeof (TRecInfo);

 

  // if there are no persistent field objects,

  // create the fields dynamically

  if DefaultFields then

    CreateFields;

  // connect the TField objects with the actual fields

  BindFields (True);

 

  for i := 0 to FieldCount-1 do

    begin

      Field := Fields[i];

      if (Field.DataType = ftFloat) and (Integer(FFileDecimals[i])>0) then

        begin

          d := '0.';

          for j := 1 to Integer(FFileDecimals[i]) do

            d := d + '0';

          (Field as TFloatField).DisplayFormat := d;

        end;

    end;

 

  // get the number of records and check size

  fRecordCount := fDBFHeader.NumberOfRecords;

 

  // everything OK: table is now open

  FIsTableOpen := True;

 

  // ShowMessage ('InternalOpen: RecCount: ' + IntToStr (FRecordCount));

end;

 

// Returns the Type of the field

function TDBF.FFieldType(F : char):TFieldType;

begin

  if F = 'C' then

    FFieldType := ftString

  else if (F = 'N'or (F = 'F') then

    FFieldType := ftFloat

  else if F = 'L' then

    FFieldType := ftBoolean

  else if F = 'D' then

    FFieldType := ftDate

//    FFieldType := ftString

  else

    FFieldType := ftUnknown;

end;

 

function TDBF.FFieldSize(FType:char;FWidth:integer):integer;

begin

  if FType = 'C' then

    FFieldSize := FWidth

  else if (FType = 'N'or (FType = 'F') then

    FFieldSize := 0

  else if FType = 'L' then

    FFieldSize := 0

  else if FType = 'D' then

    FFieldSize := 0

//    FFieldSize := 8

  else

    FFieldSize := 0;

end;

 

// ____________________________________________________________________________

// TDBF.InternalInitFieldDefs

// I: define the fields

procedure TDBF.InternalInitFieldDefs;

var

  Il : Integer;

  TmpFileOffset : Integer;

  NumberOfFields : integer;

  Fld : TDBFField;

  FldName : PChar;

begin

  FieldDefs.Clear;

  FStream.Seek(SizeOf(TDbfHeader),soFromBeginning);

  NumberOfFields := ((fDbfHeader.BytesInHeader-sizeof(DbfHeader))div 32);

  if not (assigned(FFileOffset)) then

    FFileOffset := TList.Create;

  FFileOffset.Clear;

  if not (assigned(FFileWidth)) then

    FFileWidth := TList.Create;

  FFileWidth.Clear;

  if not (assigned(FFileDecimals)) then

    FFileDecimals := TList.Create;

  FFileDecimals.Clear;

  TmpFileOffset := 0;

  if (NumberOfFields>0) then

    begin

      for Il:=0 to NumberOfFields-1 do

        begin

          FStream.Read(Fld,SizeOf(Fld));

          GetMem(FldName,Length(Fld.FieldName)+1);

          CharToOem(PChar(@Fld.FieldName),FldName);

          TFieldDef.Create(FieldDefs, FldName,FFieldType(Fld.FieldType){DescribF.DataType},

                           FFieldSize(Fld.FieldType,Fld.Width){DescribF.Size},False,Il+1);

          FreeMem(FldName);

          FFileOffset.Add(Pointer(TmpFileOffset));

          FFileWidth.Add(Pointer(Fld.Width));

          FFileDecimals.Add(Pointer(Fld.Decimals));

          Inc(tmpFileOffset,Fld.Width);

        end;

      fRecordSize := tmpFileOffset+FrecordHeaderSize;

      FStartData := FStream.Position+1;

    end;

end;

 

// ____________________________________________________________________________

// TDBF.InternalClose

// I: close the table/file

procedure TDBF.InternalClose;

begin

  // if required, save updated header

  if (fDBFHeader.NumberOfRecords <> fRecordCount) or

    (fDBFHeader.BytesInRecords = 0) then

    BEGIN

      fDBFHeader.BytesInRecords := fRecordSize;

      fDBFHeader.NumberOfRecords := fRecordCount;

      WriteHeader;

    END;

 

  // disconnet field objects

  BindFields(False);

  // destroy field object (if not persistent)

  if DefaultFields then

    DestroyFields;

 

  // free the internal list field offsets

  if Assigned(FFileOffset) then

    FFileOffset.Free;

  FFileOffset := nil;

  if Assigned(FFileWidth) then

    FFileWidth.Free;

  FFileWidth := nil;

  if Assigned(FFileDecimals) then

    FFileDecimals.Free;

  FFileDecimals := nil;

  FCurrentRecord := -1;

 

  // close the file

  FIsTableOpen := False;

  FStream.Free;

  FStream := nil;

end;

 

// ____________________________________________________________________________

// TDBF.IsCursorOpen

// I: is table open

function TDBF.IsCursorOpen: Boolean;

begin

  Result := FIsTableOpen;

end;

 

// ____________________________________________________________________________

// TDBF.WriteHeader

procedure TDBF.WriteHeader;

begin

//  Assert(FStream<>nil,'fStream=Nil');

  if fStream <> nil then

    begin

      FSTream.Seek(0,soFromBeginning);

      FStream.WriteBuffer(fDBFHeader,SizeOf(TDbfHeader));

    end;

end;

 

// ____________________________________________________________________________

// TDBF.Create

constructor TDBF.Create(AOwner:tComponent);

BEGIN

  inherited create(aOwner);

  fRecordHeaderSize := SizeOf(tRecordHeader);

END;

 

// ____________________________________________________________________________

// TDBF.CreateTable

// I: Create a new table/file

procedure TDBF.CreateTable;

var

  Ix : Integer;

//  DescribF : TBDescribField;

  Offs : Integer;

  Fld : TDbfField;

  FldName : PChar;

  i : integer;

begin

  CheckInactive;

  //  InternalInitFieldDefs;

  // create the new file

  if FileExists (FTableName) and

    (MessageDlg ('File ' + FTableName +

      ' already exists. OK to override?',

      mtConfirmation, [mbYes, mbNo], 0) = mrNo) then

    Exit;

  if FieldDefs.Count = 0 then

  begin

    for Ix := 0 to FieldCount - 1 do

    begin

      with Fields[Ix] do

      begin

        if FieldKind = fkData then

          FieldDefs.Add(FieldName,DataType,Size,Required);

      end;

    end;

  end;

  FStream := TFileStream.Create (FTableName,

    fmCreate or fmShareExclusive);

  try

    FillChar(fDBFHeader,SizeOf(TDbfHeader),0);

    fDBFHeader.BytesInRecords := 0// filled later

    fDBFHeader.NumberOfRecords := 0// empty

    WriteHeader;

    Offs:=0;

    for Ix:=0 to FieldDefs.Count-1 do

      begin

        with FieldDefs.Items[Ix] do

        begin

          FillChar(Fld,SizeOf(TDbfField),#0);

          Fld.FieldType := 'C';

          Fld.Width := Size;

          GetMem(FldName,SizeOf(FieldDefs.Items[Ix].Name));

          OemToChar(PChar(FieldDefs.Items[Ix].Name),FldName);

          for i := 1 to Length(FldName) do

            Fld.FieldName[i] := FldName[i];

          Fld.FieldName[Length(FldName)+1] := #0;

          FreeMem(FldName);

          Inc(Offs,Fld.Width);

          FStream.Write(Fld,SizeOf(TDbfField));

        end;

      end;

    fStartData := FStream.Position;

    fDBFHeader.BytesInRecords := Offs;

    FRecordSize := Offs+FRecordHeaderSize;

    WriteHeader;

  finally

    // close the file

    fStream.Free;

    fStream := nil;

  end;

end;

 

// ____________________________________________________________________________

// TDBF.PackTable

//Enhancement: Remove all deleted items from the table.

Procedure TDBF.PackTable;

var

  NewStream, OldStream : tStream;

  PC : PChar;

  Ix : Integer;

//  DescribF : TBDescribField;

  NewDataFileHeader : tDBFHeader;

  DataBuffer : Pointer;

  NumberOfFields : integer;

  Fld : TDBFField;

BEGIN

  OldStream := Nil;

  NewStream := Nil;

  CheckInactive;

//  if Active then

//    raise eBinaryDataSetError.Create ('Dataset must be closed before packing.');

  if fTableName = '' then

    raise EDBFError.Create('Table name not specified.');

  if not FileExists (FTableName) then

    raise EDBFError.Create('Table '+fTableName+' does not exist.');

  PC := @fTablename[1];

  CopyFile(PChar(PC),PChar(PC+',old'+#0),False);

  // create the new file

  if FieldDefs.Count = 0 then

  begin

    for Ix := 0 to FieldCount - 1 do

    begin

      with Fields[Ix] do

      begin

        if FieldKind = fkData then

          FieldDefs.Add(FieldName,DataType,Size,Required);

      end;

    end;

  end;

  TRY

    NewStream := TFileStream.Create (FTableName+',new',

      fmCreate or fmShareExclusive);

    OldStream := tFileStream.Create (fTableName+',old',

      fmOpenRead or fmShareExclusive);

    OldStream.ReadBuffer(NewDataFileHeader,SizeOf(TDbfHeader));

    NewStream.WriteBuffer(NewDataFileHeader,SizeOf(TDbfHeader));

    NumberOfFields := ((NewDataFileHeader.BytesInHeader-sizeof(TDbfHeader))div 32);

    for IX := 0 to NumberOfFields do

      BEGIN

        OldStream.Read(Fld,SizeOf(TDbfField));

        NewStream.Write(Fld,SizeOf(TDbfField));

      END;

    GetMem(DataBuffer,NewDataFileHeader.BytesInRecords);

    REPEAT

      IX := OldStream.Read(DataBuffer^,NewDataFileHeader.BytesInRecords);

      if (IX = NewDataFileHeader.BytesInRecords) and (pRecordHeader(DataBuffer)^.DeletedFlag <> '*') then

        NewStream.WRite(DataBuffer^,NewDataFileHeader.BytesInRecords);

    Until IX <> NewDataFileHeader.BytesInRecords;

    FreeMem(DataBuffer,NewDataFileHeader.BytesInRecords);

  finally

    // close the file

    NewStream.Free;

    OldStream.Free;

  end;

  CopyFile(PChar(PC+',new'+#0),PChar(PC),False);

  DeleteFile(Pchar(PC+',new'+#0));

  DeleteFile(Pchar(PC+',old'+#0));

END;

 

// ____________________________________________________________________________

// TDBF._SwapRecords

// Enhancement: Quick swap of two records.  Used primarily for sorting.

Procedure TDBF._SwapRecords(Rec1,REc2:Integer);

VAR

  Buffer1, Buffer2 : PChar;

  Bookmark1, BOokmark2 : TBookmarkFlag;

BEGIN

  Rec1 := Rec1 - 1;

  Rec2 := Rec2 - 1;

  if Rec1 < 0 then Exit;

  if Rec2 < 0 then Exit;

  Buffer1 := AllocRecordBuffer;

  Buffer2 := AllocRecordBuffer;

  _ReadRecord(Buffer1,Rec1);

  _ReadRecord(Buffer2,Rec2);

  Bookmark1 := GetBookmarkFlag(Buffer1);

  Bookmark2 := GetBookmarkFlag(Buffer2);

  SetBookmarkFlag(Buffer1,Bookmark2);

  SetBookmarkFlag(Buffer2,Bookmark1);

  _WriteRecord(Buffer1,Rec2);

  _WriteRecord(Buffer2,Rec1);

  StrDispose(Buffer1);

  StrDispose(Buffer2);

END;

 

// ____________________________________________________________________________

// TDBF._CompareRecords

// Compare two records.  Returns -1 if REC1 < REC2, 0 if REC1 = REC2, or

// 1 if REC1 > REC2.

Function TDBF._CompareRecords(SortFields:Array of String;Rec1,Rec2:Integer):Integer; FAR;

{-Compare the records Rec1, Rec2 and return -1 if Rec1 < Rec2, 0 if Rec1 = Rec2,

  1 if Rec1 > Rec2 }

VAR

  IX : Integer;

 

  Function CompareHelper(KeyId:String;Rec1,Rec2:Integer):Integer;

  VAR

    SKey1, SKey2 : String;

    IKey1, IKey2 : Integer;

    fKey1, fKey2 : Double;

    dKey1, dKey2 : tDateTime;

    CompareType : tFieldType;

    KeyField : tField;

  BEGIN

    KeyField := FieldByName(KeyID);

    CompareType := KeyField.DataType;

    Case CompareType of

      ftFloat,

      ftCurrency,

      ftBCD :

        BEGIN

          _ReadRecord(ActiveBuffer,Rec1-1);

          fKey1 := KeyField.AsFloat;

          _ReadRecord(ActiveBuffer,Rec2-1);

          fKey2 := KeyField.AsFloat;

          if fKey1 < fKey2 then

            Result := -1

          else

            if fKey1 > fKey2 then

              Result := 1

            else

              Result := 0;

        END;

      ftSmallInt,

      ftInteger,

      ftWord :

        BEGIN

          _ReadRecord(ActiveBuffer,Rec1-1);

          IKey1 := KeyField.AsInteger;

          _ReadRecord(ActiveBuffer,Rec2-1);

          IKey2 := KeyField.AsInteger;

          if IKey1 < IKey2 then

            Result := -1

          else

            if IKey1 > IKey2 then

              Result := 1

            else

              Result := 0;

        END;

      ftDate,

      ftTime,

      ftDateTime :

        BEGIN

          _ReadRecord(ActiveBuffer,Rec1-1);

          dKey1 := KeyField.AsDateTime;

          _ReadRecord(ActiveBuffer,Rec2-1);

          dKey2 := KeyField.AsDateTime;

          if dKey1 < dKey2 then

            Result := -1

          else

            if dKey1 > dKey2 then

              Result := 1

            else

              Result := 0;

        END;

      else

        BEGIN

          _ReadRecord(ActiveBuffer,Rec1-1);

          SKey1 := KeyField.AsString;

          _ReadRecord(ActiveBuffer,Rec2-1);

          SKey2 := KeyField.AsString;

          if SKey1 < SKey2 then

            Result := -1

          else

            if SKey1 > SKey2 then

              Result := 1

            else

              Result := 0;

        END;

    END;

  END;

 

BEGIN

  IX := 0;

  REPEAT // Loop through all available sortfields until not equal or no more sort fiels.

    Result := CompareHelper(SortFields[IX],Rec1,Rec2);

    Inc(IX);

  UNTIL (Result <> 0or (IX > High(SortFields));

END;

 

 

// ____________________________________________________________________________

// TDBF.SortTable

// Enhancement: Sort the table by the fields passed.

Procedure TDBF.SortTable(SortFields : Array of String);

 

  { This is the main sorting routine. It is passed the number of elements and the

    two callback routines. The first routine is the function that will perform

    the comparison between two elements. The second routine is the procedure that

    will swap two elements if necessary } // Source: UNDU #8

 

  procedure QSort(uNElem: Integer);

  { uNElem - number of elements to sort }

 

    procedure qSortHelp(pivotP: Integer; nElem: word);

    label

      TailRecursion,

      qBreak;

    var

      leftP, rightP, pivotEnd, pivotTemp, leftTemp: word;

      lNum: Integer;

      retval: integer;

    begin

      TailRecursion:

        if (nElem <= 2) then

          begin

            if (nElem = 2) then

 

              begin

                rightP := pivotP +1;

                if (_CompareRecords(SortFields,pivotP, rightP) > 0) then

                  _SwapRecords(pivotP, rightP);

              end;

            exit;

          end;

        rightP := (nElem -1) + pivotP;

        leftP :=  (nElem shr 1) + pivotP;

        { sort pivot, left, and right elements for "median of 3" }

        if (_CompareRecords(SortFields,leftP, rightP) > 0) then _SwapRecords(leftP, rightP);

        if (_CompareRecords(SortFields,leftP, pivotP) > 0) then _SwapRecords(leftP, pivotP)

 

        else if (_CompareRecords(SortFields,pivotP, rightP) > 0) then _SwapRecords(pivotP, rightP);

        if (nElem = 3) then

          begin

            _SwapRecords(pivotP, leftP);

            exit;

          end;

        { now for the classic Horae algorithm }

        pivotEnd := pivotP + 1;

        leftP := pivotEnd;

        repeat

          retval := _CompareRecords(SortFields,leftP, pivotP);

          while (retval <= 0) do

            begin

              if (retval = 0) then

                begin

                  _SwapRecords(LeftP, PivotEnd);

                  Inc(PivotEnd);

                end;

              if (leftP < rightP) then

                Inc(leftP)

              else

                goto qBreak;

              retval := _CompareRecords(SortFields,leftP, pivotP);

            end{while}

          while (leftP < rightP) do

            begin

 

              retval := _CompareRecords(SortFields,pivotP, rightP);

              if (retval < 0) then

                Dec(rightP)

              else

                begin

                  _SwapRecords(leftP, rightP);

                  if (retval <> 0) then

                    begin

                      Inc(leftP);

                      Dec(rightP);

                    end;

                  break;

                end;

            end{while}

 

        until (leftP >= rightP);

      qBreak:

        if (_CompareRecords(SortFields,leftP, pivotP) <= 0) then Inc(leftP);

        leftTemp := leftP -1;

        pivotTemp := pivotP;

        while ((pivotTemp < pivotEnd) and (leftTemp >= pivotEnd)) do

          begin

            _SwapRecords(pivotTemp, leftTemp);

            Inc(pivotTemp);

            Dec(leftTemp);

          end{while}

        lNum := (leftP - pivotEnd);

        nElem := ((nElem + pivotP) -leftP);

 

        if (nElem < lNum) then

          begin

            qSortHelp(leftP, nElem);

            nElem := lNum;

          end

        else

          begin

            qSortHelp(pivotP, lNum);

            pivotP := leftP;

          end;

        goto TailRecursion;

      end{qSortHelp }

 

  begin

    if (uNElem < 2) then  exit{ nothing to sort }

    qSortHelp(1, uNElem);

  end{ QSort }

 

 

BEGIN

  CheckActive;

  if fReadOnly then

    raise eDBFError.Create ('Dataset must be opened for read/write to perform sort.');

//  if fDataFileHeader.DeletedCount > 0 then

//    BEGIN

//      Close;

//      PackTable;

//      Open;

//    END;

  QSort(FRecordCount {+ fDeletedCount});

  First;

END;

 

// ____________________________________________________________________________

// TDBF.UnsortTable

// Used to help test the sort routine.  Attempts to generate a random

// dispersment of the records in the dataset.

Procedure TDBF.UnsortTable;

Var

  IX : Integer;

BEGIN

  First;

  Randomize;

  for IX := 0 to RecordCOunt do

    BEGIN

      _SwapRecords(IX,Random(RecordCount+1));

    END;

  First;

END;

 

////////////////////////////////////////

////// Part II:

////// Bookmarks management and movement

////////////////////////////////////////

 

// ____________________________________________________________________________

// TDBF.InternalGotoBookmark

// II: set the requested bookmark as current record

procedure TDBF.InternalGotoBookmark (Bookmark: Pointer);

var

  ReqBookmark: Integer;

begin

  ReqBookmark := PInteger (Bookmark)^;

//  ShowMessage ('InternalGotoBookmark: ' +

//    IntToStr (ReqBookmark));

  if (ReqBookmark >= 0and (ReqBookmark < FRecordCount {+ fDeletedCount}) then

    FCurrentRecord := ReqBookmark

  else

    raise eDBFError.Create ('Bookmark ' +

      IntToStr (ReqBookmark) + ' not found');

end;

 

// ____________________________________________________________________________

// TDBF.InternalSetToRecord

// II: same as above (but passes a buffer)

procedure TDBF.InternalSetToRecord (Buffer: PChar);

var

  ReqBookmark: Integer;

begin

//  ShowMessage ('InternalSetToRecord');

  ReqBookmark := PRecInfo(Buffer + FRecordInfoOffset).Bookmark;

  InternalGotoBookmark (@ReqBookmark);

end;

 

// ____________________________________________________________________________

// TDBF.GetBookmarkFlag

// II: retrieve bookmarks flags from buffer

function TDBF.GetBookmarkFlag (

  Buffer: PChar): TBookmarkFlag;

begin

//  ShowMessage ('GetBookmarkFlag');

  Result := PRecInfo(Buffer + FRecordInfoOffset).BookmarkFlag;

end;

 

// ____________________________________________________________________________

// TDBF.SetBookmarkFlag

// II: change the bookmark flags in the buffer

procedure TDBF.SetBookmarkFlag (Buffer: PChar;

  Value: TBookmarkFlag);

begin

//  ShowMessage ('SetBookmarkFlag');

  PRecInfo(Buffer + FRecordInfoOffset).BookmarkFlag := Value;

end;

 

// ____________________________________________________________________________

// TDBF.GetBookmarkData

// II: read the bookmark data from record buffer

procedure TDBF.GetBookmarkData (

  Buffer: PChar; Data: Pointer);

begin

//  ShowMessage ('GetBookmarkData: ' + IntToStr (PRecInfo(Buffer + FRecordInfoOffset).Bookmark));

  PInteger(Data)^ :=

    PRecInfo(Buffer + FRecordInfoOffset).Bookmark;

end;

 

// ____________________________________________________________________________

// TDBF.SetBookmarkData

// II: set the bookmark data in the buffer

procedure TDBF.SetBookmarkData (

  Buffer: PChar; Data: Pointer);

begin

//  ShowMessage ('SetBookmarkData: ' + IntToStr (PInteger(Data)^));

  PRecInfo(Buffer + FRecordInfoOffset).Bookmark :=

    PInteger(Data)^;

end;

 

// ____________________________________________________________________________

// TDBF.InternalFirst

// II: Go to a special position before the first record

procedure TDBF.InternalFirst;

begin

  FCurrentRecord := BofCrack;

end;

 

// ____________________________________________________________________________

// TDBF.InternalLast

// II: Go to a special position after the last record

procedure TDBF.InternalLast;

begin

  EofCrack := FRecordCount {+ fDeletedCount};

  FCurrentRecord := EofCrack;

end;

 

// ____________________________________________________________________________

// TDBF.GetRecordCount

// II (optional): Record count

function TDBF.GetRecordCount: Longint;

begin

  CheckActive;

  Result := FRecordCount;

end;

 

// ____________________________________________________________________________

// TDBF.GetRecNo

// II (optional): Get the number of the current record

function TDBF.GetRecNo: Longint;

begin

  UpdateCursorPos;

  if FCurrentRecord < 0 then

    Result := 1

  else

    Result := FCurrentRecord + 1;

end;

 

// ____________________________________________________________________________

// TDBF.SetRecNo

// II (optional): Move to the given record number

procedure TDBF.SetRecNo(Value: Integer);

begin

  CheckBrowseMode;

  if (Value > 1and (Value <= (FRecordCount{+FDeletedCount})) then

  begin

    FCurrentRecord := Value - 1;

    Resync([]);

  end;

end;

 

//////////////////////////////////////////

////// Part III:

////// Record buffers and field management

//////////////////////////////////////////

 

// ____________________________________________________________________________

// TDBF.GetRecordSize

/// III: Determine the size of each record buffer in memory

function TDBF.GetRecordSize: Word;

begin

  Result := FRecordSize; // data only

end;

 

// ____________________________________________________________________________

// TDBF.AllocRecordBuffer

/// III: Allocate a buffer for the record

function TDBF.AllocRecordBuffer: PChar;

begin

  Result := StrAlloc(FRecordBufferSize+1);

end;

 

// ____________________________________________________________________________

// TDBF.InternalInitRecord

// III: Initialize the record (set to zero)

procedure TDBF.InternalInitRecord(Buffer: PChar);

(*var

  Field : TField;

  i : integer;

  FieldOffset : integer;

  S : string; *)

begin

  FillChar(Buffer^, FRecordBufferSize, 32);

(*  for i := 0 to FieldCount-1 do

    begin

      Field := Fields[i];

      FieldOffset := Integer(FFileOffset[Field.FieldNo-1])+FRecordHeaderSize;

      if Field.DataType = ftString then

        begin

          pChar(Buffer+FieldOffset)^ := #0;

        end

      else if Field.DataType = ftFloat then

        begin

          pChar(Buffer+FieldOffset)^ := '0';

          pChar(Buffer+FieldOffset+1)^ := #0;

        end

      else if Field.DataType = ftDate then

        begin

          S := '19900101';

          CopyMemory(PChar(Buffer+FieldOffset),PChar(S),8);

        end

      else if Field.DataType = ftBoolean then

        begin

          pChar(Buffer+FieldOffset)^ := 'F';

        end;

    end; *)

end;

 

// ____________________________________________________________________________

// TDBF.FreeRecordBuffer

// III: Free the buffer

procedure TDBF.FreeRecordBuffer (var Buffer: PChar);

begin

  StrDispose(Buffer);

end;

 

// ____________________________________________________________________________

// TDBF.GetRecord

// III: Retrieve data for current, previous, or next record

// (eventually moving to it) and return the status

function TDBF.GetRecord(Buffer: PChar;

  GetMode: TGetMode; DoCheck: Boolean): TGetResult;

var

  Acceptable : Boolean;

begin

  result := grOk;

  if FRecordCount < 1 then

    Result := grEOF

  else

    repeat

      case GetMode of

        gmCurrent :

          begin

            // ShowMessage ('GetRecord Current');

            if (FCurrentRecord >= FRecordCount{+fDeletedCount}or

                (FCurrentRecord < 0) then

              Result := grError;

          end;

        gmNext :

          begin

            if (fCurrentRecord < (fRecordCount{+fDeletedCount})-1) then

              Inc (FCurrentRecord)

            else

              Result := grEOF;

          end;

        gmPrior :

          begin

           if (fCurrentRecord > 0) then

              Dec(fCurrentRecord)

           else

              Result := grBOF;

          end;

      end;

      // fill record data area of buffer

      if Result = grOK then

        begin

          _ReadRecord(Buffer, fCurrentRecord );

          {FStream.Position := FDataFileHeader.StartData +

          FRecordSize * FCurrentRecord;

          FStream.ReadBuffer (Buffer^, FRecordSize);}

          ClearCalcFields(Buffer);

          GetCalcFields(Buffer);

          with PRecInfo(Buffer + FRecordInfoOffset)^ do

            begin

              BookmarkFlag := bfCurrent;

              Bookmark := FCurrentRecord;

            end;

        end

      else

        if (Result = grError) and DoCheck then

          raise eDBFError.Create('GetRecord: Invalid record');

      Acceptable := pRecordHeader(Buffer)^.DeletedFlag <> '*';

      if Filtered then

        Acceptable := Acceptable and (_ProcessFilter(Buffer));

      if (GetMode=gmCurrent) and Not Acceptable then

        Result := grError;

    until (Result <> grOK) or Acceptable;

  if ((Result=grEOF)or(Result=grBOF)) and Filtered and not (_ProcessFilter(Buffer)) then

    Result := grError;

end;

 

// ____________________________________________________________________________

// TDBF.InternalPost

// III: Write the current data to the file

procedure TDBF.InternalPost;

begin

  CheckActive;

  if State = dsEdit then

    begin

      // replace data with new data

      {FStream.Position := FDataFileHeader.StartData + (FRecordSize * FCurrentRecord);

      FStream.WriteBuffer (ActiveBuffer^, FRecordSize);}

      _WriteRecord (ActiveBuffer, fCurrentRecord);

    end

  else

    begin

      // always append

      InternalLast;

      {FStream.Seek (0, soFromEnd);

      FStream.WriteBuffer (ActiveBuffer^, FRecordSize);}

      pRecordHeader(ActiveBuffer)^.DeletedFlag := ' ';

      _AppendRecord(ActiveBuffer);

      Inc (FRecordCount);

    end;

end;

 

// ____________________________________________________________________________

// TDBF.InternalAddRecord

// III: Add the current data to the file

procedure TDBF.InternalAddRecord(Buffer:Pointer; Append:Boolean);

begin

  // always append

  InternalLast;

  // add record at the end of the file

  {FStream.Seek (0, soFromEnd);}

  pRecordHeader(ActiveBuffer)^.DeletedFlag := ' ';

  _AppendRecord(ActiveBuffer);

  {FStream.WriteBuffer (ActiveBuffer^, FRecordSize);}

  Inc (FRecordCount);

end;

 

// ____________________________________________________________________________

// TDBF.InternalDelete

// III: Delete the current record

procedure TDBF.InternalDelete;

begin

  CheckActive;

  // not supported in this version

{  raise eBinaryDataSetError.Create (

    'Delete: Operation not supported');}

//  pRecordHeader(ActiveBuffer)^.DeletedFlag := fDataFileHeader.LastDeleted;

  PChar(ActiveBuffer)^ := '*';

  _WriteRecord(ActiveBuffer,fCurrentRecord);

  {FStream.Position := FDataFileHeader.StartData + (FRecordSize * FCurrentRecord);

  FStream.WriteBuffer (ActiveBuffer^, FRecordSize);}

//  fDBFHeader.LastDeleted := GetRecNo;

//  Inc(fDeletedCount);

//  Dec(fRecordCount);

//  fDBFHeader.NumberOfRecords := fRecordCount;

//  WriteHeader;

  Resync([]);

end;

 

// ____________________________________________________________________________

// TDBF.GetFieldData

// III: Move data from record buffer to field

function TDBF.GetFieldData(Field:TField; Buffer:Pointer):Boolean;

var

  FieldOffset: Integer;

  S : string;

  Buf2 : PChar;

  i,l : integer;

  D : Double;

  n : integer;

  T : TDateTime;

  j : integer;

  OldDateFormat : string;

begin

  Result := False;

  Buf2 := ActiveBuffer;

  if (FRecordCount>0and (Field.FieldNo > 0and (Assigned(Buffer)) and (Assigned(Buf2))  then

    begin

      FieldOffset := Integer(FFileOffset[Field.FieldNo-1])+FRecordHeaderSize;

      if Field.DataType = ftString then

        begin

          l := Integer(FFileWidth[Field.FieldNo-1]);

          S := '';

          i := 0;

          While (Buf2[FieldOffset+i] <> #0and (i<l) do

            begin

              S := S+pChar(Buf2+FieldOffset+i)^;

              inc(i);

            end;

          SetLength(S,l);

          S := Trim(S);

          CharToOemBuff(PChar(S), Buffer,l);

          Result := True;

        end

      else if Field.DataType = ftFloat then

        begin

          n := Integer(FFileWidth[Field.FieldNo-1]);

          S := '';

          for i := FieldOffset to FieldOffset+n-1 do

            S := S+pChar(Buf2+i)^;

          S := Trim(S);

          if S='' then

            Result := False

          else

            begin

              if (Pos('.',S) > 0and (DecimalSeparator <> '.') then

                S[Pos('.',S)] := DecimalSeparator;

              Result := True;

             try

              D := StrToFloat(S);

             except

              D := 0;

              Result := False;

             end;

              PDouble(Buffer)^ := D;

            end;

        end

      else if Field.DataType = ftDate then

        begin

          S := '';

          for j := 0 to 7 do

            S := S + pChar(Buf2+FieldOffset+j);

          SetLength(S,8);

          if (trim(S) = ''or (S='00000000') then

            Result := false

          else

            begin

              S := Copy(S,7,2)+DateSeparator+Copy(S,5,2)+DateSeparator+Copy(S,1,4);

              OldDateFormat := ShortDateFormat;

              ShortDateFormat := 'dd/mm/yyyy';

              t := StrToDate(S);

              ShortDateFormat := OldDateFormat;

              j := Trunc(pDouble(@t)^)+693594;

              pInteger(Buffer)^ := j;

              result := True;

            end;

        end

      else if Field.DataType = ftBoolean then

        begin

          Result := True;

          if PChar(Buf2+FieldOffset)^ in ['S','T','Y'] then

            pBoolean(Buffer)^ := True

          else if PChar(Buf2+FieldOffset)^ in ['N','F'] then

            pBoolean(Buffer)^ := False

          else

            Result := False;

        end

      else

        begin

          ShowMessage ('very bad error in get field data');

          Result := False;

        end;

    end;

end;

 

// ____________________________________________________________________________

// TDBF.SetFieldData

// III: Move data from field to record buffer

procedure TDBF.SetFieldData(Field: TField; Buffer: Pointer);

var

  FieldOffset: Integer;

  Buf2 : PChar;

  l,i,n:integer;

  S : string;

  D : TDateTime;

  j : integer;

begin

  Buf2 := ActiveBuffer;

  if (Field.FieldNo >= 0and (Assigned(Buffer)) and (Assigned(Buf2)) then

    begin

      FieldOffset := Integer(FFileOffset[Field.FieldNo-1])+FRecordHeaderSize;

      if Field.DataType = ftString then

        begin

          l := Integer(FFileWidth[Field.FieldNo-1]);

          S := '';

          i := 0;

          While (PChar(Buffer)[i] <> #0and (i<l) do

            begin

              S := S+PChar(Buffer)[i];

              inc(i);

            end;

          SetLength(S,l);

          OemToCharBuff(PChar(S),PChar(Buf2+FieldOffset),l);

        end

      else if Field.DataType = ftFloat then

        begin

          n := Integer(FFileWidth[Field.FieldNo-1]);

          Str(pDouble(Buffer)^:n:Integer(FFileDecimals[Field.FieldNo-1]),S);

          while Length(S)<n do

            S :=  ' '+S;

          if (Pos(DecimalSeparator,S) > 0and (DecimalSeparator <> '.') then

            S[Pos(DecimalSeparator,S)] := '.';

          CopyMemory(Pchar(Buf2+FieldOffset),PChar(S),n);

        end

      else if Field.DataType = ftDate then

        begin

          j := pInteger(Buffer)^-693594;

          pDouble(@d)^ := j;

          S := FormatDateTime('yyyymmdd',d);

          StrLCopy(pChar(Buf2+FieldOffset),pChar(S),8);

        end

      else if Field.DataType = ftBoolean then

        begin

          if pBoolean(Buffer)^ then

            PChar(Buf2+FieldOffset)^ := 'T'

          else

            PChar(Buf2+FieldOffset)^ := 'F';

        end

      else

        ShowMessage ('very bad error in setfield data');

      DataEvent (deFieldChange, Longint(Field));

    end;

end;

 

// ____________________________________________________________________________

// TDBF.InternalHandleException

// default exception handling

procedure TDBF.InternalHandleException;

begin

  // standard exception handling

  Application.HandleException(Self);

end;

 

Function TDBF._ProcessFilter(Buffer:PChar):boolean;

var

  FilterExpresion : string;

  PosComp : integer;

  FName : string;

  FieldPos : integer;

  FieldOffset : integer;

  FieldValue : Variant;

  TestValue : Variant;

  FieldText : string;

  OldShortDateFormat : string;

begin

  FilterExpresion := Filter;

  PosComp := Pos('>',FilterExpresion);

  if PosComp=0 then

    PosComp := Pos('<',FilterExpresion);

  if PosComp=0 then

    PosComp := Pos('=',FilterExpresion);

  if PosComp=0 then

    begin

      _ProcessFilter := True;

      Exit;

    end;

  FName := Trim(Copy(FilterExpresion,1,PosComp-1));

  FieldPos := FieldDefs.IndexOf(FName);

  FieldOffset := integer(FFileOffset[FieldPos]);

  if FieldPos < 0 then

    _ProcessFilter := True

  else if FieldDefs.Items[FieldPos].DataType = ftString then

    begin // STRING

     try

      FieldValue := '';

      FieldOffset := FieldOffset+1;

      While (Buffer[FieldOffset]<>#0and (Length(FieldValue)<integer(FFileWidth[FieldPos])) do

        begin

          FieldValue := FieldValue + Buffer[FieldOffset];

          FieldOffset := FieldOffset+1;

        end;

      FieldValue := Trim(FieldValue);

      TestValue := Trim(Copy(FilterExpresion,PosComp+2,Length(FilterExpresion)-PosComp-2));

      if FilterExpresion[PosComp]='=' then

        _ProcessFilter := (FieldValue=TestValue)

      else if FilterExpresion[PosComp]='>' then

        begin

          if FilterExpresion[PosComp+1]='=' then

            _ProcessFilter := (FieldValue>=Copy(TestValue,2,(Length(TestValue)-1)))

          else

            _ProcessFilter := (FieldValue>TestValue);

        end

      else if FilterExpresion[PosComp]='<' then

        begin

          if FilterExpresion[PosComp+1]='=' then

            _ProcessFilter := (FieldValue<=Copy(TestValue,2,(Length(TestValue)-1)))

          else

            _ProcessFilter := (FieldValue<TestValue);

        end

      else

        _ProcessFilter := False;

     except

       _ProcessFilter := False;

     end;

    end

  else if FieldDefs.Items[FieldPos].DataType = ftFloat then

    begin // FLOAT

     try

      FieldText := '';

      FieldOffset := FieldOffset+1;

      While (Buffer[FieldOffset]<>#0and (Length(FieldText)<integer(FFileWidth[FieldPos])) do

        begin

          FieldText := FieldText + Buffer[FieldOffset];

          FieldOffset := FieldOffset+1;

        end;

      FieldText := Trim(FieldText);

      if Pos('.',FieldText)>0 then

        FieldText[Pos('.',FieldText)] := DecimalSeparator;

      FieldValue := StrToFloat(FieldText);

      if FilterExpresion[PosComp+1]='='then

        FieldText := Trim(Copy(FilterExpresion,PosComp+2,Length(FilterExpresion)-PosComp-1))

      else

        FieldText := Trim(Copy(FilterExpresion,PosComp+1,Length(FilterExpresion)-PosComp));

      if Pos('.',FieldText)>0 then

        FieldText[Pos('.',FieldText)] := DecimalSeparator;

      TestValue := StrToFloat(FieldText);

      if FilterExpresion[PosComp]='=' then

        _ProcessFilter := (FieldValue=TestValue)

      else if FilterExpresion[PosComp]='>'then

        begin

          if FilterExpresion[PosComp+1]='='then

            _ProcessFilter := (FieldValue>=TestValue)

          else

            _ProcessFilter := (FieldValue>TestValue);

        end

      else if FilterExpresion[PosComp]='<'then

        begin

          if FilterExpresion[PosComp+1]='='then

            _ProcessFilter := (FieldValue<=TestValue)

          else

            _ProcessFilter := (FieldValue<TestValue);

        end

      else

        _ProcessFilter := False;

     except

      _ProcessFilter := False;

     end;

    end

  else if FieldDefs.Items[FieldPos].DataType = ftDate then

    begin // DATE

      OldShortDateFormat := ShortDateFormat;

     try

      FieldText := '';

      FieldOffset := FieldOffset+1;

      While (Buffer[FieldOffset]<>#0and (Length(FieldText)<integer(FFileWidth[FieldPos])) do

        begin

          FieldText := FieldText + Buffer[FieldOffset];

          FieldOffset := FieldOffset+1;

        end;

      FieldText := Trim(FieldText);

      FieldText := Copy(FieldText,1,4)+DateSeparator+Copy(FieldText,5,2)+DateSeparator+Copy(FieldText,7,2);

      ShortDateFormat := 'yyyy/mm/dd';

      FieldValue := StrToDate(FieldText);

      if FilterExpresion[PosComp+1]='=' then

        FieldText := Trim(Copy(FilterExpresion,PosComp+2,Length(FilterExpresion)-PosComp-1))

      else

        FieldText := Trim(Copy(FilterExpresion,PosComp+1,Length(FilterExpresion)-PosComp));

      FieldText := Copy(FieldText,1,4)+DateSeparator+Copy(FieldText,5,2)+DateSeparator+Copy(FieldText,7,2);

      TestValue := StrToDate(FieldText);

      if FilterExpresion[PosComp]='=' then

        begin

          _ProcessFilter := (FieldValue=TestValue);

        end

      else if FilterExpresion[PosComp]='>' then

        begin

          if FilterExpresion[PosComp+1]='='then

            _ProcessFilter := (FieldValue>=TestValue)

          else

            _ProcessFilter := (FieldValue>TestValue);

        end

      else if FilterExpresion[PosComp]='<' then

        begin

          if FilterExpresion[PosComp+1]='='then

            _ProcessFilter := (FieldValue<=TestValue)

          else

            _ProcessFilter := (FieldValue<TestValue);

        end

      else

        _ProcessFilter := False;

     except

      _ProcessFilter := False;

     end;

      ShortDateFormat := OldShortDateFormat;

    end

  else

    _ProcessFilter := False;

end;

 

{******************************************************************************}

{* Property Editors Code                                                       *}

{******************************************************************************}

 

procedure TFilenameProperty.Edit;

var

  FileOpen: TOpenDialog;

begin

  FileOpen := TOpenDialog.Create(Nil);

  FileOpen.Filename := GetValue;

  FileOpen.Filter := 'dBase Files (*.DBF)|*.DBF|All Files (*.*)|*.*';

  FileOpen.Options := FileOpen.Options + [ofPathMustExist, ofFileMustExist];

  try

    if FileOpen.Execute then SetValue(FileOpen.Filename);

  finally

    FileOpen.Free;

  end;

end;

 

function TFilenameProperty.GetAttributes: TPropertyAttributes;

begin

  Result := [paDialog, paRevertable];

end;

 

procedure Register;

begin

  RegisterComponents('Terabyte', [TDBF]);

  RegisterPropertyEditor(TypeInfo(String), TDBF, 'TableName', TFileNameProperty);

end;

 

end.