FastReport_FMX_2.8.12/Source/FMX.frxCustomDB.pas
2024-07-06 22:41:12 +02:00

834 lines
20 KiB
ObjectPascal

{******************************************}
{ }
{ FastReport FMX v1.0 }
{ Custom TDataSet-based classes }
{ for enduser DB components }
{ }
{ Copyright (c) 1998-2013 }
{ by Alexander Tzyganenko, }
{ Fast Reports Inc. }
{ }
{******************************************}
unit FMX.frxCustomDB;
interface
{$I frx.inc}
uses
System.Classes, System.SysUtils, Data.DB, FMX.frxClass, FMX.frxDBSet,
FMX.ListBox, System.Variants, System.Types, FMX.Types
{$IFDEF QBUILDER}
, FMX.fqbClass
{$ENDIF}
{$IFDEF DELPHI19}
, FMX.Graphics
{$ENDIF};
type
TfrxCustomDataset = class(TfrxDBDataSet)
private
FDBConnected: Boolean;
FDataSource: TDataSource;
FMaster: TfrxDBDataSet;
FMasterFields: String;
procedure SetActive(Value: Boolean);
procedure SetFilter(const Value: String);
procedure SetFiltered(Value: Boolean);
function GetActive: Boolean;
function GetFields: TFields;
function GetFilter: String;
function GetFiltered: Boolean;
procedure InternalSetMaster(const Value: TfrxDBDataSet);
procedure InternalSetMasterFields(const Value: String);
protected
procedure Notification(AComponent: TComponent; Operation: TOperation); override;
procedure SetParent(AParent: TfrxComponent); override;
procedure SetUserName(const Value: String); override;
procedure SetMaster(const Value: TDataSource); virtual;
procedure SetMasterFields(const Value: String); virtual;
property DataSet;
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
procedure OnPaste; override;
property DBConnected: Boolean read FDBConnected write FDBConnected;
property Fields: TFields read GetFields;
property MasterFields: String read FMasterFields write InternalSetMasterFields;
property Active: Boolean read GetActive write SetActive default False;
published
property Filter: String read GetFilter write SetFilter;
property Filtered: Boolean read GetFiltered write SetFiltered default False;
property Master: TfrxDBDataset read FMaster write InternalSetMaster;
end;
TfrxCustomTable = class(TfrxCustomDataset)
protected
function GetIndexFieldNames: String; virtual;
function GetIndexName: String; virtual;
function GetTableName: String; virtual;
procedure SetIndexFieldNames(const Value: String); virtual;
procedure SetIndexName(const Value: String); virtual;
procedure SetTableName(const Value: String); virtual;
property DataSet;
published
property MasterFields;
property TableName: String read GetTableName write SetTableName;
property IndexName: String read GetIndexName write SetIndexName;
property IndexFieldNames: String read GetIndexFieldNames write SetIndexFieldNames;
end;
TfrxParamItem = class(TCollectionItem)
private
FDataType: TFieldType;
FExpression: String;
FName: String;
FValue: Variant;
public
procedure Assign(Source: TPersistent); override;
property Value: Variant read FValue write FValue;
published
property Name: String read FName write FName;
property DataType: TFieldType read FDataType write FDataType;
property Expression: String read FExpression write FExpression;
end;
TfrxParams = class(TCollection)
private
FIgnoreDuplicates: Boolean;
function GetParam(Index: Integer): TfrxParamItem;
public
constructor Create;
function Add: TfrxParamItem;
function Find(const Name: String): TfrxParamItem;
function IndexOf(const Name: String): Integer;
procedure UpdateParams(const SQL: String);
property Items[Index: Integer]: TfrxParamItem read GetParam; default;
property IgnoreDuplicates: Boolean read FIgnoreDuplicates write FIgnoreDuplicates;
end;
TfrxCustomQuery = class(TfrxCustomDataset)
private
FParams: TfrxParams;
FSaveOnBeforeOpen: TDataSetNotifyEvent;
FSaveOnChange: TNotifyEvent;
FSQLSchema: String;
procedure ReadData(Reader: TReader);
procedure SetParams(Value: TfrxParams);
procedure WriteData(Writer: TWriter);
function GetIgnoreDupParams: Boolean;
procedure SetIgnoreDupParams(const Value: Boolean);
protected
procedure DefineProperties(Filer: TFiler); override;
procedure OnBeforeOpen(DataSet: TDataSet); virtual;
procedure OnChangeSQL(Sender: TObject); virtual;
procedure SetSQL(Value: TStrings); virtual;
function GetSQL: TStrings; virtual;
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
procedure UpdateParams; virtual;
function ParamByName(const Value: String): TfrxParamItem;
{$IFDEF QBUILDER}
function QBEngine: TfqbEngine; virtual;
{$ENDIF}
published
property IgnoreDupParams: Boolean read GetIgnoreDupParams write SetIgnoreDupParams;
property Params: TfrxParams read FParams write SetParams;
property SQL: TStrings read GetSQL write SetSQL;
property SQLSchema: String read FSQLSchema write FSQLSchema;
end;
{ emulates work of DBLookupComboBox in VCL, for back compatibility only }
TfrxDBLookupComboBox = class(TfrxDialogControl)
private
FDataSet: TfrxDBDataSet;
FDataSetName: String;
FComboBox: TComboBox;
FAutoOpenDataSet: Boolean;
FIsFilled: Boolean;
FListField: String;
FKeyField: String;
FValue: Variant;
function GetDataSetName: String;
function GetKeyField: String;
function GetKeyValue: Variant;
function GetListField: String;
function GetText: String;
procedure SetDataSet(const Value: TfrxDBDataSet);
procedure SetDataSetName(const Value: String);
procedure SetKeyField(Value: String);
procedure SetKeyValue(const Value: Variant);
procedure SetListField(Value: String);
procedure UpdateDataSet;
procedure OnOpenDS(Sender: TObject);
function GetDropDownWidth: Integer;
procedure SetDropDownWidth(const Value: Integer);
function GetDropDownRows: Integer;
procedure SetDropDownRows(const Value: Integer);
procedure DoOnChange(Sender: TObject);
procedure FillWithData;
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
class function GetDescription: String; override;
procedure BeforeStartReport; override;
property ComboBox: TComboBox read FComboBox;
property KeyValue: Variant read GetKeyValue write FValue;
property Text: String read GetText;
procedure PaintControl(Sender: TObject; Canvas: TCanvas; const ARect: TRectF);
published
property AutoOpenDataSet: Boolean read FAutoOpenDataSet write FAutoOpenDataSet default False;
property DataSet: TfrxDBDataset read FDataSet write SetDataSet;
property DataSetName: String read GetDataSetName write SetDataSetName;
property ListField: String read GetListField write SetListField;
property KeyField: String read GetKeyField write SetKeyField;
property DropDownWidth: Integer read GetDropDownWidth write SetDropDownWidth;
property DropDownRows: Integer read GetDropDownRows write SetDropDownRows;
property OnClick;
property OnDblClick;
property OnEnter;
property OnExit;
property OnKeyDown;
property OnKeyPress;
property OnKeyUp;
property OnMouseDown;
property OnMouseMove;
property OnMouseUp;
end;
procedure frxParamsToTParams(Query: TfrxCustomQuery; Params: TParams);
implementation
uses
{$IFNDEF NO_EDITORS}
FMX.frxCustomDBEditor,
{$ENDIF}
FMX.frxCustomDBRTTI, FMX.frxDsgnIntf, FMX.frxUtils, FMX.frxRes;
{ TfrxParamItem }
procedure TfrxParamItem.Assign(Source: TPersistent);
begin
if Source is TfrxParamItem then
begin
FName := TfrxParamItem(Source).Name;
FDataType := TfrxParamItem(Source).DataType;
FExpression := TfrxParamItem(Source).Expression;
FValue := TfrxParamItem(Source).Value;
end;
end;
{ TfrxParams }
constructor TfrxParams.Create;
begin
inherited Create(TfrxParamItem);
FIgnoreDuplicates := False;
end;
function TfrxParams.Add: TfrxParamItem;
begin
Result := TfrxParamItem(inherited Add);
end;
function TfrxParams.GetParam(Index: Integer): TfrxParamItem;
begin
Result := TfrxParamItem(inherited Items[Index]);
end;
function TfrxParams.Find(const Name: String): TfrxParamItem;
var
i: Integer;
begin
i := IndexOf(Name);
if i <> -1 then
Result := Items[i] else
Result := nil;
end;
function TfrxParams.IndexOf(const Name: String): Integer;
var
i: Integer;
begin
Result := -1;
for i := 0 to Count - 1 do
if CompareText(Items[i].Name, Name) = 0 then
begin
Result := i;
break;
end;
end;
procedure TfrxParams.UpdateParams(const SQL: String);
var
i, j: Integer;
QParams: TParams;
NewParams: TfrxParams;
begin
{ parse query params }
QParams := TParams.Create;
QParams.ParseSQL(SQL, True);
{ create new TfrxParams object and copy all params to it }
NewParams := TfrxParams.Create;
for i := 0 to QParams.Count - 1 do
if not ((NewParams.IndexOf(QParams[i].Name) <> -1) and FIgnoreDuplicates) then
with NewParams.Add do
begin
Name := QParams[i].Name;
j := IndexOf(Name);
if j <> -1 then
begin
DataType := Items[j].DataType;
Value := Items[j].Value;
Expression := Items[j].Expression;
end;
end;
Assign(NewParams);
QParams.Free;
NewParams.Free;
end;
{ TfrxCustomDataset }
constructor TfrxCustomDataset.Create(AOwner: TComponent);
begin
Component := Dataset;
inherited;
CloseDataSource := True;
FDataSource := TDataSource.Create(nil);
SetMaster(FDataSource);
end;
destructor TfrxCustomDataset.Destroy;
begin
FDataSource.Free;
inherited;
end;
procedure TfrxCustomDataset.Notification(AComponent: TComponent;
Operation: TOperation);
begin
inherited;
if Operation = opRemove then
if AComponent = FMaster then
Master := nil
end;
procedure TfrxCustomDataset.SetParent(AParent: TfrxComponent);
begin
inherited;
if (AParent <> nil) and (Report <> nil) then
begin
if IsDesigning and (Report.DataSets.Find(Self) = nil) then
begin
Report.DataSets.Add(Self);
if Report.Designer <> nil then
Report.Designer.UpdateDataTree;
end;
end;
end;
procedure TfrxCustomDataset.SetUserName(const Value: String);
begin
inherited;
if (Report <> nil) and (Report.Designer <> nil) then
Report.Designer.UpdateDataTree;
end;
procedure TfrxCustomDataset.OnPaste;
var
i: Integer;
sl: TStringList;
begin
if Report = nil then Exit;
if Report.DataSets.Find(Self) = nil then
Report.DataSets.Add(Self);
sl := TStringList.Create;
Report.GetDatasetList(sl);
for i := 0 to sl.Count - 1 do
if (sl.Objects[i] <> Self) and (CompareText(sl[i], UserName) = 0) then
begin
if Name <> '' then
UserName := Name;
break;
end;
sl.Free;
Report.Designer.UpdateDataTree;
end;
procedure TfrxCustomDataset.SetActive(Value: Boolean);
begin
Dataset.Active := Value;
end;
procedure TfrxCustomDataset.SetFilter(const Value: String);
begin
Dataset.Filter := Value;
end;
function TfrxCustomDataset.GetActive: Boolean;
begin
Result := Dataset.Active;
end;
function TfrxCustomDataset.GetFields: TFields;
begin
Result := Dataset.Fields;
end;
function TfrxCustomDataset.GetFilter: String;
begin
Result := Dataset.Filter;
end;
function TfrxCustomDataset.GetFiltered: Boolean;
begin
Result := Dataset.Filtered;
end;
procedure TfrxCustomDataset.SetFiltered(Value: Boolean);
begin
Dataset.Filtered := Value;
end;
procedure TfrxCustomDataset.InternalSetMaster(const Value: TfrxDBDataSet);
begin
FMaster := Value;
if FMaster <> nil then
FDataSource.DataSet := FMaster.GetDataSet
else
FDataSource.DataSet := nil;
end;
procedure TfrxCustomDataset.InternalSetMasterFields(const Value: String);
var
sl: TStringList;
s: String;
i: Integer;
function ConvertAlias(const s: String): String;
begin
if FMaster <> nil then
Result := FMaster.ConvertAlias(s)
else
Result := s;
end;
begin
FMasterFields := Value;
sl := TStringList.Create;
frxSetCommaText(Value, sl);
s := '';
for i := 0 to sl.Count - 1 do
s := s + ConvertAlias(sl.Values[sl.Names[i]]) + ';';
s := Copy(s, 1, Length(s) - 1);
SetMasterFields(s);
s := '';
for i := 0 to sl.Count - 1 do
s := s + ConvertAlias(sl.Names[i]) + ';';
s := Copy(s, 1, Length(s) - 1);
if Self is TfrxCustomTable then
TfrxCustomTable(Self).SetIndexFieldNames(s);
sl.Free;
end;
procedure TfrxCustomDataset.SetMaster(const Value: TDataSource);
begin
// do nothing
end;
procedure TfrxCustomDataset.SetMasterFields(const Value: String);
begin
// do nothing
end;
{ TfrxCustomTable }
function TfrxCustomTable.GetIndexFieldNames: String;
begin
Result := '';
end;
function TfrxCustomTable.GetIndexName: String;
begin
Result := '';
end;
function TfrxCustomTable.GetTableName: String;
begin
Result := '';
end;
procedure TfrxCustomTable.SetIndexFieldNames(const Value: String);
begin
// do nothing
end;
procedure TfrxCustomTable.SetIndexName(const Value: String);
begin
// do nothing
end;
procedure TfrxCustomTable.SetTableName(const Value: String);
begin
// do nothing
end;
{ TfrxCustomQuery }
constructor TfrxCustomQuery.Create(AOwner: TComponent);
begin
inherited;
FParams := TfrxParams.Create;
FSaveOnBeforeOpen := DataSet.BeforeOpen;
DataSet.BeforeOpen := OnBeforeOpen;
FSaveOnChange := TStringList(SQL).OnChange;
TStringList(SQL).OnChange := OnChangeSQL;
end;
destructor TfrxCustomQuery.Destroy;
begin
FParams.Free;
inherited;
end;
procedure TfrxCustomQuery.DefineProperties(Filer: TFiler);
begin
inherited;
Filer.DefineProperty('Parameters', ReadData, WriteData, True);
end;
procedure TfrxCustomQuery.ReadData(Reader: TReader);
begin
frxReadCollection(FParams, Reader, Self);
UpdateParams;
end;
procedure TfrxCustomQuery.WriteData(Writer: TWriter);
begin
frxWriteCollection(FParams, Writer, Self);
end;
procedure TfrxCustomQuery.OnBeforeOpen(DataSet: TDataSet);
begin
UpdateParams;
if Assigned(FSaveOnBeforeOpen) then
FSaveOnBeforeOpen(DataSet);
end;
procedure TfrxCustomQuery.OnChangeSQL(Sender: TObject);
begin
if Assigned(FSaveOnChange) then
FSaveOnChange(Sender);
FParams.UpdateParams(SQL.Text);
end;
procedure TfrxCustomQuery.SetParams(Value: TfrxParams);
begin
FParams.Assign(Value);
end;
function TfrxCustomQuery.ParamByName(const Value: String): TfrxParamItem;
begin
Result := FParams.Find(Value);
if Result = nil then
raise Exception.Create('Parameter "' + Value + '" not found');
end;
procedure TfrxCustomQuery.SetSQL(Value: TStrings);
begin
//
end;
function TfrxCustomQuery.GetSQL: TStrings;
begin
Result := nil;
end;
procedure TfrxCustomQuery.UpdateParams;
begin
//
end;
{$IFDEF QBUILDER}
function TfrxCustomQuery.QBEngine: TfqbEngine;
begin
Result := nil;
end;
{$ENDIF}
{ frxParamsToTParams }
procedure frxParamsToTParams(Query: TfrxCustomQuery; Params: TParams);
var
i: Integer;
Item: TfrxParamItem;
begin
for i := 0 to Params.Count - 1 do
if Query.Params.IndexOf(Params[i].Name) <> -1 then
begin
Item := Query.Params[Query.Params.IndexOf(Params[i].Name)];
Params[i].Clear;
Params[i].DataType := Item.DataType;
{ Bound should be True in design mode }
if not (Query.IsLoading or Query.IsDesigning) then
Params[i].Bound := False
else
begin
if Item.Expression <> '' then
Params[i].Value := 0;
Params[i].Bound := True;
end;
if Trim(Item.Expression) <> '' then
if not (Query.IsLoading or Query.IsDesigning) then
if Query.Report <> nil then
begin
Query.Report.CurObject := Query.Name;
Item.Value := Query.Report.Calc(Item.Expression);
end;
if not VarIsEmpty(Item.Value) then
begin
Params[i].Bound := True;
if Params[i].DataType in [ftDate, ftTime, ftDateTime] then
Params[i].Value := Item.Value
else
Params[i].Text := VarToStr(Item.Value);
end;
end;
end;
function TfrxCustomQuery.GetIgnoreDupParams: Boolean;
begin
Result := FParams.FIgnoreDuplicates;
end;
procedure TfrxCustomQuery.SetIgnoreDupParams(const Value: Boolean);
begin
FParams.FIgnoreDuplicates := Value;
FParams.UpdateParams(SQL.Text);
end;
{ TfrxDBLookupComboBox }
constructor TfrxDBLookupComboBox.Create(AOwner: TComponent);
begin
inherited;
FComboBox := TComboBox.Create(nil);
InitControl(FComboBox);
Width := 145;
Height := 21;
FComboBox.OnPainting := PaintControl;
FIsFilled := False;
FComboBox.OnChange := DoOnChange;
// FDBLookupComboBox.ListSource := FDataSource;
end;
destructor TfrxDBLookupComboBox.Destroy;
begin
inherited;
end;
procedure TfrxDBLookupComboBox.DoOnChange(Sender: TObject);
begin
if (FDataSet <> nil) and FDataSet.HasField(FListField) and (FComboBox.Selected <> nil) then
begin
FDataSet.DataSet.Locate(FDataSet.ConvertAlias(FListField), FComboBox.Selected.Text, []);
if(FDataSet.HasField(FKeyField)) then
begin
FValue := FDataSet.Value[FKeyField];
end;
end;
end;
procedure TfrxDBLookupComboBox.FillWithData;
begin
if not FIsFilled then
begin
if FDataSet = nil then Exit;
if not FDataSet.HasField(FListField) then Exit;
FDataSet.First;
{$IFDEF DELPHI23}
FComboBox.Clear;
{$ELSE}
FComboBox.Items.Clear;
{$ENDIF}
FComboBox.BeginUpdate;
try
while not FDataSet.Eof do
begin
FComboBox.Items.Add(FDataSet.Value[FListField]);
FDataSet.Next;
end;
finally
FComboBox.EndUpdate;
end;
if FComboBox.Items.Count > 0 then
FComboBox.ListItems[0].IsSelected := True;
FIsFilled := True;
end;
end;
class function TfrxDBLookupComboBox.GetDescription: String;
begin
Result := frxResources.Get('obDBLookup');
end;
function TfrxDBLookupComboBox.GetDataSetName: String;
begin
if FDataSet = nil then
Result := FDataSetName else
Result := FDataSet.UserName;
end;
function TfrxDBLookupComboBox.GetKeyField: String;
begin
Result := FKeyField;
end;
function TfrxDBLookupComboBox.GetKeyValue: Variant;
begin
Result := FValue;
end;
function TfrxDBLookupComboBox.GetListField: String;
begin
Result := FListField;
FDataSet := TfrxDBDataSet(Report.GetDataset(FDataSetName));
end;
function TfrxDBLookupComboBox.GetText: String;
begin
if FComboBox.Selected <> nil then
Result := FComboBox.Selected.Text;
end;
procedure TfrxDBLookupComboBox.SetDataSet(const Value: TfrxDBDataSet);
begin
FDataSet := Value;
if FDataSet = nil then
FDataSetName := '' else
FDataSetName := FDataSet.UserName;
UpdateDataSet;
end;
procedure TfrxDBLookupComboBox.SetDataSetName(const Value: String);
begin
FDataSetName := Value;
FDataSet := TfrxDBDataSet(FindDataSet(FDataSet, FDataSetName));
UpdateDataSet;
end;
procedure TfrxDBLookupComboBox.SetKeyField(Value: String);
begin
FKeyField := Value;
if FListField = '' then
FListField := FKeyField;
end;
procedure TfrxDBLookupComboBox.SetKeyValue(const Value: Variant);
var
i: Integer;
begin
if (FDataSet <> nil) and FDataSet.HasField(FKeyField) and FDataSet.HasField(FListField) then
begin
FillWithData;
FDataSet.DataSet.Locate(FDataSet.ConvertAlias(FKeyField), Value, []);
i := FComboBox.Items.IndexOf(FDataSet.Value[FListField]);
if i > 0 then
begin
FValue := Value;
FComboBox.ListItems[i].IsSelected := True;
FComboBox.ItemIndex := i;
end;
end;
end;
procedure TfrxDBLookupComboBox.SetListField(Value: String);
begin
FListField := Value;
FIsFilled := False;
end;
procedure TfrxDBLookupComboBox.UpdateDataSet;
begin
FIsFilled := False;
end;
procedure TfrxDBLookupComboBox.BeforeStartReport;
begin
SetListField(FListField);
SetKeyField(FKeyField);
Self.OnActivate := OnOpenDS;
end;
procedure TfrxDBLookupComboBox.OnOpenDS(Sender: TObject);
begin
UpdateDataSet;
if (FDataSet <> nil) and (FAutoOpenDataSet) then
FDataSet.Open;
FIsFilled := False;
FillWithData;
SetKeyValue(FValue);
end;
procedure TfrxDBLookupComboBox.PaintControl(Sender: TObject; Canvas: TCanvas;
const ARect: TRectF);
begin
FillWithData;
end;
function TfrxDBLookupComboBox.GetDropDownWidth: Integer;
begin
Result := 0;
end;
procedure TfrxDBLookupComboBox.SetDropDownWidth(const Value: Integer);
begin
//FDBLookupComboBox.DropDownWidth := Value;
end;
function TfrxDBLookupComboBox.GetDropDownRows: Integer;
begin
Result := FComboBox.DropDownCount;
end;
procedure TfrxDBLookupComboBox.SetDropDownRows(const Value: Integer);
begin
FComboBox.DropDownCount := Value;;
end;
initialization
frxObjects.RegisterObject1(TfrxDBLookupComboBox, nil, '', '', 0, 119);
finalization
frxObjects.UnRegister(TfrxDBLookupComboBox);
end.