RxMemDS.pas
上传用户:hylc_2004
上传日期:2014-01-23
资源大小:46800k
文件大小:43k
源码类别:

Delphi控件源码

开发平台:

Delphi

  1. {*******************************************************}
  2. {                                                       }
  3. {         Delphi VCL Extensions (RX)                    }
  4. {                                                       }
  5. {         Copyright (c) 1998 Master-Bank                }
  6. {                                                       }
  7. {*******************************************************}
  8. unit RxMemDS;
  9. {$I RX.INC}
  10. interface
  11. {$IFDEF RX_D3}
  12. uses Windows, SysUtils, Classes, Controls, DB, DBUtils, Variants;
  13. { TRxMemoryData }
  14. type
  15.   TMemBlobData = string;
  16.   TMemBlobArray = array[0..0] of TMemBlobData;
  17.   PMemBlobArray = ^TMemBlobArray;
  18.   TMemoryRecord = class;
  19.   TLoadMode = (lmCopy, lmAppend);
  20.   TCompareRecords = function (Item1, Item2: TMemoryRecord): Integer of object;
  21.   TRxMemoryData = class(TDataSet)
  22.   private
  23.     FRecordPos: Integer;
  24.     FRecordSize: Integer;
  25.     FBookmarkOfs: Integer;
  26.     FBlobOfs: Integer;
  27.     FRecBufSize: Integer;
  28.     FOffsets: PWordArray;
  29.     FLastID: Integer;
  30.     FAutoInc: Longint;
  31.     FActive: Boolean;
  32.     FRecords: TList;
  33.     FIndexList: TList;
  34.     FCaseInsensitiveSort: Boolean;
  35.     FDescendingSort: Boolean;
  36.     function AddRecord: TMemoryRecord;
  37.     function InsertRecord(Index: Integer): TMemoryRecord;
  38.     function FindRecordID(ID: Integer): TMemoryRecord;
  39.     procedure CreateIndexList(const FieldNames: string);
  40.     procedure FreeIndexList;
  41.     procedure QuickSort(L, R: Integer; Compare: TCompareRecords);
  42.     procedure Sort;
  43.     function CalcRecordSize: Integer;
  44.     function FindFieldData(Buffer: Pointer; Field: TField): Pointer;
  45.     function GetMemoryRecord(Index: Integer): TMemoryRecord;
  46.     function GetCapacity: Integer;
  47.     function RecordFilter: Boolean;
  48.     procedure SetCapacity(Value: Integer);
  49.     procedure ClearRecords;
  50.     procedure InitBufferPointers(GetProps: Boolean);
  51.   protected
  52.     procedure AssignMemoryRecord(Rec: TMemoryRecord; Buffer: PChar);
  53.     function GetActiveRecBuf(var RecBuf: PChar): Boolean; virtual;
  54.     procedure InitFieldDefsFromFields;
  55.     procedure RecordToBuffer(Rec: TMemoryRecord; Buffer: PChar);
  56.     procedure SetMemoryRecordData(Buffer: PChar; Pos: Integer); virtual;
  57.     procedure SetAutoIncFields(Buffer: PChar); virtual;
  58.     function CompareRecords(Item1, Item2: TMemoryRecord): Integer; virtual;
  59.     function GetBlobData(Field: TField; Buffer: PChar): TMemBlobData;
  60.     procedure SetBlobData(Field: TField; Buffer: PChar; Value: TMemBlobData);
  61.     function AllocRecordBuffer: PChar; override;
  62.     procedure FreeRecordBuffer(var Buffer: PChar); override;
  63. {$IFNDEF RX_D5}
  64.     function BCDToCurr(BCD: Pointer; var Curr: Currency): Boolean; override;
  65.     function CurrToBCD(const Curr: Currency; BCD: Pointer; Precision,
  66.       Decimals: Integer): Boolean; override;
  67. {$ENDIF}
  68.     procedure InternalInitRecord(Buffer: PChar); override;
  69.     procedure ClearCalcFields(Buffer: PChar); override;
  70.     function GetRecord(Buffer: PChar; GetMode: TGetMode;
  71.       DoCheck: Boolean): TGetResult; override;
  72.     function GetRecordSize: Word; override;
  73.     procedure SetFiltered(Value: Boolean); override;
  74.     procedure SetOnFilterRecord(const Value: TFilterRecordEvent); override;
  75.     procedure SetFieldData(Field: TField; Buffer: Pointer); override;
  76.     procedure CloseBlob(Field: TField); override;
  77.     procedure GetBookmarkData(Buffer: PChar; Data: Pointer); override;
  78.     function GetBookmarkFlag(Buffer: PChar): TBookmarkFlag; override;
  79.     procedure InternalGotoBookmark(Bookmark: TBookmark); override;
  80.     procedure InternalSetToRecord(Buffer: PChar); override;
  81.     procedure SetBookmarkFlag(Buffer: PChar; Value: TBookmarkFlag); override;
  82.     procedure SetBookmarkData(Buffer: PChar; Data: Pointer); override;
  83.     function GetIsIndexField(Field: TField): Boolean; override;
  84.     procedure InternalFirst; override;
  85.     procedure InternalLast; override;
  86.     procedure InitRecord(Buffer: PChar); override;
  87.     procedure InternalAddRecord(Buffer: Pointer; Append: Boolean); override;
  88.     procedure InternalDelete; override;
  89.     procedure InternalPost; override;
  90.     procedure InternalClose; override;
  91.     procedure InternalHandleException; override;
  92.     procedure InternalInitFieldDefs; override;
  93.     procedure InternalOpen; override;
  94.     procedure OpenCursor(InfoQuery: Boolean); override;
  95.     function IsCursorOpen: Boolean; override;
  96.     function GetRecordCount: Integer; override;
  97.     function GetRecNo: Integer; override;
  98.     procedure SetRecNo(Value: Integer); override;
  99.     property Records[Index: Integer]: TMemoryRecord read GetMemoryRecord;
  100.   public
  101.     constructor Create(AOwner: TComponent); override;
  102.     destructor Destroy; override;
  103.     function BookmarkValid(Bookmark: TBookmark): Boolean; override;
  104.     function CompareBookmarks(Bookmark1, Bookmark2: TBookmark): Integer; override;
  105.     function CreateBlobStream(Field: TField; Mode: TBlobStreamMode): TStream; override;
  106.     function GetFieldData(Field: TField; Buffer: Pointer): Boolean; override;
  107.     function GetCurrentRecord(Buffer: PChar): Boolean; override;
  108.     function IsSequenced: Boolean; override;
  109.     function Locate(const KeyFields: string; const KeyValues: Variant;
  110.       Options: TLocateOptions): Boolean; override;
  111.     procedure SortOnFields(const FieldNames: string;
  112. {$IFDEF RX_D4}
  113.       CaseInsensitive: Boolean = True; Descending: Boolean = False);
  114. {$ELSE}
  115.       CaseInsensitive, Descending: Boolean);
  116. {$ENDIF}
  117.     procedure EmptyTable;
  118.     procedure CopyStructure(Source: TDataSet);
  119.     function LoadFromDataSet(Source: TDataSet; RecordCount: Integer;
  120.       Mode: TLoadMode): Integer;
  121.     function SaveToDataSet(Dest: TDataSet; RecordCount: Integer): Integer;
  122.   published
  123.     property Capacity: Integer read GetCapacity write SetCapacity default 0;
  124.     property Active;
  125.     property AutoCalcFields;
  126.     property Filtered;
  127. {$IFDEF RX_D4}
  128.     property FieldDefs;
  129.     property ObjectView default False;
  130. {$ENDIF}
  131.     property BeforeOpen;
  132.     property AfterOpen;
  133.     property BeforeClose;
  134.     property AfterClose;
  135.     property BeforeInsert;
  136.     property AfterInsert;
  137.     property BeforeEdit;
  138.     property AfterEdit;
  139.     property BeforePost;
  140.     property AfterPost;
  141.     property BeforeCancel;
  142.     property AfterCancel;
  143.     property BeforeDelete;
  144.     property AfterDelete;
  145.     property BeforeScroll;
  146.     property AfterScroll;
  147.     property OnCalcFields;
  148.     property OnDeleteError;
  149.     property OnEditError;
  150.     property OnFilterRecord;
  151.     property OnNewRecord;
  152.     property OnPostError;
  153.   end;
  154. { TMemBlobStream }
  155.   TMemBlobStream = class(TStream)
  156.   private
  157.     FField: TBlobField;
  158.     FDataSet: TRxMemoryData;
  159.     FBuffer: PChar;
  160.     FMode: TBlobStreamMode;
  161.     FOpened: Boolean;
  162.     FModified: Boolean;
  163.     FPosition: Longint;
  164.     FCached: Boolean;
  165.     function GetBlobSize: Longint;
  166.     function GetBlobFromRecord(Field: TField): TMemBlobData;
  167.   public
  168.     constructor Create(Field: TBlobField; Mode: TBlobStreamMode);
  169.     destructor Destroy; override;
  170.     function Read(var Buffer; Count: Longint): Longint; override;
  171.     function Write(const Buffer; Count: Longint): Longint; override;
  172.     function Seek(Offset: Longint; Origin: Word): Longint; override;
  173.     procedure Truncate;
  174.   end;
  175. { TMemoryRecord }
  176.   TMemoryRecord = class(TPersistent)
  177.   private
  178.     FMemoryData: TRxMemoryData;
  179.     FID: Integer;
  180.     FData: Pointer;
  181.     FBlobs: Pointer;
  182.     function GetIndex: Integer;
  183.     procedure SetMemoryData(Value: TRxMemoryData; UpdateParent: Boolean);
  184.   protected
  185.     procedure SetIndex(Value: Integer); virtual;
  186.   public
  187.     constructor Create(MemoryData: TRxMemoryData); virtual;
  188.     constructor CreateEx(MemoryData: TRxMemoryData; UpdateParent: Boolean); virtual;
  189.     destructor Destroy; override;
  190.     property MemoryData: TRxMemoryData read FMemoryData;
  191.     property ID: Integer read FID write FID;
  192.     property Index: Integer read GetIndex write SetIndex;
  193.     property Data: Pointer read FData;
  194.   end;
  195. {$ENDIF RX_D3}
  196. implementation
  197. {$IFDEF RX_D3}
  198. uses Forms, DbConsts {$IFDEF RX_D5}, ComObj {$ENDIF};
  199. resourcestring
  200.   SMemNoRecords = 'No data found';
  201. {$IFNDEF RX_D4}
  202.   SInvalidFields = 'No fields defined';
  203. {$ENDIF}
  204. const
  205.   ftBlobTypes = [ftBlob, ftMemo, ftGraphic, ftFmtMemo, ftParadoxOle,
  206.     ftDBaseOle, ftTypedBinary {$IFDEF RX_D5}, ftOraBlob, ftOraClob {$ENDIF}];
  207.   ftSupported = [ftString, ftSmallint, ftInteger, ftWord, ftBoolean, ftFloat,
  208.     ftCurrency, ftDate, ftTime, ftDateTime, ftAutoInc, ftBCD, ftBytes,
  209.     ftVarBytes {$IFDEF RX_D4}, ftADT, ftFixedChar, ftWideString,
  210.     ftLargeint {$ENDIF} {$IFDEF RX_D5}, ftVariant, ftGuid {$ENDIF}] + 
  211.     ftBlobTypes;
  212.   fkStoredFields = [fkData];
  213. {$IFDEF RX_D5}
  214.   GuidSize = 38;
  215. {$ENDIF}
  216. { Utility routines }
  217. function CompareFields(Data1, Data2: Pointer; FieldType: TFieldType;
  218.   CaseInsensitive: Boolean): Integer;
  219. begin
  220.   Result := 0;
  221.   case FieldType of
  222.     ftString:
  223.       if CaseInsensitive then
  224.         Result := AnsiCompareText(PChar(Data1), PChar(Data2))
  225.       else
  226.         Result := AnsiCompareStr(PChar(Data1), PChar(Data2));
  227.     ftSmallint:
  228.       if SmallInt(Data1^) > SmallInt(Data2^) then Result := 1
  229.       else if SmallInt(Data1^) < SmallInt(Data2^) then Result := -1;
  230.     ftInteger, ftDate, ftTime, ftAutoInc:
  231.       if Longint(Data1^) > Longint(Data2^) then Result := 1
  232.       else if Longint(Data1^) < Longint(Data2^) then Result := -1;
  233.     ftWord:
  234.       if Word(Data1^) > Word(Data2^) then Result := 1
  235.       else if Word(Data1^) < Word(Data2^) then Result := -1;
  236.     ftBoolean:
  237.       if WordBool(Data1^) and not WordBool(Data2^) then Result := 1
  238.       else if not WordBool(Data1^) and WordBool(Data2^) then Result := -1;
  239.     ftFloat, ftCurrency:
  240.       if Double(Data1^) > Double(Data2^) then Result := 1
  241.       else if Double(Data1^) < Double(Data2^) then Result := -1;
  242.     ftDateTime:
  243.       if TDateTime(Data1^) > TDateTime(Data2^) then Result := 1
  244.       else if TDateTime(Data1^) < TDateTime(Data2^) then Result := -1;
  245. {$IFDEF RX_D4}
  246.     ftFixedChar:
  247.       if CaseInsensitive then
  248.         Result := AnsiCompareText(PChar(Data1), PChar(Data2))
  249.       else
  250.         Result := AnsiCompareStr(PChar(Data1), PChar(Data2));
  251.     ftWideString:
  252.       if CaseInsensitive then
  253.         Result := AnsiCompareText(WideCharToString(PWideChar(Data1)),
  254.           WideCharToString(PWideChar(Data2)))
  255.       else
  256.         Result := AnsiCompareStr(WideCharToString(PWideChar(Data1)),
  257.           WideCharToString(PWideChar(Data2)));
  258.     ftLargeint: 
  259.       if Int64(Data1^) > Int64(Data2^) then Result := 1
  260.       else if Int64(Data1^) < Int64(Data2^) then Result := -1;
  261. {$ENDIF}
  262. {$IFDEF RX_D5}
  263.     ftVariant:
  264.       Result := 0;
  265.     ftGuid:
  266.       Result := AnsiCompareText(PChar(Data1), PChar(Data2));
  267. {$ENDIF}
  268.   end;
  269. end;
  270. function CalcFieldLen(FieldType: TFieldType; Size: Word): Word;
  271. begin
  272.   if not (FieldType in ftSupported) then
  273.     Result := 0
  274.   else if (FieldType in ftBlobTypes) then
  275.     Result := SizeOf(Longint)
  276.   else begin
  277.     Result := Size;
  278.     case FieldType of
  279.       ftString: Inc(Result);
  280.       ftSmallint: Result := SizeOf(SmallInt);
  281.       ftInteger: Result := SizeOf(Longint);
  282.       ftWord: Result := SizeOf(Word);
  283.       ftBoolean: Result := SizeOf(WordBool);
  284.       ftFloat: Result := SizeOf(Double);
  285.       ftCurrency: Result := SizeOf(Double);
  286.       ftBCD: Result := 34;
  287.       ftDate, ftTime: Result := SizeOf(Longint);
  288.       ftDateTime: Result := SizeOf(TDateTime);
  289.       ftBytes: Result := Size;
  290.       ftVarBytes: Result := Size + 2;
  291.       ftAutoInc: Result := SizeOf(Longint);
  292. {$IFDEF RX_D4}
  293.       ftADT: Result := 0;
  294.       ftFixedChar: Inc(Result);
  295.       ftWideString: Result := (Result + 1) * 2;
  296.       ftLargeint: Result := SizeOf(Int64);
  297. {$ENDIF}
  298. {$IFDEF RX_D5}
  299.       ftVariant: Result := SizeOf(Variant);
  300.       ftGuid: Result := GuidSize + 1;
  301. {$ENDIF}
  302.     end;
  303.   end;
  304. end;
  305. procedure CalcDataSize(FieldDef: TFieldDef; var DataSize: Integer);
  306. {$IFDEF RX_D4}
  307. var
  308.   I: Integer;
  309. {$ENDIF}
  310. begin
  311.   with FieldDef do begin
  312.     if (DataType in ftSupported - ftBlobTypes) then
  313.       Inc(DataSize, CalcFieldLen(DataType, Size) + 1);
  314. {$IFDEF RX_D4}
  315.     for I := 0 to ChildDefs.Count - 1 do
  316.       CalcDataSize(ChildDefs[I], DataSize);
  317. {$ENDIF}
  318.   end;
  319. end;
  320. procedure Error(const Msg: string);
  321. begin
  322.   DatabaseError(Msg);
  323. end;
  324. procedure ErrorFmt(const Msg: string; const Args: array of const);
  325. begin
  326.   DatabaseErrorFmt(Msg, Args);
  327. end;
  328. type
  329.   TBookmarkData = Integer;
  330.   PMemBookmarkInfo = ^TMemBookmarkInfo;
  331.   TMemBookmarkInfo = record
  332.     BookmarkData: TBookmarkData;
  333.     BookmarkFlag: TBookmarkFlag;
  334.   end;
  335. { TMemoryRecord }
  336. constructor TMemoryRecord.Create(MemoryData: TRxMemoryData);
  337. begin
  338.   CreateEx(MemoryData, True);
  339. end;
  340. constructor TMemoryRecord.CreateEx(MemoryData: TRxMemoryData;
  341.   UpdateParent: Boolean);
  342. begin
  343.   inherited Create;
  344.   SetMemoryData(MemoryData, UpdateParent);
  345. end;
  346. destructor TMemoryRecord.Destroy;
  347. begin
  348.   SetMemoryData(nil, True);
  349.   inherited Destroy;
  350. end;
  351. function TMemoryRecord.GetIndex: Integer;
  352. begin
  353.   if FMemoryData <> nil then Result := FMemoryData.FRecords.IndexOf(Self)
  354.   else Result := -1;
  355. end;
  356. procedure TMemoryRecord.SetMemoryData(Value: TRxMemoryData; UpdateParent: Boolean);
  357. var
  358.   I: Integer;
  359.   DataSize: Integer;
  360. begin
  361.   if FMemoryData <> Value then begin
  362.     if FMemoryData <> nil then begin
  363.       FMemoryData.FRecords.Remove(Self);
  364.       if FMemoryData.BlobFieldCount > 0 then
  365.         Finalize(PMemBlobArray(FBlobs)[0], FMemoryData.BlobFieldCount);
  366.       ReallocMem(FBlobs, 0);
  367.       ReallocMem(FData, 0);
  368.       FMemoryData := nil;
  369.     end;
  370.     if Value <> nil then begin
  371.       if UpdateParent then begin
  372.         Value.FRecords.Add(Self);
  373.         Inc(Value.FLastID);
  374.         FID := Value.FLastID;
  375.       end;
  376.       FMemoryData := Value;
  377.       if Value.BlobFieldCount > 0 then begin
  378.         ReallocMem(FBlobs, Value.BlobFieldCount * SizeOf(Pointer));
  379.         Initialize(PMemBlobArray(FBlobs)[0], Value.BlobFieldCount);
  380.       end;
  381.       DataSize := 0;
  382.       for I := 0 to Value.FieldDefs.Count - 1 do
  383.         CalcDataSize(Value.FieldDefs[I], DataSize);
  384.       ReallocMem(FData, DataSize);
  385.     end;
  386.   end;
  387. end;
  388. procedure TMemoryRecord.SetIndex(Value: Integer);
  389. var
  390.   CurIndex: Integer;
  391. begin
  392.   CurIndex := GetIndex;
  393.   if (CurIndex >= 0) and (CurIndex <> Value) then
  394.     FMemoryData.FRecords.Move(CurIndex, Value);
  395. end;
  396. { TRxMemoryData }
  397. constructor TRxMemoryData.Create(AOwner: TComponent);
  398. begin
  399.   inherited Create(AOwner);
  400.   FRecordPos := -1;
  401.   FLastID := Low(Integer);
  402.   FAutoInc := 1;
  403.   FRecords := TList.Create;
  404. end;
  405. destructor TRxMemoryData.Destroy;
  406. begin
  407.   inherited Destroy;
  408.   FreeIndexList;
  409.   ClearRecords;
  410.   FRecords.Free;
  411.   ReallocMem(FOffsets, 0);
  412. end;
  413. { Records Management }
  414. function TRxMemoryData.GetCapacity: Integer;
  415. begin
  416.   if FRecords <> nil then Result := FRecords.Capacity
  417.   else Result := 0;
  418. end;
  419. procedure TRxMemoryData.SetCapacity(Value: Integer);
  420. begin
  421.   if FRecords <> nil then FRecords.Capacity := Value;
  422. end;
  423. function TRxMemoryData.AddRecord: TMemoryRecord;
  424. begin
  425.   Result := TMemoryRecord.Create(Self);
  426. end;
  427. function TRxMemoryData.FindRecordID(ID: Integer): TMemoryRecord;
  428. var
  429.   I: Integer;
  430. begin
  431.   for I := 0 to FRecords.Count - 1 do begin
  432.     Result := TMemoryRecord(FRecords[I]);
  433.     if Result.ID = ID then Exit;
  434.   end;
  435.   Result := nil;
  436. end;
  437. function TRxMemoryData.InsertRecord(Index: Integer): TMemoryRecord;
  438. begin
  439.   Result := AddRecord;
  440.   Result.Index := Index;
  441. end;
  442. function TRxMemoryData.GetMemoryRecord(Index: Integer): TMemoryRecord;
  443. begin
  444.   Result := TMemoryRecord(FRecords[Index]);
  445. end;
  446. { Field Management }
  447. {$IFNDEF RX_D5}
  448. function TRxMemoryData.BCDToCurr(BCD: Pointer; var Curr: Currency): Boolean;
  449. begin
  450.   Move(BCD^, Curr, SizeOf(Currency));
  451.   Result := True;
  452. end;
  453. function TRxMemoryData.CurrToBCD(const Curr: Currency; BCD: Pointer; Precision,
  454.   Decimals: Integer): Boolean;
  455. begin
  456.   Move(Curr, BCD^, SizeOf(Currency));
  457.   Result := True;
  458. end;
  459. {$ENDIF RX_D5}
  460. procedure TRxMemoryData.InitFieldDefsFromFields;
  461. var
  462.   I: Integer;
  463.   Offset: Word;
  464. begin
  465.   if FieldDefs.Count = 0 then begin
  466.     for I := 0 to FieldCount - 1 do begin
  467.       with Fields[I] do
  468.         if (FieldKind in fkStoredFields) and not (DataType in ftSupported) then
  469.           ErrorFmt(SUnknownFieldType, [DisplayName]);
  470.     end;
  471.     FreeIndexList;
  472.   end;
  473.   Offset := 0;
  474. {$IFDEF RX_D4}
  475.   inherited InitFieldDefsFromFields;
  476.   { Calculate fields offsets }
  477.   ReallocMem(FOffsets, FieldDefList.Count * SizeOf(Word));
  478.   for I := 0 to FieldDefList.Count - 1 do begin
  479.     FOffsets^[I] := Offset;
  480.     with FieldDefList[I] do begin
  481.       if (DataType in ftSupported - ftBlobTypes) then
  482.         Inc(Offset, CalcFieldLen(DataType, Size) + 1);
  483.     end;
  484.   end;
  485. {$ELSE}
  486.   { Create FieldDefs from persistent fields if needed }
  487.   if FieldDefs.Count = 0 then
  488.     for I := 0 to FieldCount - 1 do begin
  489.       with Fields[I] do
  490.         if (FieldKind = fkData) then
  491.           FieldDefs.Add(FieldName, DataType, Size, Required);
  492.     end;
  493.   { Calculate fields offsets }
  494.   ReallocMem(FOffsets, FieldDefs.Count * SizeOf(Word));
  495.   for I := 0 to FieldDefs.Count - 1 do begin
  496.     FOffsets^[I] := Offset;
  497.     with FieldDefs[I] do begin
  498.       if (DataType in ftSupported - ftBlobTypes) then
  499.         Inc(Offset, CalcFieldLen(DataType, Size) + 1);
  500.     end;
  501.   end;
  502. {$ENDIF}
  503. end;
  504. function TRxMemoryData.FindFieldData(Buffer: Pointer; Field: TField): Pointer;
  505. var
  506.   Index: Integer;
  507. begin
  508. {$IFDEF RX_D4}
  509.   Index := FieldDefList.IndexOf(Field.FullName);
  510. {$ELSE}
  511.   Index := FieldDefs.IndexOf(Field.FieldName);
  512. {$ENDIF}
  513.   if (Index >= 0) and (Buffer <> nil) and
  514. {$IFDEF RX_D4}
  515.     (FieldDefList[Index].DataType in ftSupported - ftBlobTypes) then
  516. {$ELSE}
  517.     (FieldDefs[Index].DataType in ftSupported - ftBlobTypes) then
  518. {$ENDIF}
  519.     Result := (PChar(Buffer) + FOffsets[Index])
  520.   else Result := nil;
  521. end;
  522. { Buffer Manipulation }
  523. function TRxMemoryData.CalcRecordSize: Integer;
  524. var
  525.   I: Integer;
  526. begin
  527.   Result := 0;
  528.   for I := 0 to FieldDefs.Count - 1 do
  529.     CalcDataSize(FieldDefs[I], Result);
  530. end;
  531. procedure TRxMemoryData.InitBufferPointers(GetProps: Boolean);
  532. begin
  533.   if GetProps then FRecordSize := CalcRecordSize;
  534.   FBookmarkOfs := FRecordSize + CalcFieldsSize;
  535.   FBlobOfs := FBookmarkOfs + SizeOf(TMemBookmarkInfo);
  536.   FRecBufSize := FBlobOfs + BlobFieldCount * SizeOf(Pointer);
  537. end;
  538. procedure TRxMemoryData.ClearRecords;
  539. begin
  540.   while FRecords.Count > 0 do TObject(FRecords.Last).Free;
  541.   FLastID := Low(Integer);
  542.   FRecordPos := -1;
  543. end;
  544. function TRxMemoryData.AllocRecordBuffer: PChar;
  545. begin
  546.   Result := StrAlloc(FRecBufSize);
  547.   if BlobFieldCount > 0 then
  548.     Initialize(PMemBlobArray(Result + FBlobOfs)[0], BlobFieldCount);
  549. end;
  550. procedure TRxMemoryData.FreeRecordBuffer(var Buffer: PChar);
  551. begin
  552.   if BlobFieldCount > 0 then
  553.     Finalize(PMemBlobArray(Buffer + FBlobOfs)[0], BlobFieldCount);
  554.   StrDispose(Buffer);
  555.   Buffer := nil;
  556. end;
  557. procedure TRxMemoryData.ClearCalcFields(Buffer: PChar);
  558. begin
  559.   FillChar(Buffer[FRecordSize], CalcFieldsSize, 0);
  560. end;
  561. procedure TRxMemoryData.InternalInitRecord(Buffer: PChar);
  562. var
  563.   I: Integer;
  564. begin
  565.   FillChar(Buffer^, FBlobOfs, 0);
  566.   for I := 0 to BlobFieldCount - 1 do
  567.     PMemBlobArray(Buffer + FBlobOfs)[I] := '';
  568. end;
  569. procedure TRxMemoryData.InitRecord(Buffer: PChar);
  570. begin
  571.   inherited InitRecord(Buffer);
  572.   with PMemBookmarkInfo(Buffer + FBookmarkOfs)^ do begin
  573.     BookmarkData := Low(Integer);
  574.     BookmarkFlag := bfInserted;
  575.   end;
  576. end;
  577. function TRxMemoryData.GetCurrentRecord(Buffer: PChar): Boolean;
  578. begin
  579.   Result := False;
  580.   if not IsEmpty and (GetBookmarkFlag(ActiveBuffer) = bfCurrent) then begin
  581.     UpdateCursorPos;
  582.     if (FRecordPos >= 0) and (FRecordPos < RecordCount) then begin
  583.       Move(Records[FRecordPos].Data^, Buffer^, FRecordSize);
  584.       Result := True;
  585.     end;
  586.   end;
  587. end;
  588. procedure TRxMemoryData.RecordToBuffer(Rec: TMemoryRecord; Buffer: PChar);
  589. var
  590.   I: Integer;
  591. begin
  592.   Move(Rec.Data^, Buffer^, FRecordSize);
  593.   with PMemBookmarkInfo(Buffer + FBookmarkOfs)^ do begin
  594.     BookmarkData := Rec.ID;
  595.     BookmarkFlag := bfCurrent;
  596.   end;
  597.   for I := 0 to BlobFieldCount - 1 do
  598.     PMemBlobArray(Buffer + FBlobOfs)[I] := PMemBlobArray(Rec.FBlobs)[I];
  599.   GetCalcFields(Buffer);
  600. end;
  601. function TRxMemoryData.GetRecord(Buffer: PChar; GetMode: TGetMode;
  602.   DoCheck: Boolean): TGetResult;
  603. var
  604.   Accept: Boolean;
  605. begin
  606.   Result := grOk;
  607.   Accept := True;
  608.   case GetMode of
  609.     gmPrior:
  610.       if FRecordPos <= 0 then begin
  611.         Result := grBOF;
  612.         FRecordPos := -1;
  613.       end
  614.       else begin
  615.         repeat
  616.           Dec(FRecordPos);
  617.           if Filtered then Accept := RecordFilter;
  618.         until Accept or (FRecordPos < 0);
  619.         if not Accept then begin
  620.           Result := grBOF;
  621.           FRecordPos := -1;
  622.         end;
  623.       end;
  624.     gmCurrent:
  625.       if (FRecordPos < 0) or (FRecordPos >= RecordCount) then
  626.         Result := grError
  627.       else if Filtered then begin
  628.         if not RecordFilter then Result := grError;
  629.       end;
  630.     gmNext:
  631.       if FRecordPos >= RecordCount - 1 then Result := grEOF
  632.       else begin
  633.         repeat
  634.           Inc(FRecordPos);
  635.           if Filtered then Accept := RecordFilter;
  636.         until Accept or (FRecordPos > RecordCount - 1);
  637.         if not Accept then begin
  638.           Result := grEOF;
  639.           FRecordPos := RecordCount - 1;
  640.         end;
  641.       end;
  642.   end;
  643.   if Result = grOk then RecordToBuffer(Records[FRecordPos], Buffer)
  644.   else if (Result = grError) and DoCheck then Error(SMemNoRecords);
  645. end;
  646. function TRxMemoryData.GetRecordSize: Word;
  647. begin
  648.   Result := FRecordSize;
  649. end;
  650. function TRxMemoryData.GetActiveRecBuf(var RecBuf: PChar): Boolean;
  651. begin
  652.   case State of
  653.     dsBrowse:
  654.       if IsEmpty then RecBuf := nil
  655.       else RecBuf := ActiveBuffer;
  656.     dsEdit, dsInsert: RecBuf := ActiveBuffer;
  657.     dsCalcFields: RecBuf := CalcBuffer;
  658.     dsFilter: RecBuf := TempBuffer;
  659.     else RecBuf := nil;
  660.   end;
  661.   Result := RecBuf <> nil;
  662. end;
  663. function TRxMemoryData.GetFieldData(Field: TField; Buffer: Pointer): Boolean;
  664. var
  665.   RecBuf, Data: PChar;
  666. {$IFDEF RX_D5}
  667.   VarData: Variant;
  668. {$ENDIF}
  669. begin
  670.   Result := False;
  671.   if not GetActiveRecBuf(RecBuf) then Exit;
  672.   if Field.FieldNo > 0 then begin
  673.     Data := FindFieldData(RecBuf, Field);
  674.     if Data <> nil then begin
  675.       Result := Boolean(Data[0]);
  676.       Inc(Data);
  677.       if Field.DataType in [ftString {$IFDEF RX_D4}, ftFixedChar,
  678.         ftWideString {$ENDIF} {$IFDEF RX_D5}, ftGuid {$ENDIF}] then
  679.         Result := Result and (StrLen(Data) > 0);
  680.       if Result and (Buffer <> nil) then
  681. {$IFDEF RX_D5}
  682.         if Field.DataType = ftVariant then begin
  683.           VarData := PVariant(Data)^;
  684.           PVariant(Buffer)^ := VarData;
  685.         end else
  686. {$ENDIF}
  687.         Move(Data^, Buffer^, CalcFieldLen(Field.DataType, Field.Size));
  688.     end;
  689.   end
  690.   else begin
  691.     if State in [dsBrowse, dsEdit, dsInsert, dsCalcFields] then begin
  692.       Inc(RecBuf, FRecordSize + Field.Offset);
  693.       Result := Boolean(RecBuf[0]);
  694.       if Result and (Buffer <> nil) then
  695.         Move(RecBuf[1], Buffer^, Field.DataSize);
  696.     end;
  697.   end;
  698. end;
  699. procedure TRxMemoryData.SetFieldData(Field: TField; Buffer: Pointer);
  700. var
  701.   RecBuf, Data: PChar;
  702. {$IFDEF RX_D5}
  703.   VarData: Variant;
  704. {$ENDIF}
  705. begin
  706.   if not (State in dsWriteModes) then Error(SNotEditing);
  707.   GetActiveRecBuf(RecBuf);
  708.   with Field do begin
  709.     if FieldNo > 0 then
  710.     begin
  711.       if State in [dsCalcFields, dsFilter] then Error(SNotEditing);
  712.       if ReadOnly and not (State in [dsSetKey, dsFilter]) then
  713.         ErrorFmt(SFieldReadOnly, [DisplayName]);
  714.       Validate(Buffer);
  715.       if FieldKind <> fkInternalCalc then begin
  716.         Data := FindFieldData(RecBuf, Field);
  717.         if Data <> nil then begin
  718. {$IFDEF RX_D5}
  719.           if DataType = ftVariant then begin
  720.             if Buffer <> nil then
  721.               VarData := PVariant(Buffer)^
  722.             else
  723.               VarData := EmptyParam;
  724.             Boolean(Data[0]) := LongBool(Buffer) and not
  725.               (VarIsNull(VarData) or VarIsEmpty(VarData));
  726.             if Boolean(Data[0]) then begin
  727.               Inc(Data);
  728.               PVariant(Data)^ := VarData;
  729.             end
  730.             else FillChar(Data^, CalcFieldLen(DataType, Size), 0);
  731.           end else
  732. {$ENDIF}
  733.           begin
  734.             Boolean(Data[0]) := LongBool(Buffer);
  735.             Inc(Data);
  736.             if LongBool(Buffer) then
  737.               Move(Buffer^, Data^, CalcFieldLen(DataType, Size))
  738.             else FillChar(Data^, CalcFieldLen(DataType, Size), 0);
  739.           end;
  740.         end;
  741.       end;
  742.     end else {fkCalculated, fkLookup}
  743.     begin
  744.       Inc(RecBuf, FRecordSize + Offset);
  745.       Boolean(RecBuf[0]) := LongBool(Buffer);
  746.       if Boolean(RecBuf[0]) then Move(Buffer^, RecBuf[1], DataSize);
  747.     end;
  748.     if not (State in [dsCalcFields, dsFilter, dsNewValue]) then
  749.       DataEvent(deFieldChange, Longint(Field));
  750.   end;
  751. end;
  752. { Filter }
  753. procedure TRxMemoryData.SetFiltered(Value: Boolean);
  754. begin
  755.   if Active then begin
  756.     CheckBrowseMode;
  757.     if Filtered <> Value then begin
  758.       inherited SetFiltered(Value);
  759.       First;
  760.     end;
  761.   end
  762.   else inherited SetFiltered(Value);
  763. end;
  764. procedure TRxMemoryData.SetOnFilterRecord(const Value: TFilterRecordEvent);
  765. begin
  766.   if Active then begin
  767.     CheckBrowseMode;
  768.     inherited SetOnFilterRecord(Value);
  769.     if Filtered then First;
  770.   end
  771.   else inherited SetOnFilterRecord(Value);
  772. end;
  773. function TRxMemoryData.RecordFilter: Boolean;
  774. var
  775.   SaveState: TDataSetState;
  776. begin
  777.   Result := True;
  778.   if Assigned(OnFilterRecord) then begin
  779.     if (FRecordPos >= 0) and (FRecordPos < RecordCount) then begin
  780.       SaveState := SetTempState(dsFilter);
  781.       try
  782.         RecordToBuffer(Records[FRecordPos], TempBuffer);
  783.         OnFilterRecord(Self, Result);
  784.       except
  785.         Application.HandleException(Self);
  786.       end;
  787.       RestoreState(SaveState);
  788.     end
  789.     else Result := False;
  790.   end;
  791. end;
  792. { Blobs }
  793. function TRxMemoryData.GetBlobData(Field: TField; Buffer: PChar): TMemBlobData;
  794. begin
  795.   Result := PMemBlobArray(Buffer + FBlobOfs)[Field.Offset];
  796. end;
  797. procedure TRxMemoryData.SetBlobData(Field: TField; Buffer: PChar;
  798.   Value: TMemBlobData);
  799. begin
  800.   if (Buffer = ActiveBuffer) then begin
  801.     if State = dsFilter then Error(SNotEditing);
  802.     PMemBlobArray(Buffer + FBlobOfs)[Field.Offset] := Value;
  803.   end;
  804. end;
  805. procedure TRxMemoryData.CloseBlob(Field: TField);
  806. begin
  807.   if (FRecordPos >= 0) and (FRecordPos < FRecords.Count) and
  808.     (State = dsEdit) then
  809.     PMemBlobArray(ActiveBuffer + FBlobOfs)[Field.Offset] := 
  810.       PMemBlobArray(Records[FRecordPos].FBlobs)[Field.Offset]
  811.   else PMemBlobArray(ActiveBuffer + FBlobOfs)[Field.Offset] := '';
  812. end;
  813. function TRxMemoryData.CreateBlobStream(Field: TField; Mode: TBlobStreamMode): TStream;
  814. begin
  815.   Result := TMemBlobStream.Create(Field as TBlobField, Mode);
  816. end;
  817. { Bookmarks }
  818. function TRxMemoryData.BookmarkValid(Bookmark: TBookmark): Boolean;
  819. begin
  820.   Result := FActive and (TBookmarkData(Bookmark^) > Low(Integer)) and
  821.     (TBookmarkData(Bookmark^) <= FLastID);
  822. end;
  823. function TRxMemoryData.CompareBookmarks(Bookmark1, Bookmark2: TBookmark): Integer;
  824. begin
  825.   if (Bookmark1 = nil) and (Bookmark2 = nil) then Result := 0
  826.   else if (Bookmark1 <> nil) and (Bookmark2 = nil) then Result := 1
  827.   else if (Bookmark1 = nil) and (Bookmark2 <> nil) then Result := -1
  828.   else if TBookmarkData(Bookmark1^) > TBookmarkData(Bookmark2^) then
  829.     Result := 1
  830.   else if TBookmarkData(Bookmark1^) < TBookmarkData(Bookmark2^) then
  831.     Result := -1
  832.   else Result := 0;
  833. end;
  834. procedure TRxMemoryData.GetBookmarkData(Buffer: PChar; Data: Pointer);
  835. begin
  836.   Move(PMemBookmarkInfo(Buffer + FBookmarkOfs)^.BookmarkData, Data^,
  837.     SizeOf(TBookmarkData));
  838. end;
  839. procedure TRxMemoryData.SetBookmarkData(Buffer: PChar; Data: Pointer);
  840. begin
  841.   Move(Data^, PMemBookmarkInfo(Buffer + FBookmarkOfs)^.BookmarkData,
  842.     SizeOf(TBookmarkData));
  843. end;
  844. function TRxMemoryData.GetBookmarkFlag(Buffer: PChar): TBookmarkFlag;
  845. begin
  846.   Result := PMemBookmarkInfo(Buffer + FBookmarkOfs)^.BookmarkFlag;
  847. end;
  848. procedure TRxMemoryData.SetBookmarkFlag(Buffer: PChar; Value: TBookmarkFlag);
  849. begin
  850.   PMemBookmarkInfo(Buffer + FBookmarkOfs)^.BookmarkFlag := Value;
  851. end;
  852. procedure TRxMemoryData.InternalGotoBookmark(Bookmark: TBookmark);
  853. var
  854.   Rec: TMemoryRecord;
  855.   SavePos: Integer;
  856.   Accept: Boolean;
  857. begin
  858.   Rec := FindRecordID(TBookmarkData(Bookmark^));
  859.   if Rec <> nil then begin
  860.     Accept := True;
  861.     SavePos := FRecordPos;
  862.     try
  863.       FRecordPos := Rec.Index;
  864.       if Filtered then Accept := RecordFilter;
  865.     finally
  866.       if not Accept then FRecordPos := SavePos;
  867.     end;
  868.   end;
  869. end;
  870. { Navigation }
  871. procedure TRxMemoryData.InternalSetToRecord(Buffer: PChar);
  872. begin
  873.   InternalGotoBookmark(@PMemBookmarkInfo(Buffer + FBookmarkOfs)^.BookmarkData);
  874. end;
  875. procedure TRxMemoryData.InternalFirst;
  876. begin
  877.   FRecordPos := -1;
  878. end;
  879. procedure TRxMemoryData.InternalLast;
  880. begin
  881.   FRecordPos := FRecords.Count;
  882. end;
  883. { Data Manipulation }
  884. procedure TRxMemoryData.AssignMemoryRecord(Rec: TMemoryRecord; Buffer: PChar);
  885. var
  886.   I: Integer;
  887. begin
  888.   Move(Buffer^, Rec.Data^, FRecordSize);
  889.   for I := 0 to BlobFieldCount - 1 do
  890.     PMemBlobArray(Rec.FBlobs)[I] := PMemBlobArray(Buffer + FBlobOfs)[I];
  891. end;
  892. procedure TRxMemoryData.SetMemoryRecordData(Buffer: PChar; Pos: Integer);
  893. var
  894.   Rec: TMemoryRecord;
  895. begin
  896.   if State = dsFilter then Error(SNotEditing);
  897.   Rec := Records[Pos];
  898.   AssignMemoryRecord(Rec, Buffer);
  899. end;
  900. procedure TRxMemoryData.SetAutoIncFields(Buffer: PChar);
  901. var
  902.   I, Count: Integer;
  903.   Data: PChar;
  904. begin
  905.   Count := 0;
  906.   for I := 0 to FieldCount - 1 do
  907.     if (Fields[I].FieldKind in fkStoredFields) and
  908.       (Fields[I].DataType = ftAutoInc) then
  909.     begin
  910.       Data := FindFieldData(Buffer, Fields[I]);
  911.       if Data <> nil then begin
  912.         Boolean(Data[0]) := True;
  913.         Inc(Data);
  914.         Move(FAutoInc, Data^, SizeOf(Longint));
  915.         Inc(Count);
  916.       end;
  917.     end;
  918.   if Count > 0 then Inc(FAutoInc);
  919. end;
  920. procedure TRxMemoryData.InternalAddRecord(Buffer: Pointer; Append: Boolean);
  921. var
  922.   RecPos: Integer;
  923.   Rec: TMemoryRecord;
  924. begin
  925.   if Append then begin
  926.     Rec := AddRecord;
  927.     FRecordPos := FRecords.Count - 1;
  928.   end
  929.   else begin
  930.     if FRecordPos = -1 then RecPos := 0
  931.     else RecPos := FRecordPos;
  932.     Rec := InsertRecord(RecPos);
  933.     FRecordPos := RecPos;
  934.   end;
  935.   SetAutoIncFields(Buffer);
  936.   SetMemoryRecordData(Buffer, Rec.Index);
  937. end;
  938. procedure TRxMemoryData.InternalDelete;
  939. var
  940.   Accept: Boolean;
  941. begin
  942.   Records[FRecordPos].Free;
  943.   if FRecordPos >= FRecords.Count then Dec(FRecordPos);
  944.   Accept := True;
  945.   repeat
  946.     if Filtered then Accept := RecordFilter;
  947.     if not Accept then Dec(FRecordPos);
  948.   until Accept or (FRecordPos < 0);
  949.   if FRecords.Count = 0 then FLastID := Low(Integer);
  950. end;
  951. procedure TRxMemoryData.InternalPost;
  952. var
  953.   RecPos: Integer;
  954. begin
  955.   if State = dsEdit then
  956.     SetMemoryRecordData(ActiveBuffer, FRecordPos)
  957.   else begin
  958.     if State in [dsInsert] then SetAutoIncFields(ActiveBuffer);
  959.     if FRecordPos >= FRecords.Count then begin
  960.       SetMemoryRecordData(ActiveBuffer, AddRecord.Index);
  961.       FRecordPos := FRecords.Count - 1;
  962.     end
  963.     else begin
  964.       if FRecordPos = -1 then RecPos := 0
  965.       else RecPos := FRecordPos;
  966.       SetMemoryRecordData(ActiveBuffer, InsertRecord(RecPos).Index);
  967.       FRecordPos := RecPos;
  968.     end;
  969.   end;
  970. end;
  971. procedure TRxMemoryData.OpenCursor(InfoQuery: Boolean);
  972. begin
  973.   if not InfoQuery then begin
  974.     if FieldCount > 0 then FieldDefs.Clear;
  975.     InitFieldDefsFromFields;
  976.   end;
  977.   FActive := True;
  978.   inherited OpenCursor(InfoQuery);
  979. end;
  980. procedure TRxMemoryData.InternalOpen;
  981. begin
  982.   BookmarkSize := SizeOf(TBookmarkData);
  983. {$IFDEF RX_D4}
  984.   if DefaultFields then CreateFields;
  985. {$ELSE}
  986.   if DefaultFields then Error(SInvalidFields);
  987. {$ENDIF}
  988.   BindFields(True);
  989.   InitBufferPointers(True);
  990.   InternalFirst;
  991. end;
  992. procedure TRxMemoryData.InternalClose;
  993. begin
  994.   ClearRecords;
  995.   FAutoInc := 1;
  996.   BindFields(False);
  997. {$IFDEF RX_D4}
  998.   if DefaultFields then DestroyFields;
  999. {$ENDIF}
  1000.   FreeIndexList;
  1001.   FActive := False;
  1002. end;
  1003. procedure TRxMemoryData.InternalHandleException;
  1004. begin
  1005.   Application.HandleException(Self);
  1006. end;
  1007. procedure TRxMemoryData.InternalInitFieldDefs;
  1008. begin
  1009. end;
  1010. function TRxMemoryData.IsCursorOpen: Boolean;
  1011. begin
  1012.   Result := FActive;
  1013. end;
  1014. { Informational }
  1015. function TRxMemoryData.GetRecordCount: Integer;
  1016. begin
  1017.   Result := FRecords.Count;
  1018. end;
  1019. function TRxMemoryData.GetRecNo: Integer;
  1020. begin
  1021.   CheckActive;
  1022.   UpdateCursorPos;
  1023.   if (FRecordPos = -1) and (RecordCount > 0) then Result := 1
  1024.   else Result := FRecordPos + 1;
  1025. end;
  1026. procedure TRxMemoryData.SetRecNo(Value: Integer);
  1027. begin
  1028.   if (Value > 0) and (Value <= FRecords.Count) then begin
  1029.     FRecordPos := Value - 1;
  1030.     Resync([]);
  1031.   end;
  1032. end;
  1033. function TRxMemoryData.IsSequenced: Boolean;
  1034. begin
  1035.   Result := not Filtered;
  1036. end;
  1037. function TRxMemoryData.Locate(const KeyFields: string;
  1038.   const KeyValues: Variant; Options: TLocateOptions): Boolean;
  1039. begin
  1040.   DoBeforeScroll;
  1041.   Result := DataSetLocateThrough(Self, KeyFields, KeyValues, Options);
  1042.   if Result then begin
  1043.     DataEvent(deDataSetChange, 0);
  1044.     DoAfterScroll;
  1045.   end;
  1046. end;
  1047. { Table Manipulation }
  1048. procedure TRxMemoryData.EmptyTable;
  1049. begin
  1050.   if Active then begin
  1051.     CheckBrowseMode;
  1052.     ClearRecords;
  1053.     ClearBuffers;
  1054.     DataEvent(deDataSetChange, 0);
  1055.   end;
  1056. end;
  1057. procedure TRxMemoryData.CopyStructure(Source: TDataSet);
  1058.   procedure CheckDataTypes(FieldDefs: TFieldDefs);
  1059.   var
  1060.     I: Integer;
  1061.   begin
  1062.     for I := FieldDefs.Count - 1 downto 0 do begin
  1063.       if not (FieldDefs.Items[I].DataType in ftSupported) then
  1064.         FieldDefs.Items[I].Free
  1065. {$IFDEF RX_D4}
  1066.       else CheckDataTypes(FieldDefs[I].ChildDefs);
  1067. {$ENDIF}
  1068.     end;
  1069.   end;
  1070. var
  1071.   I: Integer;
  1072. begin
  1073.   CheckInactive;
  1074.   for I := FieldCount - 1 downto 0 do Fields[I].Free;
  1075.   if (Source = nil) then Exit;
  1076.   Source.FieldDefs.Update;
  1077.   FieldDefs := Source.FieldDefs;
  1078.   CheckDataTypes(FieldDefs);
  1079. {$IFDEF RX_D4}
  1080.   CreateFields;
  1081. {$ELSE}
  1082.   for I := 0 to FieldDefs.Count - 1 do begin
  1083.     if (csDesigning in ComponentState) and (Owner <> nil) then
  1084.       FieldDefs.Items[I].CreateField(Owner)
  1085.     else
  1086.       FieldDefs.Items[I].CreateField(Self);
  1087.   end;
  1088. {$ENDIF}
  1089. end;
  1090. function TRxMemoryData.LoadFromDataSet(Source: TDataSet; RecordCount: Integer;
  1091.   Mode: TLoadMode): Integer;
  1092. var
  1093.   SourceActive: Boolean;
  1094.   MovedCount: Integer;
  1095. begin
  1096.   Result := 0;
  1097.   if Source = Self then Exit;
  1098.   SourceActive := Source.Active;
  1099.   Source.DisableControls;
  1100.   try
  1101.     DisableControls;
  1102.     try
  1103.       Filtered := False;
  1104.       with Source do begin
  1105.         Open;
  1106.         CheckBrowseMode;
  1107.         UpdateCursorPos;
  1108.       end;
  1109.       if Mode = lmCopy then begin
  1110.         Close;
  1111.         CopyStructure(Source);
  1112.       end;
  1113.       FreeIndexList;
  1114.       if not Active then Open;
  1115.       CheckBrowseMode;
  1116.       if RecordCount > 0 then MovedCount := RecordCount
  1117.       else begin
  1118.         Source.First;
  1119.         MovedCount := MaxInt;
  1120.       end;
  1121.       try
  1122.         while not Source.EOF do begin
  1123.           Append;
  1124.           AssignRecord(Source, Self, True);
  1125.           Post;
  1126.           Inc(Result);
  1127.           if Result >= MovedCount then Break;
  1128.           Source.Next;
  1129.         end;
  1130.       finally
  1131.         First;
  1132.       end;
  1133.     finally
  1134.       EnableControls;
  1135.     end;
  1136.   finally
  1137.     if not SourceActive then Source.Close;
  1138.     Source.EnableControls;
  1139.   end;
  1140. end;
  1141. function TRxMemoryData.SaveToDataSet(Dest: TDataSet; RecordCount: Integer): Integer;
  1142. var
  1143.   MovedCount: Integer;
  1144. begin
  1145.   Result := 0;
  1146.   if Dest = Self then Exit;
  1147.   CheckBrowseMode;
  1148.   UpdateCursorPos;
  1149.   Dest.DisableControls;
  1150.   try
  1151.     DisableControls;
  1152.     try
  1153.       if not Dest.Active then Dest.Open
  1154.       else Dest.CheckBrowseMode;
  1155.       if RecordCount > 0 then MovedCount := RecordCount
  1156.       else begin
  1157.         First;
  1158.         MovedCount := MaxInt;
  1159.       end;
  1160.       try
  1161.         while not EOF do begin
  1162.           Dest.Append;
  1163.           AssignRecord(Self, Dest, True);
  1164.           Dest.Post;
  1165.           Inc(Result);
  1166.           if Result >= MovedCount then Break;
  1167.           Next;
  1168.         end;
  1169.       finally
  1170.         Dest.First;
  1171.       end;
  1172.     finally
  1173.       EnableControls;
  1174.     end;
  1175.   finally
  1176.     Dest.EnableControls;
  1177.   end;
  1178. end;
  1179. { Index Related }
  1180. procedure TRxMemoryData.SortOnFields(const FieldNames: string;
  1181. {$IFDEF RX_D4}
  1182.   CaseInsensitive: Boolean = True; Descending: Boolean = False);
  1183. {$ELSE}
  1184.   CaseInsensitive, Descending: Boolean);
  1185. {$ENDIF}
  1186. begin
  1187.   CreateIndexList(FieldNames);
  1188.   FCaseInsensitiveSort := CaseInsensitive;
  1189.   FDescendingSort := Descending;
  1190.   try
  1191.     Sort;
  1192.   except
  1193.     FreeIndexList;
  1194.     raise;
  1195.   end;
  1196. end;
  1197. procedure TRxMemoryData.Sort;
  1198. var
  1199.   Pos: TBookmarkStr;
  1200. begin
  1201.   if Active and (FRecords <> nil) and (FRecords.Count > 0) then begin
  1202.     Pos := Bookmark;
  1203.     try
  1204.       QuickSort(0, FRecords.Count - 1, CompareRecords);
  1205.       SetBufListSize(0);
  1206.       InitBufferPointers(False);
  1207.       try
  1208.         SetBufListSize(BufferCount + 1);
  1209.       except
  1210.         SetState(dsInactive);
  1211.         CloseCursor;
  1212.         raise;
  1213.       end;
  1214.     finally
  1215.       Bookmark := Pos;
  1216.     end;
  1217.     Resync([]);
  1218.   end;
  1219. end;
  1220. procedure TRxMemoryData.QuickSort(L, R: Integer; Compare: TCompareRecords);
  1221. var
  1222.   I, J: Integer;
  1223.   P: TMemoryRecord;
  1224. begin
  1225.   repeat
  1226.     I := L;
  1227.     J := R;
  1228.     P := Records[(L + R) shr 1];
  1229.     repeat
  1230.       while Compare(Records[I], P) < 0 do Inc(I);
  1231.       while Compare(Records[J], P) > 0 do Dec(J);
  1232.       if I <= J then begin
  1233.         FRecords.Exchange(I, J);
  1234.         Inc(I);
  1235.         Dec(J);
  1236.       end;
  1237.     until I > J;
  1238.     if L < J then QuickSort(L, J, Compare);
  1239.     L := I;
  1240.   until I >= R;
  1241. end;
  1242. function TRxMemoryData.CompareRecords(Item1, Item2: TMemoryRecord): Integer;
  1243. var
  1244.   Data1, Data2: PChar;
  1245.   F: TField;
  1246.   I: Integer;
  1247. begin
  1248.   Result := 0;
  1249.   if FIndexList <> nil then begin
  1250.     for I := 0 to FIndexList.Count - 1 do begin
  1251.       F := TField(FIndexList[I]);
  1252.       Data1 := FindFieldData(Item1.Data, F);
  1253.       if Data1 <> nil then begin
  1254.         Data2 := FindFieldData(Item2.Data, F);
  1255.         if Data2 <> nil then begin
  1256.           if Boolean(Data1[0]) and Boolean(Data2[0]) then begin
  1257.             Inc(Data1);
  1258.             Inc(Data2);
  1259.             Result := CompareFields(Data1, Data2, F.DataType,
  1260.               FCaseInsensitiveSort);
  1261.           end
  1262.           else if Boolean(Data1[0]) then Result := 1
  1263.           else if Boolean(Data2[0]) then Result := -1;
  1264.           if FDescendingSort then Result := -Result;
  1265.         end;
  1266.       end;
  1267.       if Result <> 0 then Exit;
  1268.     end;
  1269.   end;
  1270.   if (Result = 0) then begin
  1271.     if Item1.ID > Item2.ID then Result := 1
  1272.     else if Item1.ID < Item2.ID then Result := -1;
  1273.     if FDescendingSort then Result := -Result;
  1274.   end;
  1275. end;
  1276. function TRxMemoryData.GetIsIndexField(Field: TField): Boolean;
  1277. begin
  1278.   if FIndexList <> nil then
  1279.     Result := FIndexList.IndexOf(Field) >= 0
  1280.   else Result := False;
  1281. end;
  1282. procedure TRxMemoryData.CreateIndexList(const FieldNames: string);
  1283. var
  1284.   Pos: Integer;
  1285.   F: TField;
  1286. begin
  1287.   if FIndexList = nil then FIndexList := TList.Create
  1288.   else FIndexList.Clear;
  1289.   Pos := 1;
  1290.   while Pos <= Length(FieldNames) do begin
  1291.     F := FieldByName(ExtractFieldName(FieldNames, Pos));
  1292.     if (F.FieldKind = fkData) and
  1293.       (F.DataType in ftSupported - ftBlobTypes) then
  1294.       FIndexList.Add(F)
  1295.     else ErrorFmt(SFieldTypeMismatch, [F.DisplayName]);
  1296.   end;
  1297. end;
  1298. procedure TRxMemoryData.FreeIndexList;
  1299. begin
  1300.   FIndexList.Free;
  1301.   FIndexList := nil;
  1302. end;
  1303. { TMemBlobStream }
  1304. constructor TMemBlobStream.Create(Field: TBlobField; Mode: TBlobStreamMode);
  1305. begin
  1306.   FMode := Mode;
  1307.   FField := Field;
  1308.   FDataSet := FField.DataSet as TRxMemoryData;
  1309.   if not FDataSet.GetActiveRecBuf(FBuffer) then Exit;
  1310.   if not FField.Modified and (Mode <> bmRead) then begin
  1311.     if FField.ReadOnly then ErrorFmt(SFieldReadOnly, [FField.DisplayName]);
  1312.     if not (FDataSet.State in [dsEdit, dsInsert]) then Error(SNotEditing);
  1313.     FCached := True;
  1314.   end
  1315.   else FCached := (FBuffer = FDataSet.ActiveBuffer);
  1316.   FOpened := True;
  1317.   if Mode = bmWrite then Truncate;
  1318. end;
  1319. destructor TMemBlobStream.Destroy;
  1320. begin
  1321.   if FOpened and FModified then FField.Modified := True;
  1322.   if FModified then
  1323.   try
  1324.     FDataSet.DataEvent(deFieldChange, Longint(FField));
  1325.   except
  1326.     Application.HandleException(Self);
  1327.   end;
  1328. end;
  1329. function TMemBlobStream.GetBlobFromRecord(Field: TField): TMemBlobData;
  1330. var
  1331.   Rec: TMemoryRecord;
  1332.   Pos: Integer;
  1333. begin
  1334.   Result := '';
  1335.   Pos := FDataSet.FRecordPos;
  1336.   if (Pos < 0) and (FDataSet.RecordCount > 0) then Pos := 0
  1337.   else if Pos >= FDataSet.RecordCount then Pos := FDataSet.RecordCount - 1;
  1338.   if (Pos >= 0) and (Pos < FDataSet.RecordCount) then begin
  1339.     Rec := FDataSet.Records[Pos];
  1340.     if Rec <> nil then 
  1341.       Result := PMemBlobArray(Rec.FBlobs)[FField.Offset];
  1342.   end;
  1343. end;
  1344. function TMemBlobStream.Read(var Buffer; Count: Longint): Longint;
  1345. begin
  1346.   Result := 0;
  1347.   if FOpened then begin
  1348.     if Count > Size - FPosition then Result := Size - FPosition
  1349.     else Result := Count;
  1350.     if Result > 0 then begin
  1351.       if FCached then begin
  1352.         Move(PChar(FDataSet.GetBlobData(FField, FBuffer))[FPosition], Buffer,
  1353.           Result);
  1354.         Inc(FPosition, Result);
  1355.       end
  1356.       else begin
  1357.         Move(PChar(GetBlobFromRecord(FField))[FPosition], Buffer,
  1358.           Result);
  1359.         Inc(FPosition, Result);
  1360.       end;
  1361.     end;
  1362.   end;
  1363. end;
  1364. function TMemBlobStream.Write(const Buffer; Count: Longint): Longint;
  1365. var
  1366.   Temp: TMemBlobData;
  1367. begin
  1368.   Result := 0;
  1369.   if FOpened and FCached and (FMode <> bmRead) then begin
  1370.     Temp := FDataSet.GetBlobData(FField, FBuffer);
  1371.     if Length(Temp) < FPosition + Count then
  1372.       SetLength(Temp, FPosition + Count);
  1373.     Move(Buffer, PChar(Temp)[FPosition], Count);
  1374.     FDataSet.SetBlobData(FField, FBuffer, Temp);
  1375.     Inc(FPosition, Count);
  1376.     Result := Count;
  1377.     FModified := True;
  1378.   end;
  1379. end;
  1380. function TMemBlobStream.Seek(Offset: Longint; Origin: Word): Longint;
  1381. begin
  1382.   case Origin of
  1383.     0: FPosition := Offset;
  1384.     1: Inc(FPosition, Offset);
  1385.     2: FPosition := GetBlobSize + Offset;
  1386.   end;
  1387.   Result := FPosition;
  1388. end;
  1389. procedure TMemBlobStream.Truncate;
  1390. begin
  1391.   if FOpened and FCached and (FMode <> bmRead) then begin
  1392.     FDataSet.SetBlobData(FField, FBuffer, '');
  1393.     FModified := True;
  1394.   end;
  1395. end;
  1396. function TMemBlobStream.GetBlobSize: Longint;
  1397. begin
  1398.   Result := 0;
  1399.   if FOpened then
  1400.     if FCached then
  1401.       Result := Length(FDataSet.GetBlobData(FField, FBuffer))
  1402.     else
  1403.       Result := Length(GetBlobFromRecord(FField))
  1404. end;
  1405. {$ENDIF RX_D3}
  1406. end.