unit ULObjTblUsru;{v0.48}

interface
uses
  SysUtils, Classes, Variants, Menus, Controls, DB, DBTables, Forms,
  ULRecTyp, ULObju, ULObjUsru, ULObjDBu, DBUtl, DBGrids, DBGridFrm, DBGridFrameu,
  Language{v0.50},ULObjDes, GPTimeZone{/v0.50}{v0.61}, ObjList{/v0.61};

type
  {v0.61}
  TULObjTblUsr = class;
  TULObjTblUsrFields = class;

  TULObjTblUsrField = class(TObject)
  private
    FTblUsrFields: TULObjTblUsrFields;
    FField: TField;
    FFldDesc: TULObjFldDesc;
    procedure OnEnumGetText(Sender: TField; var Text: string; DisplayText: Boolean);
    procedure OnEnumSetText(Sender: TField; const Text: string);
  public
    constructor Create(AULObjTblUsrFields: TULObjTblUsrFields; AField: TField; AFldDesc: TULObjFldDesc); reintroduce;
    property Field: TField read FField;
    property FldDesc: TULObjFldDesc read FFldDesc;
  end;

  TULObjTblUsrFields = class(TObjList)
  private
    FTblUsr: TULObjTblUsr;
    FCalcFieldCount: integer;
    function GetUsrField(Index: integer): TULObjTblUsrField;
  public
    constructor Create(AULObjTblUsr: TULObjTblUsr); reintroduce;
    procedure FieldAdd(AField: TField; AFldDesc: TULObjFldDesc);
    function Find(AField: TField; var AUsrField: TULObjTblUsrField): boolean;
    property CalcFieldCount: integer read FCalcFieldCount;
    property UsrFields[Index: integer]: TULObjTblUsrField read GetUsrField; default;
  end;

  {/v0.61}

  TULObjTblUsr = class(TULObjUsr)
  private
    FForm: TDBGridForm;
    FTmpChild: TULObjUsr;
    FIsInSetText: boolean;
    FUsrFields: TULObjTblUsrFields;
      { list of TULObjTblUsrField, of fields that should eventually get
        special treatment; created automatically in TableFieldsCreated;
        can be modified in overriden method. }
    FInFieldChanged: TList; //use to prevent infinite recursions in DoOnFieldChanged
    FInFieldValidate: TList;
    FBookmarks: TList;
    FOldTableHandlersAssigned: boolean;
    function GetTmpChild: TULObjUsr;
    function GetUsrFields: TULObjTblUsrFields;
    {/v0.61}
    {v0.63}
    { Assigned to RxDBGrid.OnShowEditr. Calls DoShowEditor virtual method. }

    procedure DoOnBeforeEdit(DataSet: TDataSet);
    procedure DoOnBeforeInsert(DataSet: TDataSet);
    procedure DoOnBeforePost(DataSet: TDataSet);
    procedure DoOnAfterPost(DataSet: TDataSet);
    procedure DoOnBeforeDelete(DataSet: TDataSet);
    procedure DoOnAfterDelete(DataSet: TDataSet);
    procedure DoOnBeforeCancel(DataSet: TDataSet);
    procedure DoOnAfterCancel(DataSet: TDataSet);
    {/v0.63}
    function GetTable: TDataSet;//Table;
    procedure DoOnOnNewRecord(DataSet: TDataSet);


  protected
    FTable: TDataSet;//TTable;
    FOwnsTable: boolean; // true if FTable created here
    FBeforePost: TDataSetNotifyEvent; // eventual original Table.BeforePost handler
    FOnCalcFields: TDataSetNotifyEvent;
    FBeforeInsert: TDataSetNotifyEvent;
    FBeforeEdit: TDataSetNotifyEvent;
    FAfterPost: TDataSetNotifyEvent;
    FBeforeDelete: TDataSetNotifyEvent;
    FAfterDelete: TDataSetNotifyEvent;
    FAfterCancel: TDataSetNotifyEvent;
    FBeforeCancel: TDataSetNotifyEvent;

    FOnNewRecord: TDataSetNotifyEvent;

    FDBGridFrame: TDBGridFrame;
    FDataSetCancelingNewRecord: TDataSet;// set to DataSet in DoOnBeforeCancel if DataSet.State = dsInsert, otherwise set to nil

    procedure SetTable(ATable: TDataSet);virtual;
    { called when field changed in browser; calls DoFieldChanged }
    procedure DoOnFieldChanged(AField: TField);
    procedure DoOnFieldValidate(AField: TField);
    { Called when ellipsis button in grid cell pressed (for SelectedField).
      Calls DoFieldChangedModal if field value changed. }
    procedure DoOnEditButtonClick(Sender: TObject);
    procedure DoOnShowEditor(Sender: TObject; Field: TField;
      var AllowEdit: Boolean);
    procedure DoOnDBGridFormActivate(Sender: TObject);
    procedure DoOnDBGridFormDeactivate(Sender: TObject);
    procedure DoOnDBGridCellClick(AColumn: TColumn);


    function GetChildRecID: TULRecID; virtual; abstract;
    procedure TableCreate(ATable: TDataSet{TTable}); virtual;
      { create FieldDefs, IndexesDefs; called if table does not exists, after
        the call table will be created using the specified Defs;
        can be overriden to add some indexdefs }
      procedure IndexesCreate(ATable: TDataSet); virtual;
    procedure Notification(AComponent: TComponent; Operation: TOperation); override;
    procedure DoCalcFields(ADataSet: TDataSet); virtual;
    { called when field changed in browser from DoOnFieldChanged }
    procedure DoFieldChanged(AField: TField); virtual;
    procedure DoFieldValidate(AField: TField); virtual;

    procedure DoOnNewRecord; virtual;
    procedure DoShowEditor(Sender: TObject; Field: TField;
      var AllowEdit: Boolean); virtual;
    procedure DoFieldChangedModal(AField: TField; const ALastValue: string); virtual;
    { Called from DoOnAfterEditPost }
    procedure DoBeforeInsert; virtual;
    procedure DoBeforeEdit; virtual;
    procedure DoBeforePost; virtual;
    procedure DoAfterPost; virtual;

    procedure DoBeforeDelete(DataSet: TDataSet); virtual;
    procedure DoAfterDelete(DataSet: TDataSet); virtual;
    procedure DoBeforeCancel(DataSet: TDataSet); virtual;
    procedure DoAfterCancel(DataSet: TDataSet); virtual;
    procedure DoDBGridCellClick(AColumn: TColumn); virtual;


    procedure ChildDestroyed(AChild: TULObjUsr);override;
    function ChildObjAdd: TULObjUsr;
    procedure OnBooleanGetText(Sender: TField; var Text: string; DisplayText: Boolean);
    procedure OnBooleanSetText(Sender: TField; const Text: string);
    procedure OnDateTimeGetText(Sender: TField; var Text: string; DisplayText: Boolean);
    procedure OnDateTimeSetText(Sender: TField; const Text: string);
    procedure OnFloatGetText(Sender: TField; var Text: string; DisplayText: Boolean);
    {procedure OnFloatSetText(Sender: TField; const Text: string);}
    {procedure OnEnumGetText(Sender: TField; var Text: string; DisplayText: Boolean);}
    {procedure OnEnumSetText(Sender: TField; const Text: string);}
    procedure TableFieldsCreated(ATable: TDataSet); virtual;
      { called when Table.Fields created, after TableOpen;
        assigns ftBoolean fields OnGetText,OnSetText;
        call inherited if overriden; can scan fields and  assign OnGetTxt/SetText
        to other fields }
    procedure UsrFieldAdd(AField: TField; AFldDesc: TULObjFldDesc);
    { called from DoOnDBGridFormActivate }
    procedure DoDBGridFormActivate;virtual;
    { called from DoOnDBGridFormDeactivate }
    procedure DoDBGridFormDeactivate;virtual;


    property DBGridForm: TDBGridForm read FForm;

    procedure ChildToRec(AObj: TULObj; ATable: TDataSet); overload;
    procedure ChildToRec; overload;
    procedure RecToChild(ATable: TDataSet; AObj: TULObj); overload;
    procedure RecToChild; overload;
    { Copy all records from Table to Child records. Added to the current
      childs. Call Clear first if only records from the table should be present. }
    procedure TableToChilds;
    { Copy all child records to Table. Added to the table, i.e. empty the dataset
      first if only child records should be there. }
    procedure ChildsTable;

    function GetBookmarks: TList;
    procedure FreeBookmarks;
    procedure SetObj(AObj: TULObj); override;
    function GetActiveGridForm: TForm; override;
    function FindMaxID: integer; virtual;


    property Bookmarks: TList read GetBookmarks;//tbookmark
  public
    procedure DoOnMenuPopup(Sender: TObject);
    { Returns true if FDBGridBrowser.Visible }
    function Browsing: boolean;

    procedure TableOpen; virtual;
      { Creates TDataSet descencant and assign TableName and Database/Connection }
      function TableTableOpen: TDataSet;virtual;

    { Assign dataset event methods of this object to (foreign) ATable }
    procedure TableAssignEvents(ATable: TDataSet);

    destructor Destroy; override;
    procedure Browse; override;
    procedure Save; override;
    procedure AddChildsToTable;
    function HasRecordWithFieldValue(const AFieldName: string; const AValue: string): boolean;

    { Opens new ATable (clone of ATable), creates AULObjUsr := ChildObjAdd,
      calls TableFirst.
      Use ATable and AULObjUsr with methods TableFirst, TableNext, TableEOF,
      TablePost, TableDelete - scanning the dataset without disturbing the
      current Table record. Call TableDone to dispose ATable and AULObjUsr }
    procedure TableInit(var ATable: TDataSet; var AULObjUsr: TULObjUsr);
    procedure TableDone(var ATable: TDataSet; var AULObjUsr: TULObjUsr);

    { Calls Table.First, if not eof calls RecToChild }
    procedure TableFirst;overload;
    procedure TableFirst(ATable: TDataSet; AULObjUsr: TULObjUsr);overload;
    { Calls Table.Next, if not eof calls RecToChild }
    procedure TableNext;overload;
    procedure TableNext(ATable: TDataSet; AULObjUsr: TULObjUsr);overload;
    { Returns Table.EOF }
    function TableEOF: boolean;overload;
    function TableEOF(ATable: TDataSet): boolean;overload;

    { Calls Table.Edit (if was not dsInsert, dsEdit), ChildToRec, Table.Post
      (if was not in dsInsert, dsEdit) }
    procedure TablePost;overload;
    procedure TablePost(ATable: TDataSet; AULObjUsr: TULObjUsr);overload;

    { Calls Table.Delete, if not eof calls RecToChild}
    procedure TableDelete;overload;
    procedure TableDelete(ATable: TDataSet; AULObjUsr: TULObjUsr);overload;


    { Creates new TDataSet based on Table (for scanning the same table using
      two cursors }
    //function GetTableClone: TDataSet;

    { Create bookmark for current Table position, push it to Bookmarks, and
      copy current table record to TmpChild calling RecToChild }
    //procedure PushBookmark;
    //procedure PopBookmark;// pop bookmark for Bookmarks, go to that bookmark and destroy the bookmark

    property TmpChild: TULObjUsr read GetTmpChild;
    property Table: TDataSet{TTable} read GetTable write SetTable;
    {v0.61}
    property UsrFields: TULObjTblUsrFields read GetUsrFields;
    {/v0.61}
  end;

implementation

{const
  FCurTblUsr: TULObjTblUsr = nil;}

{
procedure TblUsrTableCreate(ATable: TDataSet);
begin
  if FCurTblUsr = nil then
    exit;
  FCurTblUsr.TableCreate(ATable);
end;
}

procedure TULObjTblUsr.TableCreate(ATable: TDataSet{TTable});
var added: boolean;
begin
  with ATable do begin
    // Next, describe the fields in the table
    added := false;
    try
      if ChildCount = 0 then begin
        ChildObjAdd;
        added := true;
      end;
      ULObjDescToTable(Childs[0].Obj.ObjDesc, ATable);
    finally
      if added then begin
        Childs[0].Free
      end;
    end;
    {with ATable.FieldDefs.Find('UsrID') do begin
      Required := true;
    end;}

    {
    with FieldDefs do
    begin
      with AddFieldDef do begin
        Name := 'UsrID';
        DataType := ftInteger;
        Required := true;
      end;
      with AddFieldDef do begin
        Name := 'UsrName';
        DataType := ftString;
        Required := true;
        Size := UsrNameLen;
      end;
    end;
    }
    IndexesCreate(ATable);
  end;
end;

procedure TULObjTblUsr.IndexesCreate(ATable: TDataSet);
begin
  //  Next, describe any indexes
  if ATable is TTable then with ATable as TTable do
  with IndexDefs do
  begin
    // Name := xx
    // Fields :=
    // Options :=
    // TIndexOption = (ixPrimary, ixUnique, ixDescending, ixCaseInsensitive, ixExpression, ixNonMaintained);
    // The first index has no name because it is a Paradox primary key
    //Add('', 'UsrID', [ixPrimary, ixUnique]);
    //Add('UsrName', 'UsrName', [ixCaseInsensitive]);
  end;
end;

function TULObjTblUsr.TableTableOpen: TDataSet;
var
  t: TTable;
begin
  //FTable := DBUtl .TableOpen(Self, ExtractFilePath(FileName){DataDir}, FileName, TblUsrTableCreate);
  //function TableOpen(AOwner: TComponent; const ADatabaseName: string; const ATableName: string; ATableCreate: TTableCreate): TDataSet{TTable};
  t := TTable.Create(Self);
  t.DatabaseName := ExtractFilePath(FileName);
  t.TableName := ExtractFileName(ChangeFileExt(FileName, ''));
  if not t.Exists then begin
     {t.Active := false;}
     t.TableType := DefTableType;
     t.FieldDefs.Clear;
     t.IndexDefs.Clear;
     TableCreate(t);
     t.CreateTable;
     {t.IndexDefs.Update;}
  end;
  t.Active := true;
  Result := t;
  {
  FTable := t;
  FOwnsTable := true;
  FTable.Active := true;}
end;

procedure TULObjTblUsr.SetTable(ATable: TDataSet);
begin
  if ATable = FTable then
    exit;
  FOldTableHandlersAssigned := false;
  if FOwnsTable then
    FTable.Free;
  FOwnsTable := false;
  FTable := ATable;
  if FTable <> nil then begin
    TableAssignEvents(FTable);
    FTable.Active := true;
  end;
end;


procedure TULObjTblUsr.TableAssignEvents(ATable: TDataSet);
var
  c: TULObjUsr;
  added: boolean;
begin
  added := false;
  if ChildCount = 0 then begin
    c := ChildObjAdd;
    added := true;
  end else begin
    c := Childs[0];
  end;
  try
    ULObjDescFieldsToTable(c.Obj.ObjDesc, ATable);
    TableFieldsCreated(ATable);
  finally
    if added then
      c.Free;
  end;

  if FTable = ATable then begin
    if not FOldTableHandlersAssigned then begin
      FOnCalcFields := FTable.OnCalcFields;
      FBeforeInsert := FTable.BeforeInsert;
      FBeforeEdit := FTable.BeforeEdit;
      FBeforePost := FTable.BeforePost;
      FAfterPost := FTable.AfterPost;
      FBeforeDelete := FTable.BeforeDelete;
      FAfterDelete := FTable.AfterDelete;
      FAfterCancel := FTable.AfterCancel;
      FBeforeCancel := FTable.BeforeCancel;
      FOnNewRecord := FTable.OnNewRecord;
      FOldTableHandlersAssigned := true;
    end;
  end;

  ATable.AutoCalcFields := true;
  ATable.OnCalcFields := DoCalcFields;
  if ChildCount > 2 then begin
    { converting }
    //ULObjChildsToTable(Obj, ATable);
    //Clear;
    //Save;
  end;

  ATable.BeforeInsert := DoOnBeforeInsert;
  ATable.BeforeEdit := DoOnBeforeEdit;

  FBeforePost := ATable.BeforePost;
  ATable.BeforePost := DoOnBeforePost;

  ATable.AfterPost := DoOnAfterPost;
  ATable.BeforeDelete := DoOnBeforeDelete;
  ATable.AfterDelete := DoOnAfterDelete;
  ATable.AfterCancel := DoOnAfterCancel;
  ATable.BeforeCancel := DoOnBeforeCancel;
  ATable.OnNewRecord := DoOnOnNewRecord;
end;

procedure TULObjTblUsr.TableOpen;
begin
  //FCurTblUsr := Self;
  try
    FTable := TableTableOpen;
    FOwnsTable := true;
    //FTable := DBUtl .TableOpen(Self, ExtractFilePath(FileName){DataDir}, FileName, TblUsrTableCreate);
    TableAssignEvents(FTable);
  finally
    //FCurTblUsr := nil;
  end;
end;

function TULObjTblUsr.ChildObjAdd: TULObjUsr;
begin
  Result := ChildAdd(nil, GetChildRecID, '');
end;

function TULObjTblUsr.GetTmpChild: TULObjUsr;
begin
  if FTmpChild = nil then
    FTmpChild := ChildObjAdd;
  Result := FTmpChild;
end;

procedure TULObjTblUsr.TableFieldsCreated(ATable: TDataSet);
var
  i: integer;
  f: TField;
  fd: TULObjFldDesc;
begin
  FUsrFields.Free;
  FUsrFields := nil;

  for i := 0 to ATable.Fields.Count - 1 do begin
    f := ATable.Fields[i];
    f.OnChange := DoOnFieldChanged;//ulobju
    f.OnValidate := DoOnFieldValidate;
    fd := nil;
    TmpChild.Obj.ObjDesc.HasFldDesc(f.FieldName, fd);
    if (f.DataType = ftBoolean) then begin
      f.OnGetText := OnBooleanGetText;
      f.OnSetText := OnBooleanSetText;
    end else if fd <> nil then begin
      if fd.IsDateOrTime then begin
        if fd.IsDateTime then begin
          if UserMode = umUser then begin
            f.OnGetText := OnDateTimeGetText;
            {v0.61}
            f.OnSetText := OnDateTimeSetText;
            {/v0.61}
          end;
        end;
        {f.OnSetText := OnDateTimeSetText;}
      end else if fd.IsFloat then begin
        f.OnGetText := OnFloatGetText;
        {f.OnSetText := OnFloatSetText;}
      end {v0.61} else begin
        UsrFieldAdd(f, fd);
      end {/v0.61};
    end;
    {/v0.50};
  end;
end;

procedure TULObjTblUsr.ChildToRec(AObj: TULObj; ATable: TDataSet);
begin
  ULObjChildObjToTableRec(AObj, ATable);
end;

procedure TULObjTblUsr.ChildToRec; 
begin
  ChildToRec(TmpChild.Obj, Table);
end;

procedure TULObjTblUsr.RecToChild(ATable: TDataSet; AObj: TULObj);
begin
  ULObjTableRecToChildObj(ATable, AObj);
end;

procedure TULObjTblUsr.RecToChild;
begin
  RecToChild(Table, TmpChild.Obj);
end;

procedure TULObjTblUsr.TableToChilds;
begin
  Table.First;
  while not Table.EOF do begin //ulobju
    ULObjTableRecToChildObj(Table, ChildObjAdd.Obj);
    Table.Next;
  end;
end;

procedure TULObjTblUsr.ChildsTable;
begin
  ULObjChildsToTable(Obj, Table);
end;

procedure TULObjTblUsr.Browse;
begin
  if FForm = nil then begin
    FForm := DataSetBrowse(Table);
    FForm.DBGridFrame.OnMenuPopup := DoOnMenuPopup;
    FForm.DBGridFrame.OnFieldChanged := DoOnFieldChanged;
      { called when field changed in browser }
    FForm.DBGridFrame.DBGrid.OnEditButtonClick := DoOnEditButtonClick;
    FForm.DBGridFrame.DBGrid.OnShowEditor := DoOnShowEditor;
    FForm.OnActivate := DoOnDBGridFormActivate;
    FForm.OnDeactivate := DoOnDBGridFormDeactivate;  //tfield
    FForm.OnDestroy := DoOnDBGridFormDeactivate;

    FForm.FreeNotification(Self);
    ULObjToDBGrid(Obj, TmpChild.Obj, FForm.DBGridFrame.DBGrid);
    DoDBGridFormActivate;
  end else begin
    FForm.BringToFront;
  end;
end;

procedure TULObjTblUsr.AddChildsToTable;
begin
  ULObjChildsToTable(Obj, Table);
end;

procedure TULObjTblUsr.Notification(AComponent: TComponent; Operation: TOperation);
begin
  inherited;
  if Operation = opRemove then begin
    if AComponent = FForm then
      FForm := nil;
  end;
  if Operation = opRemove then begin
    if AComponent = FDBGridFrame then
      FDBGridFrame := nil;
  end;
end;

procedure TULObjTblUsr.DoCalcFields(ADataSet: TDataSet);
var
  uf: TULObjTblUsrField;
  i: integer;
begin
  if Assigned(FOnCalcFields) then
    FOnCalcFields(ADataSet);

  {v0.61}
  if UsrFields.CalcFieldCount > 0 then begin
    {v0.62}
    if Table.State = dsInsert then
      exit; // Table.Append is usually calle after TmpChild.Obj has some
            // date to be copied to Table, i.e. values in TmpChild.Obj would be cleared here
    {/v0.62}
    ULObjTableRecToChildObj(Table, TmpChild.Obj);
    for i := 0 to UsrFields.Count - 1 do begin
      uf := UsrFields[i];
      if uf.FldDesc.IsCalcField then begin
        uf.Field.AsString := TmpChild.Obj.FindField(uf.FldDesc.Name).AsString;
      end;
    end;
  end;
  {/v0.61}
end;

procedure TULObjTblUsr.DoOnFieldChanged(AField: TField);
begin
  if FInFieldChanged = nil then begin
    FInFieldChanged := TList.Create;
  end;
  if FInFieldChanged.IndexOf(AField) >= 0 then
    exit;
  FInFieldChanged.Add(AField);
  try

    DoFieldChanged(AField);

  finally
    FInFieldChanged.Remove(AField);
  end;
end;

procedure TULObjTblUsr.DoOnFieldValidate(AField: TField);
begin
  if FInFieldValidate = nil then begin
    FInFieldValidate := TList.Create;
  end;
  if FInFieldValidate.IndexOf(AField) >= 0 then
    exit;
  FInFieldValidate.Add(AField);
  try

    DoFieldValidate(AField);

  finally
    FInFieldValidate.Remove(AField);
  end;
end;

procedure TULObjTblUsr.DoFieldChanged(AField: TField);
begin

end;

procedure TULObjTblUsr.DoFieldValidate(AField: TField);
begin

end;

procedure TULObjTblUsr.DoOnAfterPost(DataSet: TDataSet);
begin
  if Assigned(FAfterPost) then
    FAfterPost(DataSet);
  DoAfterPost;
end;

procedure TULObjTblUsr.DoAfterPost;
begin
end;

procedure TULObjTblUsr.DoOnBeforePost(DataSet: TDataSet);
begin
  if Assigned(FBeforePost) then
    FBeforePost(DataSet);
  DoBeforePost;
end;

procedure TULObjTblUsr.DoBeforePost;
begin
end;

procedure TULObjTblUsr.DoOnBeforeEdit(DataSet: TDataSet);
begin
  if Assigned(FBeforeEdit) then
    FBeforeEdit(DataSEt);
  DoBeforeEdit;
end;

procedure TULObjTblUsr.DoBeforeEdit;
begin
end;

procedure TULObjTblUsr.DoOnBeforeInsert(DataSet: TDataSet);
begin
  if Assigned(FBeforeInsert) then
    FBeforeInsert(DataSet);
  DoBeforeInsert;
end;

procedure TULObjTblUsr.DoBeforeInsert;
begin
end;


procedure TULObjTblUsr.DoOnBeforeDelete(DataSet: TDataSet);
begin
  if Assigned(FBeforeDelete) then
    FBeforeDelete(DataSet);
  DoBeforeDelete(DataSet);
end;

procedure TULObjTblUsr.DoOnAfterDelete(DataSet: TDataSet);
begin
  if Assigned(FAfterDelete) then
    FAfterDelete(DataSet);
  DoAfterDelete(DataSet);
end;

procedure TULObjTblUsr.DoAfterDelete(DataSet: TDataSet);
begin
end;

procedure TULObjTblUsr.DoBeforeDelete(DataSet: TDataSet);
begin
end;

procedure TULObjTblUsr.DoOnAfterCancel(DataSet: TDataSet);
begin
  if Assigned(FAfterCancel) then
    FAfterCancel(DataSet);
  DoAfterCancel(DataSet);
  
  if FDataSetCancelingNewRecord = DataSet then
    FDataSetCancelingNewRecord := nil;
end;

procedure TULObjTblUsr.DoAfterCancel(DataSet: TDataSet);
begin
end;

procedure TULObjTblUsr.DoOnBeforeCancel(DataSet: TDataSet);
begin
  if DataSet.State = dsInsert then
    FDataSetCancelingNewRecord := DataSet
  else
    FDataSetCancelingNewRecord := nil;

  if Assigned(FBeforeCancel) then
    FBeforeCancel(DataSet);
  DoBeforeCancel(DataSet);
end;

procedure TULObjTblUsr.DoBeforeCancel(DataSet: TDataSet);
begin
end;


{v0.70}
procedure TULObjTblUsr.DoOnOnNewRecord(DataSet: TDataSet);
begin
  if Assigned(FOnNewRecord) then
    FOnNewRecord(DataSet);
  DoOnNewRecord;
end;

procedure TULObjTblUsr.DoOnNewRecord;
begin
end;
{/v0.70}

procedure TULObjTblUsr.DoOnDBGridCellClick(AColumn: TColumn);
begin
  DoDBGridCellClick(AColumn);
end;

procedure TULObjTblUsr.DoDBGridCellClick(AColumn: TColumn);
begin
end;


procedure TULObjTblUsr.DoOnShowEditor(Sender: TObject; Field: TField;
      var AllowEdit: Boolean);
begin
  DoShowEditor(Sender, Field, AllowEdit);
end;

procedure TULObjTblUsr.DoShowEditor(Sender: TObject; Field: TField;
      var AllowEdit: Boolean);
begin
end;
{/v0.63}

procedure TULObjTblUsr.DoOnMenuPopup(Sender: TObject);
begin
  if UserMode = umSysOp then begin
    Obj.Actions.CurMenu := TMenu(Sender);
    FirstMenuActionNeeded;
    Obj.Actions.CurMenu := nil;
  end;
end;

destructor TULObjTblUsr.Destroy;
begin
  FInFieldChanged.Free;
  FInFieldValidate.Free;
  FreeBookmarks;
  Table := nil;
  if FForm <> nil then begin
    FForm.DBGridFrame.OnMenuPopup := nil;
    {FForm.RemoveFreeNotification(Self);}
  end;
  if ChildCount > 0 then begin
    {i.e. has some temporary child objects; clear them}
    Clear;
    Save;
  end;
  {v0.61}
  FUsrFields.Free;
  {/v0.61}
  inherited;
end;

procedure TULObjTblUsr.OnBooleanGetText(Sender: TField; var Text: string; DisplayText: Boolean);
begin
  {if DisplayText then}
  if FIsInSetText then
    exit;
  begin
    Text := Sender.AsString;
    if (Text = '') then begin
      Text := GetTxt({#}'False')
    end else begin
      if upcase(Text[1]) in ['Y','T','1','A'] then
        Text := GetTxt({#}'True')
      else
        Text := GetTxt('False');
    end;
  end;
end;

procedure TULObjTblUsr.OnBooleanSetText(Sender: TField; const Text: string);
{var s: string;}
begin
  FIsInSetText := true;
  try
  if (Text = '') then begin
    Sender.AsString := 'False';
  end else begin
    if upcase(Text[1]) in ['Y','T','1','A'] then
      Sender.AsString := 'True'
    else
      Sender.AsString := 'False';
  end;
  finally
    FIsInSetText := false;
  end;
end;

{v0.61}
procedure TULObjTblUsr.OnDateTimeSetText(Sender: TField; const Text: string);
begin
  FIsInSetText := true;
  try
    if Text = '' then begin
      Sender.AsFloat := 0;
    end else begin
      Sender.AsFloat := UsrStrToStdDateTime(Text);
    end;
  finally
    FIsInSetText := false;
  end;
end;
{/v0.61}

{v0.50}
procedure TULObjTblUsr.OnDateTimeGetText(Sender: TField; var Text: string; DisplayText: Boolean);
{var
  fd: TULObjFldDesc;}
begin
  if FIsInSetText then
    exit;
  begin
    {v0.61}
    if Sender.AsFloat = 0 then begin
      Text := '';
    end else
    {/v0.61}
    begin
      Text := StdDateTimeToUsrStr(Sender.AsFloat);
    end;
  end;
end;

procedure TULObjTblUsr.OnFloatGetText(Sender: TField; var Text: string; DisplayText: Boolean);
var
  fd: TULObjFldDesc;
begin
  if FIsInSetText then
    exit;
  begin
    fd := TmpChild.Obj.ObjDesc.FindFldDesc(Sender.FieldName);
    if fd.BrowseWidth <> 0 then begin
      Text := FloatToStrF(Sender.AsFloat, ffFixed, fd.BrowseWidth, fd.NumDec);
      {ulrectyp}
    end else begin
      Text := Sender.AsString;
    end;
  end;
end;

{procedure TULObjTblUsr.OnFloatSetText(Sender: TField; const Text: string);
begin
  FIsInSetText := true;
  try
  finally
    FIsInSetText := false;
  end;
end;}

function TULObjTblUsr.GetTable: TDataSet{TTable};
begin
  if FTable = nil then
    TableOpen;
  Result := FTable;
end;

procedure TULObjTblUsr.Save;
begin
  inherited;
  if FTable <> nil then begin
    Table.Active := false;
    Table.Active := true;
  end;
end;

procedure TULObjTblUsr.ChildDestroyed(AChild: TULObjUsr);
begin
  if AChild = FTmpChild then
    FTmpChild := nil;
end;

{v0.61}
function TULObjTblUsr.GetUsrFields: TULObjTblUsrFields;
begin
  if FUsrFields = nil then
    FUsrFields := TULObjTblUsrFields.Create(Self);
  Result := FUsrFields;
end;

procedure TULObjTblUsr.UsrFieldAdd(AField: TField; AFldDesc: TULObjFldDesc);
begin
  UsrFields.FieldAdd(AField, AFldDesc);
end;

procedure TULObjTblUsr.DoOnEditButtonClick(Sender: TObject);
{ called when ellipsis button in grid cell pressed (for SelectedField) }
var
  f: TField;
  uf: TULObjTblUsrField;
  vs, ac: TULObj;
{v0.63}
  lv: string;
{/v0.63}
begin
  f := nil;
  if FForm <> nil then
    f := FForm.DBGridFrame.DBGrid.SelectedField
  else if FDBGridFrame <> nil then
    f := FDBGridFrame.DBGrid.SelectedField;

  if f = nil then
    exit;

  if UsrFields.Find(f, uf) then begin
    {v0.63}
    lv := f.AsString;
    {/v0.63}
    if uf.FldDesc.IsULEnum then begin

      vs := TULObj(uf.FldDesc.ValuesSource);
      if vs.HasChildWithFieldUsrValue(f.FieldName, f.AsString, ac) then
        vs.ActiveChild := ac;
      if vs.BrowseForChild = mrOK then begin
        Table.Edit;
        f.AsString := vs.ActiveChild.FindField(f.FieldName).AsString;
        {v0.63}
        if f.AsString <> lv then begin
          DoFieldChangedModal(f, lv);
        end;
        {/v0.63}
        Table.Post;
      end; {ulobju}

    end;
  end;
end;
{/v0.61}
{v0.63}
procedure TULObjTblUsr.DoFieldChangedModal(AField: TField; const ALastValue: string);
begin
end;
{/v0.63}

function TULObjTblUsr.GetActiveGridForm: TForm;
begin
  Result := nil;
  if (FDBGridFrame <> nil) and FDBGridFrame.Visible then
    Result := TForm(GetParentForm(FDBGridFrame));
end;

procedure TULObjTblUsr.DoOnDBGridFormActivate(Sender: TObject);
begin
  DoDBGridFormActivate;
end;

procedure TULObjTblUsr.DoOnDBGridFormDeactivate(Sender: TObject);
begin
  DoDBGridFormDeactivate;
end;

procedure TULObjTblUsr.DoDBGridFormActivate;
begin
end;

procedure TULObjTblUsr.DoDBGridFormDeactivate;
begin
end;

{ Calls Table.First, if not eof calls RecToChild }
procedure TULObjTblUsr.TableInit(var ATable: TDataSet; var AULObjUsr: TULObjUsr);
begin
  ATable := TableTableOpen;
  AULObjUsr := ChildObjAdd;
  TableFirst(ATable, AULObjUsr);
end;

procedure TULObjTblUsr.TableDone(var ATable: TDataSet; var AULObjUsr: TULObjUsr);
begin
  ATable.Free;
  ATable := nil;
  AULObjUsr.Free;
  AULObjUsr := nil;
end;

procedure TULObjTblUsr.TableFirst;
begin
  TableFirst(Table, TmpChild);
end;

procedure TULObjTblUsr.TableFirst(ATable: TDataSet; AULObjUsr: TULObjUsr);
begin
  ATable.First;
  if not ATable.EOF then
    RecToChild(ATable, AULObjUsr.Obj);
end;

{ Calls Table.Next, if not eof calls RecToChild }
procedure TULObjTblUsr.TableNext;
begin
  TableNext(Table, TmpChild)
end;

procedure TULObjTblUsr.TableNext(ATable: TDataSet; AULObjUsr: TULObjUsr);
begin
  ATable.Next;
  if not ATable.EOF then
    RecToChild(ATable, AULObjUSr.Obj);
end;

{ Returns Table.EOF }
function TULObjTblUsr.TableEOF:boolean;
begin
  Result := TableEOF(Table);
end;

function TULObjTblUsr.TableEOF(ATable: TDataSet):boolean;
begin
  Result := ATable.EOF;
end;

{ Calls Table.Edit, ChildToRec, Table.Post }
procedure TULObjTblUsr.TablePost(ATable: TDataSet; AULObjUsr: TULObjUsr);
var wasEditing: boolean;
begin
  wasEditing := ATable.State in [dsEdit, dsInsert];
  if not wasEditing then
    ATable.Edit;
  ChildToRec(AULObjUsr.Obj, ATable);
  if not wasEditing then
    ATable.Post;
end;

procedure TULObjTblUsr.TablePost;
begin
  TablePost(Table, TmpChild);
end;


procedure TULObjTblUsr.TableDelete(ATable: TDataSet; AULObjUsr: TULObjUsr);
var i: integer;
begin
  i := ATable.RecNo;
  ATable.Delete;
  if i > ATable.RecNo then begin
    // i.e. was deleting the last record, call TableNext to cause EOF condition
    //
    if not ATable.EOF then
      TableNext(ATable, AULObjUsr);
  end else begin
    if not ATable.Eof then
      RecToChild(ATable, AULObjUsr.Obj);
  end;
end;

procedure TULObjTblUsr.TableDelete;
begin
  TableDelete(Table, TmpChild);
end;


function TULObjTblUsr.GetBookmarks: TList;
begin
  if FBookmarks = nil then
    FBookmarks := TList.Create;
  Result := FBookmarks;
end;

procedure TULObjTblUsr.FreeBookmarks;
var i: integer;
begin
  if FBookmarks = nil then
    exit;
  if Table <> nil then begin
    for i := 0 to FBookmarks.Count - 1 do begin
     Table.FreeBookmark(FBookmarks[i]);
    end;
  end;
  FBookmarks.Free;
  FBookmarks := nil;
end;
{
procedure TULObjTblUsr.PushBookmark;
var b: TBookmark;
begin
  RecToChild;
  b := Table.GetBookmark;
  Bookmarks.Add(b);
end;

procedure TULObjTblUsr.PopBookmark;
var
  b: TBookmark;
  i: integer;
begin
  if (FBookmarks = nil) or (Bookmarks.Count = 0) then
    exit;
  i := Bookmarks.Count - 1;
  b := Bookmarks[i];
  Bookmarks.Delete(i);
  Table.GotoBookmark(b);
  Table.FreeBookmark(b);
end;
}

{
function TULObjTblUsr.GetTableClone: TDataSet;
begin
  Result := nil;
  if Table = nil then
    exit;
  Result := Table.ClassType.Create(nil);
  ClassAssign(Result, Table, caoPropsCopy);
end;
}

procedure TULObjTblUsr.SetObj(AObj: TULObj);
begin
  inherited;
  if Obj <> nil then begin
    Obj.ObjDesc.SetClassFlag(cfFldNamesCaseNonSensitive, true);
  end;
end;

function TULObjTblUsr.Browsing: boolean;
begin
  Result := FDBGridFrame.Visible;
end;

function TULObjTblUsr.FindMaxID: integer;
var
  t: TDataSet; c: TULObjUsr;
  i: integer;
begin
  TableInit(t, c);
  try
    i := 0;
    while not TableEOF(t) do begin
      if t.Fields[0].AsInteger > i then
        i := t.Fields[0].AsInteger;
      TableNext(t, c);
    end;
  finally
    TableDone(t, c);
  end;
  Result := i;//tfield
end;

function TULObjTblUsr.HasRecordWithFieldValue(const AFieldName: string; const AValue: string): boolean;
begin
  Result := Table.Locate(AFieldName, VarArrayOf([AValue]),[]);
end;
{/TULObjTblUsr.}

{TULObjTblUsrField.}
constructor TULObjTblUsrField.Create(AULObjTblUsrFields: TULObjTblUsrFields;
  AField: TField; AFldDesc: TULObjFldDesc);
begin
  inherited Create;
  FTblUsrFields := AULObjTblUsrFields;
  FField := AField;
  FFldDesc := AFldDesc;
  if FFldDesc.IsCalcField then
    inc(FTblUsrFields.FCalcFieldCount);
  if FFldDesc.IsEnum then begin
    FField.OnGetText := OnEnumGetText;
    FField.OnSetText := OnEnumSetText;
  end;
end;

procedure TULObjTblUsrField.OnEnumGetText(Sender: TField; var Text: string; DisplayText: Boolean);
begin
  if Sender = Field then begin
    Text := FldDesc.GridItems[Sender.AsInteger];
  end;
end;

procedure TULObjTblUsrField.OnEnumSetText(Sender: TField; const Text: string);
var i: integer;
begin
  if Sender = Field then begin
    i := FldDesc.GridItems.IndexOf(Text);
    if i >= 0 then
      Field.AsInteger := i;
  end;
end;

{/TULObjTblUsrField.}

{TULObjTblUsrFields.}
constructor TULObjTblUsrFields.Create(AULObjTblUsr: TULObjTblUsr);
begin
  inherited Create;
  FTblUsr := AULObjTblUsr;
end;

procedure TULObjTblUsrFields.FieldAdd(AField: TField; AFldDesc: TULObjFldDesc);
begin
  Add(TULObjTblUsrField.Create(Self, AField, AFldDesc));
end;

function TULObjTblUsrFields.GetUsrField(Index: integer): TULObjTblUsrField;
begin
  Result := TULObjTblUsrField(Items[Index]);
end;

function TULObjTblUsrFields.Find(AField: TField; var AUsrField: TULObjTblUsrField): boolean;
var i: integer;
begin
  Result := false;
  for i := 0 to Count - 1 do begin
    AUsrField := UsrFields[i];
    if AUsrField.Field = AField then begin
      Result := true;
      exit;
    end;
  end;
end;

{/TULObjTblUsrFields.}

end.
