📄 memtableeh.pas
字号:
property OnCalcFields;
property OnDeleteError;
property OnEditError;
property OnFetchRecord;
property OnFilterRecord;
property OnNewRecord;
property OnPostError;
property OnUpdateRecord;
end;
implementation
uses Forms, DbConsts, Math,
{$IFDEF EH_LIB_6}
SqlTimSt, FmtBcd,
{$ENDIF}
TypInfo;
resourcestring
SMemNoRecords = 'No data found';
const
ftBlobTypes = [ftBlob, ftMemo, ftGraphic, ftFmtMemo, ftParadoxOle,
ftDBaseOle, ftTypedBinary {$IFDEF EH_LIB_5}, ftOraBlob, ftOraClob {$ENDIF}];
ftSupported = [ftString, ftSmallint, ftInteger, ftWord, ftBoolean, ftFloat,
ftCurrency, ftDate, ftTime, ftDateTime, ftAutoInc, ftBCD, ftBytes,
ftVarBytes, ftADT, ftFixedChar, ftWideString,
ftLargeint {$IFDEF EH_LIB_5}, ftVariant, ftGuid {$ENDIF}] +
ftBlobTypes;
fkStoredFields = [fkData];
{$IFDEF EH_LIB_5}
GuidSize = 38;
{$ENDIF}
type
CharArray = array of Char;
{ Utility routines }
function CalcFieldLen(FieldType: TFieldType; Size: Word): Word;
begin
if not (FieldType in ftSupported) then
Result := 0
else if (FieldType in ftBlobTypes) then
Result := SizeOf(Longint)
else
begin
Result := Size;
case FieldType of
ftString: Inc(Result);
ftSmallint: Result := SizeOf(SmallInt);
ftInteger: Result := SizeOf(Longint);
ftWord: Result := SizeOf(Word);
ftBoolean: Result := SizeOf(WordBool);
ftFloat: Result := SizeOf(Double);
ftCurrency: Result := SizeOf(Double);
ftBCD: Result := 34;
ftDate, ftTime: Result := SizeOf(Longint);
ftDateTime: Result := SizeOf(TDateTime);
ftBytes: Result := Size;
ftVarBytes: Result := Size + 2;
ftAutoInc: Result := SizeOf(Longint);
ftADT: Result := 0;
ftFixedChar: Inc(Result);
ftWideString: Result := (Result + 1) * 2;
ftLargeint: Result := SizeOf(Int64);
{$IFDEF EH_LIB_5}
ftVariant: Result := SizeOf(Variant);
ftGuid: Result := GuidSize + 1;
{$ENDIF}
end;
end;
end;
procedure CalcDataSize(FieldDef: TFieldDef; var DataSize: Integer);
var
I: Integer;
begin
with FieldDef do
begin
if (DataType in ftSupported - ftBlobTypes) then
Inc(DataSize, CalcFieldLen(DataType, Size) + 1);
for I := 0 to ChildDefs.Count - 1 do
CalcDataSize(ChildDefs[I], DataSize);
end;
end;
procedure Error(const Msg: string);
begin
DatabaseError(Msg);
end;
procedure ErrorFmt(const Msg: string; const Args: array of const);
begin
DatabaseErrorFmt(Msg, Args);
end;
//{$DEBUGINFO OFF}
function VarEquals(const V1, V2: Variant): Boolean;
var i: Integer;
begin
Result := not (VarIsArray(V1) xor VarIsArray(V2));
if not Result then Exit;
Result := False;
try
if VarIsArray(V1) and VarIsArray(V2) and
(VarArrayDimCount(V1) = VarArrayDimCount(V2)) and
(VarArrayLowBound(V1, 1) = VarArrayLowBound(V2, 1)) and
(VarArrayHighBound(V1, 1) = VarArrayHighBound(V2, 1))
then
for i := VarArrayLowBound(V1, 1) to VarArrayHighBound(V1, 1) do
begin
Result := V1[i] = V2[i];
if not Result then Exit;
end
else
Result := V1 = V2;
except
end;
end;
//{$DEBUGINFO ON}
function GetOldFieldValue(DataSet: TDataSet; const FieldName: string): Variant;
var
I: Integer;
Fields: TObjectList;
begin
if Pos(';', FieldName) <> 0 then
begin
Fields := TObjectList.Create(False);
try
DataSet.GetFieldList(Fields, FieldName);
Result := VarArrayCreate([0, Fields.Count - 1], varVariant);
for I := 0 to Fields.Count - 1 do
Result[I] := TField(Fields[I]).OldValue;
finally
Fields.Free;
end;
end else
Result := DataSet.FieldByName(FieldName).OldValue
end;
{ TMasterDataLinkEh }
constructor TMasterDataLinkEh.Create(DataSet: TDataSet);
begin
inherited Create;
FDataSet := DataSet;
FFields := TObjectList.Create(False);
end;
destructor TMasterDataLinkEh.Destroy;
begin
FFields.Free;
inherited Destroy;
end;
procedure TMasterDataLinkEh.ActiveChanged;
begin
FFields.Clear;
if Active then
try
DataSet.GetFieldList(FFields, FFieldNames);
except
FFields.Clear;
raise;
end;
if FDataSet.Active and not (csDestroying in FDataSet.ComponentState) then
if Active {and (FFields.Count > 0)} then
begin
if Assigned(FOnMasterChange) then FOnMasterChange(Self);
end else
if Assigned(FOnMasterDisable) then FOnMasterDisable(Self);
end;
procedure TMasterDataLinkEh.CheckBrowseMode;
begin
if FDataSet.Active then FDataSet.CheckBrowseMode;
end;
function TMasterDataLinkEh.GetDetailDataSet: TDataSet;
begin
Result := FDataSet;
end;
procedure TMasterDataLinkEh.LayoutChanged;
begin
ActiveChanged;
end;
procedure TMasterDataLinkEh.RecordChanged(Field: TField);
begin
if (DataSource.State <> dsSetKey) and FDataSet.Active and
{(FFields.Count > 0) and }((Field = nil) or
(FFields.IndexOf(Field) >= 0)) and
Assigned(FOnMasterChange)
then
FOnMasterChange(Self);
end;
procedure TMasterDataLinkEh.SetFieldNames(const Value: string);
begin
if FFieldNames <> Value then
begin
FFieldNames := Value;
ActiveChanged;
end;
end;
{ TCustomMemTableEh }
constructor TCustomMemTableEh.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
FRecordPos := -1;
FInstantReadCurRow := -1;
FAutoInc := 1;
FRecordCache := TObjectList.Create(True);
FRecordsView := TRecordsViewEh.Create(Self);
FRecordsView.OnFilterRecord := IsRecordInFilter;
FRecordsView.OnParseOrderByStr := ParseOrderByStr;
FRecordsView.OnCompareRecords := CompareRecords;
FRecordsView.OnCompareTreeNode := CompareTreeNodes;
FMasterDataLink := TMasterDataLinkEh.Create(Self);
FMasterDataLink.OnMasterChange := MasterChange;
FDetailFieldList := TObjectList.Create(False);
FParams := TParams.Create(Self);
FFilterExpr := TDataSetExprParserEh.Create(Self, dsptFilterEh);
FTreeList := TMemTableTreeListEh.Create(Self);
end;
destructor TCustomMemTableEh.Destroy;
begin
Close;
FFilterExpr.Free;
FParams.Free;
FDetailFieldList.Clear;
FDetailFieldList.Free;
ClearRecords;
FRecordsView.Free;
FMasterDataLink.Free;
FTreeList.Free;
FRecordCache.Free;
inherited Destroy;
end;
{ Field Management }
{$IFNDEF EH_LIB_5}
function TCustomMemTableEh.BCDToCurr(BCD: Pointer; var Curr: Currency): Boolean;
begin
Move(BCD^, Curr, SizeOf(Currency));
Result := True;
end;
function TCustomMemTableEh.CurrToBCD(const Curr: Currency; BCD: Pointer; Precision,
Decimals: Integer): Boolean;
begin
Move(Curr, BCD^, SizeOf(Currency));
Result := True;
end;
{$ENDIF EH_LIB_5}
procedure TCustomMemTableEh.InitFieldDefsFromFields;
var
I: Integer;
begin
if FieldDefs.Count = 0 then
begin
for I := 0 to FieldCount - 1 do
begin
with Fields[I] do
if (FieldKind in fkStoredFields) and not (DataType in ftSupported) then
ErrorFmt(SUnknownFieldType, [DisplayName]);
end;
// FreeIndexList;
end;
inherited InitFieldDefsFromFields;
end;
{ Buffer Manipulation }
procedure TCustomMemTableEh.InitBufferPointers(GetProps: Boolean);
begin
// if GetProps then
// FDataRecordSize := (Fields.Count * SizeOf(OleVariant));
{ TODO : FRecBufSize need? }
FRecBufSize := -1; //SizeOf(TRecInfo) + (Fields.Count * SizeOf(Pointer));
end;
procedure TCustomMemTableEh.ClearRecords;
begin
RecordsView.MemTableData.RecordsList.Clear;
RecordsView.MemTableData.AutoIncrement.Reset;
FRecordPos := -1;
FInstantReadCurRow := -1;
end;
function TCustomMemTableEh.IndexToBuffer(I: Integer): {$IFDEF CIL}TRecordBuffer{$ELSE}PChar{$ENDIF};
begin
{$IFDEF CIL}
Result := TRecordBuffer(I + 1);
{$ELSE}
Result := PChar(I + 1);
{$ENDIF}
end;
function TCustomMemTableEh.BufferToIndex(Buf: {$IFDEF CIL}TRecordBuffer{$ELSE}PChar{$ENDIF}): Integer;
begin
Result := Integer(Buf) - 1; // Buf is off by one so that nil (0) represents an invalid buffer
end;
function TCustomMemTableEh.BufferToRecBuf(Buf: {$IFDEF CIL}TRecordBuffer{$ELSE}PChar{$ENDIF}): TRecBuf;
begin
Result := TRecBuf(FRecordCache[BufferToIndex(Buf)]);
end;
function TCustomMemTableEh.AllocRecordBuffer: {$IFDEF CIL}TRecordBuffer{$ELSE}PChar{$ENDIF};
function InitializeBuffer(I: Integer): {$IFDEF CIL}TRecordBuffer{$ELSE}PChar{$ENDIF};
begin
TRecBuf(FRecordCache[I]).InUse := True;
TRecBuf(FRecordCache[I]).RecordNumber := -2;
Result := IndexToBuffer(I);
end;
var
RecBuf: TRecBuf;
I, NewIndex: Integer;
begin
for I := 0 to FRecordCache.Count - 1 do
if not TRecBuf(FRecordCache[I]).InUse then
begin
Result := InitializeBuffer(I);
Exit;
end;
RecBuf := TRecBuf.Create;
SetLength(RecBuf.Values, FieldCount);
for I := 0 to Fields.Count - 1 do
RecBuf.Values[I] := Null;
RecBuf.RecordStatus := -2;
RecBuf.TreeNode := nil;
NewIndex := FRecordCache.Add(RecBuf);
Result := InitializeBuffer(NewIndex);
end;
procedure TCustomMemTableEh.FreeRecordBuffer(var Buffer: {$IFDEF CIL}TRecordBuffer{$ELSE}PChar{$ENDIF});
var
// RecBuf: TRecBuf;
I: Integer;
begin
I := BufferToIndex(Buffer);
if I = FRecordCache.Count - 1 then
begin
// FRecordCache[FRecordCache.Count-1].Free;
FRecordCache.Count := I;
end else
begin
TRecBuf(FRecordCache[I]).InUse := False;
TRecBuf(FRecordCache[I]).RecordNumber := -1;
TRecBuf(FRecordCache[I]).TreeNode := nil;
end;
{ RecBuf := PRecBuf(Buffer);
SetLength(RecBuf^.Values, 0);
Dispose(RecBuf);}
Buffer := nil;
end;
procedure TCustomMemTableEh.ClearCalcFields(Buffer: {$IFDEF CIL}TRecordBuffer{$ELSE}PChar{$ENDIF});
var
I: Integer;
begin
if CalcFieldsSize > 0 then
for I := 0 to Fields.Count - 1 do
with Fields[I] do
if FieldKind in [fkCalculated, fkLookup] then
//new PRecBuf(Buffer)^.Values[Offset + DataFieldsCount].IsNull := True;
//ddd PRecBuf(Buffer)^.Values[FCalcFieldIndexes[I] + DataFieldsCount].VarValue := Null;
BufferToRecBuf(Buffer).Values[Index] := Null;
end;
procedure TCustomMemTableEh.InternalInitRecord(Buffer: {$IFDEF CIL}TRecordBuffer{$ELSE}PChar{$ENDIF});
var
I: Integer;
begin
for I := 0 to Fields.Count - 1 do
BufferToRecBuf(Buffer).Values[I] := Null;
BufferToRecBuf(Buffer).TreeNode := nil;
end;
procedure TCustomMemTableEh.InitRecord(Buffer: {$IFDEF CIL}TRecordBuffer{$ELSE}PChar{$ENDIF});
begin
inherited InitRecord(Buffer);
with BufferToRecBuf(Buffer) do
begin
Bookmark := Low(TRecIdEh);
BookmarkFlag := bfInserted;
// RecordStatus := 0;
RecordNumber := FRecordPos;
end;
end;
function TCustomMemTableEh.GetCurrentRecord(Buffer: {$IFDEF CIL}TRecordBuffer{$ELSE}PChar{$ENDIF}): Boolean;
begin
Result := False;
{ if not IsEmpty and (GetBookmarkFlag(ActiveBuffer) = bfCurrent) then
begin
UpdateCursorPos;
if (FRecordPos >= 0) and (FRecordPos < RecordCount) then
begin
Move(FRecords[FRecordPos]^, Buffer^, FDataRecordSize);
Result := True;
end;
end;
}
end;
procedure TCustomMemTableEh.RecordToBuffer(MemRec: TMemoryRecordEh;
DataValueVersion: TDataValueVersionEh; Buffer: {$IFDEF CIL}TRecordBuffer{$ELSE}PChar{$ENDIF});
var
i: Integer;
begin
with BufferToRecBuf(Buffer) do
begin
// RecordStatus := 0; //Recordset.Status;
BookmarkFlag := bfCurrent;
// Bookmark := FRecordPos;
end;
// Don't need assign data values
// Will do in on first SetFieldData
for i := 0 to FieldCount-1 do
if Fields[i].FieldNo > 0 then
BufferToRecBuf(Buffer).Values[Fields[i].Index] := MemRec.Value[Fields[i].FieldNo-1, dvvValueEh];
// VarValueToFieldValue(MemRec.Value[Fields[i].FieldNo-1, dvvValueEh],
// @(PRecBuf(Buffer)^.Values[Fields[i].Index]), Fields[i]);
GetCalcFields(Buffer);
end;
procedure TCustomMemTableEh.SetMemoryRecordData(Buffer: {$IFDEF CIL}TRecordBuffer{$ELSE}PChar{$ENDIF};
Rec: TMemoryRecordEh);
var
i: Integer;
begin
if State = dsFilter then
Error(SNotEditing);
for i := 0 to FieldCount-1 do
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -