dbf_c.pas
上传用户:hbtcygglw
上传日期:2007-01-07
资源大小:281k
文件大小:48k
源码类别:

其他

开发平台:

Delphi

  1. unit DBF_C;
  2. (* ===========================================================================
  3.  * dbf.dcu - tDBF : A custom data set which uses a flat binary
  4.  *             structured datafile for single client usage only.
  5.  *
  6.  * Author:  Horacio Jamilis
  7.  * Copyright (C) 1998, Terabyte Computacion
  8.  *
  9.  * ===========================================================================
  10.  * v 0.91
  11.  *   C++ Builder version
  12.  * - Fixed error on deleting records
  13.  * - Added filtering capabilities (work wrong when there are no records within
  14.  *   the filter expresion - Only support expresion with one field like
  15.  *   "NUMFIELD>10" or "TEXTFIELD<='TEST'" or "DATEFIELD=19980626"
  16.  *   (in yyyymmdd format))
  17.  *   the OnFilterRecord event does not work yet.
  18.  * Especial thanks to Michael Beauregard (Michael_Beauregard@mck.com).
  19.  * ===========================================================================
  20.  *)
  21. interface
  22. uses
  23.   SysUtils, Classes, Db, DsgnIntf;
  24. type
  25.   TFilenameProperty = class(TStringProperty)
  26.   public
  27.     procedure Edit; override;
  28.     function GetAttributes: TPropertyAttributes; override;
  29.   end;
  30.   EDBFError = class (Exception);
  31.   pDateTime = ^TDateTime;
  32.   pBoolean = ^Boolean;
  33.   pInteger = ^Integer;
  34.   PRecInfo = ^TRecInfo;
  35.   TRecInfo = record
  36.     Bookmark: Longint;
  37.     BookmarkFlag: TBookmarkFlag;
  38.   end;
  39.   TdbfHeader = record  { Dbase III + header definition        }
  40.      VersionNumber    :byte;  { version number (03h or 83h ) }
  41.      LastUpdateYear   :byte;  { last update YY MM DD         }
  42.      LastUpdateMonth  :byte;
  43.      LastUpdateDay    :byte;
  44.      NumberOfRecords  :longint; { number of record in database }
  45.      BytesInHeader    :smallint;{ number of bytes in header }
  46.      BytesInRecords   :smallint;{ number of bytes in records }
  47.      ReservedInHeader :array[1..20] of char;   { reserved bytes in header }
  48.   end;
  49.   TdbfField = record
  50.      FieldName   :array[1..11] of char; { Name of this record             }
  51.      FieldType   :char;           { type of record - C,N,D,L,etc.         }
  52.      fld_addr    :longint;        { not used }
  53.      Width       :byte;           { total field width of this record      }
  54.      Decimals    :byte;           { number of digits to right of decimal  }
  55.      MultiUser   :smallint;       { reserved for multi user }
  56.      WorkAreaID  :byte;           { Work area ID }
  57.      MUser       :smallint;       { reserved for multi_user }
  58.      SetFields   :byte;           { SET_FIELDS flag }
  59.      Reserved    :array[1..4] of byte;      { 8 bytes reserved }
  60.   end;                           { record starts                         }
  61. Type
  62.   pRecordHeader = ^tRecordHeader;
  63.   tRecordHeader = record
  64.     DeletedFlag : char;
  65.   end;
  66. type
  67.   TDBF = class(TDataSet)
  68.   protected
  69.     FStream: TStream; // the physical table
  70.     FTableName: string; // table path and file name
  71.     fDBFHeader : TdbfHeader;
  72.     // record data
  73.     fRecordHeaderSize : Integer;   // The size of the record header
  74.     FRecordCount,                  // current number of record
  75.     FRecordSize,                   // the size of the actual data
  76.     FRecordBufferSize,             // data + housekeeping (TRecInfo)
  77.     FRecordInfoOffset,             // offset of RecInfo in record buffer
  78.     FCurrentRecord,                // current record (0 to FRecordCount - 1)
  79.     BofCrack,                      // before the first record (crack)
  80.     EofCrack: Integer;             // after the last record (crack)
  81.     FIsTableOpen: Boolean;         // status
  82.     FFileWidth,                    // field widths in record
  83.     FFileDecimals,                 // field decimals in record
  84.     FFileOffset: TList;            // field offsets in record
  85.     fReadOnly : Boolean;           // Enhancements
  86.     fStartData : Integer;          // Position in file where data starts
  87.     function FFieldType(F : char):TFieldType;
  88.     function FFieldSize(FType:char;FWidth:integer):integer;
  89.   protected
  90.     // TDataSet virtual abstract method
  91.     function AllocRecordBuffer: PChar; override;
  92.     procedure FreeRecordBuffer(var Buffer: PChar); override;
  93.     procedure GetBookmarkData(Buffer: PChar; Data: Pointer); override;
  94.     function GetBookmarkFlag(Buffer: PChar): TBookmarkFlag; override;
  95.     function GetRecord(Buffer: PChar; GetMode: TGetMode; DoCheck: Boolean): TGetResult; override;
  96.     function GetRecordSize: Word; override;
  97.     procedure InternalAddRecord(Buffer: Pointer; Append: Boolean); override;
  98.     procedure InternalClose; override;
  99.     procedure InternalDelete; override;
  100.     procedure InternalFirst; override;
  101.     procedure InternalGotoBookmark(Bookmark: Pointer); override;
  102.     procedure InternalHandleException; override;
  103.     procedure InternalInitFieldDefs; override;
  104.     procedure InternalInitRecord(Buffer: PChar); override;
  105.     procedure InternalLast; override;
  106.     procedure InternalOpen; override;
  107.     procedure InternalPost; override;
  108.     procedure InternalSetToRecord(Buffer: PChar); override;
  109.     function IsCursorOpen: Boolean; override;
  110.     procedure SetBookmarkFlag(Buffer: PChar; Value: TBookmarkFlag); override;
  111.     procedure SetBookmarkData(Buffer: PChar; Data: Pointer); override;
  112.     procedure SetFieldData(Field: TField; Buffer: Pointer); override;
  113.     // TDataSet virtual method (optional)
  114.     function GetRecordCount: Integer; override;
  115.     procedure SetRecNo(Value: Integer); override;
  116.     function GetRecNo: Integer; override;
  117.     Procedure WriteHeader;
  118.   private
  119.     Procedure _ReadRecord(Buffer:PChar;IntRecNum:Integer);
  120.     Procedure _WriteRecord(Buffer:PChar;IntRecNum:Integer);
  121.     Procedure _AppendRecord(Buffer:PChar);
  122.     Procedure _SwapRecords(Rec1,REc2:Integer);
  123.     Function _CompareRecords(SortFields:Array of String;Rec1,Rec2:Integer):Integer;
  124.     Function _ProcessFilter(Buffer:PChar):boolean;
  125.   public
  126.     constructor Create(AOwner:tComponent); override;
  127.     procedure CreateTable;
  128.     Procedure PackTable;
  129.     Procedure SortTable(SortFields : Array of String);
  130.     Procedure UnsortTable;
  131.     function GetFieldData(Field: TField; Buffer: Pointer): Boolean; override;
  132.   published
  133.     property TableName: string read FTableName write FTableName;
  134.     property ReadOnly : Boolean read fReadOnly write fReadonly default False;
  135.     property DBFHeader : tDBFHeader read fDBFHeader;
  136.     // redeclared data set properties
  137.     property Active;
  138.     property Filter;
  139.     property Filtered;
  140.     property BeforeOpen;
  141.     property AfterOpen;
  142.     property BeforeClose;
  143.     property AfterClose;
  144.     property BeforeInsert;
  145.     property AfterInsert;
  146.     property BeforeEdit;
  147.     property AfterEdit;
  148.     property BeforePost;
  149.     property AfterPost;
  150.     property BeforeCancel;
  151.     property AfterCancel;
  152.     property BeforeDelete;
  153.     property AfterDelete;
  154.     property BeforeScroll;
  155.     property AfterScroll;
  156.     property OnCalcFields;
  157.     property OnDeleteError;
  158.     property OnEditError;
  159.     property OnNewRecord;
  160.     property OnPostError;
  161.   end;
  162. procedure Register;
  163. implementation
  164. uses
  165.   TypInfo, Dialogs, Windows, Forms, Controls, IniFiles;
  166. Const
  167.   dfhVersionNumber = 13;
  168. TYPE
  169.   PBufArray = ^BufArray;
  170.   BufArray = Array[0..0] of Char;
  171. // ****************************************************************************
  172. // Low Level Routines for accessing an internal record
  173. // ____________________________________________________________________________
  174. // TDBF._ReadRecord
  175. Procedure TDBF._ReadRecord(Buffer:PChar;IntRecNum:Integer);
  176.   {-Read a record based on the internal record number (absolute)}
  177. BEGIN
  178.   FStream.Position := FStartData + (FRecordSize * IntRecNum);
  179.  try
  180.   FStream.ReadBuffer(Buffer^, FRecordSize);
  181.  except
  182.  end;
  183. END;
  184. // ____________________________________________________________________________
  185. // TDBF._WriteRecord
  186. Procedure TDBF._WriteRecord(Buffer:PChar;IntRecNum:Integer);
  187.   {-Write a record based on the internal record number (absolute)}
  188. BEGIN
  189.   FStream.Position := FStartData + (FRecordSize * IntRecNum);
  190.   FStream.WriteBuffer (Buffer^, FRecordSize);
  191. END;
  192. // ____________________________________________________________________________
  193. // TDBF._AppendRecord
  194. Procedure TDBF._AppendRecord(Buffer:PChar);
  195. BEGIN
  196.   FStream.Position := FStartData + (FRecordSize * (FRecordCount{+FDeletedCount}));
  197.   FStream.WriteBuffer (Buffer^, FRecordSize);
  198. END;
  199. /////////////////////////////////////////////////
  200. ////// Part I:
  201. ////// Initialization, opening, and closing
  202. /////////////////////////////////////////////////
  203. // ____________________________________________________________________________
  204. // TDBF.InternalOpen
  205. // I: open the table/file
  206. procedure TDBF.InternalOpen;
  207. var
  208.   Field : TField;
  209.   i,j : integer;
  210.   d : string;
  211. begin
  212.   // check if the file exists
  213.   if not FileExists (FTableName) then
  214.     raise eDBFError.Create ('Open: Table file not found');
  215.   // create a stream for the file
  216.   if fReadOnly then
  217.     fStream := tFileStream.Create( fTableName, fmOpenRead + fmShareDenyWrite)
  218.   else
  219.     FStream := TFileStream.Create (FTableName, fmOpenReadWrite + fmShareExclusive);
  220.   fStream.ReadBuffer(fDBFHeader,SizeOf(TDBFHeader));
  221.   // sets cracks and record position
  222.   BofCrack := -1;
  223.   EofCrack := fRecordCount{+fDeletedCount};
  224.   FCurrentRecord := BofCrack;
  225.   // set the bookmark size
  226.   BookmarkSize := sizeOf (Integer);
  227.   if not (assigned(FFileOffset)) then
  228.     FFileOffset := TList.Create;
  229.   if not (assigned(FFileWidth)) then
  230.     FFileWidth := TList.Create;
  231.   if not (assigned(FFileDecimals)) then
  232.     FFileDecimals := TList.Create;
  233.   // initialize the field definitions
  234.   // (another virtual abstract method of TDataSet)
  235.   InternalInitFieldDefs;
  236.   FRecordInfoOffset := FRecordSize;
  237.   FRecordBufferSize := FRecordSize + sizeof (TRecInfo);
  238.   // if there are no persistent field objects,
  239.   // create the fields dynamically
  240.   if DefaultFields then
  241.     CreateFields;
  242.   // connect the TField objects with the actual fields
  243.   BindFields (True);
  244.   for i := 0 to FieldCount-1 do
  245.     begin
  246.       Field := Fields[i];
  247.       if (Field.DataType = ftFloat) and (Integer(FFileDecimals[i])>0) then
  248.         begin
  249.           d := '0.';
  250.           for j := 1 to Integer(FFileDecimals[i]) do
  251.             d := d + '0';
  252.           (Field as TFloatField).DisplayFormat := d;
  253.         end;
  254.     end;
  255.   // get the number of records and check size
  256.   fRecordCount := fDBFHeader.NumberOfRecords;
  257.   // everything OK: table is now open
  258.   FIsTableOpen := True;
  259.   // ShowMessage ('InternalOpen: RecCount: ' + IntToStr (FRecordCount));
  260. end;
  261. // Returns the Type of the field
  262. function TDBF.FFieldType(F : char):TFieldType;
  263. begin
  264.   if F = 'C' then
  265.     FFieldType := ftString
  266.   else if (F = 'N') or (F = 'F') then
  267.     FFieldType := ftFloat
  268.   else if F = 'L' then
  269.     FFieldType := ftBoolean
  270.   else if F = 'D' then
  271.     FFieldType := ftDate
  272. //    FFieldType := ftString
  273.   else
  274.     FFieldType := ftUnknown;
  275. end;
  276. function TDBF.FFieldSize(FType:char;FWidth:integer):integer;
  277. begin
  278.   if FType = 'C' then
  279.     FFieldSize := FWidth
  280.   else if (FType = 'N') or (FType = 'F') then
  281.     FFieldSize := 0
  282.   else if FType = 'L' then
  283.     FFieldSize := 0
  284.   else if FType = 'D' then
  285.     FFieldSize := 0
  286. //    FFieldSize := 8
  287.   else
  288.     FFieldSize := 0;
  289. end;
  290. // ____________________________________________________________________________
  291. // TDBF.InternalInitFieldDefs
  292. // I: define the fields
  293. procedure TDBF.InternalInitFieldDefs;
  294. var
  295.   Il : Integer;
  296.   TmpFileOffset : Integer;
  297.   NumberOfFields : integer;
  298.   Fld : TDBFField;
  299.   FldName : PChar;
  300.   NewFieldDef : TFieldDef;
  301. begin
  302.   FieldDefs.Clear;
  303.   FStream.Seek(SizeOf(TDbfHeader),soFromBeginning);
  304.   NumberOfFields := ((fDbfHeader.BytesInHeader-sizeof(DbfHeader))div 32);
  305.   if not (assigned(FFileOffset)) then
  306.     FFileOffset := TList.Create;
  307.   FFileOffset.Clear;
  308.   if not (assigned(FFileWidth)) then
  309.     FFileWidth := TList.Create;
  310.   FFileWidth.Clear;
  311.   if not (assigned(FFileDecimals)) then
  312.     FFileDecimals := TList.Create;
  313.   FFileDecimals.Clear;
  314.   TmpFileOffset := 0;
  315.   if (NumberOfFields>0) then
  316.     begin
  317.       for Il:=0 to NumberOfFields-1 do
  318.         begin
  319.           FStream.Read(Fld,SizeOf(Fld));
  320.           GetMem(FldName,Length(Fld.FieldName)+1);
  321.           CharToOem(PChar(@Fld.FieldName),FldName);
  322.           NewFieldDef := TFieldDef.Create(FieldDefs);
  323.           NewFieldDef.DataType := FFieldType(Fld.FieldType);
  324.           NewFieldDef.Size := FFieldSize(Fld.FieldType,Fld.Width);
  325.           NewFieldDef.Name := FldName;
  326.           NewFieldDef.FieldNo := Il+1;
  327.           NewFieldDef.Required := FALSE;
  328.           FreeMem(FldName);
  329.           FFileOffset.Add(Pointer(TmpFileOffset));
  330.           FFileWidth.Add(Pointer(Fld.Width));
  331.           FFileDecimals.Add(Pointer(Fld.Decimals));
  332.           Inc(tmpFileOffset,Fld.Width);
  333.         end;
  334.       fRecordSize := tmpFileOffset+FrecordHeaderSize;
  335.       FStartData := FStream.Position+1;
  336.     end;
  337. end;
  338. // ____________________________________________________________________________
  339. // TDBF.InternalClose
  340. // I: close the table/file
  341. procedure TDBF.InternalClose;
  342. begin
  343.   // if required, save updated header
  344.   if (fDBFHeader.NumberOfRecords <> fRecordCount) or
  345.     (fDBFHeader.BytesInRecords = 0) then
  346.     BEGIN
  347.       fDBFHeader.BytesInRecords := fRecordSize;
  348.       fDBFHeader.NumberOfRecords := fRecordCount;
  349.       WriteHeader;
  350.     END;
  351.   // disconnet field objects
  352.   BindFields(False);
  353.   // destroy field object (if not persistent)
  354.   if DefaultFields then
  355.     DestroyFields;
  356.   // free the internal list field offsets
  357.   if Assigned(FFileOffset) then
  358.     FFileOffset.Free;
  359.   FFileOffset := nil;
  360.   if Assigned(FFileWidth) then
  361.     FFileWidth.Free;
  362.   FFileWidth := nil;
  363.   if Assigned(FFileDecimals) then
  364.     FFileDecimals.Free;
  365.   FFileDecimals := nil;
  366.   FCurrentRecord := -1;
  367.   // close the file
  368.   FIsTableOpen := False;
  369.   FStream.Free;
  370.   FStream := nil;
  371. end;
  372. // ____________________________________________________________________________
  373. // TDBF.IsCursorOpen
  374. // I: is table open
  375. function TDBF.IsCursorOpen: Boolean;
  376. begin
  377.   Result := FIsTableOpen;
  378. end;
  379. // ____________________________________________________________________________
  380. // TDBF.WriteHeader
  381. procedure TDBF.WriteHeader;
  382. begin
  383. //  Assert(FStream<>nil,'fStream=Nil');
  384.   if fStream <> nil then
  385.     begin
  386.       FSTream.Seek(0,soFromBeginning);
  387.       FStream.WriteBuffer(fDBFHeader,SizeOf(TDbfHeader));
  388.     end;
  389. end;
  390. // ____________________________________________________________________________
  391. // TDBF.Create
  392. constructor TDBF.Create(AOwner:tComponent);
  393. BEGIN
  394.   inherited create(aOwner);
  395.   fRecordHeaderSize := SizeOf(tRecordHeader);
  396. END;
  397. // ____________________________________________________________________________
  398. // TDBF.CreateTable
  399. // I: Create a new table/file
  400. procedure TDBF.CreateTable;
  401. var
  402.   Ix : Integer;
  403. //  DescribF : TBDescribField;
  404.   Offs : Integer;
  405.   Fld : TDbfField;
  406.   FldName : PChar;
  407.   i : integer;
  408. begin
  409.   CheckInactive;
  410.   //  InternalInitFieldDefs;
  411.   // create the new file
  412.   if FileExists (FTableName) and
  413.     (MessageDlg ('File ' + FTableName +
  414.       ' already exists. OK to override?',
  415.       mtConfirmation, [mbYes, mbNo], 0) = mrNo) then
  416.     Exit;
  417.   if FieldDefs.Count = 0 then
  418.   begin
  419.     for Ix := 0 to FieldCount - 1 do
  420.     begin
  421.       with Fields[Ix] do
  422.       begin
  423.         if FieldKind = fkData then
  424.           FieldDefs.Add(FieldName,DataType,Size,Required);
  425.       end;
  426.     end;
  427.   end;
  428.   FStream := TFileStream.Create (FTableName,
  429.     fmCreate or fmShareExclusive);
  430.   try
  431.     FillChar(fDBFHeader,SizeOf(TDbfHeader),0);
  432.     fDBFHeader.BytesInRecords := 0; // filled later
  433.     fDBFHeader.NumberOfRecords := 0; // empty
  434.     WriteHeader;
  435.     Offs:=0;
  436.     for Ix:=0 to FieldDefs.Count-1 do
  437.       begin
  438.         with FieldDefs.Items[Ix] do
  439.         begin
  440.           FillChar(Fld,SizeOf(TDbfField),#0);
  441.           Fld.FieldType := 'C';
  442.           Fld.Width := Size;
  443.           GetMem(FldName,SizeOf(FieldDefs.Items[Ix].Name));
  444.           OemToChar(PChar(FieldDefs.Items[Ix].Name),FldName);
  445.           for i := 1 to Length(FldName) do
  446.             Fld.FieldName[i] := FldName[i];
  447.           Fld.FieldName[Length(FldName)+1] := #0;
  448.           FreeMem(FldName);
  449.           Inc(Offs,Fld.Width);
  450.           FStream.Write(Fld,SizeOf(TDbfField));
  451.         end;
  452.       end;
  453.     fStartData := FStream.Position;
  454.     fDBFHeader.BytesInRecords := Offs;
  455.     FRecordSize := Offs+FRecordHeaderSize;
  456.     WriteHeader;
  457.   finally
  458.     // close the file
  459.     fStream.Free;
  460.     fStream := nil;
  461.   end;
  462. end;
  463. // ____________________________________________________________________________
  464. // TDBF.PackTable
  465. //Enhancement: Remove all deleted items from the table.
  466. Procedure TDBF.PackTable;
  467. var
  468.   NewStream, OldStream : tStream;
  469.   PC : PChar;
  470.   Ix : Integer;
  471. //  DescribF : TBDescribField;
  472.   NewDataFileHeader : tDBFHeader;
  473.   DataBuffer : Pointer;
  474.   NumberOfFields : integer;
  475.   Fld : TDBFField;
  476. BEGIN
  477.   OldStream := Nil;
  478.   NewStream := Nil;
  479.   CheckInactive;
  480. //  if Active then
  481. //    raise eBinaryDataSetError.Create ('Dataset must be closed before packing.');
  482.   if fTableName = '' then
  483.     raise EDBFError.Create('Table name not specified.');
  484.   if not FileExists (FTableName) then
  485.     raise EDBFError.Create('Table '+fTableName+' does not exist.');
  486.   PC := @fTablename[1];
  487.   CopyFile(PChar(PC),PChar(PC+',old'+#0),False);
  488.   // create the new file
  489.   if FieldDefs.Count = 0 then
  490.   begin
  491.     for Ix := 0 to FieldCount - 1 do
  492.     begin
  493.       with Fields[Ix] do
  494.       begin
  495.         if FieldKind = fkData then
  496.           FieldDefs.Add(FieldName,DataType,Size,Required);
  497.       end;
  498.     end;
  499.   end;
  500.   TRY
  501.     NewStream := TFileStream.Create (FTableName+',new',
  502.       fmCreate or fmShareExclusive);
  503.     OldStream := tFileStream.Create (fTableName+',old',
  504.       fmOpenRead or fmShareExclusive);
  505.     OldStream.ReadBuffer(NewDataFileHeader,SizeOf(TDbfHeader));
  506.     NewStream.WriteBuffer(NewDataFileHeader,SizeOf(TDbfHeader));
  507.     NumberOfFields := ((NewDataFileHeader.BytesInHeader-sizeof(TDbfHeader))div 32);
  508.     for IX := 0 to NumberOfFields do
  509.       BEGIN
  510.         OldStream.Read(Fld,SizeOf(TDbfField));
  511.         NewStream.Write(Fld,SizeOf(TDbfField));
  512.       END;
  513.     GetMem(DataBuffer,NewDataFileHeader.BytesInRecords);
  514.     REPEAT
  515.       IX := OldStream.Read(DataBuffer^,NewDataFileHeader.BytesInRecords);
  516.       if (IX = NewDataFileHeader.BytesInRecords) and (pRecordHeader(DataBuffer)^.DeletedFlag <> '*') then
  517.         NewStream.WRite(DataBuffer^,NewDataFileHeader.BytesInRecords);
  518.     Until IX <> NewDataFileHeader.BytesInRecords;
  519.     FreeMem(DataBuffer,NewDataFileHeader.BytesInRecords);
  520.   finally
  521.     // close the file
  522.     NewStream.Free;
  523.     OldStream.Free;
  524.   end;
  525.   CopyFile(PChar(PC+',new'+#0),PChar(PC),False);
  526.   DeleteFile(Pchar(PC+',new'+#0));
  527.   DeleteFile(Pchar(PC+',old'+#0));
  528. END;
  529. // ____________________________________________________________________________
  530. // TDBF._SwapRecords
  531. // Enhancement: Quick swap of two records.  Used primarily for sorting.
  532. Procedure TDBF._SwapRecords(Rec1,REc2:Integer);
  533. VAR
  534.   Buffer1, Buffer2 : PChar;
  535.   Bookmark1, BOokmark2 : TBookmarkFlag;
  536. BEGIN
  537.   Rec1 := Rec1 - 1;
  538.   Rec2 := Rec2 - 1;
  539.   if Rec1 < 0 then Exit;
  540.   if Rec2 < 0 then Exit;
  541.   Buffer1 := AllocRecordBuffer;
  542.   Buffer2 := AllocRecordBuffer;
  543.   _ReadRecord(Buffer1,Rec1);
  544.   _ReadRecord(Buffer2,Rec2);
  545.   Bookmark1 := GetBookmarkFlag(Buffer1);
  546.   Bookmark2 := GetBookmarkFlag(Buffer2);
  547.   SetBookmarkFlag(Buffer1,Bookmark2);
  548.   SetBookmarkFlag(Buffer2,Bookmark1);
  549.   _WriteRecord(Buffer1,Rec2);
  550.   _WriteRecord(Buffer2,Rec1);
  551.   StrDispose(Buffer1);
  552.   StrDispose(Buffer2);
  553. END;
  554. // ____________________________________________________________________________
  555. // TDBF._CompareRecords
  556. // Compare two records.  Returns -1 if REC1 < REC2, 0 if REC1 = REC2, or
  557. // 1 if REC1 > REC2.
  558. Function TDBF._CompareRecords(SortFields:Array of String;Rec1,Rec2:Integer):Integer; FAR;
  559. {-Compare the records Rec1, Rec2 and return -1 if Rec1 < Rec2, 0 if Rec1 = Rec2,
  560.   1 if Rec1 > Rec2 }
  561. VAR
  562.   IX : Integer;
  563.   Function CompareHelper(KeyId:String;Rec1,Rec2:Integer):Integer;
  564.   VAR
  565.     SKey1, SKey2 : String;
  566.     IKey1, IKey2 : Integer;
  567.     fKey1, fKey2 : Double;
  568.     dKey1, dKey2 : tDateTime;
  569.     CompareType : tFieldType;
  570.     KeyField : tField;
  571.   BEGIN
  572.     KeyField := FieldByName(KeyID);
  573.     CompareType := KeyField.DataType;
  574.     Case CompareType of
  575.       ftFloat,
  576.       ftCurrency,
  577.       ftBCD :
  578.         BEGIN
  579.           _ReadRecord(ActiveBuffer,Rec1-1);
  580.           fKey1 := KeyField.AsFloat;
  581.           _ReadRecord(ActiveBuffer,Rec2-1);
  582.           fKey2 := KeyField.AsFloat;
  583.           if fKey1 < fKey2 then
  584.             Result := -1
  585.           else
  586.             if fKey1 > fKey2 then
  587.               Result := 1
  588.             else
  589.               Result := 0;
  590.         END;
  591.       ftSmallInt,
  592.       ftInteger,
  593.       ftWord :
  594.         BEGIN
  595.           _ReadRecord(ActiveBuffer,Rec1-1);
  596.           IKey1 := KeyField.AsInteger;
  597.           _ReadRecord(ActiveBuffer,Rec2-1);
  598.           IKey2 := KeyField.AsInteger;
  599.           if IKey1 < IKey2 then
  600.             Result := -1
  601.           else
  602.             if IKey1 > IKey2 then
  603.               Result := 1
  604.             else
  605.               Result := 0;
  606.         END;
  607.       ftDate,
  608.       ftTime,
  609.       ftDateTime :
  610.         BEGIN
  611.           _ReadRecord(ActiveBuffer,Rec1-1);
  612.           dKey1 := KeyField.AsDateTime;
  613.           _ReadRecord(ActiveBuffer,Rec2-1);
  614.           dKey2 := KeyField.AsDateTime;
  615.           if dKey1 < dKey2 then
  616.             Result := -1
  617.           else
  618.             if dKey1 > dKey2 then
  619.               Result := 1
  620.             else
  621.               Result := 0;
  622.         END;
  623.       else
  624.         BEGIN
  625.           _ReadRecord(ActiveBuffer,Rec1-1);
  626.           SKey1 := KeyField.AsString;
  627.           _ReadRecord(ActiveBuffer,Rec2-1);
  628.           SKey2 := KeyField.AsString;
  629.           if SKey1 < SKey2 then
  630.             Result := -1
  631.           else
  632.             if SKey1 > SKey2 then
  633.               Result := 1
  634.             else
  635.               Result := 0;
  636.         END;
  637.     END;
  638.   END;
  639. BEGIN
  640.   IX := 0;
  641.   REPEAT // Loop through all available sortfields until not equal or no more sort fiels.
  642.     Result := CompareHelper(SortFields[IX],Rec1,Rec2);
  643.     Inc(IX);
  644.   UNTIL (Result <> 0) or (IX > High(SortFields));
  645. END;
  646. // ____________________________________________________________________________
  647. // TDBF.SortTable
  648. // Enhancement: Sort the table by the fields passed.
  649. Procedure TDBF.SortTable(SortFields : Array of String);
  650.   { This is the main sorting routine. It is passed the number of elements and the
  651.     two callback routines. The first routine is the function that will perform
  652.     the comparison between two elements. The second routine is the procedure that
  653.     will swap two elements if necessary } // Source: UNDU #8
  654.   procedure QSort(uNElem: Integer);
  655.   { uNElem - number of elements to sort }
  656.     procedure qSortHelp(pivotP: Integer; nElem: word);
  657.     label
  658.       TailRecursion,
  659.       qBreak;
  660.     var
  661.       leftP, rightP, pivotEnd, pivotTemp, leftTemp: word;
  662.       lNum: Integer;
  663.       retval: integer;
  664.     begin
  665.       TailRecursion:
  666.         if (nElem <= 2) then
  667.           begin
  668.             if (nElem = 2) then
  669.               begin
  670.                 rightP := pivotP +1;
  671.                 if (_CompareRecords(SortFields,pivotP, rightP) > 0) then
  672.                   _SwapRecords(pivotP, rightP);
  673.               end;
  674.             exit;
  675.           end;
  676.         rightP := (nElem -1) + pivotP;
  677.         leftP :=  (nElem shr 1) + pivotP;
  678.         { sort pivot, left, and right elements for "median of 3" }
  679.         if (_CompareRecords(SortFields,leftP, rightP) > 0) then _SwapRecords(leftP, rightP);
  680.         if (_CompareRecords(SortFields,leftP, pivotP) > 0) then _SwapRecords(leftP, pivotP)
  681.         else if (_CompareRecords(SortFields,pivotP, rightP) > 0) then _SwapRecords(pivotP, rightP);
  682.         if (nElem = 3) then
  683.           begin
  684.             _SwapRecords(pivotP, leftP);
  685.             exit;
  686.           end;
  687.         { now for the classic Horae algorithm }
  688.         pivotEnd := pivotP + 1;
  689.         leftP := pivotEnd;
  690.         repeat
  691.           retval := _CompareRecords(SortFields,leftP, pivotP);
  692.           while (retval <= 0) do
  693.             begin
  694.               if (retval = 0) then
  695.                 begin
  696.                   _SwapRecords(LeftP, PivotEnd);
  697.                   Inc(PivotEnd);
  698.                 end;
  699.               if (leftP < rightP) then
  700.                 Inc(leftP)
  701.               else
  702.                 goto qBreak;
  703.               retval := _CompareRecords(SortFields,leftP, pivotP);
  704.             end; {while}
  705.           while (leftP < rightP) do
  706.             begin
  707.               retval := _CompareRecords(SortFields,pivotP, rightP);
  708.               if (retval < 0) then
  709.                 Dec(rightP)
  710.               else
  711.                 begin
  712.                   _SwapRecords(leftP, rightP);
  713.                   if (retval <> 0) then
  714.                     begin
  715.                       Inc(leftP);
  716.                       Dec(rightP);
  717.                     end;
  718.                   break;
  719.                 end;
  720.             end; {while}
  721.         until (leftP >= rightP);
  722.       qBreak:
  723.         if (_CompareRecords(SortFields,leftP, pivotP) <= 0) then Inc(leftP);
  724.         leftTemp := leftP -1;
  725.         pivotTemp := pivotP;
  726.         while ((pivotTemp < pivotEnd) and (leftTemp >= pivotEnd)) do
  727.           begin
  728.             _SwapRecords(pivotTemp, leftTemp);
  729.             Inc(pivotTemp);
  730.             Dec(leftTemp);
  731.           end; {while}
  732.         lNum := (leftP - pivotEnd);
  733.         nElem := ((nElem + pivotP) -leftP);
  734.         if (nElem < lNum) then
  735.           begin
  736.             qSortHelp(leftP, nElem);
  737.             nElem := lNum;
  738.           end
  739.         else
  740.           begin
  741.             qSortHelp(pivotP, lNum);
  742.             pivotP := leftP;
  743.           end;
  744.         goto TailRecursion;
  745.       end; {qSortHelp }
  746.   begin
  747.     if (uNElem < 2) then  exit; { nothing to sort }
  748.     qSortHelp(1, uNElem);
  749.   end; { QSort }
  750. BEGIN
  751.   CheckActive;
  752.   if fReadOnly then
  753.     raise eDBFError.Create ('Dataset must be opened for read/write to perform sort.');
  754. //  if fDataFileHeader.DeletedCount > 0 then
  755. //    BEGIN
  756. //      Close;
  757. //      PackTable;
  758. //      Open;
  759. //    END;
  760.   QSort(FRecordCount {+ fDeletedCount});
  761.   First;
  762. END;
  763. // ____________________________________________________________________________
  764. // TDBF.UnsortTable
  765. // Used to help test the sort routine.  Attempts to generate a random
  766. // dispersment of the records in the dataset.
  767. Procedure TDBF.UnsortTable;
  768. Var
  769.   IX : Integer;
  770. BEGIN
  771.   First;
  772.   Randomize;
  773.   for IX := 0 to RecordCOunt do
  774.     BEGIN
  775.       _SwapRecords(IX,Random(RecordCount+1));
  776.     END;
  777.   First;
  778. END;
  779. ////////////////////////////////////////
  780. ////// Part II:
  781. ////// Bookmarks management and movement
  782. ////////////////////////////////////////
  783. // ____________________________________________________________________________
  784. // TDBF.InternalGotoBookmark
  785. // II: set the requested bookmark as current record
  786. procedure TDBF.InternalGotoBookmark (Bookmark: Pointer);
  787. var
  788.   ReqBookmark: Integer;
  789. begin
  790.   ReqBookmark := PInteger (Bookmark)^;
  791. //  ShowMessage ('InternalGotoBookmark: ' +
  792. //    IntToStr (ReqBookmark));
  793.   if (ReqBookmark >= 0) and (ReqBookmark < FRecordCount {+ fDeletedCount}) then
  794.     FCurrentRecord := ReqBookmark
  795.   else
  796.     raise eDBFError.Create ('Bookmark ' +
  797.       IntToStr (ReqBookmark) + ' not found');
  798. end;
  799. // ____________________________________________________________________________
  800. // TDBF.InternalSetToRecord
  801. // II: same as above (but passes a buffer)
  802. procedure TDBF.InternalSetToRecord (Buffer: PChar);
  803. var
  804.   ReqBookmark: Integer;
  805. begin
  806. //  ShowMessage ('InternalSetToRecord');
  807.   ReqBookmark := PRecInfo(Buffer + FRecordInfoOffset).Bookmark;
  808.   InternalGotoBookmark (@ReqBookmark);
  809. end;
  810. // ____________________________________________________________________________
  811. // TDBF.GetBookmarkFlag
  812. // II: retrieve bookmarks flags from buffer
  813. function TDBF.GetBookmarkFlag (
  814.   Buffer: PChar): TBookmarkFlag;
  815. begin
  816. //  ShowMessage ('GetBookmarkFlag');
  817.   Result := PRecInfo(Buffer + FRecordInfoOffset).BookmarkFlag;
  818. end;
  819. // ____________________________________________________________________________
  820. // TDBF.SetBookmarkFlag
  821. // II: change the bookmark flags in the buffer
  822. procedure TDBF.SetBookmarkFlag (Buffer: PChar;
  823.   Value: TBookmarkFlag);
  824. begin
  825. //  ShowMessage ('SetBookmarkFlag');
  826.   PRecInfo(Buffer + FRecordInfoOffset).BookmarkFlag := Value;
  827. end;
  828. // ____________________________________________________________________________
  829. // TDBF.GetBookmarkData
  830. // II: read the bookmark data from record buffer
  831. procedure TDBF.GetBookmarkData (
  832.   Buffer: PChar; Data: Pointer);
  833. begin
  834. //  ShowMessage ('GetBookmarkData: ' + IntToStr (PRecInfo(Buffer + FRecordInfoOffset).Bookmark));
  835.   PInteger(Data)^ :=
  836.     PRecInfo(Buffer + FRecordInfoOffset).Bookmark;
  837. end;
  838. // ____________________________________________________________________________
  839. // TDBF.SetBookmarkData
  840. // II: set the bookmark data in the buffer
  841. procedure TDBF.SetBookmarkData (
  842.   Buffer: PChar; Data: Pointer);
  843. begin
  844. //  ShowMessage ('SetBookmarkData: ' + IntToStr (PInteger(Data)^));
  845.   PRecInfo(Buffer + FRecordInfoOffset).Bookmark :=
  846.     PInteger(Data)^;
  847. end;
  848. // ____________________________________________________________________________
  849. // TDBF.InternalFirst
  850. // II: Go to a special position before the first record
  851. procedure TDBF.InternalFirst;
  852. begin
  853.   FCurrentRecord := BofCrack;
  854. end;
  855. // ____________________________________________________________________________
  856. // TDBF.InternalLast
  857. // II: Go to a special position after the last record
  858. procedure TDBF.InternalLast;
  859. begin
  860.   EofCrack := FRecordCount {+ fDeletedCount};
  861.   FCurrentRecord := EofCrack;
  862. end;
  863. // ____________________________________________________________________________
  864. // TDBF.GetRecordCount
  865. // II (optional): Record count
  866. function TDBF.GetRecordCount: Longint;
  867. begin
  868.   CheckActive;
  869.   Result := FRecordCount;
  870. end;
  871. // ____________________________________________________________________________
  872. // TDBF.GetRecNo
  873. // II (optional): Get the number of the current record
  874. function TDBF.GetRecNo: Longint;
  875. begin
  876.   UpdateCursorPos;
  877.   if FCurrentRecord < 0 then
  878.     Result := 1
  879.   else
  880.     Result := FCurrentRecord + 1;
  881. end;
  882. // ____________________________________________________________________________
  883. // TDBF.SetRecNo
  884. // II (optional): Move to the given record number
  885. procedure TDBF.SetRecNo(Value: Integer);
  886. begin
  887.   CheckBrowseMode;
  888.   if (Value > 1) and (Value <= (FRecordCount{+FDeletedCount})) then
  889.   begin
  890.     FCurrentRecord := Value - 1;
  891.     Resync([]);
  892.   end;
  893. end;
  894. //////////////////////////////////////////
  895. ////// Part III:
  896. ////// Record buffers and field management
  897. //////////////////////////////////////////
  898. // ____________________________________________________________________________
  899. // TDBF.GetRecordSize
  900. /// III: Determine the size of each record buffer in memory
  901. function TDBF.GetRecordSize: Word;
  902. begin
  903.   Result := FRecordSize; // data only
  904. end;
  905. // ____________________________________________________________________________
  906. // TDBF.AllocRecordBuffer
  907. /// III: Allocate a buffer for the record
  908. function TDBF.AllocRecordBuffer: PChar;
  909. begin
  910.   Result := StrAlloc(FRecordBufferSize+1);
  911. end;
  912. // ____________________________________________________________________________
  913. // TDBF.InternalInitRecord
  914. // III: Initialize the record (set to zero)
  915. procedure TDBF.InternalInitRecord(Buffer: PChar);
  916. (*var
  917.   Field : TField;
  918.   i : integer;
  919.   FieldOffset : integer;
  920.   S : string; *)
  921. begin
  922.   FillChar(Buffer^, FRecordBufferSize, 32);
  923. (*  for i := 0 to FieldCount-1 do
  924.     begin
  925.       Field := Fields[i];
  926.       FieldOffset := Integer(FFileOffset[Field.FieldNo-1])+FRecordHeaderSize;
  927.       if Field.DataType = ftString then
  928.         begin
  929.           pChar(Buffer+FieldOffset)^ := #0;
  930.         end
  931.       else if Field.DataType = ftFloat then
  932.         begin
  933.           pChar(Buffer+FieldOffset)^ := '0';
  934.           pChar(Buffer+FieldOffset+1)^ := #0;
  935.         end
  936.       else if Field.DataType = ftDate then
  937.         begin
  938.           S := '19900101';
  939.           CopyMemory(PChar(Buffer+FieldOffset),PChar(S),8);
  940.         end
  941.       else if Field.DataType = ftBoolean then
  942.         begin
  943.           pChar(Buffer+FieldOffset)^ := 'F';
  944.         end;
  945.     end; *)
  946. end;
  947. // ____________________________________________________________________________
  948. // TDBF.FreeRecordBuffer
  949. // III: Free the buffer
  950. procedure TDBF.FreeRecordBuffer (var Buffer: PChar);
  951. begin
  952.   StrDispose(Buffer);
  953. end;
  954. // ____________________________________________________________________________
  955. // TDBF.GetRecord
  956. // III: Retrieve data for current, previous, or next record
  957. // (eventually moving to it) and return the status
  958. function TDBF.GetRecord(Buffer: PChar;
  959.   GetMode: TGetMode; DoCheck: Boolean): TGetResult;
  960. var
  961.   Acceptable : Boolean;
  962. begin
  963.   result := grOk;
  964.   if FRecordCount < 1 then
  965.     Result := grEOF
  966.   else
  967.     repeat
  968.       case GetMode of
  969.         gmCurrent :
  970.           begin
  971.             // ShowMessage ('GetRecord Current');
  972.             if (FCurrentRecord >= FRecordCount{+fDeletedCount}) or
  973.                 (FCurrentRecord < 0) then
  974.               Result := grError;
  975.           end;
  976.         gmNext :
  977.           begin
  978.             if (fCurrentRecord < (fRecordCount{+fDeletedCount})-1) then
  979.               Inc (FCurrentRecord)
  980.             else
  981.               Result := grEOF;
  982.           end;
  983.         gmPrior :
  984.           begin
  985.            if (fCurrentRecord > 0) then
  986.               Dec(fCurrentRecord)
  987.            else
  988.               Result := grBOF;
  989.           end;
  990.       end;
  991.       // fill record data area of buffer
  992.       if Result = grOK then
  993.         begin
  994.           _ReadRecord(Buffer, fCurrentRecord );
  995.           {FStream.Position := FDataFileHeader.StartData +
  996.           FRecordSize * FCurrentRecord;
  997.           FStream.ReadBuffer (Buffer^, FRecordSize);}
  998.           ClearCalcFields(Buffer);
  999.           GetCalcFields(Buffer);
  1000.           with PRecInfo(Buffer + FRecordInfoOffset)^ do
  1001.             begin
  1002.               BookmarkFlag := bfCurrent;
  1003.               Bookmark := FCurrentRecord;
  1004.             end;
  1005.         end
  1006.       else
  1007.         if (Result = grError) and DoCheck then
  1008.           raise eDBFError.Create('GetRecord: Invalid record');
  1009.       Acceptable := pRecordHeader(Buffer)^.DeletedFlag <> '*';
  1010.       if Filtered then
  1011.         Acceptable := Acceptable and (_ProcessFilter(Buffer));
  1012.       if (GetMode=gmCurrent) and Not Acceptable then
  1013.         Result := grError;
  1014.     until (Result <> grOK) or Acceptable;
  1015.   if ((Result=grEOF)or(Result=grBOF)) and Filtered and not (_ProcessFilter(Buffer)) then
  1016.     Result := grError;
  1017. end;
  1018. // ____________________________________________________________________________
  1019. // TDBF.InternalPost
  1020. // III: Write the current data to the file
  1021. procedure TDBF.InternalPost;
  1022. begin
  1023.   CheckActive;
  1024.   if State = dsEdit then
  1025.     begin
  1026.       // replace data with new data
  1027.       {FStream.Position := FDataFileHeader.StartData + (FRecordSize * FCurrentRecord);
  1028.       FStream.WriteBuffer (ActiveBuffer^, FRecordSize);}
  1029.       _WriteRecord (ActiveBuffer, fCurrentRecord);
  1030.     end
  1031.   else
  1032.     begin
  1033.       // always append
  1034.       InternalLast;
  1035.       {FStream.Seek (0, soFromEnd);
  1036.       FStream.WriteBuffer (ActiveBuffer^, FRecordSize);}
  1037.       pRecordHeader(ActiveBuffer)^.DeletedFlag := ' ';
  1038.       _AppendRecord(ActiveBuffer);
  1039.       Inc (FRecordCount);
  1040.     end;
  1041. end;
  1042. // ____________________________________________________________________________
  1043. // TDBF.InternalAddRecord
  1044. // III: Add the current data to the file
  1045. procedure TDBF.InternalAddRecord(Buffer:Pointer; Append:Boolean);
  1046. begin
  1047.   // always append
  1048.   InternalLast;
  1049.   // add record at the end of the file
  1050.   {FStream.Seek (0, soFromEnd);}
  1051.   pRecordHeader(ActiveBuffer)^.DeletedFlag := ' ';
  1052.   _AppendRecord(ActiveBuffer);
  1053.   {FStream.WriteBuffer (ActiveBuffer^, FRecordSize);}
  1054.   Inc (FRecordCount);
  1055. end;
  1056. // ____________________________________________________________________________
  1057. // TDBF.InternalDelete
  1058. // III: Delete the current record
  1059. procedure TDBF.InternalDelete;
  1060. begin
  1061.   CheckActive;
  1062.   // not supported in this version
  1063. {  raise eBinaryDataSetError.Create (
  1064.     'Delete: Operation not supported');}
  1065. //  pRecordHeader(ActiveBuffer)^.DeletedFlag := fDataFileHeader.LastDeleted;
  1066.   PChar(ActiveBuffer)^ := '*';
  1067.   _WriteRecord(ActiveBuffer,fCurrentRecord);
  1068.   {FStream.Position := FDataFileHeader.StartData + (FRecordSize * FCurrentRecord);
  1069.   FStream.WriteBuffer (ActiveBuffer^, FRecordSize);}
  1070. //  fDBFHeader.LastDeleted := GetRecNo;
  1071. //  Inc(fDeletedCount);
  1072. //  Dec(fRecordCount);
  1073. //  fDBFHeader.NumberOfRecords := fRecordCount;
  1074. //  WriteHeader;
  1075.   Resync([]);
  1076. end;
  1077. // ____________________________________________________________________________
  1078. // TDBF.GetFieldData
  1079. // III: Move data from record buffer to field
  1080. function TDBF.GetFieldData(Field:TField; Buffer:Pointer):Boolean;
  1081. var
  1082.   FieldOffset: Integer;
  1083.   S : string;
  1084.   Buf2 : PChar;
  1085.   i,l : integer;
  1086.   D : Double;
  1087.   n : integer;
  1088.   T : TDateTime;
  1089.   j : integer;
  1090.   OldDateFormat : string;
  1091. begin
  1092.   Result := False;
  1093.   Buf2 := ActiveBuffer;
  1094.   if (FRecordCount>0) and (Field.FieldNo > 0) and (Assigned(Buffer)) and (Assigned(Buf2))  then
  1095.     begin
  1096.       FieldOffset := Integer(FFileOffset[Field.FieldNo-1])+FRecordHeaderSize;
  1097.       if Field.DataType = ftString then
  1098.         begin
  1099.           l := Integer(FFileWidth[Field.FieldNo-1]);
  1100.           S := '';
  1101.           i := 0;
  1102.           While (Buf2[FieldOffset+i] <> #0) and (i<l) do
  1103.             begin
  1104.               S := S+pChar(Buf2+FieldOffset+i)^;
  1105.               inc(i);
  1106.             end;
  1107.           SetLength(S,l);
  1108.           S := Trim(S);
  1109.           CharToOemBuff(PChar(S), Buffer,l);
  1110.           Result := True;
  1111.         end
  1112.       else if Field.DataType = ftFloat then
  1113.         begin
  1114.           n := Integer(FFileWidth[Field.FieldNo-1]);
  1115.           S := '';
  1116.           for i := FieldOffset to FieldOffset+n-1 do
  1117.             S := S+pChar(Buf2+i)^;
  1118.           S := Trim(S);
  1119.           if S='' then
  1120.             Result := False
  1121.           else
  1122.             begin
  1123.               if (Pos('.',S) > 0) and (DecimalSeparator <> '.') then
  1124.                 S[Pos('.',S)] := DecimalSeparator;
  1125.               Result := True;
  1126.              try
  1127.               D := StrToFloat(S);
  1128.              except
  1129.               D := 0;
  1130.               Result := False;
  1131.              end;
  1132.               PDouble(Buffer)^ := D;
  1133.             end;
  1134.         end
  1135.       else if Field.DataType = ftDate then
  1136.         begin
  1137.           S := '';
  1138.           for j := 0 to 7 do
  1139.             S := S + pChar(Buf2+FieldOffset+j);
  1140.           SetLength(S,8);
  1141.           if (trim(S) = '') or (S='00000000') then
  1142.             Result := false
  1143.           else
  1144.             begin
  1145.               S := Copy(S,7,2)+DateSeparator+Copy(S,5,2)+DateSeparator+Copy(S,1,4);
  1146.               OldDateFormat := ShortDateFormat;
  1147.               ShortDateFormat := 'dd/mm/yyyy';
  1148.               t := StrToDate(S);
  1149.               ShortDateFormat := OldDateFormat;
  1150.               j := Trunc(pDouble(@t)^)+693594;
  1151.               pInteger(Buffer)^ := j;
  1152.               result := True;
  1153.             end;
  1154.         end
  1155.       else if Field.DataType = ftBoolean then
  1156.         begin
  1157.           Result := True;
  1158.           if PChar(Buf2+FieldOffset)^ in ['S','T','Y'] then
  1159.             pBoolean(Buffer)^ := True
  1160.           else if PChar(Buf2+FieldOffset)^ in ['N','F'] then
  1161.             pBoolean(Buffer)^ := False
  1162.           else
  1163.             Result := False;
  1164.         end
  1165.       else
  1166.         begin
  1167.           ShowMessage ('very bad error in get field data');
  1168.           Result := False;
  1169.         end;
  1170.     end;
  1171. end;
  1172. // ____________________________________________________________________________
  1173. // TDBF.SetFieldData
  1174. // III: Move data from field to record buffer
  1175. procedure TDBF.SetFieldData(Field: TField; Buffer: Pointer);
  1176. var
  1177.   FieldOffset: Integer;
  1178.   Buf2 : PChar;
  1179.   l,i,n:integer;
  1180.   S : string;
  1181.   D : TDateTime;
  1182.   j : integer;
  1183. begin
  1184.   Buf2 := ActiveBuffer;
  1185.   if (Field.FieldNo >= 0) and (Assigned(Buffer)) and (Assigned(Buf2)) then
  1186.     begin
  1187.       FieldOffset := Integer(FFileOffset[Field.FieldNo-1])+FRecordHeaderSize;
  1188.       if Field.DataType = ftString then
  1189.         begin
  1190.           l := Integer(FFileWidth[Field.FieldNo-1]);
  1191.           S := '';
  1192.           i := 0;
  1193.           While (PChar(Buffer)[i] <> #0) and (i<l) do
  1194.             begin
  1195.               S := S+PChar(Buffer)[i];
  1196.               inc(i);
  1197.             end;
  1198.           SetLength(S,l);
  1199.           OemToCharBuff(PChar(S),PChar(Buf2+FieldOffset),l);
  1200.         end
  1201.       else if Field.DataType = ftFloat then
  1202.         begin
  1203.           n := Integer(FFileWidth[Field.FieldNo-1]);
  1204.           Str(pDouble(Buffer)^:n:Integer(FFileDecimals[Field.FieldNo-1]),S);
  1205.           while Length(S)<n do
  1206.             S :=  ' '+S;
  1207.           if (Pos(DecimalSeparator,S) > 0) and (DecimalSeparator <> '.') then
  1208.             S[Pos(DecimalSeparator,S)] := '.';
  1209.           CopyMemory(Pchar(Buf2+FieldOffset),PChar(S),n);
  1210.         end
  1211.       else if Field.DataType = ftDate then
  1212.         begin
  1213.           j := pInteger(Buffer)^-693594;
  1214.           pDouble(@d)^ := j;
  1215.           S := FormatDateTime('yyyymmdd',d);
  1216.           StrLCopy(pChar(Buf2+FieldOffset),pChar(S),8);
  1217.         end
  1218.       else if Field.DataType = ftBoolean then
  1219.         begin
  1220.           if pBoolean(Buffer)^ then
  1221.             PChar(Buf2+FieldOffset)^ := 'T'
  1222.           else
  1223.             PChar(Buf2+FieldOffset)^ := 'F';
  1224.         end
  1225.       else
  1226.         ShowMessage ('very bad error in setfield data');
  1227.       DataEvent (deFieldChange, Longint(Field));
  1228.     end;
  1229. end;
  1230. // ____________________________________________________________________________
  1231. // TDBF.InternalHandleException
  1232. // default exception handling
  1233. procedure TDBF.InternalHandleException;
  1234. begin
  1235.   // standard exception handling
  1236.   Application.HandleException(Self);
  1237. end;
  1238. Function TDBF._ProcessFilter(Buffer:PChar):boolean;
  1239. var
  1240.   FilterExpresion : string;
  1241.   PosComp : integer;
  1242.   FName : string;
  1243.   FieldPos : integer;
  1244.   FieldOffset : integer;
  1245.   FieldValue : Variant;
  1246.   TestValue : Variant;
  1247.   FieldText : string;
  1248.   OldShortDateFormat : string;
  1249. begin
  1250.   FilterExpresion := Filter;
  1251.   PosComp := Pos('>',FilterExpresion);
  1252.   if PosComp=0 then
  1253.     PosComp := Pos('<',FilterExpresion);
  1254.   if PosComp=0 then
  1255.     PosComp := Pos('=',FilterExpresion);
  1256.   if PosComp=0 then
  1257.     begin
  1258.       _ProcessFilter := True;
  1259.       Exit;
  1260.     end;
  1261.   FName := Trim(Copy(FilterExpresion,1,PosComp-1));
  1262.   FieldPos := FieldDefs.IndexOf(FName);
  1263.   FieldOffset := integer(FFileOffset[FieldPos]);
  1264.   if FieldPos < 0 then
  1265.     _ProcessFilter := True
  1266.   else if FieldDefs.Items[FieldPos].DataType = ftString then
  1267.     begin // STRING
  1268.      try
  1269.       FieldValue := '';
  1270.       FieldOffset := FieldOffset+1;
  1271.       While (Buffer[FieldOffset]<>#0) and (Length(FieldValue)<integer(FFileWidth[FieldPos])) do
  1272.         begin
  1273.           FieldValue := FieldValue + Buffer[FieldOffset];
  1274.           FieldOffset := FieldOffset+1;
  1275.         end;
  1276.       FieldValue := Trim(FieldValue);
  1277.       TestValue := Trim(Copy(FilterExpresion,PosComp+2,Length(FilterExpresion)-PosComp-2));
  1278.       if FilterExpresion[PosComp]='=' then
  1279.         _ProcessFilter := (FieldValue=TestValue)
  1280.       else if FilterExpresion[PosComp]='>' then
  1281.         begin
  1282.           if FilterExpresion[PosComp+1]='=' then
  1283.             _ProcessFilter := (FieldValue>=Copy(TestValue,2,(Length(TestValue)-1)))
  1284.           else
  1285.             _ProcessFilter := (FieldValue>TestValue);
  1286.         end
  1287.       else if FilterExpresion[PosComp]='<' then
  1288.         begin
  1289.           if FilterExpresion[PosComp+1]='=' then
  1290.             _ProcessFilter := (FieldValue<=Copy(TestValue,2,(Length(TestValue)-1)))
  1291.           else
  1292.             _ProcessFilter := (FieldValue<TestValue);
  1293.         end
  1294.       else
  1295.         _ProcessFilter := False;
  1296.      except
  1297.        _ProcessFilter := False;
  1298.      end;
  1299.     end
  1300.   else if FieldDefs.Items[FieldPos].DataType = ftFloat then
  1301.     begin // FLOAT
  1302.      try
  1303.       FieldText := '';
  1304.       FieldOffset := FieldOffset+1;
  1305.       While (Buffer[FieldOffset]<>#0) and (Length(FieldText)<integer(FFileWidth[FieldPos])) do
  1306.         begin
  1307.           FieldText := FieldText + Buffer[FieldOffset];
  1308.           FieldOffset := FieldOffset+1;
  1309.         end;
  1310.       FieldText := Trim(FieldText);
  1311.       if Pos('.',FieldText)>0 then
  1312.         FieldText[Pos('.',FieldText)] := DecimalSeparator;
  1313.       FieldValue := StrToFloat(FieldText);
  1314.       if FilterExpresion[PosComp+1]='='then
  1315.         FieldText := Trim(Copy(FilterExpresion,PosComp+2,Length(FilterExpresion)-PosComp-1))
  1316.       else
  1317.         FieldText := Trim(Copy(FilterExpresion,PosComp+1,Length(FilterExpresion)-PosComp));
  1318.       if Pos('.',FieldText)>0 then
  1319.         FieldText[Pos('.',FieldText)] := DecimalSeparator;
  1320.       TestValue := StrToFloat(FieldText);
  1321.       if FilterExpresion[PosComp]='=' then
  1322.         _ProcessFilter := (FieldValue=TestValue)
  1323.       else if FilterExpresion[PosComp]='>'then
  1324.         begin
  1325.           if FilterExpresion[PosComp+1]='='then
  1326.             _ProcessFilter := (FieldValue>=TestValue)
  1327.           else
  1328.             _ProcessFilter := (FieldValue>TestValue);
  1329.         end
  1330.       else if FilterExpresion[PosComp]='<'then
  1331.         begin
  1332.           if FilterExpresion[PosComp+1]='='then
  1333.             _ProcessFilter := (FieldValue<=TestValue)
  1334.           else
  1335.             _ProcessFilter := (FieldValue<TestValue);
  1336.         end
  1337.       else
  1338.         _ProcessFilter := False;
  1339.      except
  1340.       _ProcessFilter := False;
  1341.      end;
  1342.     end
  1343.   else if FieldDefs.Items[FieldPos].DataType = ftDate then
  1344.     begin // DATE
  1345.       OldShortDateFormat := ShortDateFormat;
  1346.      try
  1347.       FieldText := '';
  1348.       FieldOffset := FieldOffset+1;
  1349.       While (Buffer[FieldOffset]<>#0) and (Length(FieldText)<integer(FFileWidth[FieldPos])) do
  1350.         begin
  1351.           FieldText := FieldText + Buffer[FieldOffset];
  1352.           FieldOffset := FieldOffset+1;
  1353.         end;
  1354.       FieldText := Trim(FieldText);
  1355.       FieldText := Copy(FieldText,1,4)+DateSeparator+Copy(FieldText,5,2)+DateSeparator+Copy(FieldText,7,2);
  1356.       ShortDateFormat := 'yyyy/mm/dd';
  1357.       FieldValue := StrToDate(FieldText);
  1358.       if FilterExpresion[PosComp+1]='=' then
  1359.         FieldText := Trim(Copy(FilterExpresion,PosComp+2,Length(FilterExpresion)-PosComp-1))
  1360.       else
  1361.         FieldText := Trim(Copy(FilterExpresion,PosComp+1,Length(FilterExpresion)-PosComp));
  1362.       FieldText := Copy(FieldText,1,4)+DateSeparator+Copy(FieldText,5,2)+DateSeparator+Copy(FieldText,7,2);
  1363.       TestValue := StrToDate(FieldText);
  1364.       if FilterExpresion[PosComp]='=' then
  1365.         begin
  1366.           _ProcessFilter := (FieldValue=TestValue);
  1367.         end
  1368.       else if FilterExpresion[PosComp]='>' then
  1369.         begin
  1370.           if FilterExpresion[PosComp+1]='='then
  1371.             _ProcessFilter := (FieldValue>=TestValue)
  1372.           else
  1373.             _ProcessFilter := (FieldValue>TestValue);
  1374.         end
  1375.       else if FilterExpresion[PosComp]='<' then
  1376.         begin
  1377.           if FilterExpresion[PosComp+1]='='then
  1378.             _ProcessFilter := (FieldValue<=TestValue)
  1379.           else
  1380.             _ProcessFilter := (FieldValue<TestValue);
  1381.         end
  1382.       else
  1383.         _ProcessFilter := False;
  1384.      except
  1385.       _ProcessFilter := False;
  1386.      end;
  1387.       ShortDateFormat := OldShortDateFormat;
  1388.     end
  1389.   else
  1390.     _ProcessFilter := False;
  1391. end;
  1392. {******************************************************************************}
  1393. {* Property Editors Code                                                       *}
  1394. {******************************************************************************}
  1395. procedure TFilenameProperty.Edit;
  1396. var
  1397.   FileOpen: TOpenDialog;
  1398. begin
  1399.   FileOpen := TOpenDialog.Create(Nil);
  1400.   FileOpen.Filename := GetValue;
  1401.   FileOpen.Filter := 'dBase Files (*.DBF)|*.DBF|All Files (*.*)|*.*';
  1402.   FileOpen.Options := FileOpen.Options + [ofPathMustExist, ofFileMustExist];
  1403.   try
  1404.     if FileOpen.Execute then SetValue(FileOpen.Filename);
  1405.   finally
  1406.     FileOpen.Free;
  1407.   end;
  1408. end;
  1409. function TFilenameProperty.GetAttributes: TPropertyAttributes;
  1410. begin
  1411.   Result := [paDialog, paRevertable];
  1412. end;
  1413. procedure Register;
  1414. begin
  1415.   RegisterComponents('Terabyte', [TDBF]);
  1416.   RegisterPropertyEditor(TypeInfo(String), TDBF, 'TableName', TFileNameProperty);
  1417. end;
  1418. end.