unit uBafFrmModule;

// this code is under the BAF fair use license (BFUL) - https://bafbal.de/index.php?title=Bful
// interpreter-Modul for froms and controls
// https://bafbal.de/index.php?title=Modul_Form

interface



uses System.Math, System.SysUtils, System.Classes, uBafTypes, uBafInterpreter,
  System.Contnrs, FMX.StdCtrls, FMX.Memo, FMX.Types, FMX.Objects, uBafControls,
  FMX.Controls, uStringIniFile, Data.DB, System.StrUtils, uBafPage,
  Windows, FMX.DialogService, System.UITypes, FMX.Dialogs, uBafComboHelper,
  uBafDataCache, FMX.TreeView, FMX.Graphics, System.Types, FMX.Forms, uOsStuff,
  foBafDialog, uBafTree, ScHttp, System.RegularExpressions;


type
  TBafFrmModule = class(TBafInterpreterCustomModule)

  protected
    FFilterStatement: string;
    procedure CreateForm;
  protected // console and live
    FConsoleMemo, FCommandMemo: TMemo;
    FCommandButton, FClearButton: TButton;   // xlive
    FPanelTop, FPanelBottom: TPanel;
    FSplitter: TSplitter;
    FLastConsoleRefresh: TDateTime;
    FSrvLog: TStringList;
    procedure CreateConsole;
    procedure CreateLive;
    procedure ConsoleOut;
    procedure ConsoleOutLine;
    procedure ConsoleOutSrvProc(ALine: boolean);
    procedure ConsoleSaveToFile;
    procedure ServerOperations;
    procedure CreateMemo(var AMemo: TMemo; AParent: TFmxObject; AReadOnly: boolean);
    procedure CommandButtonClick(Sender: TObject);
    procedure ClearButtonClick(Sender: TObject);
    procedure SrvProcForm;
    procedure WriteSrvLog(AText: string); override;
  protected // Haupt-Komponenten für TreeGrid
    FCompList: TObjectList;
    FBafSplitter: TBafSplitter;
    FBafSingleSplitter: TBafSingleSplitter;
    FTree: TBafTree;
    FPage: TBafPage;
    FTreeSelectNode, FOldSelectedNode: TBafTreeNode;
    FPageRefreshCommand: string;
    FCmdTimer: TTimer;
    FCommand: string;
    procedure CmdTimerTimer(Sender: TObject);
    procedure CreateTreePage(AVertical: boolean = false);
    procedure CreatePage;
  protected // general
    FProgressDialog: TfrmBafDialog;
    FProgressValue, FProgressMax: integer;
    procedure ShowMsg;
    procedure ProgressInc;
    procedure ProgressDlg;
    procedure ProgressAbort(Sender: TObject);
    procedure BafDlg;
    procedure BafDlgClose;
  protected  // Edits und Buttons
    FEditList: TStringList;
    FRefreshDocks: boolean;
    FHintText: string;
    FPanelHint: TPanel;
    procedure AddButton;
    procedure AddEdit;
    procedure AddLabel;
    procedure AddCheckbox;
    procedure ButtonAndMenuItelClick(Sender: TObject);
    function FindDock(ADefault, ARoutine: string): TBafButtonDock;
    procedure DoFilter(Sender: TObject);
    function GetEditText(AName: string): string;
    function GetConsoleValue(): string;
    procedure HintMouseLeave(Sender: TObject);
    procedure HintMouseEnter(Sender: TObject);
    procedure HintPaint(Sender: TObject; Canvas: TCanvas; const ARect: TRectF);
  protected // Tree
    FTreeLock: boolean;  // no tree events
    FTreeEmptyOpen: boolean; // while the expanding of an empty node
    FNode, FLast, FLastExpanded, FBeforeSelected, FSpecialNode: TBafTreeNode;
    FDefNodes: TStringList;
    FTreeNodeList: TObjectList; //list of nodes in order of selection, allows to go back
    FTreeNodeListPointer: integer;
    FTreeNodeListInProcess: boolean;
    FCheckMemo: TMemo;
    procedure ClearTree;
    procedure AddTreeItem;
    procedure FillTree;
    procedure AddDefNode;
    procedure FillTreeSql;
    procedure FillTreePath;
    procedure FillThread;
    procedure TreeSelect;
    procedure TreeSelect2;
    procedure TreeBack(ABackBack: boolean);
    procedure ClearTreeNode;

    procedure TreeViewExpanded(Sender: TBafTree; ANode: TBafTreeNode);
    procedure TreeViewChange(ASender: TBafTree; ANode: TBafTreeNode);
    procedure SetFNode(ALineP: string);
    function GetNode(AName: string): TBafTreeNode;
    procedure FillNodeIni(ALineP: string; AChanged, AAddFields: boolean;
        AIni: TStringIniFile; AQuelle: TBafDataQuelle = dqSql);
    function InsertNewItem(AParent: TBafTreeNode; ACaption: string;
        ACheckDummy: boolean): TBafTreeNode;
    function FindTreeValue(ANode: TBafTreeViewItem; ASection: string;
        AParams: TStrings; AFind: boolean): string; overload;
    function FindTreeValue2(AParams: TStrings): string;
    function FindTreeValue(ANode: TBafTreeViewItem; ASection, AField: string;
        AFind: boolean): string; overload;
    procedure TreeItemLookup(ASpecial: boolean; AName, AKey: string; var AResult: string);
    procedure TreeItemFunction(var AText: string);

  protected // Page
    FPageName: string;
    FGridObjectList: TObjectList;   // List of all created objects we have to free
    FLastPrim: TBafPagePrimaryDiv;
    FLastCat: TBafPageCategory;
    FLastSeg: TBafPageSegment;
    FGridRefreshAfterSave: boolean;
    FGridRefreshAfterReturn: boolean;
    FPageBeforeSaveCommand, FPageAfterSaveCommand: string;
    FHeaderRowCount, FFooterRowCount, FGridDataRow, FChangeRow, FChangeCol,
        FGridSaveRow, FLinkRow: integer;
    FCellCalcGrid: TBafSimpleGrid;
    FGridDataCol: integer;
    FInserted: boolean;
    FXGridCols, FXXGridCols: TStringList;
    FPageFailed: TStringList;
    FPageCheckPause: boolean;
    FLiveLookupHelper: TBafComboHelper;
    FLiveCell: TBafSgCell;
    FThreadList: TObjectList;


    procedure FillPage;
    procedure PageDefs;
    procedure PageReady;
    procedure PageXls;
    procedure PagePdf;
    procedure PrimaryDefs;
    procedure CatDefs;
    procedure SegText;    // Text
    procedure SegMemo;    // Memo
    procedure SegGridButtons;
    procedure SegGridButton;
    procedure SegVl;      // VL
    procedure SegVlLine;
    procedure SegVlData;
    procedure SegVlHist;
    procedure SegVlThread;
    procedure SegGrid;     // Grid
    procedure SegGridCol(ALineP: string; ADataSet: TDataSet;
        AXGridIndex, AXXGridIndex: integer);
    procedure SegGridData;
    procedure SegGridAdd;
    procedure SegGridLoop;
    procedure SegGridPaste;
    procedure SegGridAddOrChange;
    procedure SegGridPos;
    procedure SegGridSort;
    procedure SegGridDub;
    procedure SegGridCalc;
    procedure SegGrdThread;
    procedure SegXGrid;   // X-Grid
    procedure SegXGridXCol(ADoubleX: boolean = false);
    procedure SegXGridData;
    procedure SegXGridXData;
    procedure SegXGridCalc;
    function SegXGridCalcIntern(ASegment: TBafPageSegment;
        ATyp, AFieldName, A1Func, A2Func: string;
        ANv0: boolean; AResultType: TBafPageCellType): string;
    procedure SegXXGrid;   // Double-X-Grid
    procedure SegXXGridXData;
    procedure SegXXGridData;
    procedure SegSGrid;     // S-Grid
    procedure SGridNode;
    procedure SGridData;
    procedure SGridHeaderFooter;
    procedure SegDia;     // Dia
    procedure SegDiaCol;
    procedure SegDiaData;
    procedure SegMap;     // Map
    procedure SegMapData;
    procedure SegPic;     // Pictures

    procedure PageValue;
    procedure PageCheck;
    procedure PageLookupLiveFill;
    procedure PageLookupLiveClear;

    procedure GridHeaderButtonClick(const Sender: TBafPageParents; AButton: Char);
    procedure GridSegButtonClick(const Sender: TBafPageParents);
    procedure PageStatus(ASender: TObject; AStatus: TBafGridStatus);
    procedure GridAfterEdit(ASender: TBafPageParents; var AText: string;
      var AAbort: boolean; ACommand: string; AChanged: boolean);
    procedure GridCellCalc(ASender: TBafPageParents; var AText: string;
      var AAbort: boolean; ACommand: string; AChanged: boolean);
    procedure PageLinkClick(const Sender: TBafPageParents; ACommand: string);
    procedure PageOpenClose(const Sender: TBafPageParents);
    procedure PageCellButton(const Sender: TBafPageParents);

    function GetPrimaryKey(ALineP: string; APostfix: string = ''): string; overload;
    function GetPrimaryKey: string; overload;
    function GetLineType: TBafGridLineType;
    procedure AddSegment(ALineP: string; AType: TBafPageSegmentType);
    procedure FetchGridFields(ADataSet: TDataSet; var ARow: integer;
        AXGridIndex, AXXGridindex: integer; AIncRow: boolean);
    procedure FetchGridCellValue(ADataSet: TDataSet; ACell: TBafSgCell;
        AColumn: TBafSgColumn; ARow: integer);
    function FindLookupHelper(AZeileP, APostfix: string; var ACmd: string): TBafComboHelper;
    function DoPageCheck: boolean;
    procedure RefresCheckMemo;
    procedure ClearPage;
    procedure GridCheckCols(ADataSet: TDataSet);
    function FindSegment(AFunktion: string; var ASegment: TBafPageSegment): boolean;
    function FindPageValue(AParams: TStrings): string;
    function GetPageValue(AParams: TStrings): string;
    function GridCheck(AParams: TStrings): string;
    function GridRegex(AParams: TStrings): string;
    function XGridCalc(AParams: TStrings): string;
    function GetCellColorInteger(AParams: TStrings): string;
    function DlgYesNo(AParams: TStrings): string;
    procedure GridOpenJoin(ADataSet: TDataSet; var ARow: integer);
    procedure ColumnNullValue(ACell: TBafSgCell);
    procedure PageFillLookupLive(ASender: TObject; ALookupHelper: TBafComboHelper;
        ACell: TBafSgCell);
    procedure GridSaveRect(Sender: TBafPageParents; ARect: TRectF;
        ADefinition: string; AHorzVal, AHorzVP, AVertVal, AVertVP, AStretch: single);


    procedure SaveOrCancel(ASave: boolean);
    procedure PageSave;
    procedure GridSaveMemo(ASegment: TBafPageSegment);
    procedure GridSaveVL(ASegment: TBafPageSegment);
    procedure GridSaveVLJson(ASegment: TBafPageSegment);
    procedure GridSaveGrid(ASegment: TBafPageSegment);
    procedure GridSaveGridJson(ASegment: TBafPageSegment);
    procedure GridSaveGridXml(ASegment: TBafPageSegment);
    procedure GridSaveXGrid(ASegment: TBafPageSegment);
    procedure GridSaveSGrid(ASegment: TBafPageSegment);
    procedure GridSaveXXGrid(ASegment: TBafPageSegment);
    procedure GridSaveMap(ASegment: TBafPageSegment);

    procedure Return2Tab(Sender: TObject);
    procedure LoadDataThreadTerminate(Sender: TObject);
    procedure LoadSegmentThreadTerminate(Sender: TObject);
    procedure DataThreadListClear;
  public
    constructor Create; override;
    destructor Destroy; override;
    procedure RegisterModule(AInter: TBafInterpreter); override;
    function InterpretLine(AExecInter: TBafCustomInterpreter): boolean; override;
    function ReplaceFunction(ACommand: string; AParams: TStrings;
        var AResult: string): boolean; override;
    function GetFormObject(AFormObject: TBafFrmObject): TObject; override;
    procedure DoModuleEvent(AInter: TBafCustomInterpreter;
        AEvent: TBafInterpreterEvent; AText: string); override;
    procedure AddGridObjectList(AObject: TObject); override;
    function GetFrmComponent(AType: string): TComponent; override;

  end;

implementation

{ TBafFrmModule }

uses dmMain, foMain, uBafTranslationModule, uBafLoadDataThread, foBafDlg,
  uBafXmlModule, uBafJsonModule, uBafWebModule;

procedure TBafFrmModule.AddButton;
var
  LTyp: string;
  dockParent: TBafButtonDock;
  LButton: TBafButton;

  procedure lokStandardButton(ATyp: integer);
  begin
    LButton.StyledSettings := [TStyledSetting.FontColor];
    LButton.TextSettings.Font.Family := 'Font Awesome 5 Free Regular';
    LButton.TextSettings.Font.Size := 15;
    case ATyp of
      0: LButton.Text := #$f1c1;
      1: LButton.Text := #$f1c3;
      2: LButton.Text := #$f359;
      3: LButton.Text := #$f35a;
      4: LButton.Text := #$f0c7;
      5: LButton.Text := #$f165;
      6: LButton.Text := #$f56e;
      7: LButton.Text := #$f56f;
      8: LButton.Text := #$f4da;
    end;
  end; // procedure lokStandardButton

begin
  if FindParamBooleanReplaced('cnd', true) then begin
    dockParent := FindDock('b', 'AddButton');
    LButton := TBafButton.Create(dockParent);
    LButton.Parent := dockParent;
    LButton.Width := 30;

    LTyp := FindParamStringLower('y', '');
    if LTyp = 'pdf' then
      lokStandardButton(0)
    else if LTyp = 'xls' then
      lokStandardButton(1)
    else if LTyp = 'back' then
      lokStandardButton(2)
    else if LTyp = 'backback' then
      lokStandardButton(3)
    else if LTyp = 'save' then
      lokStandardButton(4)
    else if LTyp = 'cancel' then
      lokStandardButton(5)
    else if LTyp = 'export' then
      lokStandardButton(6)
    else if LTyp = 'import' then
      lokStandardButton(7)
    else begin
      LButton.Text := FindParamStringReplaced('c', '(Caption fehlt)');
      LButton.Width := FindParamInteger('w', 200);
    end;
    LButton.Command := FindParamString('s', '');
    if LButton.Command = '' then
      LButton.Command := FindParamString('cmd', '');
    LButton.Hint := FindParamStringReplaced('h', '');
    LButton.OnMouseEnter := HintMouseEnter;
    LButton.OnMouseLeave := HintMouseLeave;
    LButton.StatusEnabled := FindParamStringReplacedLower('se', '');
    LButton.Right := FExecInter.GetRight;
    if FindParamBooleanReplaced('nl', false) then
      LButton.Tag := 1337;
    LButton.OnClick := ButtonAndMenuItelClick;
    FCompList.Add(LButton);
    FRefreshDocks := true;
  end;
end;

procedure TBafFrmModule.AddCheckbox;
var
  LName: string;
  dockParent: TBafButtonDock;
  LCheck: TBafCheckBox;
begin
  if FindParamBooleanReplaced('cnd', true) then begin
    if FindParam('n', LName) then begin
      dockParent := FindDock('f', 'AddButton');
      LCheck := TBafCheckBox.Create(dockParent);
      LCheck.Name := LName;
      LCheck.Parent := dockParent;
      LCheck.Width := FindParamInteger('w', 200);
      if FindParamBoolean('nl', false) then
        LCheck.Tag := 1337;
      LCheck.Hint := FindParamStringReplaced('h', '');
      LCheck.OnMouseEnter := HintMouseEnter;
      LCheck.OnMouseLeave := HintMouseLeave;
      LCheck.Text := FindParamStringReplaced('c', '');
      LCheck.Value := FindParamStringReplaced('z', '');
      FEditList.AddObject(LCheck.Name, LCheck);
      FCompList.Add(LCheck);
    end
    else
      FInter.DoLog('E', '#check Name n missed');
  end;
end;

procedure TBafFrmModule.AddDefNode;
var
  LDef: TDefNodeObject;
begin
  if FindParamBooleanReplaced('cnd', true) then begin
    LDef := TDefNodeObject.Create;
    LDef.FKeyColumn := GetPrimaryKey;
    LDef.FKeyColumnValue := BAF_OTHER;
    FDefNodes.AddObject(FExecInter.LineP, LDef);
  end;
end;

procedure TBafFrmModule.AddEdit;
var
  LName: string;
  dockParent: TBafButtonDock;
  LEdit: TBafEdit;
begin
  if FindParamBooleanReplaced('cnd', true) then begin
    if FindParam('n', LName) then begin
      dockParent := FindDock('f', 'AddEdit');
      LEdit := TBafEdit.Create(dockParent);
      LEdit.Name := AnsiLowerCase(LName);
      LEdit.Parent := dockParent;
      LEdit.Width := FindParamInteger('w', 200);
      LEdit.MaxLength := FindParamIntegerReplaced('l', 0);
      LEdit.CharCase := FindParamCharCase('', 'cy', TEditCharCase.ecNormal);
      LEdit.DataType := FindParamCelltype('y');

      if FindParamBoolean('nl', false) then
        LEdit.Tag := 1337;
      LEdit.Hint := FindParamStringReplaced('h', '');
      LEdit.OnMouseEnter := HintMouseEnter;
      LEdit.OnMouseLeave := HintMouseLeave;
      LEdit.Text := FindParamStringReplaced('z', '');
      LEdit.OnFilter := DoFilter;
      if FindParamBooleanReplaced('sf', false) then begin
        if LEdit.CanFocus then
          LEdit.SetFocus;
      end;
      if FindParamBoolean('lbl', false) then begin
        LEdit.StyleLookup := 'transparentedit';
        LEdit.ReadOnly := true;
      end
      else
        LEdit.StyleLookup := 'editstyle';
      FEditList.AddObject(LEdit.Name, LEdit);
      FCompList.Add(LEdit);
    end
    else
      FInter.DoLog('E', '#edit name n missed');
  end;
end;

procedure TBafFrmModule.AddLabel;
var
  dockParent: TBafButtonDock;
  LEdit: TBafEdit;
begin
  if FindParamBooleanReplaced('cnd', true) then begin
    dockParent := FindDock('f', 'AddLabel');
    LEdit := TBafEdit.Create(dockParent);
    LEdit.Parent := dockParent;
    LEdit.Width := FindParamInteger('w', 200);
    LEdit.ReadOnly;
    if FindParamBoolean('nl', false) then
      LEdit.Tag := 1337;
    LEdit.Hint := FindParamStringReplaced('h', '');
    LEdit.OnMouseEnter := HintMouseEnter;
    LEdit.OnMouseLeave := HintMouseLeave;
    LEdit.Text := FindParamStringReplaced('c', '');
    LEdit.ReadOnly := true;
    LEdit.StyleLookup := 'transparentedit';
    FCompList.Add(LEdit);
  end;
end;

procedure TBafFrmModule.AddSegment(ALineP: string; AType: TBafPageSegmentType);
begin
  FLastSeg := FLastCat.Segments.Add;
  FLastSeg.LineP := ALineP;
  FLastSeg.SegmentName := FindParamStringReplaced(ALineP, 'n', '');
  FLastSeg.SegmentType := AType;
  FLastSeg.SegRight := FExecInter.GetRight(ALineP, ''); // ReadOnly too
  FLastSeg.ReadOnly := FLastSeg.SegRight in [brNone, brRead];
  FLastSeg.Header := FindParamStringReplaced(ALineP, 'c', '');
  FLastSeg.Buttons := FindParamStringReplaced(ALineP, 'b', '');
  FLastSeg.OpenClose := GetOpenClose(FindParamStringLower('oc', 'o'));
  FLastSeg.MaxHeightOpened := FindParamSingleReplaced('mho', -1);
  FLastSeg.MaxHeightClosed := FindParamSingleReplaced('mhc', -1);
  FLastSeg.AutoSizeOpened := FindParamBooleanReplaced('aso', false);
  FLastSeg.AutoSizeClosed := FindParamBooleanReplaced('asc', false);
  FLastSeg.PdfCommand := FindParamStringReplaced('pc', '');
  FLastSeg.XlsCommand := FindParamStringReplaced('xc', '');
end;

procedure TBafFrmModule.AddTreeItem;
var
  LRight: TBafRight;
  LItem: TBafTreeNode;
begin
  if FindParamBooleanReplaced('cnd', true) then begin
    LItem := nil;
    FTreeLock := true;
    try
      LRight := FExecInter.GetRight;
      if (LRight > brNone) then begin
        FBeforeSelected := FTree.Selected;
        SetFNode(FExecInter.LineP);
        LItem := InsertNewItem(FNode, '', true);
        FillNodeIni(FExecInter.LineP, true, true, LItem.Ini);
        LItem.Text := FindParamStringReplaced('c', LItem.GetCaption);
        if FindParamString('o', '') <> '' then
          LItem.HasOpenCommand := true;
        if FindParamBoolean('si', false) then
          FTreeSelectNode := LItem;
        if FindParamBooleanReplaced('ne', false) and Assigned(FNode) then
          FNode.Opened := true;
        if FindParamBooleanReplaced('sn', false) then
          FSpecialNode := LItem;
        FLast := LItem;
        FTree.RefreshVisibleNodes;
      end;
    finally
      FTreeLock := false;
    end;
    if FindParamBoolean('sii', false) then
      FTree.Selected := LItem;
  end;
end;

procedure TBafFrmModule.BafDlg;
var
  LTitle, LCmd, LDef, LResult, LTYpe, LIni: string;
  LIniNum: integer;
begin
  if FindParamBooleanReplaced('cnd', true) then begin
    LTitle := FindParamStringReplaced('title', '');
    if LTitle = '' then
      LTitle := dataMain.ProgName;
    LCmd := FindParamStringReplaced('cmd', '');
    LDef := FindParamStringReplaced('d', BafGetGuid);
    LType := FindParamStringReplacedLower('y', '');
    LIniNum := FindParamIntegerReplaced('ini', 0);
    if LIniNum > 0 then
      LIni := FInter.GetIni(LIniNum).AsString;
    TfrmDlg.ShowDlg(LTitle, LType, LDef, LCmd, LIni, LResult);
    if LIniNum > 0 then
      FInter.GetIni(LIniNum).AsString := LIni;
    FExecInter.SetVarOrValue('n', LResult);
  end;
end;

procedure TBafFrmModule.BafDlgClose;
begin
  if FindParamBooleanReplaced('cnd', true) then begin
    FInter.Variable['result'] := FindParamStringReplaced('z', '');
    FExecInter.TabClose;
  end;
end;

procedure TBafFrmModule.ButtonAndMenuItelClick(Sender: TObject);
var
  LCommand: string;
begin
  if (Sender is TBafButton) then
    LCommand := TBafButton(Sender).Command;

//  Screen.Cursor := crHourGlass;
  try
    FInter.Execute(LCommand);
  finally
//    Screen.Cursor := crDefault;
  end;
end;

procedure TBafFrmModule.CatDefs;
begin
  FLastCat := FLastPrim.Categories.Add;
  FLastCat.AutoSize := FindParamBooleanReplaced('as', false);
  FLastCat.Size := FindParamSingleReplaced('sz', 120);

  FLastCat.Caption := FindParamStringReplaced('c', '');
  FLastCat.OpenCloseIni := FindParamStringReplaced('oci', '');
  if FLastCat.OpenCloseIni = '' then
    FLastCat.Opened := FindParamBooleanReplaced('o', true)
  else
    FLastCat.Opened := dataMain.UserIni.ReadString(BAF_INI_OC,
        FLastCat.OpenCloseIni, 'O') = 'O';
  FLastCat.LineP := FExecInter.LineP;
end;

function TBafFrmModule.DoPageCheck: boolean;
var
  i: integer;
  LCheck: TBafCheck;
begin
  result := true;
  if FPageCheckPause then
    exit;
  FPageFailed.Clear;
  for i := 0 to FPage.CheckList.Count - 1 do begin
    LCheck := FPage.CheckList.Objects[i] as TBafCheck;
    LCheck.Failed := false;
    if BafIsYesChar(FExecInter.ReplaceFunctions(LCheck.Condition)) then begin
      if not BafIsYesChar(FExecInter.ReplaceFunctions(LCheck.Check)) then begin
        LCheck.Failed := true;
        if LCheck.CheckType in [ctWarning, ctError] then
          FPageFailed.Add('- '
              + FExecInter.ReplaceFunctions(LCheck.Caption) + #13#10);
      end;
    end;
    if LCheck.Failed and (LCheck.CheckType in [ctError]) then
      result := false;
  end;
  if Assigned(FCheckMemo) then
    RefresCheckMemo;
// function TBafFrmModule.CheckPlausi
end;

procedure TBafFrmModule.ClearButtonClick(Sender: TObject);
begin
  FConsoleMemo.Lines.Clear;
end;

procedure TBafFrmModule.ClearPage;
begin
  FPage.Clear;
end;

procedure TBafFrmModule.ClearTree;
begin
  if FindParamBooleanReplaced('cnd', true) then begin
    FTreeLock := true;
    try
      FOldSelectedNode := nil;
      FLastExpanded := nil;
      FTreeNodeList.Clear;
      FTreeNodeListPointer := -1;
      FTree.Root.ClearChilds;
      FTree.Selected := nil;
      FTree.RefreshVisibleNodes;
      FBeforeSelected := nil;
      FPage.Clear;
    finally
      FTreeLock := false;
    end;
  end;
end;

procedure TBafFrmModule.ClearTreeNode;
begin
  if FindParamBooleanReplaced('cnd', true) then begin
    FTreeLock := true;
    try
      FTree.Selected.Opened := false;
      FTree.Selected.ClearChilds;
    finally
      FTreeLock := false;
    end;
  end;
end; // procedure TBafFrmModule.ClearTreeNode

procedure TBafFrmModule.CmdTimerTimer(Sender: TObject);
begin
  FCmdTimer.Enabled := false;
  TBafInterpreterLevel.ExecInNewLevel(FCommand, FExecInter, FInter);
  frmMain.SpecialPageRefresh(true);
end;

procedure TBafFrmModule.ColumnNullValue(ACell: TBafSgCell);
var
  LColumn: TBafSgColumn;
begin
  if ACell.Text = '' then begin
    LColumn := ACell.Parents.Column;
    case LColumn.CellNullValueAction of
      nvValue: ACell.Text := FInter.ReplaceFunctions(LColumn.CellNullValue);
      nvInsert: begin
        ACell.Text := FInter.ReplaceFunctions(LColumn.CellNullValue);
        ACell.Parents.Row.RowInserted := true;
        ACell.Inserted := true;
      end;
      nvInsertChange: begin
        ACell.Text := FInter.ReplaceFunctions(LColumn.CellNullValue);
        ACell.HasChanged := true;
        ACell.Parents.Row.RowInserted := true;
        ACell.Inserted := true;
      end;
      nvChange: begin
        ACell.Text := FInter.ReplaceFunctions(LColumn.CellNullValue);
        ACell.HasChanged := true;
      end;
    end;
  end;
end;

procedure TBafFrmModule.CommandButtonClick(Sender: TObject);
begin
  FInter.ExecuteCode(FCommandMemo.Lines);
end;

procedure TBafFrmModule.ConsoleOut;
var
  s: string;
  LMax, LMaxDel: integer;
begin
  if FindParamBooleanReplaced('cnd', true) then begin
    if FConsoleMemo = nil then
      exit;
    LMax := FindParamIntegerReplaced('m', MaxInt);
    LMaxDel := FindParamIntegerReplaced('md', 0);
    if FindParamBooleanReplaced('clr', false) then begin
      FConsoleMemo.Lines.Clear;
      FConsoleMemo.ShowScrollBars := false;
      FConsoleMemo.ShowScrollBars := true;     // to hide the scrollbar
    end;
    s := FindParamStringReplaced('cn', '');
    if s = '' then begin
      s := FindParamStringReplaced('c', '#cout, Parameter c nicht gesetzt');
      s := TrimRight(FExecInter.ReplaceFunctions(s)); // to allow functions of functions
    end;
//    if s = '#cout, Parameter c nicht gesetzt' then
//      s := 'Trololo';
    if not FindParamBooleanReplaced('wts', false) then
      s := FormatDateTime('hh:mm:ss', now) + '   ' + FindParamString('y', 'I') + '   ' + s;
    FConsoleMemo.Lines.Add(s);
    while FConsoleMemo.Lines.Count > LMax do begin
      if FConsoleMemo.Lines.Count > LMaxDel then
        FConsoleMemo.Lines.Delete(LMaxDel)
      else
        Break;
    end;
    if (now - FLastConsoleRefresh) > (1 / (24 * 3600 * 3)) then begin
      Application.ProcessMessages;
      FLastConsoleRefresh := now;
      FConsoleMemo.ScrollBy(0, MaxInt, false);
    end;
  end;
end;

procedure TBafFrmModule.ConsoleOutLine;
var
  s: string;
begin
  s := TrimRight(FExecInter.ReplaceFunctions(FExecInter.LineP));
  if FConsoleMemo = nil then
    exit;
  FConsoleMemo.Lines.Add(s);
end;

procedure TBafFrmModule.ConsoleOutSrvProc(ALine: boolean);
var
  s: string;
begin
  if FindParamBooleanReplaced('cnd', true) then begin
    if ALine then
      s := TrimRight(FExecInter.ReplaceFunctions(FExecInter.LineP))
    else begin
      s := FindParamStringReplaced('c', '#cout, Parameter c empty') + '   ';
      s := TrimRight(FExecInter.ReplaceFunctions(s)); // to allow functions of functions
      if not FindParamBooleanReplaced('wts', false) then
        s := FormatDateTime('hh:mm:ss', now) + '   ' + FindParamString('y', 'I') + '   ' + s;
    end;
    FInter.DoLog('O', s);
  end;
end;

procedure TBafFrmModule.ConsoleSaveToFile;
var
  LFileName: string;
begin
  if FindParamBooleanReplaced('cnd', true) then begin
    LFileName := FindParamStringReplaced('fn', '');
    if trim(LFileName) <> '' then
      FConsoleMemo.Lines.SaveToFile(LFileName);
  end;
end;

constructor TBafFrmModule.Create;
begin
  inherited;
  FEditList := TStringList.Create;
  FEditList.OwnsObjects := true;
  FCompList := TObjectList.Create(false);
  FDefNodes := TStringList.Create;
  FDefNodes.OwnsObjects := true;
  FGridObjectList := TObjectList.Create(true);
  FXGridCols := TStringList.Create(true);
  FXXGridCols := TStringList.Create(true);
  FTreeNodeList := TObjectList.Create(false);
  FPageFailed := TStringList.Create;
  FSrvLog := TStringList.Create;
  FThreadList := TObjectList.Create(false);
  FCmdTimer := TTimer.Create(FTree);
  FCmdTimer.Enabled := false;
  FCmdTimer.OnTimer := CmdTimerTimer;
  FCmdTimer.Interval := 50;
end;

procedure TBafFrmModule.CreateConsole;
var
  LRightName: string;
begin
  CreateMemo(FConsoleMemo, FInter.ParentPanel, true);
  FConsoleMemo.Margins.Left := 8;
  FConsoleMemo.Margins.Top := 8;
  FConsoleMemo.Margins.Right := 8;
  FConsoleMemo.Margins.Bottom := 8;
  FConsoleMemo.StyleLookup := 'memotextstyle';

  LRightName := FindParamStringLower('r', 'frm');
  if FInter.GetRightOfDef(LRightName) <> brWrite then begin
    FExecInter.ExecutionBreak;
    FConsoleMemo.Lines.Add('No execution right');
  end;
  FInter.IsConsole := true;
  if not FindParamBooleanReplaced('ncd', false) then begin
    if not TfrmBafDialog.DialogYesNow(TBafTranslationModule.BafTranslate('$T(confirmation)'),
          TBafTranslationModule.BafTranslate('$T(console_confirmation)'), frmMain) then
      FInter.ExecutionBreak;
  end;
end;

procedure TBafFrmModule.CreateForm;
var
  LCaption, LTyp: string;

  procedure lokInit;
  begin
    FEditList.Clear;
    FCompList.Clear;
    while FInter.ParentPanel.ControlsCount > 0
      do FInter.ParentPanel.Controls[0].Free;
    FPage := nil;
    FConsoleMemo := nil;
    FBafSplitter := nil;
    FBafSingleSplitter := nil;
    FTree := nil;
    FLastExpanded := nil;
    FTreeNodeList.Clear;
  end;

begin
  Application.ProcessMessages;
  lokInit;
  // caption
  LCaption := FindParamStringReplaced('c', 'unnamed form');
  FInter.DoCaption(LCaption);
  // create form
  LTyp := FindParamStringLower('y', 'treepage');
  if LTyp = 'treepage' then
    CreateTreePage
  else if LTyp = 'console' then
    CreateConsole
  else if LTyp = 'page' then
    CreatePage
  else if LTyp = 'treeoverpage' then
    CreateTreePage(true)
  else if LTyp = 'live' then
    CreateLive
  ;
  // Other
  FFilterStatement := FindParamString('flt', '');
  FInter.LogHideCommand := FindParamBooleanReplaced('lhc', false);
end;

procedure TBafFrmModule.CreatePage;
var
  LRightName: string;
begin
  FCheckMemo := nil;
  FBafSingleSplitter := TBafSingleSplitter.Create(FInter.ParentPanel);
  FBafSingleSplitter.Parent := FInter.ParentPanel;
  FBafSingleSplitter.Align := TAlignLayout.Client;
  FRefreshDocks := true;


  LRightName := FindParamStringLower('r', 'frm');
  if FInter.GetRightOfDef(LRightName) = brNone then begin
    FExecInter.ExecutionBreak;
    exit;
  end;
  try
    FPage.Free;
  except
    on E: Exception do
      TfrmBafDialog.ShowMessage('Error', E.Message, frmMain);
  end;
  FPage := TBafPage.Place(FBafSingleSplitter, FBafSingleSplitter.Panel1);

  FPage.AfterEdit := GridAfterEdit;
  FPage.OnCellCalc := GridCellCalc;
//  FGrid.OnButtonClick := GridButtonClick;
  FPage.OnSegButtonClick := GridSegButtonClick;
  FPage.OnStatus := PageStatus;
  FPage.OnLinkClick := PageLinkClick;
  FPage.OnHintMouseEnter := HintMouseEnter;
  FPage.OnHintMouseLeave := HintMouseLeave;

//  FGrid.OnReplaceParameter := GridReplaceParameter;
  FPage.OnFillLookupLiveEvent := PageFillLookupLive;
//  FGrid.LookAndFeel.OnEnterGoDown := true;
  FPage.OnOpenClose := PageOpenClose;
  FPage.OnCellButton := PageCellButton;
  FCompList.Add(FPage);

  FPanelHint := TPanel.Create(FInter.ParentPanel);
  FPanelHint.Parent := FInter.ParentPanel;
  FPanelHint.Visible := false;
  FPanelHint.OnPaint := HintPaint;

  FPageRefreshCommand := FindParamString('prc', '');
  FInter.IsConsole := false;
end;

procedure TBafFrmModule.CreateLive;
var
  LRightName: string;
begin
  FPanelTop := TPanel.Create(FInter.ParentPanel);
  FPanelTop.Parent := FInter.ParentPanel;
  FPanelTop.Align := TAlignLayout.Top;
  FPanelTop.Height := FInter.ParentPanel.Height / 2;
  FPanelTop.StyleLookup := 'pushpanel';
  FPanelTop.Margins.Left := 8;
  FPanelTop.Margins.Top := 40;
  FPanelTop.Margins.Right := 8;
  FPanelTop.Margins.Bottom := 8;
  CreateMemo(FCommandMemo, FPanelTop, false);

  FSplitter := TSplitter.Create(FInter.ParentPanel);
  FSplitter.Parent := FInter.ParentPanel;
  FSplitter.Align := TAlignLayout.Top;
  FSplitter.Height := 5;

  FPanelBottom := TPanel.Create(FInter.ParentPanel);
  FPanelBottom.Parent := FInter.ParentPanel;
  FPanelBottom.Align := TAlignLayout.Client;
  FPanelBottom.StyleLookup := 'pushpanel';
  FPanelBottom.Margins.Left := 8;
  FPanelBottom.Margins.Top := 8;
  FPanelBottom.Margins.Right := 8;
  FPanelBottom.Margins.Bottom := 8;
  CreateMemo(FConsoleMemo, FPanelBottom, true);

  FCommandButton := TButton.Create(FInter.ParentPanel);
  FCommandButton.Parent := FInter.ParentPanel;
  FCommandButton.Position.X := 8;
  FCommandButton.Position.Y := 8;
  FCommandButton.Width := 120;
  FCommandButton.Text := 'Script execute';
  FCommandButton.OnClick := CommandButtonClick;

  FClearButton := TButton.Create(FInter.ParentPanel);
  FClearButton.Parent := FInter.ParentPanel;
  FClearButton.Position.X := 138;
  FClearButton.Position.Y := 8;
  FClearButton.Width := 120;
  FClearButton.Text := 'clear output';
  FClearButton.OnClick := ClearButtonClick;

  LRightName := FindParamStringLower('r', 'frm');
  if FInter.GetRightOfDef(LRightName) <> brWrite then begin
    FExecInter.ExecutionBreak;
    FConsoleMemo.Lines.Add('No execution right.');
    FCommandButton.Enabled := false;
  end;
  FInter.IsConsole := false;
end;

procedure TBafFrmModule.CreateMemo(var AMemo: TMemo; AParent: TFmxObject;
  AReadOnly: boolean);
begin
  AMemo := TMemo.Create(AParent);
  AMemo.Parent := AParent;
  AMemo.Align := TAlignLayout.Client;
  AMemo.ReadOnly := AReadOnly;
  AMemo.StyleLookup := 'memotextstyle';
end;

procedure TBafFrmModule.FetchGridCellValue(ADataSet: TDataSet;
  ACell: TBafSgCell; AColumn: TBafSgColumn; ARow: integer);
var
  LFieldName, LValue: string;
  LField: TField;
begin
  LFieldName := AColumn.CellFieldName;
  ACell.CellType := AColumn.CellType;
  if (LFieldName <> '') and (AColumn.CellDataQuelle in [dqSQL, dqY]) then begin
    LField := ADataSet.FindField(LFieldName);
    if Assigned(LField) then begin
      case ACell.CellType of
        ctDateMin: if  LField.AsDateTime < 1 then
            LValue := ''
          else
            LValue := FormatDateTime('dd.mm.yyyy hh:mm', LField.AsDateTime);
        ctDateSek: if  LField.AsDateTime < 1 then
            LValue := ''
          else
            LValue := FormatDateTime('dd.mm.yyyy hh:mm:ss', LField.AsDateTime);
        ctCurrInt: LValue := BafInt2CurrInt(LField.AsString)
      else
        LValue := LField.AsString;
      end;
      ACell.Text := CellCheckFormat(LValue, AColumn.CellCommand)
    end
    else begin
      if ARow = 0 then
        FInter.DoLog('E', '#grd_data / #xgrd_data, f "' + LFieldName + '" not found');
    end;
  end
  else if (AColumn.CellCommand <> '') and (AColumn.CellType in [ctButton]) then begin
    ACell.Command := AColumn.CellCommand;
    ACell.Text := FindParamStringReplaced(AColumn.LineP, 'z', '');
  end
  else if AColumn.CellCommand <> '' then
    ACell.Command := AColumn.CellCommand
  else
    ACell.Text := FindParamStringReplaced(AColumn.LineP, 'z', '');
  ACell.CellColorCommand := AColumn.CellColorCommand;
  ColumnNullValue(ACell);
// procedure TBafFrmModule.FetchGridCellValue
end;

procedure TBafFrmModule.FetchGridFields(ADataSet: TDataSet; var ARow: integer;
  AXGridIndex, AXXGridindex: integer; AIncRow: boolean);
// Goes through the cells and gets the values from the database
var
  LCol, ix, ixx: integer;
  LColumn: TBafSgColumn;
  LColumns: TBafSgColumns;
  LFieldName: string;
  LField: TField;
  LCell: TBafSgCell;

  procedure lokOther;
  begin
    if LColumn.CellHintFieldName <> '' then begin
      LFieldName := LColumn.CellHintFieldName;
      LField := ADataSet.FindField(LFieldName);
      if Assigned(LField) then
        LCell.Hint := CellCheckFormat(LField.AsString, LColumn.CellCommand)
      else
        LCell.Hint := LFieldName;
    end;
    if LColumn.CellReadOnlyFieldName <> '' then begin
      LFieldName := LColumn.CellReadOnlyFieldName;
      LField := ADataSet.FindField(LFieldName);
      if Assigned(LField) and BafIsYesChar((LField.AsString + BAFYESCHAR)[1]) then   // No data row, then ro=Y
        LCell.ReadOnly := true;
    end;
    if LColumn.CellType = ctLookupLive then begin
      LCell.LookupHelper := TBafComboHelper.Create(false);
      LCell.LookupHelper.Command := LColumn.CellLookupHelper.Command;
      FGridObjectList.Add(LCell.LookupHelper);
    end;
    if FLastSeg.DataKey[LColumn.CellDataQIndex] = LColumn.CellFieldName then
      LCell.Parents.Row.DataKeyValue := LCell.Text;
  end; // procedure lokOther

begin
  if FLastSeg.SegmentType = stMap then
    LColumns := FLastSeg.Map.Columns
  else
    LColumns := FLastSeg.Grid.Columns;
  for LCol := 0 to LColumns.Count - 1 do begin
    LColumn := LColumns.Items[LCol];
    if not LColumn.IsMapSpecCol then begin
      ix := LColumn.XGridIndex;
      ixx := LColumn.XXGridIndex;
      if ((ix = -1) or (ix = AXGridIndex))
          and ((ixx = -1) or (ixx = AXXGridIndex)) then begin
        if FLastSeg.SegmentType = stMap then
          LCell := FLastSeg.Map.Cells[rtData, LCol, ARow]
        else
          LCell := FLastSeg.Grid.Cells[rtData, LCol, ARow];
        FetchGridCellValue(ADataSet, LCell, LColumn, ARow);  // <-----
        lokOther;
      end;
    end;
  end;
  if AIncRow then
    inc(ARow);
// procedure TBafFrmModule.FetchGridFields
end;

procedure TBafFrmModule.FillPage;
var
  LDef: string;
  t, c, t1, t2, tb: int64;
begin
  LDef := FindParamStringReplaced('cmd', '');
  if LDef = '' then
    LDef := FindParamStringReplaced('d', '');
  if LDef <> '' then begin
    LDef := FExecInter.ReplaceFunctions(LDef);
    FPage.SaveGridCache;
    DataThreadListClear;
    FPage.Clear;
    FChangeRow := -1;
    FChangeCol := -1;

    FPageName := '';
    QueryPerformanceFrequency (c);
    QueryPerformanceCounter(t1);
    TBafInterpreterLevel.ExecInNewLevel(LDef, FExecInter, FInter);
    QueryPerformanceCounter(t2);
    t := 1000 * (t2 - t1) div c;
    QueryPerformanceCounter(t1);
    PageReady;

    QueryPerformanceCounter(t2);
    tb := 1000 * (t2 - t1) div c;

    BafPerformanceLog(Format('FillPage, Exec %d, Ready %d', [t, tb]));
  end;
// procedure TBafFrmModule.FillGrid
end;

procedure TBafFrmModule.FillNodeIni(ALineP: string; AChanged,
  AAddFields: boolean; AIni: TStringIniFile; AQuelle: TBafDataQuelle = dqSql);
var
  LTable, s: string;
begin
  if AQuelle in [dqTable, dqSql] then begin
    LTable := FindParamStringLower(ALineP, 't', '');
    AIni.WriteStringNotEmpty(SEC_ADD, 'table', LTable);
    AIni.WriteStringNotEmpty(SEC_ADD, 'k', GetPrimaryKey(ALineP));
    if AChanged and (LTable <> '') then
      AIni.WriteBool(SEC_ADD, 'ins', true);
  end;
  AIni.WriteString(SEC_ADD, 'sel', FindParamString(ALineP, 's', '_page_empty'));
  AIni.WriteBoolJN(SEC_ADD, 'treefocus', FindParamBooleanReplaced(ALineP, 'tf', true));
  AIni.WriteStringNotEmpty(SEC_ADD, 'open', FindParamString(ALineP, 'o', ''));

  AIni.WriteStringNotEmpty(SEC_ADD, 'c1', FindParamStringLower(ALineP, 'c1', ''));
  AIni.WriteStringNotEmpty(SEC_ADD, 'c2', FindParamStringLower(ALineP, 'c2', ''));
  AIni.WriteStringNotEmpty(SEC_ADD, 'ld1', FindParamStringLower(ALineP, 'ld1', ''));
  AIni.WriteStringNotEmpty(SEC_ADD, 'ld2', FindParamStringLower(ALineP, 'ld2', ''));
  AIni.WriteStringNotEmpty(SEC_ADD, 'ls1', FindParamStringLower(ALineP, 'ls1', ''));
  AIni.WriteStringNotEmpty(SEC_ADD, 'ls2', FindParamStringLower(ALineP, 'ls2', ''));

  if AAddFields and (AQuelle in [dqTable, dqSql]) then
    FExecInter.Fields2Ini(ALineP, AIni);

end;

procedure TBafFrmModule.FillThread;
var
  LThread: TBafLoadDataThread;
  LSql: string;
begin
  LSql := FInter.GetSqlAndClear(1);
  if FindParamBooleanReplaced('cnd', true) then begin
    LThread := TBafLoadDataThread.Create(true);
    LThread.ThreadType := ttTree;
    LThread.Tree := FTree;
    LThread.Sql := LSql;
    LThread.BafConName := FindParamStringReplacedLower('db', DC_DEFAULT);
    LThread.KeyName := FindParamStringReplaced('k', '');
    LThread.RootNode := GetNode(FindParamStringReplacedLower('u', 'r'));
    LThread.FreeOnTerminate := true;
    LThread.OnTerminate := LoadDataThreadTerminate;
    FThreadList.Add(LThread);
    LThread.Suspended := false;
  end;
end;

procedure TBafFrmModule.FillTree;
// selects the nodes from a DB table or a SQL statement
var
  LSql, LTable, LOrder, LWhere, LOpen, s, LBafConName, LName: string;
  LItem, LFirstItem: TBafTreeNode;
  LIni: TStringIniFile;
  LDataSet: TDataSet;
  LMax, LCount, ix: integer;
  LDoFirstItem: boolean;
  LRight: TBafRight;
  LQuelle: TBafDataQuelle;
  LRoot, LLoop, LCurrentNode: TBafJsonNode;


  procedure lokAddItem;
  var
    i: integer;
  begin
    LItem := InsertNewItem(FNode, '', true);
    LItem.OnFunction := TreeItemFunction;
    FillNodeIni(FExecInter.LineP, false, false, LItem.Ini, LQuelle);
    case LQuelle of
      dqTable, dqSql: FExecInter.Fields2IniData(FExecInter.LineP, '',
          LItem.Ini, LDataSet, true);
      dqJson: FExecInter.Fields2IniData(FExecInter.LineP, '.',
          LItem.Ini, LCurrentNode, true, LQuelle);
    end;
    LItem.Text := FindParamStringReplaced('c', LItem.GetCaption);
    if FindParamString('o', '') <> '' then
      LItem.HasOpenCommand := true;
    if LDoFirstItem and (LFirstItem = nil) then
      LFirstItem := LItem;
    if FindParamBooleanReplaced('ne', false) and Assigned(FNode) then
      FNode.Opened := true;
    if FindParamBooleanReplaced('sn', false) then
      FSpecialNode := LItem;
    FLast := LItem;
  end;

  procedure lokTableQuery;
  // Creates the SQL statement and get additional data
  begin
    LBafConName := FindParamStringReplacedLower('db', DC_DEFAULT);
    LName := FInter.Name + '~' + LBafConName;
    FindParam('t', LTable);
    FindParam('o', LOpen);
    LMax := FindParamInteger('m', dataMain.DefaultMaxRows);
    LDoFirstItem := FindParamBooleanReplaced('fi', false);
    LCount := 0;
    if LSql = '' then begin
      LOrder := FindParamStringReplaced('qo', '');
      LWhere := FindParamStringReplaced('qw', '');
      LSql := 'select * from ' + LTable
          + IfThen(LWhere = '', '', ' where ' + LWhere)
          + IfThen(LOrder = '', '', ' order by ' + LOrder);
    end;

    SqlAndParams(LBafConName, LName, LSql);
    LDataSet := dataMain.QueryOpen(LBafConName, LName);
    while not LDataSet.Eof do begin
      lokAddItem;
      inc(LCount);
      if LCount >= LMax then begin
        FInter.DoLog('I', Format('#filltree, Max (%d) reached, data load breaked',
            [LMax]));
        Break;
      end;
      LDataSet.Next;
    end;
  end; // procedure lokTableQuery

  procedure lokJson;
  var
    LPath, LSep: string;
    i, LNum: integer;
  begin
    LNum := FindParamIntegerReplaced('n', 1);
    LRoot := (FInter.GetModule('json') as TBafJsonModule).GetParsedJson(LNum);
    LPath := FindParamStringReplaced('path', '');
    LSep := FindParamStringReplaced('sep', '.');
    if LRoot.FindNode(LPath, LSep, LLoop) then begin
      for i := 0 to LLoop.ChildCount - 1 do begin
        LCurrentNode := LLoop.Child[i];
        lokAddItem;
      end;
    end;
  end; // procedure lokJson

begin
  LSql := FInter.GetSqlAndClear(1);
  if FindParamBooleanReplaced('cnd', true) then begin
    LFirstItem := nil;
    SetFNode(FExecInter.LineP);
    FTree.BeginUpdate;
    try
      LRight := FExecInter.GetRight;
      if (LRight > brNone) then begin
        LQuelle := FExecInter.FindParamQuelle(FExecInter.LineP, dqTable, ix);
        case LQuelle of
          dqTable, dqSQL: lokTableQuery;
          dqJSON: lokJson;

        end;

        FTree.RefreshVisibleNodes;
      end;
    finally
      FTree.EndUpdate;
    end;
    if LDoFirstItem and Assigned(LFirstItem) then
      FTreeSelectNode := LFirstItem;
    frmMain.SpecialPageRefresh(true);
  end;
// procedure TBafFrmModule.FillTree
end;

procedure TBafFrmModule.FillTreePath;
var
  LSql, LTable, LPathName, LWhere, LPathValue, LPrefix, LBafConName, LName: string;
  LMax, LCount: integer;
  LDoFirstItem: boolean;
  LItem, LParent, LFirstItem, LLastItem: TBafTreeNode;
  LDataSet: TDataSet;
  LOpenList: TObjectList;

  procedure lokAddItem(AZeileP: string; AIndex: integer);
  begin
    if AIndex > 0 then begin
      SetFNode(AZeileP);
      LParent := FNode;
    end;

    LItem := InsertNewItem(LParent, '', false);
    LItem.OnFunction := TreeItemFunction;
    FillNodeIni(AZeileP, false, false, LItem.Ini);
    FExecInter.Fields2IniData(AZeileP, '', LItem.Ini, LDataSet, true);
    LItem.Text := FExecInter.FindParamString(AZeileP, 'c', '');
    LItem.Text := LItem.GetCaption;
    if FindParamBooleanReplaced(AZeileP, 'sn', false) then
      FSpecialNode := LItem;
    FLast := LItem;
    if LFirstItem = nil then
      LFirstItem := LItem;
    if AIndex = 0 then begin
      LLastItem := FLast;
      FLast.Ini.WriteString(SEC_ADD, 'path', LPathValue);
    end;

    if FExecInter.FindParamString(AZeileP, 'o', '') <> '' then
      LOpenList.Add(LItem);
  end; // procedure lokAddItem

  function lokPfadRauf(APfad: string): string;
  var
    i: integer;
  begin
    result := '';
    for i := Length(APfad) downto 1 do begin
      if APfad[i] = '.' then begin
        result := copy(APfad, 1, i - 1);
        exit;
      end;
    end;
  end; // function lokPfadRauf

  function lokFindPath(APfad: string): TBafTreeNode;
  var
    s: string;
  begin
    result := LLastItem;
    if Assigned(result) then
      s := result.Ini.ReadString(SEC_ADD, 'path', '');
    while (s <> APfad) and (result <> nil) do begin
      result := (result.ParentNode as TBafTreeNode);
      if Assigned(result) then
        s := result.Ini.ReadString(SEC_ADD, 'path', '');
    end;
  end; // function lokFindPfad

  function lokAddRow: boolean;
  var
    i: integer;
    LCondition: string;
  begin
    FInter.DebugDbRow(LDataSet);
    result := false; // we don't abort

    // where to insert in the tree
    LPathValue := LDataSet.FieldByName(LPathName).AsString;
    LParent := lokFindPath(lokPfadRauf(LPathValue));

    // we go through the DefNodes and insert a node for every item
    for i := 0 to FDefNodes.Count - 1 do begin
      LCondition := FindParamStringReplaced(FDefNodes[i], 'cnd', '');
      if (LCondition = '') or FExecInter.ParseBoolStatement(LCondition) then
        lokAddItem(FDefNodes[i], i);                   // <-------------------
    end; // for i

    // we inc the counter, when max is arrived we abort
    inc(LCount);
    if LCount >= LMax then begin
      FInter.DoLog('I', Format('#filltreepath, Max (%d) arrived, loop aborted', [LMax]));
      result := true; // we abort
    end;
    LDataSet.Next;          // and now the next data row
  end; // procedure lokAddRow

  procedure lokAddDummys;
  // We go through all new nodes with an open statement,
  // if they have noi children we insert a dummy
  var
    i: integer;
    LItem: TBafTreeViewItem;
  begin
    for i := 0 to LOpenList.Count - 1 do begin
      if LOpenList[i] is TBafTreeViewItem then begin
        LItem := TBafTreeViewItem(LOpenList[i]);
//        if LItem.Count = 0 then
//          InsertNewItem(LItem, BAF_DUMMYNODE_CAPTION, false);
      end;
    end;
  end; // procedure lokAddDummys

begin
  LSql := FInter.GetSqlAndClear(1);
  if FindParamBooleanReplaced('cnd', true) then begin
    LFirstItem := nil;
    SetFNode(FDefNodes[0]);
    LOpenList := TObjectList.Create(false);
    FTree.BeginUpdate;
    try
      LBafConName := FindParamStringReplacedLower('db', DC_DEFAULT);
      LName := FInter.Name + '~' + LBafConName;
      FindParam('t', LTable);
      FindParam('pn', LPathName);
      LPrefix := FindParamString('pfx', '');
      if Assigned(FNode) then
        FNode.Ini.WriteString(SEC_ADD, 'path', LPrefix);
      LMax := FindParamInteger('m', MaxInt);
      LDoFirstItem := FindParamBoolean('fi', false);
      LLastItem := FNode;
      LCount := 0;
      if LSql = '' then begin
        LWhere := FindParamStringReplaced('qw', '');
        LSql := 'select * from ' + LTable
            + IfThen(LWhere = '', '', ' where ' + LWhere)
            + ' order by ' + LPathName;
      end;

      SqlAndParams(LBafConName, LName, LSql);
      LDataSet := dataMain.QueryOpen(LBafConName, LName);
      while not LDataSet.Eof do begin
        if lokAddRow then
          Break;
      end;
      lokAddDummys;
      FTree.RefreshVisibleNodes;
    finally
      FDefNodes.Clear;
      FTree.EndUpdate;
      LOpenList.Free;
    end;
    if LDoFirstItem and Assigned(LFirstItem) then
      FTreeSelectNode := LFirstItem;
    frmMain.SpecialPageRefresh(true);
  end;
// procedure TBafFrmModule.FillTreePath
end;

procedure TBafFrmModule.FillTreeSql;
var
  LRight: TBafRight;
  LSql, LKeyValue, LBafConName, LName: string;
  LDataSet: TDataSet;
  LMax, LCount, ix: integer;
  LOpenList: TObjectList;
  LDef, LSubDef: TDefNodeObject;
  LQuelle: TBafDataQuelle;
  LDoFirstItem: boolean;
  LFirstItem: TBafTreeNode;

  procedure lokAddItem(ALineP: string; AIndex: integer);
  var
    i: integer;
    LIni: TStringIniFile;
    LItem, LParent: TBafTreeNode;
  begin
    // If we start over on an upper level, we must also reset everything below it
    if FindParamBoolean(ALineP, 'nc', false) then begin
      for i := AIndex + 1 to FDefNodes.Count - 1 do begin
        LSubDef := TDefNodeObject(FDefNodes.Objects[i]);
        LSubDef.FLastInsertedNode := nil;
        LSubDef.FKeyColumnValue := BAF_OTHER;
      end;
    end;

    // find the parent
    LParent := nil;
    if Assigned(LDef.FLastInsertedNode) then
      LParent := LDef.FLastInsertedNode.ParentNode;
    if LParent = nil then begin
      SetFNode(ALineP);
      LParent := FNode;
    end;


    // create and insert the  node
    LItem := InsertNewItem(LParent, '', true);
    LItem.OnLookup := TreeItemLookup;
    LItem.OnFunction := TreeItemFunction;
    FillNodeIni(ALineP, false, false, LItem.Ini);
    FExecInter.Fields2IniData(ALineP, '', LItem.Ini, LDataSet, true);
    LItem.Text := FindParamStringReplaced(ALineP, 'c', LItem.GetCaption);
    if FindParamString(ALineP, 'o', '') <> '' then
      LItem.HasOpenCommand := true;
    if LDoFirstItem and (LFirstItem = nil) then
      LFirstItem := LItem;
    if FindParamBooleanReplaced(ALineP, 'ne', false) and Assigned(LItem) then
      LItem.Opened := true;
    if FindParamBooleanReplaced(ALineP, 'sn', false) then
      FSpecialNode := LItem;
    FLast := LItem;
    if LDoFirstItem and (LFirstItem = nil) then
      LFirstItem := LItem;
    LDef.FLastInsertedNode := LItem;
    LDef.FKeyColumnValue := LKeyValue;
  end; // procedure lokAddItem

  function lokAddRow: boolean;
  var
    i: integer;
    LField: TField;
    LIgnoreEmptyKey: boolean;
    LCondition: string;
  begin
    result := false;

    // we go through the DefNodes and create a node for each entry
    for i := 0 to FDefNodes.Count - 1 do begin
      LDef := TDefNodeObject(FDefNodes.Objects[i]);
      LField := LDataSet.FindField(LDef.FKeyColumn);
      if Assigned(LField) then
        LKeyValue := LDataSet.FindField(LDef.FKeyColumn).AsString
      else begin
        LKeyValue := FExecInter.FindParamString(FDefNodes[i], 'c', '');
        if LDef.FKeyColumn <> '' then
          FInter.DoLog('W', '#filltreesql - can not find key field: ' + LDef.FKeyColumn);
      end;
      if LKeyValue <> LDef.FKeyColumnValue then begin
        LIgnoreEmptyKey := FindParamBooleanReplaced(FDefNodes[i], 'iek', false);
        if (LKeyValue <> '') or not LIgnoreEmptyKey then begin
          LCondition := FindParamStringReplaced(FDefNodes[i], 'cnd', '');
          if (LCondition = '') or FExecInter.ParseBoolStatement(LCondition) then
            lokAddItem(FDefNodes[i], i);                   // <-------------------
        end;
      end;
    end; // for i

    // We increase the counter and abort if necessary
    inc(LCount);
    if LCount >= LMax then begin
      FInter.DoLog('I', Format('#filltreesql, Max (%d) reached, loop aborted', [LMax]));
      result := true; // abort
    end;
    LDataSet.Next;
  end; // procedure lokAddRow

  procedure lokSql;
  begin
    if LSql <> '' then begin
      SqlAndParams(LBafConName, LName, LSql);
      LMax := FindParamInteger('m', MaxInt);
      LCount := 0;
      LDataSet := dataMain.QueryOpen(LBafConName, LName);
      while not LDataSet.Eof do begin
        if lokAddRow then
          Break;
      end;
    end;
  end;

  procedure lokCheckMaxExpand;
  var
    LMax, i: integer;
  begin
    LMax := FindParamIntegerReplaced('mex', 0);
    if FTree.Root.ChildCount <= LMax then begin
      for i := 0 to FTree.Root.ChildCount - 1 do
        FTree.Root.Childs[i].Opened := true;
    end;
  end; // procedure lokCheckMaxExpand

begin
  LSql := FInter.GetSqlAndClear(1);
  if FindParamBooleanReplaced('cnd', true) then begin
    LFirstItem := nil;
    LRight := FExecInter.GetRight;
    if (LRight > brNone) then begin
      LBafConName := FindParamStringReplacedLower('db', DC_DEFAULT);
      LName := FInter.Name + '~' + LBafConName;
      FTreeLock := false;
      LOpenList := TObjectList.Create(false);
      LDoFirstItem := FindParamBooleanReplaced('fi', false);
      LFirstItem := nil;
      FTree.BeginUpdate;
      try
        LQuelle := FExecInter.FindParamQuelle(FExecInter.LineP, dqSQL, ix);
        case LQuelle of
          dqSQL: lokSql;
        end;
      finally
        FTree.EndUpdate;
        LOpenList.Free;
        FTreeLock := false;
      end;
      FTree.RefreshVisibleNodes;
    end;
    FDefNodes.Clear;
    lokCheckMaxExpand;
    if LDoFirstItem and Assigned(LFirstItem) then
      FTreeSelectNode := LFirstItem;
    frmMain.SpecialPageRefresh(true);
  end;
// procedure TBafFrmModule.FillTreeSql
end;

procedure TBafFrmModule.CreateTreePage(AVertical: boolean);
begin
  FBafSplitter := TBafSplitter.Create(FInter.ParentPanel, AVertical);
  FBafSplitter.Parent := FInter.ParentPanel;
  FBafSplitter.Align := TAlignLayout.Client;
  FBafSplitter.Panel1Size := FindParamInteger('w', 300);
  FRefreshDocks := true;

  FTree := TBafTree.Place(FBafSplitter, FBafSplitter.Panel1);

  FTree.OnOpenClose := TreeViewExpanded;
  FTree.OnSelect := TreeViewChange;
  FCompList.Add(FTree);

  FCheckMemo := TMemo.Create(FBafSplitter);
  FCheckMemo.Parent := FBafSplitter.Panel1;
  FCheckMemo.Align := TAlignLayout.Client;
  FCheckMemo.Visible := false;
  FCheckMemo.ReadOnly := true;

//  FPage.Free;
  FPage := TBafPage.Place(FBafSplitter, FBafSplitter.Panel2);

  FPage.AfterEdit := GridAfterEdit;
  FPage.OnCellCalc := GridCellCalc;

  FPage.OnHeaderButtonClickedEvent := GridHeaderButtonClick;
  FPage.OnSegButtonClick := GridSegButtonClick;
  FPage.OnStatus := PageStatus;
  FPage.OnLinkClick := PageLinkClick;
  FPage.OnHintMouseEnter := HintMouseEnter;
  FPage.OnHintMouseLeave := HintMouseLeave;
  FPage.OnFillLookupLiveEvent := PageFillLookupLive;
  FPage.OnOpenClose := PageOpenClose;
  FPage.OnCellButton := PageCellButton;
  FCompList.Add(FPage);

  FPanelHint := TPanel.Create(FInter.ParentPanel);
  FPanelHint.Parent := FInter.ParentPanel;
  FPanelHint.Visible := false;
  FPanelHint.OnPaint := HintPaint;
  FInter.IsConsole := false;
end;

procedure TBafFrmModule.DataThreadListClear;
var
  i: integer;
begin
  for i := 0 to FThreadList.Count - 1 do
    (FThreadList[i] as TThread).Terminate;
end;

destructor TBafFrmModule.Destroy;
begin
  FreeAndNil(FSrvLog);
  FreeAndNil(FPageFailed);
  FreeAndNil(FTreeNodeList);
  FreeAndNil(FXXGridCols);
  FreeAndNil(FXGridCols);
  FreeAndNil(FGridObjectList);
//  FreeAndNil(FDefCols);
  FreeAndNil(FDefNodes);
  FreeAndNil(FEditList);
  FreeAndNil(FCompList);
  DataThreadListClear;
  FreeandNil(FThreadList);
  inherited;
end;

function TBafFrmModule.DlgYesNo(AParams: TStrings): string;
// 0 - Title
// 1 - Message
begin
  result := IfThen(TfrmBafDialog.DialogYesNow(AParams[0], AParams[1], frmMain), 'Y', 'N');
end;

procedure TBafFrmModule.DoModuleEvent(AInter: TBafCustomInterpreter;
  AEvent: TBafInterpreterEvent; AText: string);
begin
  inherited;
  if (AEvent = ieBeforeExec) and (AInter = FInter) then begin
    FTreeSelectNode := nil;
//    FTreeExpandList.Clear;
  end;
  if (AEvent = ieAfterExec) then begin
    if FRefreshDocks then begin
      if Assigned(FBafSplitter) then
        FBafSplitter.RefreshDocks
      else if Assigned(FBafSingleSplitter) then
        FBafSingleSplitter.RefreshDocks;
      FRefreshDocks := false;
    end;
    if Assigned(FTreeSelectNode) and (AInter = FInter) then begin
      FTree.Selected := FTreeSelectNode;
      FTreeSelectNode := nil;
    end;
  end;
end;

procedure TBafFrmModule.DoFilter(Sender: TObject);
var
  LFieldName, LFieldValue: string;
  i: integer;
  LItem: TBafTreeNode;
  LClearInter: boolean;

  function lokSearchInChilds(AParent: TBafTreeNode): boolean;
  var
    i: integer;
    LChildItem: TBafTreeNode;
  begin
    result := false;
    for i := 0 to AParent.ChildCount - 1 do begin
      LChildItem := AParent.Childs[i];
      if LChildItem.IsValue(LFieldName, LFieldValue) then begin
        FTree.Selected := LChildItem;
        if FindParamBooleanReplaced('o', false) then
          FTree.Selected.Opened := true;
        result := true;
        exit;
      end;
      if lokSearchInChilds(LChildItem) then
        exit;
    end;
  end; // function lokSearchInChilds

begin
  LClearInter := (FExecInter = nil);
  if LClearInter then
    FExecInter := FInter;
  try
    LFieldName := FindParamStringReplaced('f', '');
    if LFieldName <> '' then
      LFieldValue := FTree.Selected.GetNodeValue(SEC_DATA, LFieldName, '');
    TBafInterpreterLevel.ExecInNewLevel(FFilterStatement, FExecInter, FInter);
    FPage.SetStatus;
    if (LFieldName <> '') and (LFieldValue <> '') then begin
      for i := 0 to FTree.Root.ChildCount - 1 do begin
        LItem := FTree.Root.Childs[i];
        if LItem.IsValue(LFieldName, LFieldValue) then begin
          FTree.Selected := LItem;
          if FindParamBooleanReplaced('o', false) then
            FTree.Selected.Opened := true;
          exit;
        end;
        if lokSearchInChilds(LItem) then
          exit;
      end;
    end;
  finally
    if LClearInter then
      FExecInter := nil;
  end;
// procedure TBafFrmModule.DoFilter
end;

function TBafFrmModule.FindDock(ADefault, ARoutine: string): TBafButtonDock;
var
  LParent: string;
begin
  result := nil;
  if Assigned(FBafSplitter) then begin
    LParent := FindParamStringLower('p', ADefault);
    if LParent = 'b' then
      result := FBafSplitter.Dock2
    else if LParent = 'f' then
      result := FBafSplitter.Dock1;
  end
  else if Assigned(FBafSingleSplitter) then
    result := FBafSingleSplitter.Dock1;
  if result = nil then
    raise Exception.Create(ARoutine + ', Parent not found: ' + LParent);
end;

function TBafFrmModule.FindPageValue(AParams: TStrings): string;
var
  LCol, LRow, LValue: integer;
  LSegment: TBafPageSegment;
  LGrid: TBafSimpleGrid;
  LMap: TBafMap;
  s, LSep: string;
  LDisplay: boolean;

  procedure lokGetFieldname;
  var
    i, j: integer;
  begin
    if LSegment.SegmentType in [stGrid, stXGrid, stXXGrid] then begin
      LCol := -1;
      for i := 0 to LGrid.Columns.Count - 1 do begin
        if AnsiCompareText(s, LGrid.Columns.Items[i].CellFieldName) = 0 then begin
          LCol := i;
          exit;
        end;
      end;
    end
    else if LSegment.SegmentType in [stMap] then begin
      LCol := -1;
      for i := 0 to LMap.Columns.Count - 1 do begin
        if AnsiCompareText(s, LMap.Columns.Items[i].CellFieldName) = 0 then begin
          LCol := i;
          exit;
        end;
      end;
    end
    else if LSegment.SegmentType in [stValueList] then begin
      LCol := -1;
      for i := 0 to LGrid.Columns.Count - 1 do begin
        for j := 0 to LGrid.RowCount(rtData) - 1 do begin
          if AnsiCompareText(s, LGrid.Cells[rtData, i, j].DataFieldName) = 0 then begin
            LCol := i;
            LRow := j;
            exit;
          end;
        end;
      end;
    end;
  end; //  procedure lokGetFieldname

  procedure lokCol;
  begin
    if Assigned(LGrid) then
      LCol := LGrid.SelectedCol
    else
      LCol := 0;
    if AParams.Count > 1 then begin
      s := AnsiLowerCase(AParams[1]);
      LValue := StrToIntDef(s, -1);
      if LValue >= 0 then
        LCol := LValue
      else if copy(s, 1, 4) = '!col' then begin
        Delete(s, 1, 4);
        LValue := StrToIntDef(s, 0);
        LCol := FGridDataCol + LValue;
      end
      else
        lokGetFieldname;
    end;
  end; // procedure lokCol

  procedure lokAll;
  var
    i, LSel: integer;
  begin
    if AParams.Count > 3 then
      LSep := FExecInter.ReplaceFunctions(AParams[3]);
      if s = 'allsel' then
        LSel := LGrid.SelectionColumn
      else
        LSel := -1;
      s := '';
      for i := 0 to LGrid.RowCount(rtData) - 1 do begin
        if (Lsel = -1) or BafIsYesChar(LGrid.Cells[rtData, LSel, i].Text) then
          s := s + LSep + LGrid.Cells[rtData, LCol, i].Text;
      end;
    result := copy(s, Length(LSep) + 1, MaxInt);
    LRow := -1;
  end; // procedure lokAll

  procedure lokCalc(ASel: boolean);
  var
    i, LSel, LCount, LRowCount: integer;
    LSum, LMin, LMax, LValue: extended;
    LCellType: TBafPageCellType;
    t: string;
    LRowType: TBafGridRowType;
    LIsSel: boolean;

    function lokValue(AValue: extended; AInt: boolean): string;
    begin
      if AInt then
//        result := FormatFloat('0', round(AValue))     // nicht IntToStr, weil round nur 32 Bit
        result := IntToStr(round(AValue))     // nicht IntToStr, weil round nur 32 Bit
      else begin
        case LCellType of
          ctCurr, ctInt, ctCurrInt, ctText: result := FormatFloat('0.00', AValue);
          ctCurr4: result := FormatFloat('0.0000', AValue);
          ctDate: FormatDateTime('dd.mm.yyyy', AValue);
          ctDateMin: FormatDateTime('dd.mm.yyyy hh:mm', AValue);
          ctDateSek: FormatDateTime('dd.mm.yyyy hh:mm:ss', AValue);
          ctBool, ctBool2: result := FormatFloat('0.00', AValue);
        else
          result := '';
        end;
      end;
    end; // function lokValue

  begin
    if ASel and Assigned(LGrid) then
      LSel := LGrid.SelectionColumn
    else if ASel and Assigned(LMap) then
      LSel := LMap.Columns.FieldName2Ix(LMap.Field_IsChecked)
    else
      LSel := -1;
    LSum := 0;
    LCount := 0;
    LMax := - MaxInt;
    LMin := MaxInt;
    if Pos('vis', s) > 0 then
      LRowType := rtDisplayData
    else
      LRowType := rtData;
    if Assigned(LGrid) then
      LCellType := LGrid.Columns.Items[LCol].CellType
    else if Assigned(LMap) then
      LCellType := LMap.Columns.Items[LCol].CellType;
    if Assigned(LGrid) then
      LRowCount := LGrid.RowCount(LRowType)
    else if Assigned(LMap) then
      LRowCount := LMap.RowCount(LRowType);
    for i := 0 to LRowCount - 1 do begin
      if (Lsel > -1) and Assigned(LGrid) then
        LIsSel := BafIsYesChar(LGrid.Cells[LRowType, LSel, i].Text)
      else if (Lsel > -1) and Assigned(LMap) then
        LIsSel := BafIsYesChar(LMap.Cells[LRowType, LSel, i].Text);
      if (Lsel = -1) or LIsSel then begin
        if Assigned(LGrid) then
          t := LGrid.Cells[LRowType, LCol, i].Text
        else if Assigned(LMap) then
          t := LMap.Cells[LRowType, LCol, i].Text;
        case LCellType of
          ctCurr, ctCurr4, ctInt, ctCurrInt, ctText: LValue := StrToFloatDef(t, 0);
          ctDate, ctDateMin, ctDateSek: LValue := StrToDateTimeDef(t, 0);
          ctBool, ctBool2: LValue := IfThen(BafIsYesChar(t), 1, 0);
        else
          LValue := 0;
        end;
        inc(LCount);
        LSum := LSum + LValue;
        LMin := System.Math.Min(LMin, LValue);
        LMax := System.Math.Max(LMax, LValue);
      end;
    end;
    if (s = 'count') or (s = 'countsel') or (s = 'countvis') then
      result := lokValue(LCount, true)
    else if (s = 'sum') or (s = 'sumsel') or (s = 'sumvis') then
      result := lokValue(LSum, LCellType = ctInt)
    else if (s = 'min') or (s = 'minsel')  or (s = 'minvis') then
      result := lokValue(LMin, LCellType = ctInt)
    else if (s = 'max') or (s = 'maxsel') or (s = 'maxvis') then
      result := lokValue(LMax, LCellType = ctInt)
    else if (s = 'avg') or (s = 'avgsel') or (s = 'avgvis') then
      result := lokValue(LSum / System.Math.Max(1, LCount), false);
    LRow := -1;
  end; // procedure lokCalc

  procedure lokMedian(ASel, AVisible: boolean);
  var
    sl: TStringList;
    LSel, i, L1, L2: integer;
    LCellType: TBafPageCellType;
    LRowType: TBafGridRowType;

    procedure lokAvgMiddle;
    begin
      if sl.Count > 0 then begin
        L1 := (sl.Count div 2) - 1;
        L2 := sl.Count div 2;
        case LCellType of
          ctInt: result := IntToStr(round((StrToIntDef(sl[L1], 0)
              + StrToIntDef(sl[L2], 0)) / 2));
          ctCurr, ctCurrInt: result := FormatFloat('0.00', (StrToCurrDef(sl[L1], 0)
              + StrToCurrDef(sl[L2], 0)) / 2);
          ctCurr4: result := FormatFloat('0.0000', (StrToCurrDef(sl[L1], 0)
              + StrToCurrDef(sl[L2], 0)) / 2);
          ctDate: result := FormatDateTime('dd.mm.yyyy',
              (StrToDateTimeDef(sl[L1], 0) + StrToDateTimeDef(sl[L2], 0)) / 2);
          ctDateMin: result := FormatDateTime('dd.mm.yyyy  hh:mm',
              (StrToDateTimeDef(sl[L1], 0) + StrToDateTimeDef(sl[L2], 0)) / 2);
          ctDateSek: result := FormatDateTime('dd.mm.yyyy hh:mm:ss',
              (StrToDateTimeDef(sl[L1], 0) + StrToDateTimeDef(sl[L2], 0)) / 2);
        end;
      end;
    end; // procedure lokAvgMiddle

  begin
    if ASel then
      LSel := LGrid.SelectionColumn
    else
      LSel := -1;
    if AVisible then
      LRowType := rtDisplayData
    else
      LRowType := rtData;
    sl := TStringList.Create;
    try
      LCellType := LGrid.Columns.Items[LCol].CellType;
      for i := 0 to LGrid.RowCount(LRowType) - 1 do begin
        if (Lsel = -1) or BafIsYesChar(LGrid.Cells[LRowType, LSel, i].Text) then
          sl.Add(LGrid.Cells[LRowType, LCol, i].Text);
      end;
      case LCellType of
        ctInt, ctCurr, ctCurr4, ctCurrInt: sl.CustomSort(BafStringListCompareCurrency);
        ctDate, ctDateMin, ctDateSek: sl.CustomSort(BafStringListCompareDateTime);
      else
        sl.Sort;
      end;
//      Clipboard.AsText := sl.Text;
      if sl.Count mod 2 = 1 then
        result := sl[sl.Count div 2]
      else
        lokAvgMiddle;
    finally
      sl.Free;
    end;
    LRow := -1;
  end; // procedure lokMedian


  procedure lokRow;
  begin
    if Assigned(LGrid) then
      LRow := LGrid.SelectedRow;
    if AParams.Count > 2 then begin
      LRow := -1;
      s := AnsiLowerCase(AParams[2]);
      LValue := StrToIntDef(s, -1);
      if LValue >= 0 then
        LRow := LValue
      else if (s = 'looprow') and Assigned(LGrid) then
        LRow := LGrid.LoopRow
      else if (s = 'checkrow') and Assigned(LGrid) then
        LRow := LGrid.CheckRow
      else if (s = 'calcrow') then
        LRow := LSegment.CountRow
      else if ((s = 'data') or (s = 'datarow')) and Assigned(LGrid) then
        LRow := FGridDataRow
      else if ((s = 'link') or (s = 'linkrow')) and Assigned(LGrid) then
        LRow := FLinkRow
      else if (s = 'sel') and Assigned(LGrid) then
        LRow :=  LGrid.SelectedRow
      else if s = 'lookuplive' then
        LRow := FLiveCell.Parents.Row.RowIndex
      else if s = 'change' then
        LRow := FChangeRow
      else if s = 'saverow' then
        LRow := FGridSaveRow
      else if (s = 'radio') and Assigned(LGrid) then
        LRow := LGrid.RadioRow

      { TODO -oME : für Maps ergänzen }
      else if ((s = 'count') or (s = 'sum') or (s = 'min') or (s = 'max')
          or (s = 'avg'))      and (LCol >= 0) then
        lokCalc(false)
      else if ((s = 'countvis') or (s = 'sumvis') or (s = 'minvis')
          or (s = 'maxvis') or (s = 'avgvis'))      and (LCol >= 0) then
        lokCalc(false)
      else if ((s = 'countsel') or (s = 'sumsel') or (s = 'minsel')
          or (s = 'maxsel') or (s = 'avgsel'))      and (LCol >= 0) then
        lokCalc(true)
      else if (s = 'med') and (LCol >= 0) then
        lokMedian(false, false)
      else if (s = 'medsel') and (LCol >= 0) then
        lokMedian(true, false)
      else if (s = 'medvis') and (LCol >= 0) then
        lokMedian(false, true)
      else if ((s = 'all') or (s = 'allsel')) and (LCol >= 0) then
        lokAll;
    end;
  end; // procedure lokRow

  function lokSpecial: string;
  begin
    result := '';
    s := AnsiLowerCase(AParams[2]);
    if Assigned(LGrid) then begin
      if s = 'looprow' then
        result := IntToStr(LGrid.LoopRow)
      else if s = 'checkrow' then
        result := IntToStr(LGrid.CheckRow)
      else if s = 'calcrow' then
        result := IntToStr(LSegment.CountRow)
      else if s = 'rowcount' then
        result := IntToStr(LSegment.Grid.RowCount(rtData))
      else if s = 'visiblerowcount' then
        result := IntToStr(LSegment.Grid.RowCount(rtDisplayData));
    end
    else if Assigned(LGrid) then begin
      if s = 'calcrow' then
        result := IntToStr(LSegment.CountRow);
    end;
  end; // function lokSpecial

  function lokRowSpecial: string;
  var
    LGroup, LCells, t, LTyp, LFirst, LLast: string;
    LCol, LCount, LColCount: integer;
    LColumn: TBafSgColumn;
    LCell: TBafSgCell;
    LSum, LMin, LMax, LValue: extended;

    function lokValue(AValue: extended; AInt: boolean): string;
    begin
      if AInt then
        result := IntToStr(round(AValue))
      else begin
        if LTyp = 'curr4' then
          result := FormatFloat('0.0000', AValue)
        else if LTyp = 'int' then
          result := IntToStr(round(AValue))
        else if LTyp = 'date' then
          result := FormatDateTime('dd.mm.yyyy', AValue)
        else if LTyp = 'datemin' then
          result := FormatDateTime('dd.mm.yyyy hh:mm', AValue)
        else if LTyp = 'datesek' then
          result := FormatDateTime('dd.mm.yyyy hh:mm:ss', AValue)
        else if LTyp = 'bool' then
          result := IfThen(round(AValue) >= 1, BAFYESCHAR, BAFNOCHAR)
        else
          result := FormatFloat('0.00', AValue);
      end;
    end; // function lokValue

    procedure lokGetValue;
    begin
      if (LGroup = '') or (Pos(LGroup, LColumn.ColumnGroups) > 0) then begin
        case LSegment.SegmentType of
          stMap: LCell := LMap.Cells[rtData, LCol, LRow];
          else
            LCell := LGrid.Cells[rtData, LCol, LRow];
        end;
        LCells := LCells + LCell.Text + LSep;
        t := LCell.Text;
        case LGrid.Columns.Items[LCol].CellType of
          ctCurr, ctCurr4, ctInt, ctCurrInt: LValue := StrToFloatDef(t, 0);
          ctDate, ctDateMin, ctDateSek: LValue := StrToDateTimeDef(t, 0);
          ctBool, ctBool2: LValue := IfThen(BafIsYesChar(t), 1, 0);
        else
          LValue := 0;
        end;
        inc(LCount);
        LSum := LSum + LValue;
        LMin := System.Math.Min(LMin, LValue);
        LMax := System.Math.Max(LMax, LValue);
        if LFirst = '' then
          LFirst := t;
        if t <> '' then
          LLast := t;
      end;

    end; // procedure lokGetValue

  begin
    lokRow;
    s := AParams[1];
    if AParams.Count > 3 then
      LSep := FExecInter.ReplaceFunctions(AParams[3]);
    if (AParams.Count > 4) then
      LGroup := AParams[4];
    if (AParams.Count > 5) then
      LTyp := AnsiLowerCase(AParams[5]);
    LCells := '';
    LCount := 0;
    LSum := 0;
    LMax := - MaxInt;
    LMin := MaxInt;
    LFirst := '';
    LLast := '';

    case LSegment.SegmentType of
      stMap: begin
        for LCol := 0 to LMap.Columns.Count - 1 do begin
          LColumn := LMap.Columns.Items[LCol];
          lokGetValue;
        end;
      end;
      else begin
        for LCol := 0 to LGrid.Columns.Count - 1 do begin
          LColumn := LGrid.Columns.Items[LCol];
          lokGetValue;
        end;
      end;
    end;

    if (s = '?count') then
      result := lokValue(LCount, true)
    else if (s = '?sum') then
      result := lokValue(LSum, false)
    else if (s = '?min') then
      result := lokValue(LMin, false)
    else if (s = '?max') then
      result := lokValue(LMax, false)
    else if (s = '?avg') then
      result := lokValue(LSum / System.Math.Max(1, LCount), false)
    else if (s = '?firstnempty') then
      result := LFirst
    else if (s = '?lastnempty') then
      result := LLast
    else if s = '?all' then
      result := LCells;
  end; // function lokRowSpecial

begin
  if AParams.Count > 0 then begin
    LRow := -1;
    LGrid := nil;
    LMap := nil;
    LDisplay := (AParams.Count > 6) and (AnsiCompareText(AParams[6], 'display') = 0);
    LSegment := FPage.PrimaryDivs.FindSegmentByName(AParams[0]);
    if Assigned(LSegment) then begin
      if LSegment.SegmentType in [stValueList, stGrid, stXGrid, stXXGrid] then begin
        LGrid := LSegment.Grid;
        if (AParams.Count > 2) and (AParams[1] = '!') then
          result := lokSpecial
        else if (AParams.Count > 2) and (copy(AParams[1], 1, 1) = '?') then
          result := lokRowSpecial
        else begin
          lokCol;
          if LRow = -1 then
            lokRow;
          if (LCol >= 0) and (LRow >= 0) then begin
            if LDisplay then
              result := LGrid.Cells[rtData, LCol, LRow].GetDisplayText(true)
            else
              result := LGrid.Cells[rtData, LCol, LRow].Text;
          end;
        end;
      end
      else if LSegment.SegmentType in [stMap] then begin
        LMap := LSegment.Map;
        if (AParams.Count > 2) and (AParams[1] = '!') then
          result := lokSpecial
        else if (AParams.Count > 2) and (copy(AParams[1], 1, 1) = '?') then
          result := lokRowSpecial
        else begin
          lokCol;
          if LRow = -1 then
            lokRow;
          if (LCol >= 0) and (LRow >= 0) then
            result := LMap.Cells[rtData, LCol, LRow].Text;
        end;
      end
      else if LSegment.SegmentType in [stMemo, stText] then
        result := LSegment.Lines.Text;
    end; // if Assigned(LSegment)
  end; // if AParams.Count > 0
// function TBafFrmModule.FindPageValue
end;

function TBafFrmModule.FindLookupHelper(AZeileP, APostfix: string;
    var ACmd: string): TBafComboHelper;
var
  LParam, LSql, s: string;
  ix: integer;

  procedure lokSql;
  var
    i: integer;
  begin
    s := Trim(copy(LParam, 4, MaxInt));
    ix := StrToIntDef(s, 0);
    LSql := FInter.GetSqlAndClear(ix);
    result := TBafComboHelper.Create(false);
    FGridObjectList.Add(result);
    case dataMain.DefaultCon.BafGen of
      bg302, bg303: result.FillFromQuery(LSql, 'ckey', 'cvalue', '');
      else
        result.FillFromQuery(LSql, 'key', 'value', '');
    end;
    for i := 0 to result.count - 1 do begin
      result.Strings[i] := FInter.ReplaceFunctions(result.Strings[i]);
    end;
  end; // procedure lokSql

  procedure lokTextValueList;
  begin
    s := Trim(copy(LParam, 4, MaxInt));
    s := FExecInter.ReplaceFunctions(s);
    ix := StrToIntDef(s, 1);
    result := TBafComboHelper.Create(false);
    FGridObjectList.Add(result);
    result.FillFromValueList(FInter.GetTextStringList(ix));
  end; // procedure lokTextValueList;

begin
  result := nil;
  if FindParam(AZeileP, 'ld' + APostfix, LParam) then begin
    if AnsiCompareText(copy(LParam, 1, 3), 'sql') = 0 then
      lokSql
    else if AnsiCompareText(copy(LParam, 1, 3), 'tvl') = 0 then
      lokTextValueList
    else begin
      LParam := FExecInter.ReplaceFunctions(LParam);
      result := gvBafDataCache.Lookup[LParam];
      ACmd := 'ld=' + LParam;
    end;
  end
  else if FindParam(AZeileP, 'ls' + APostfix, LParam) then begin
    LParam := FExecInter.ReplaceFunctions(LParam);
    result := gvBafDataCache.Special[LParam];
    ACmd := 'ls=' + LParam;
  end
  else if FindParam(AZeileP, 'llc' + APostfix, LParam) then begin
    result := TBafComboHelper.Create(false);
    result.Command := LParam;
    FGridObjectList.Add(result);
  end;
// function TBafFrmModule.FindLookupHelper
end;

function TBafFrmModule.FindSegment(AFunktion: string;
  var ASegment: TBafPageSegment): boolean;
var
  LItemName: string;
begin
  result := false;
  LItemName := FindParamStringReplaced('i', '');
  if LItemName = '' then
    FInter.DoLog('E', Format('FindSegment (%s) - Parameter i not set', [AFunktion]))
  else begin
    ASegment := FPage.PrimaryDivs.FindSegmentByName(LItemName);
    result := Assigned(ASegment);
  end; // else LItemName = ''

end;

function TBafFrmModule.FindTreeValue(ANode: TBafTreeViewItem; ASection: string;
  AParams: TStrings; AFind: boolean): string;
begin
  result := '';
  if Assigned(ANode) then begin
    if AFind then
      result := ANode.GetNodeOrParentValue(ASection, AParams[0], '')
    else
      result := ANode.GetNodeValue(ASection, AParams[0], '');
  end;
  if (result = '') and (AParams.Count > 1) then
    result := AParams[1];
end;

function TBafFrmModule.FindTreeValue(ANode: TBafTreeViewItem; ASection,
  AField: string; AFind: boolean): string;
begin
  result := '';
  if Assigned(ANode) then begin
    if AFind then
      result := ANode.GetNodeOrParentValue(ASection, AField, '')
    else
      result := ANode.GetNodeValue(ASection, AField, '');
  end;
end;

function TBafFrmModule.FindTreeValue2(AParams: TStrings): string;
var
  LNode: TBafTreeNode;
begin
  result := '';
  if AParams.Count < 2 then
    raise Exception.Create('FindTreeValue2 - 2 params needed');
  LNode := GetNode(AParams[0]);
  if Assigned(LNode) then
    result := LNode.GetNodeOrParentValue(SEC_DATA, AParams[1], '');
  if (result = '') and (AParams.Count > 2) then
    result := AParams[2];
end;

procedure TBafFrmModule.RefresCheckMemo;
begin
  if FPageFailed.Count = 0 then
    FCheckMemo.Visible := false
  else begin
    FCheckMemo.Visible := true;
    FCheckMemo.BringToFront;
    FCheckMemo.Lines.Assign(FPageFailed);
  end;
end;

procedure TBafFrmModule.RegisterModule(AInter: TBafInterpreter);
begin
  inherited;
  FInter.OnReturn2Tab := Return2Tab;
end;

function TBafFrmModule.ReplaceFunction(ACommand: string; AParams: TStrings;
  var AResult: string): boolean;
begin
  inherited;
  result := true;
  if ACommand = '$EDT' then AResult := GetEditText(AParams[0])
  else if ACommand = '$COUT' then AResult := GetConsoleValue()

  // Tree
  else if ACommand = '$FND' then AResult := FindTreeValue2(AParams)

//  else if ACommand = '$GLV' then AResult := FGridLinkValue

  // Page
  else if ACommand = '$GVAL' then AResult := FindPageValue(AParams)
  else if ACommand = '$PVAL' then AResult := FindPageValue(AParams)
  else if ACommand = '$PAGE' then AResult := GetPageValue(AParams)

  else if ACommand = '$GRD_CHECK' then AResult := GridCheck(AParams)
  else if ACommand = '$GRD_REGEX' then AResult := GridRegex(AParams)
  else if ACommand = '$XGRD_CALC' then AResult := XGridCalc(AParams)

  else if ACommand = '$CCI' then AResult := GetCellColorInteger(AParams)

  else if ACommand = '$DLG_YESNO' then AResult := DlgYesNo(AParams)

  else
  result := false;

end;

procedure TBafFrmModule.Return2Tab(Sender: TObject);
var
  LSelect: string;
begin
  if FGridRefreshAfterReturn then begin
    if Assigned(FTree) then begin
      LSelect := FTree.Selected.GetSelectionCommand;
      if LSelect <> '' then
        TBafInterpreterLevel.ExecInNewLevel(LSelect, FExecInter, FInter);
    end
    else if Assigned(FPage) and (FPageRefreshCommand <> '') then
      TBafInterpreterLevel.ExecInNewLevel(FPageRefreshCommand, FExecInter, FInter);
  end;
end;

function TBafFrmModule.GetPrimaryKey(ALineP: string; APostfix: string = ''): string;
begin
  result := FInter.GetPrimaryKey(ALineP, APostFix);
end;

function TBafFrmModule.GetCellColorInteger(AParams: TStrings): string;
// Calcs the Cell Color from an integer input
// 0 - Input value or ! for the current cell
// 1 - lower oder higher
// 2 - red, if input value equal or hiher (lower)  this value
// 3 - yellow, if input value equal or hiher (lower)  this value
// 4 - green, if input value equal or hiher (lower)  this value
var
  LValue: integer;
begin
  if AParams.Count > 2 then begin
    if (AParams[0] = '!') and Assigned(FCellCalcGrid) then
      LValue := StrToIntDef(FCellCalcGrid.DataCells[FGridDataCol, FGridDataRow].Text, 0)
    else
      LValue := StrToIntDef(AParams[0], 0);
    if UpperCase(copy(AParams[1], 1, 1)) = 'L' then begin
      if LValue <= StrToIntDef(AParams[2], -MaxInt) then
        result := 'red'
      else if (AParams.Count > 3) and (LValue <= StrToIntDef(AParams[3], -MaxInt)) then
        result := 'yellow'
      else if (AParams.Count > 4) and (LValue <= StrToIntDef(AParams[4], -MaxInt)) then
        result := 'green'
      else
        result := 'none';
    end
    else begin
      if LValue >= StrToIntDef(AParams[2], MaxInt) then
        result := 'red'
      else if (AParams.Count > 3) and (LValue >= StrToIntDef(AParams[3], MaxInt)) then
        result := 'yellow'
      else if (AParams.Count > 4) and (LValue >= StrToIntDef(AParams[4], MaxInt)) then
        result := 'green'
      else
        result := 'none';
    end;
  end
  else
    raise Exception.Create('$CCI - 3 params needed');
// function TBafFrmModule.GetCellColorInteger
end;

function TBafFrmModule.GetConsoleValue(): string;
begin
  case gvInterType of
    itClient: result := FConsoleMemo.Lines.Text;
    itSrvProc: result := FSrvLog.Text;
  end;
end;

function TBafFrmModule.GetEditText(AName: string): string;
var
  LIx: integer;
begin
  result := '';
  LIx := FEditList.IndexOf(AnsiLowerCase(AName));
  if (Lix >= 0) and Assigned(FEditList.Objects[LIx]) then begin
    if (FEditList.Objects[LIx] is TBafEdit) then
      result := TBafEdit(FEditList.Objects[LIx]).Text
    else if (FEditList.Objects[LIx] is TBafCheckBox) then
      result := IfThen(TBafCheckBox(FEditList.Objects[LIx]).IsChecked,
          BAFYESCHAR, BAFNOCHAR);

  end;
end;

function TBafFrmModule.GetFormObject(AFormObject: TBafFrmObject): TObject;
begin
  inherited;
  case AFormObject of
    foTree: result := FTree;
    foPage: result := FPage;
    else
      result := nil;
  end;
end;

function TBafFrmModule.GetFrmComponent(AType: string): TComponent;
begin
  AType := AnsiLowerCase(AType);
  if AType = 'page' then
    result := FPage
  else if AType = 'tree' then
    result := FTree;
end;

function TBafFrmModule.GetLineType: TBafGridLineType;
begin
  result := BafGetGridLineType(FindParamStringReplaced('clt', 'sf'));
end;

function TBafFrmModule.GetNode(AName: string): TBafTreeNode;
begin
  result := nil;
  if Assigned(FTree) then begin
    try
      if (AName = 's') or (AName = 'sel') then
        result := FTree.Selected
      else if (AName = 'sp') or (AName = 'selp') then
        result := FTree.Selected.ParentNode
      else if (AName = 'spp') or (AName = 'selpp') then
        result := FTree.Selected.ParentNode.ParentNode
      else if (AName = 'sppp') or (AName = 'selppp') then
        result := FTree.Selected.ParentNode.ParentNode.ParentNode
      else if (AName = 'o') or (AName = 'exp') then
        result := FLastExpanded
      else if (AName = 'l') or (AName = 'last') then
        result := FLast
      else if (AName = 'lp') or (AName = 'lastp') then
        result := FLast.ParentNode
      else if (AName = 'lpp') or (AName = 'lastpp') then
        result := FLast.ParentNode.ParentNode
      else if (AName = 'lppp') or (AName = 'lastppp') then
        result := FLast.ParentNode.ParentNode.ParentNode
      else if (AName = 'spec') or (AName = 'special') then
        result := FSpecialNode
      else if (AName = 'r') or (AName = 'root') then
        result := nil;
    except
      result := nil;
    end;
  end;
end;

function TBafFrmModule.GetPageValue(AParams: TStrings): string;
var
  LParam: string;
  LAdd: integer;
begin
  LParam := AnsiLowerCase(AParams[0]);
  if AParams.Count > 1 then
    LAdd := StrToIntDef(AParams[1], 0);
  if LParam = 'link' then
    result := FPage.LinkValue
  else if LParam = 'debuginfos' then
    result := FPage.GetDebugInfos
  else if LParam = 'changecol' then
    result := IntToStr(FChangeCol + LAdd)
  else if LParam = 'changerow' then
    result := IntToStr(FChangeRow + LAdd)

  else
    result := FPageName;
end;

function TBafFrmModule.GetPrimaryKey: string;
begin
  result := FInter.GetPrimaryKey(FExecInter.LineP);
end;

procedure TBafFrmModule.GridAfterEdit(ASender: TBafPageParents;
  var AText: string; var AAbort: boolean; ACommand: string; AChanged: boolean);

  procedure lokValues2Tree;
  var
    LIni: TStringIniFile;
    LRefresh: boolean;
    LFieldName: string;
    LTreeSelectedNode: TBafTreeNode;

    procedure lokCheck(AIdent: string);
    begin
      LFieldName := LIni.ReadString(SEC_ADD, AIdent, '');
      if ASender.Cell.DataFieldName = LFieldName then begin
        LIni.WriteString(SEC_DATA, LFieldName,
            FInter.ReplaceFunctions(ASender.Cell.Text));
        LRefresh := true;
      end;
    end; // procedure lokCheck

  begin
    if Assigned(FTree) and Assigned(ASender.Segment) and Assigned(ASender.Cell)
        and Assigned(FTree.Selected)
        and (ASender.Segment.SegmentType = stValueList) then begin
      LTreeSelectedNode := FTree.Selected as TBafTreeNode;
      if LTreeSelectedNode.GetTableName = ASender.Segment.DataTable[0] then begin
        LRefresh := false;
        LIni := LTreeSelectedNode.Ini;
        lokCheck('k');
        lokCheck('c1');
        lokCheck('c2');
        if LRefresh then
          LTreeSelectedNode.Text := LTreeSelectedNode.GetCaption;
      end;
      FInter.DebugNodeIni(FTree.Selected.Ini);
    end;
  end; // procedure lokValues2Tree

  procedure lokChangeCommand;
  begin
    if Assigned(ASender.Segment) and Assigned(ASender.Cell) then begin
      if ASender.Cell.ChangeCommand <> '' then begin
        FChangeRow := ASender.Row.RowIndex;
        FChangeCol := ASender.Column.Index;
        FInter.Execute(ASender.Cell.ChangeCommand);
        FChangeRow := -1;
        FChangeCol := -1;
      end
      else if ASender.Column.CellChangeCommand <> '' then begin
        FChangeRow := ASender.Row.RowIndex;
        FChangeCol := ASender.Column.Index;
        FInter.Execute(ASender.Column.CellChangeCommand);
        FChangeRow := -1;
        FChangeCol := -1;
      end;
    end;
  end; // procedure lokChangeCommand

begin
  try
    FPage.AfterEdit := nil;
    if AChanged then
      lokValues2Tree;
    if AChanged then
      lokChangeCommand;
    FPage.RecalcCells;
    if Assigned(FTree) then
      FTree.Repaint;
  finally
    FPage.AfterEdit := GridAfterEdit;
  end;
// procedure TBafFrmModule.GridAfterEdit
end;

procedure TBafFrmModule.PageCellButton(const Sender: TBafPageParents);
var
  LCell: TBafSgCell;
  LCmd: string;
begin
  LCell := Sender.Cell;
  if Assigned(LCell) then begin
    LCmd := LCell.Parents.Column.CellCommand;
    if LCmd <> '' then
      TBafInterpreterLevel.ExecInNewLevel(LCmd, FExecInter, FInter);
  end;
end;

procedure TBafFrmModule.PageCheck;
var
  LCheck: TBafCheck;
  s: string;
begin
  LCheck := TBafCheck.Create;
  s := FindParamStringLower('y', 'e') + ' ';
  case s[1] of
    'n': LCheck.CheckType := ctNone;
    'w': LCheck.CheckType := ctWarning;
//    'a': LCheck.CheckType := ctAutomatic;    not implemented yet
  else
    LCheck.CheckType := ctError;
  end;
  LCheck.Condition := FindParamString('cnd', BAFYESCHAR);
  LCheck.Check := FindParamString('chk', '');
  LCheck.Caption := FindParamString('c', '');        // hier kein Replace !!!
  FPage.CheckList.AddObject(FindParamStringLower('n', ''), LCheck);
end;

procedure TBafFrmModule.GridCellCalc(ASender: TBafPageParents;
  var AText: string; var AAbort: boolean; ACommand: string; AChanged: boolean);
begin
  FCellCalcGrid := ASender.SimpleGrid;
  FGridDataRow := ASender.Row.RowIndex;
  FGridDataCol := ASender.Column.Index;
  try
    if ACommand <> '' then
      AText := FInter.ReplaceFunctions(ACommand);
    if ASender.Cell.CellColorCommand <> '' then
      ASender.Cell.SetCellColorString(FInter.ReplaceFunctions(ASender.Cell.CellColorCommand));
  finally
    FGridDataRow := -1;
    FGridDataCol := -1;
    FCellCalcGrid := nil;
  end;
end;

function TBafFrmModule.GridCheck(AParams: TStrings): string;
// 0 - Segment
// 1 - ColumnIndex or FieldName
// 2 - Rowtype
// 3 - Condition
type
  TGridCheckRowType = (rtAll, rtCellChanged, rtRowChanged, rtRowSelected);
var
  LSegment: TBafPageSegment;
  LResult: boolean;
  LCol, LColIx: integer;
  LRowTypeText: string;
  LRowType: TGridCheckRowType;
  LCell: TBafSgCell;

  procedure lokRowType;
  begin
    LRowTypeText := AnsiLowerCase(AParams[2]);
    if (LRowTypeText = 'a') or (LRowTypeText = 'all') then
      LRowType := rtAll
    else if (LRowTypeText = 'cc') or (LRowTypeText = 'cellchanged') then
      LRowType := rtCellChanged
    else if (LRowTypeText = 'rc') or (LRowTypeText = 'rowchanged') then
      LRowType := rtRowChanged
    else if (LRowTypeText = 'rs') or (LRowTypeText = 'rowselected') then
      LRowType := rtRowSelected;
  end; // procedure lokRowType

  procedure lokCheckColumn(ACol: integer);
  var
    LRow: integer;
    LCheckRow: boolean;
    s: string;
  begin
    for LRow := 0 to LSegment.Grid.RowCount(rtData) - 1 do begin
      LCell := LSegment.Grid.Cells[rtData, ACol, LRow];
      case LRowType of
        rtAll: LCheckRow := true;
        rtCellChanged: LCheckRow := LCell.HasChanged;
        rtRowChanged: LCheckRow := LCell.Parents.Row.HasChanged;
        rtRowSelected: LCheckRow := (LCell.Parents.SimpleGrid.SelectedRow = LRow);
      else
        LCheckRow := false;
      end;
      if LCheckRow then begin
        LSegment.Grid.CheckRow := LRow;
        s := StringReplace(AParams[3], '$CELL$', LCell.Text, [rfReplaceAll, rfIgnoreCase]);
        if not FExecInter.ParseBoolStatement(s) then begin
//        if not FExecInter.ParseBoolStatement(LCell.Text + ' ' + AParams[3]) then begin
          LResult := false;
          Break;
        end;
      end;
    end;
  end; // procedure lokCheckColumn;

begin
  if AParams.Count > 3 then begin
    LResult := true;
    LSegment := FPage.PrimaryDivs.FindSegmentByName(AParams[0]);
    if LSegment.SegmentType in [stGrid, stXGrid, stXXGrid] then begin
      lokRowType;
      LColIx := StrToIntDef(AParams[1], -1);
      if LColIx >= 0 then
        lokCheckColumn(LColIx)
      else begin
        for LCol := 0 to LSegment.Grid.Columns.Count - 1 do begin
          if (AnsiCompareText(LSegment.Grid.Columns.Items[LCol].CellFieldName,
              AParams[1]) = 0) and LResult then
            lokCheckColumn(LCol);
        end;
      end;
    end
    else
      FInter.DoLog('E', 'Wrong segment type');
  end
  else
    FInter.DoLog('E', '$GRD_CHECK number of params less 4');
  result := IfThen(LResult, BAFYESCHAR, BAFNOCHAR);
// function TBafFrmModule.GridCheck
end;

procedure TBafFrmModule.GridCheckCols(ADataSet: TDataSet);
var
  LType: string;

  procedure lokHistory;
  var
    i: integer;
    s: string;
    LWidth, LStretch: single;
    LNoShowList: TStringList;
  begin
    FLastSeg.Grid.Columns.AddColumn('baf_datechg', 'Date', '', true, true, 120, 0);
    FLastSeg.Grid.Columns.AddColumn('baf_shortname', 'User', '', true, true, 150, 0);
    FLastSeg.Grid.Columns.AddColumn('progchg', 'Program', '', true, true, 120, 0);
    LNoShowList := TStringList.Create;
    try
      LNoShowList.Text := FInter.NeedInfo('NoShowList', '');
      for i := 0 to ADataSet.Fields.Count - 1 do begin
        s := AnsiLowerCase(ADataSet.Fields[i].FieldName);
        if (s <> 'datechg') and (s <> 'usrchg') and (s <> 'progchg')
            and (s <> 'baf_shortname') and (s <> 'baf_datechg')
            and (LNoShowList.IndexOf(s) = -1) then begin
          case ADataSet.Fields[i].DataType of
            ftSmallint, ftInteger, ftFloat, ftCurrency:
              FLastSeg.Grid.Columns.AddColumn(s, s, s, true, true, 80, 0);
            ftDate, ftDateTime:
              FLastSeg.Grid.Columns.AddColumn(s, s, s, true, true, 80, 0);
            ftString, ftWideString: begin
              LStretch := 0;
              LWidth := Max(9 * ADataSet.Fields[i].Size, 40);
              if LWidth > 120 then begin
                LStretch := LWidth - 120;
                LWidth := 120;
              end;
              FLastSeg.Grid.Columns.AddColumn(s, s, s, true, true, LWidth, LStretch);
            end;
            else
              FLastSeg.Grid.Columns.AddColumn(s, s, s, true, true, 80, 0);
          end; // case
        end;
      end;
    finally
      LNoShowList.Free;
    end;
  end; // procedure lokHistory;

  procedure lokStandard;
  var
    i: integer;
    s: string;
    LWidth, LStretch: single;
  begin
    for i := 0 to ADataSet.Fields.Count - 1 do begin
      s := AnsiLowerCase(ADataSet.Fields[i].FieldName);
      case ADataSet.Fields[i].DataType of
        ftSmallint, ftInteger, ftFloat, ftCurrency:
          FLastSeg.Grid.Columns.AddColumn(s, s, s, true, true, 80, 0);
        ftDate, ftDateTime:
          FLastSeg.Grid.Columns.AddColumn(s, s, s, true, true, 80, 0);
        ftString, ftWideString: begin
          LStretch := 0;
          LWidth := Max(9 * ADataSet.Fields[i].Size, 40);
          if LWidth > 120 then begin
            LStretch := LWidth - 120;
            LWidth := 120;
          end;
          FLastSeg.Grid.Columns.AddColumn(s, s, s, true, true, LWidth, LStretch);
        end;
        else
          FLastSeg.Grid.Columns.AddColumn(s, s, s, true, true, 80, 0);
      end; // case
    end;
  end; // lokStandard

begin
  if FLastSeg.Grid.Columns.Count = 0 then begin
    // if we don't have cols, we create them automatically
    LType := FindParamStringLower('acy', 'standard');
    if LType = 'standard' then
      lokStandard
    else if (LType = 'history') or (LType = 'hist') then
      lokHistory;

  end;
end;

procedure TBafFrmModule.PageFillLookupLive(ASender: TObject;
  ALookupHelper: TBafComboHelper; ACell: TBafSgCell);
var
  LCommand: string;
begin
  if ACell.LLException then
    exit;
  FLiveCell := ACell;
  FLiveLookupHelper := ALookupHelper;
  FLiveLookupHelper.Clear;
  try
    if ALookupHelper.Command <> '' then
      TBafInterpreterLevel.ExecInNewLevel(ALookupHelper.Command, FExecInter, FInter);
  except
    ACell.LLException := true;
  end;
end;

procedure TBafFrmModule.GridHeaderButtonClick(const Sender: TBafPageParents;
  AButton: Char);
begin
  case AButton of
    'X': FInter.ExportSegmentXls(Sender.Segment);
    'Y': FInter.ExportSegmentPdf(Sender.Segment);
    '+': FInter.CommandOnTimer(Sender.Segment.Grid.AddCommand);
  end;
end;

procedure TBafFrmModule.PageDefs;
var
  s: string;
begin
  FGridObjectList.Clear;
  FPage.CheckList.Clear;
  FPage.PrimaryDivs.Clear;
  FPage.PageButtonList.Clear;
  FPage.ViewportPosition := TPointF.Create(0, 0);
  FPageName := FindParamString('n', '');
  FGridRefreshAfterSave := FindParamBooleanReplaced('ras', false);
  FGridRefreshAfterReturn := FindParamBooleanReplaced('rar', false);
  FPageBeforeSaveCommand := FindParamStringReplaced('bsc', '');
  FPageAfterSaveCommand := FindParamStringReplaced('asc', '');
  s := FindParamStringLower('y', '');
  if s = 'dashhor' then
    FPage.PageKind := pkDashHor
  else if s = 'dashvert' then
    FPage.PageKind := pkDashVert
  else
    FPage.PageKind := pkPage;
  s := FindParamStringReplaced('tac', '');
  if s <> '' then
    FInter.NeedInfo('settabcaption', s);
end;

procedure TBafFrmModule.PageLinkClick(const Sender: TBafPageParents;
  ACommand: string);
begin
  FLinkRow := Sender.Row.RowIndex;
  ACommand := FInter.ReplaceFunctions(ACommand);
  FInter.Execute(ACommand);
end;

procedure TBafFrmModule.PageLookupLiveClear;
var
  i: integer;
begin
  if FindParamBooleanReplaced('cnd', true) then begin
    for i := 0 to FGridObjectList.Count - 1 do begin
      if FGridObjectList[i] is TBafComboHelper then
        TBafComboHelper(FGridObjectList[i]).Filled := false;
    end;
  end;
end;

procedure TBafFrmModule.PageLookupLiveFill;
var
  LNumber, ix: integer;
  LSql, LBafConName, LTyp, LKat, LCount: string;
  LKatList, LText: TStringList;
begin
  LTyp := FindParamStringReplacedLower('y', 'sql');
  if LTyp = 'kat' then begin
    if FindParamBooleanReplaced('cnd', true) then begin
      LNumber := FindParamInteger('n', 1);
      LKatList := FInter.GetKatStringList(LNumber);
      LKat := FindParamStringReplacedLower('k', '');
      ix := LKatList.IndexOf(LKat);
      if ix = -1 then
        FLiveLookupHelper.Clear
      else
        FLiveLookupHelper.FillFromValueList(LKatList.Objects[ix] as TStringList);
      FLiveLookupHelper.Filled := true;
    end;
  end
  else begin  // sql
    LNumber := FindParamInteger('n', 1);
    LSql := FInter.GetSqlAndClear(LNumber);
    if FindParamBooleanReplaced('cnd', true) then begin
      LBafConName := FindParamStringReplacedLower('db', DC_DEFAULT);
      FLiveLookupHelper.FillFromQueryDb(LSql, 'ckey', 'cvalue', LBafConName);
      FLiveLookupHelper.Filled := true;
    end;
  end;
  LCount := FindParamStringReplaced('cnt', '');
  if LCount <> '' then
    FExecInter.SetVarOrValue('cnt', IntToStr(FLiveLookupHelper.Count));
// procedure TBafFrmModule.PageLookupLiveFill;
end;

procedure TBafFrmModule.PagePdf;
begin
  FInter.ExportPagePdf(FPage);
end;

procedure TBafFrmModule.PageReady;
begin
  FPage.SetAutoSize;
  FPage.RefreshOpenClose;
  FPage.RecalcCells;

  if FindParamBooleanReplaced('rs', false) then begin
    if Assigned(FBafSplitter) then
      FBafSplitter.ResizePage;
  end
  else if Assigned(FPage) then begin
//    FPage.Resize;
    Application.ProcessMessages;
    FPage.Resize;
  end;
  frmMain.SpecialPageRefresh;
end;

procedure TBafFrmModule.AddGridObjectList(AObject: TObject);
begin
  FGridObjectList.Add(AObject);
end;

procedure TBafFrmModule.PageOpenClose(const Sender: TBafPageParents);
var
  LCategory: TBafPageCategory;
begin
  LCategory := Sender.Category;
  if Assigned(LCategory) and (LCategory.OpenCloseIni <> '') then
      dataMain.UserIni.WriteString(BAF_INI_OC, LCategory.OpenCloseIni,
          IfThen(LCategory.Opened, 'O', 'C')) ;
end;

procedure TBafFrmModule.GridOpenJoin(ADataSet: TDataSet; var ARow: integer);
var
  LKeyField, LKeyValue, LFieldName, LLineP: string;
  LColumn: TBafSgColumn;
  LQuelle: TBafDataQuelle;
  LCell: TBafSgCell;
  LField: TField;

  procedure lokDataRow;
  // for each row in the dataset
  var
    LCol: integer;
  begin
    for LCol := 0 to FLastSeg.Grid.Columns.Count - 1 do begin
      LColumn := FLastSeg.Grid.Columns.Items[LCol];
      LFieldName := LColumn.CellFieldName;
      LQuelle := LColumn.CellDataQuelle;
      if (LFieldName <> '') and (LQuelle in [dqSQL, dqY]) then begin
        LField := ADataSet.FindField(LFieldName);
        if Assigned(LField) then
          LColumn.JoinList.Add(LField.AsString)
        else begin
          if ARow = 0 then
            FInter.DoLog('E', '#grddata, f "' + LFieldName + '" not found');
        end;
      end;
      LFieldName := LColumn.CellHintFieldName;
      if (LFieldName <> '') and (LQuelle in [dqSQL, dqY]) then begin
        LField := ADataSet.FindField(LFieldName);
        if Assigned(LField) then
          LColumn.HintJoinList.Add(LField.AsString)
        else
          LColumn.HintJoinList.Add(LFieldName);
      end;
      if LColumn.CellReadOnlyFieldName <> '' then begin
        LFieldName := LColumn.CellReadOnlyFieldName;
        LField := ADataSet.FindField(LFieldName);
        if Assigned(LField) and BafIsYesChar((LField.AsString + BAFYESCHAR)[1]) then
          LColumn.JoinReadOnly := true;
      end;
    end; // for LCol := 0 to
  end; // procedure lokDataRow

  procedure lokGridRow;
  // for every line
  var
    LCol: integer;

    procedure lokSetJoinValue;
    var
      s: string;
      LSum: currency;
      i, p: integer;
    begin
      case LColumn.JoinType of
        jtKomma: begin
          s := '';
          for i := 0 to LColumn.JoinList.Count - 1 do
            s := s + ', ' + LColumn.JoinList[i];
          LCell.Text := CellCheckFormat(copy(s, 3, MaxInt), LColumn.CellCommand);
        end;
        jtSum: begin
          LSum := 0;
          for i := 0 to LColumn.JoinList.Count - 1 do begin
            s := LColumn.JoinList[i];
            p := Pos('~', s);
            if p > 0 then
              s := copy(s, 1, p - 1);
            LSum := LSum + StrToCurrDef(s, 0);
          end;
          LCell.Text := IntToStr(round(LSum));
        end;
      end;
    end; // procedure lokSetJoinValue

  begin
    for LCol := 0 to FLastSeg.Grid.Columns.Count - 1 do begin
      LColumn := FLastSeg.Grid.Columns.Items[LCol];
      LFieldName := LColumn.CellFieldName;
      LQuelle := LColumn.CellDataQuelle;
      LCell := FLastSeg.Grid.DataCells[LCol, ARow];
      if (LFieldName <> '') and (LQuelle in [dqSQL, dqY]) then
        lokSetJoinValue
      else if LColumn.CellCommand <> '' then
        LCell.Command := LColumn.CellCommand
      else
        LCell.Text := FindParamStringReplaced(LColumn.LineP, 'z', '');
      ColumnNullValue(LCell);
      if LColumn.CellHintFieldName <> '' then
        LCell.Hint := Trim(LColumn.HintJoinList.Text);
      if LColumn.CellType = ctLookupLive then begin
        LCell.LookupHelper := TBafComboHelper.Create(false);
        FGridObjectList.Add(LCell.LookupHelper);
      end;
      LCell.ReadOnly := LColumn.JoinReadOnly;
    end; // for LCol := 0 to
    inc(ARow);
    FLastSeg.Grid.Columns.ClearJoinLists
  end; // procedure lokGridRow

begin
  LKeyField := FindParamStringReplaced(FExecInter.LineP, 'jk', '');
  if LKeyField = '' then
    LKeyField := FLastSeg.DataKey[0];
  if not ADataSet.Eof then begin
    while not ADataSet.Eof do begin
      FInter.DebugDbRow(ADataSet);
      if LKeyValue <> ADataSet.FieldByName(LKeyField).AsString then begin
        if LKeyValue <> '' then     // new KeyValue, we begin a new line
          lokGridRow;
        LKeyValue := ADataSet.FieldByName(LKeyField).AsString;
      end;
      lokDataRow;
      ADataSet.Next;
    end;
    lokGridRow;
  end;
// procedure TBafFrmModule.GridOpenJoin
end;

function TBafFrmModule.GridRegex(AParams: TStrings): string;
// 0 - Segment
// 1 - ColumnIndex or FieldName
// 2 - Rowtype
// 3 - Condition
// 4 - Options: e - allow empty
type
  TGridCheckRowType = (rtAll, rtCellChanged, rtRowChanged, rtRowSelected);
var
  LSegment: TBafPageSegment;
  LResult, LAllowEmpty: boolean;
  LCol, LColIx: integer;
  LRowTypeText, s: string;
  LRowType: TGridCheckRowType;
  LCell: TBafSgCell;

  procedure lokRowType;
  begin
    LRowTypeText := AnsiLowerCase(AParams[2]);
    if (LRowTypeText = 'a') or (LRowTypeText = 'all') then
      LRowType := rtAll
    else if (LRowTypeText = 'cc') or (LRowTypeText = 'cellchanged') then
      LRowType := rtCellChanged
    else if (LRowTypeText = 'rc') or (LRowTypeText = 'rowchanged') then
      LRowType := rtRowChanged
    else if (LRowTypeText = 'rs') or (LRowTypeText = 'rowselected') then
      LRowType := rtRowSelected;
  end; // procedure lokRowType

  procedure lokCheckColumn(ACol: integer);
  var
    LRow: integer;
    LCheckRow: boolean;
    s: string;
  begin
    for LRow := 0 to LSegment.Grid.RowCount(rtData) - 1 do begin
      LCell := LSegment.Grid.Cells[rtData, ACol, LRow];
      case LRowType of
        rtAll: LCheckRow := true;
        rtCellChanged: LCheckRow := LCell.HasChanged;
        rtRowChanged: LCheckRow := LCell.Parents.Row.HasChanged;
        rtRowSelected: LCheckRow := (LCell.Parents.SimpleGrid.SelectedRow = LRow);
      else
        LCheckRow := false;
      end;
      if LCheckRow then begin
        LSegment.Grid.CheckRow := LRow;
        s := LCell.Text;
        if (s <> '') or not LAllowEmpty then begin
          if not TRegEx.IsMatch(s, Trim(AParams[3])) then begin
            LResult := false;
            Break;
          end;
        end;
      end;
    end;
  end; // procedure lokCheckColumn;

begin
  if AParams.Count > 3 then begin
    LResult := true;
    LSegment := FPage.PrimaryDivs.FindSegmentByName(AParams[0]);
    if LSegment.SegmentType in [stGrid, stXGrid, stXXGrid] then begin
      lokRowType;
      LColIx := StrToIntDef(AParams[1], -1);
      if AParams.Count > 4 then begin      // Options
        s := AnsiLowerCase(AParams[4]);
        LAllowEmpty := (Pos('e', s) > 0);
      end;
      if LColIx >= 0 then
        lokCheckColumn(LColIx)
      else begin
        for LCol := 0 to LSegment.Grid.Columns.Count - 1 do begin
          if (AnsiCompareText(LSegment.Grid.Columns.Items[LCol].CellFieldName,
              AParams[1]) = 0) and LResult then
            lokCheckColumn(LCol);
        end;
      end;
    end
    else
      FInter.DoLog('E', 'Wrong segment type');
  end
  else
    FInter.DoLog('E', '$GRD_REGEX number of params less 4');
  result := IfThen(LResult, BAFYESCHAR, BAFNOCHAR);
// function TBafFrmModule.GridRegex
end;

procedure TBafFrmModule.PageSave;
var
  LPrim, LCat, LSeg, LRow, LCol: integer;
  LCategory: TBafPageCategory;
  LSegment: TBafPageSegment;
begin
  for LPrim := 0 to FPage.PrimaryDivs.Count - 1 do begin
    for LCat := 0 to FPage.PrimaryDivs.Items[LPrim].Categories.Count - 1 do begin
      LCategory := FPage.PrimaryDivs.Items[LPrim].Categories.Items[LCat];
      for LSeg := 0 to LCategory.Segments.Count - 1 do begin
        LSegment := LCategory.Segments.Items[LSeg];
        if LSegment.HasChanged then begin
          if LSegment.DataQuelle in [dqXML, dqXMLThread] then begin
            case LSegment.SegmentType of
              stGrid: GridSaveGridXml(LSegment);
            end;
          end
          else if LSegment.DataQuelle in [dqJSON, dqJSONThread] then begin
            case LSegment.SegmentType of
              stValueList: GridSaveVLJson(LSegment);
              stGrid: GridSaveGridJson(LSegment);
            end;
          end
          else begin
            case LSegment.SegmentType of
              stMemo: GridSaveMemo(LSegment);
              stValueList: GridSaveVL(LSegment);
              stGrid: GridSaveGrid(LSegment);
              stXGrid: GridSaveXGrid(LSegment);
              stXXGrid: GridSaveXXGrid(LSegment);
              stMap: GridSaveMap(LSegment);
              stSGrid: GridSaveSGrid(LSegment);
            end;
          end;
          LSegment.HasChanged := false;
          if Assigned(LSegment.Grid) then begin
            for LRow := 0 to LSegment.Grid.RowCount(rtData) - 1 do
              for LCol := 0 to LSegment.Grid.Columns.Count - 1 do
                LSegment.Grid.Cells[rtData, LCol, LRow].Inserted := false;
          end;
        end;
      end;
    end;
  end;
end;

procedure TBafFrmModule.GridSaveGrid(ASegment: TBafPageSegment);
var
  LLineP, LKeyFieldValue, LKeyFieldName, LIniVorlage, LYTableName, s,
      LBafConName: string;
  LIni: TStringIniFile;
  LRow, LColCount, LChangedFieldCount, LInsertedFieldCount, LSelectedRow,
      ix, LRowAffected: integer;
  LGrid: TBafSimpleGrid;
  LGridRow: TBafSgRow;
  LCell: TBafSgCell;
  LHist, LInsert, LLinkChanged: boolean;

  procedure lokWriteLine;
  var
    LCol: integer;
    LColumn: TBafSgColumn;
    LCon: TBafDbCon;

    procedure lokWriteColumns;
    var
      LCol: integer;
    begin
      LKeyFieldValue := '';
      LKeyFieldName := '';
      for LCol := 0 to LGrid.Columns.Count - 1 do begin
        LColumn := LGrid.Columns.Items[LCol];
        if (LColumn.CellDataQuelle in [dqSQL, dqY, dqThread]) then begin
          LCell := LGrid.DataCells[LCol, LRow];
          if (LCell.HasChanged or (LInsertedFieldCount > 0))
              and (LColumn.CellYFieldName <> '')
              and not LColumn.CellNoData
              and (LColumn.CellDataQIndex = ix) then begin
            case LColumn.CellType of
              ctCurrInt: LIni.WriteString(SEC_DATA, LColumn.CellYFieldName,
                  BafCurrInt2Int(LCell.Text));
              else
                LIni.WriteString(SEC_DATA, LColumn.CellYFieldName, LCell.Text);
            end;
          end;

          // is it the key column? then save
          if ((LColumn.CellFieldName = ASegment.DataKey[ix])
                or (LColumn.CellYFieldName = ASegment.DataKey[ix]))
              and not LColumn.CellNoData then begin
            LKeyFieldValue := LCell.Text;
            LKeyFieldName := LColumn.CellYFieldName;
            if ix = 0 then
              LGridRow.DataKeyValue := LCell.Text;    { TODO : Do we need this? }
            LIni.WriteString(SEC_DATA, LKeyFieldName, LKeyFieldValue);
          end;
        end;
      end;
    end; // procedure lokWriteColumns

    procedure lokCount;
    begin
      LCell := LGrid.DataCells[LCol, LRow];
      LColumn := LCell.Parents.Column;
      if (LColumn.CellYFieldName <> '')
          and not LColumn.CellNoData and (LColumn.CellDataQIndex = ix) then begin
        if LCell.HasChanged then
          inc(LChangedFieldCount);
        if LCell.Inserted then
          inc(LInsertedFieldCount);
      end;
    end; // procedure lokCount

  begin
    LChangedFieldCount := 0;
    LInsertedFieldCount := 0;
    for LCol := 0 to ASegment.Grid.Columns.Count - 1 do
      lokCount;

    if LChangedFieldCount > 0 then begin
      LIni.AsString := LIniVorlage;
      LIni.WriteBool(SEC_ADD, 'ins', LInsertedFieldCount > 0);
      lokWriteColumns;

      LIni.WriteString(SEC_ADD, 'k', LKeyFieldName);
      LIni.WriteBool(SEC_ADD, 'ins', LInsertedFieldCount > 0);
      LIni.WriteString(SEC_ADD, 'kv', LKeyFieldValue);
      LCon := dataMain.GetBafDbCon(LBafConName);
      dataMain.UpsertIni(LIni, LBafConName, LCon.ProgChg + FInter.CommandName, LRowAffected);
    end;
  end; // procedure lokWriteLine

begin
  LLineP := ASegment.DataLineP;
  LGrid := ASegment.Grid;
  if (ASegment.DataTable[0] = '') or (ASegment.DataKey[0] = '') then
    FInter.DoLog('E', '#grddata, Parameter t or k not set')
  else begin
    LSelectedRow := LGrid.SelectedRow;
    LGrid.SelectedRow := -1;
    for ix := 0 to ASegment.GetDataTableCount - 1 do begin
      s := IfThen(ix = 0, '', IntToStr(ix));
      if ASegment.DataTable[ix] <> '' then begin
        LBafConName := FindParamStringReplaced(LLineP, 'db'+ s, DC_DEFAULT);
        LHist := FindParamBoolean(LLineP, 'hst' + s, true);
        LIni := TStringIniFile.Create('');
        try
          LIni.WriteString(SEC_ADD, 't', ASegment.DataTable[ix]);
          LIni.WriteString(SEC_ADD, 'k', ASegment.DataKey[ix]);
          LIni.WriteBool(SEC_ADD, 'hst', LHist);
          LIniVorlage := LIni.AsString;
          for LRow := 0 to LGrid.RowCount - 1 do begin
            FGridSaveRow := LRow;
            LGridRow := LGrid.DataRow[LRow];
            if LGridRow.HasChanged then
              lokWriteLine;      // <-------
          end;
        finally
          LIni.Free;
        end;
      end;
    end;
    LGrid.SelectedRow := LSelectedRow;
  end;
// procedure TBafFrmModule.GridSaveGrid
end;

procedure TBafFrmModule.GridSaveGridJson(ASegment: TBafPageSegment);
var
  LLineP, LCmd, LUrl, LMethod, LSep, LEncReq, LResponse: string;
  LRow, LColCount, LSelectedRow, LChangedFieldCount, LInsertedFieldCount: integer;
  LGrid: TBafSimpleGrid;
  LGridRow: TBafSgRow;
  LCell: TBafSgCell;
  LColumn: TBafSgColumn;
  LRoot, LNode: TBafJsonNode;
  LIgnoreServerCertificate: boolean;

  procedure lokInsert;
  var
    LCol: integer;
    LIsString: boolean;
  begin
    LUrl := FindParamStringReplaced(LLineP, 'iurl', '');
    LMethod := FindParamStringReplaced(LLineP, 'mi', 'post');
    for LCol := 0 to ASegment.Grid.Columns.Count - 1 do begin
      LCell := LGrid.DataCells[LCol, LRow];
      LColumn := LCell.Parents.Column;
      if (LColumn.CellYFieldName <> '') and not LColumn.CellNoData then begin
        LIsString := not (LCell.CellType in [ctCurr, ctCurr4, ctInt,
            ctDate, ctDateMin, ctDateSek, ctCurrInt]);
        if LRoot.FindNode(LColumn.CellFieldName, LSep, LNode, true) then
          LNode.SetValueText(LCell.Text, LIsString);
      end;
    end;
  end; // procedure lokInsert

  procedure lokUpdate;
  var
    LCol: integer;
    LIsString: boolean;
  begin
    LUrl := FindParamStringReplaced(LLineP, 'uurl', '');
    LMethod := FindParamStringReplaced(LLineP, 'mu', 'put');
    if LMethod = 'put' then begin
//      TBafJsonModule.Parse(LRoot, LGrid.GridJson);
//      for LRow := 0 to LGrid.RowCount - 1 do begin
//        for LCol := 0 to ASegment.Grid.Columns.Count - 1 do begin
//          LCell := ASegment.Grid.Cells[rtData, LCol, LRow];
//          if (LCell.HasChanged or LInserted) and not LCell.NoData then begin
//            LIsString := not (LCell.CellType in [ctCurr, ctCurr4, ctInt,
//                ctDate, ctDateMin, ctDateSek]);
//            if LRoot.FindNode(LCell.DataFieldName, LSep, LNode) then
//              LNode.SetValueText(LCell.Text, LIsString);
//          end;
//        end;
//      end;
    end
    else if (LMethod = 'patch') or (LMethod = 'put') then begin
      for LCol := 0 to ASegment.Grid.Columns.Count - 1 do begin
        LCell := LGrid.DataCells[LCol, LRow];
        LColumn := LCell.Parents.Column;
        if LCell.HasChanged and (LColumn.CellYFieldName <> '')
            and not LColumn.CellNoData then begin
          LIsString := not (LCell.CellType in [ctCurr, ctCurr4, ctInt,
              ctDate, ctDateMin, ctDateSek]);
          if LRoot.FindNode(LColumn.CellFieldName, LSep, LNode, true) then
            LNode.SetValueText(LCell.Text, LIsString);
        end;

      end;
    end;
  end; // procedure lokUpdate

begin
  LLineP := ASegment.DataLineP;
  LCmd := FindParamStringReplaced(LLineP, 'sc', '');
  LSep := FindParamStringReplaced(LLineP, 'sep', '.');
  LIgnoreServerCertificate := FindParamBooleanReplaced(LLineP, 'isc', false);
  LEncReq := FindParamStringReplaced(LLineP, 'e_req', 'utf8');
  LGrid := ASegment.Grid;
  LSelectedRow := LGrid.SelectedRow;
  LGrid.SelectedRow := -1;
  for LRow := 0 to LGrid.RowCount - 1 do begin
    FGridSaveRow := LRow;
    LGridRow := LGrid.DataRow[LRow];
    if LGridRow.HasChanged then begin
      if LCmd <> '' then
        TBafInterpreterLevel.ExecInNewLevel(LCmd, FExecInter, FInter)
      else begin
        LRoot := TBafJsonNode.Create(nil);
        try
          if LGridRow.RowInserted then
            lokInsert
          else
            lokUpdate;
          TBafWebModule.ExecHttp(LUrl, LMethod, LRoot.GetText(jmFormatted, 0),
              LEncReq, LIgnoreServerCertificate, FInter, LResponse);
        finally
          LRoot.Free;
        end;
      end;
    end;
  end;
  LGrid.SelectedRow := LSelectedRow;
// procedure TBafFrmModule.GridSaveGridJson
end;

procedure TBafFrmModule.GridSaveGridXml(ASegment: TBafPageSegment);
var
  LLineP, LCmd: string;
  LRow, LColCount, LSelectedRow: integer;
  LGrid: TBafSimpleGrid;
  LGridRow: TBafSgRow;
begin
  LLineP := ASegment.DataLineP;
  LCmd := FindParamStringReplaced(LLineP, 'sc', '');
  LGrid := ASegment.Grid;
  if LCmd = '' then
    FInter.DoLog('E', '#save, XML, sc not set')
  else begin
    LSelectedRow := LGrid.SelectedRow;
    LGrid.SelectedRow := -1;
    if LGrid.LoopRow = -1 then begin
      try
        for LRow := 0 to LGrid.RowCount - 1 do begin
          LGrid.LoopRow := LRow;
          FGridSaveRow := LRow;
          if LGrid.DataRow[LRow].HasChanged then
            TBafInterpreterLevel.ExecInNewLevel(LCmd, FExecInter, FInter);
        end;
      finally
        LGrid.LoopRow := -1;
      end;
    end
    else
      raise Exception.Create('#save, XML, LoopRow is not -1');
    LGrid.SelectedRow := LSelectedRow;
  end;
//  procedure TBafFrmModule.GridSaveGridXml
end;

procedure TBafFrmModule.GridSaveMap(ASegment: TBafPageSegment);
var
  LLineP, LKeyFieldValue, LKeyFieldName, LIniVorlage, s: string;
  LIni: TStringIniFile;
  LRow, LColCount, LInsertedFieldCount, LRowAffected: integer;
  LMap: TBafMap;
  LMapRow: TBafSgRow;
  LCell: TBafSgCell;
  LHist, LInsert, LLinkChanged: boolean;

  procedure lokWriteLine;
  var
    LCol: integer;
    LColumn: TBafSgColumn;

    procedure lokWriteColumns;
    var
      LCol: integer;
    begin
      LKeyFieldValue := '';
      LKeyFieldName := '';
      for LCol := 0 to LMap.Columns.Count - 1 do begin
        LColumn := LMap.Columns.Items[LCol];
        if (LColumn.CellDataQuelle in [dqSQL, dqY]) then begin
          LCell := LMap.Cells[rtData, LCol, LRow];
          if (LCell.HasChanged or (LInsertedFieldCount > 0))
              and (LColumn.CellYFieldName <> '')
              and not LColumn.CellNoData
              and (LColumn.CellDataQIndex = 0) then begin
            LIni.WriteString(SEC_DATA, LColumn.CellFieldName, LCell.Text);
          end;

          // is it the key column? then save
          if (LColumn.CellFieldName = ASegment.DataKey[0])
              and not LColumn.CellNoData then begin
            LKeyFieldValue := LCell.Text;
            LKeyFieldName := LColumn.CellYFieldName;
            LMapRow.DataKeyValue := LCell.Text;    { TODO : Do we need this? }
            LIni.WriteString(SEC_DATA, LKeyFieldName, LKeyFieldValue);
          end;
        end;
      end;
    end; // procedure lokWriteColumns

    procedure lokCount;
    begin
      LCell := LMap.Cells[rtData,LCol, LRow];
      LColumn := LCell.Parents.Column;
      if (LColumn.CellYFieldName <> '')
          and not LColumn.CellNoData and (LColumn.CellDataQIndex = 0) then begin
        if LCell.Inserted then
          inc(LInsertedFieldCount);
      end;
    end; // procedure lokCount

  begin
    LInsertedFieldCount := 0;
    for LCol := 0 to ASegment.Map.Columns.Count - 1 do
      lokCount;

    LIni.AsString := LIniVorlage;
    LIni.WriteBool(SEC_ADD, 'ins', LInsertedFieldCount > 0);
    lokWriteColumns;

    LIni.WriteString(SEC_ADD, 'k', LKeyFieldName);
    LIni.WriteBool(SEC_ADD, 'ins', LInsertedFieldCount > 0);
    LIni.WriteString(SEC_ADD, 'kv', LKeyFieldValue);
    if ASegment.Map.Field_IsChecked <> '' then
      LIni.WriteString(SEC_DATA, ASegment.Map.Field_IsChecked,
          IfThen(LMapRow.IsChecked, 'Y', 'N'));
    dataMain.UpsertIni(LIni, ASegment.BafConName[0],
        dataMain.GetBafDbCon(ASegment.BafConName[0]).ProgChg + FInter.CommandName, LRowAffected);
  end; // procedure lokWriteLine

begin
  LLineP := ASegment.DataLineP;
  LMap := ASegment.Map;
  if (ASegment.DataTable[0] = '') or (ASegment.DataKey[0] = '') then
    FInter.DoLog('E', '#map_data, Parameter t or k not set')
  else begin
    LHist := FindParamBoolean(LLineP, 'hst' + s, true);
    LIni := TStringIniFile.Create('');
    try
      LIni.WriteString(SEC_ADD, 't', ASegment.DataTable[0]);
      LIni.WriteString(SEC_ADD, 'k', ASegment.DataKey[0]);
      LIni.WriteBool(SEC_ADD, 'hst', LHist);
      LIniVorlage := LIni.AsString;
      for LRow := 0 to LMap.DataRowCount - 1 do begin
        LMapRow := LMap.DataRow[LRow];
        if LMapRow.HasChanged then
          lokWriteLine;      // <-------
      end;
    finally
      LIni.Free;
    end;
  end;
// procedure TBafFrmModule.GridSaveMap
end;

procedure TBafFrmModule.GridSaveMemo(ASegment: TBafPageSegment);
begin
  case ASegment.DataQuelle of
    dqData: dataMain.SaveDataMemo(ASegment.DataItem, ASegment.DataRef,
        ASegment.DataKeyValue, ASegment.Lines.Text, FInter.CommandName);
    dqFile: ASegment.Lines.SaveToFile(ASegment.DataTable[0]);
  end;
end;

procedure TBafFrmModule.GridSaveRect(Sender: TBafPageParents; ARect: TRectF;
  ADefinition: string; AHorzVal, AHorzVP, AVertVal, AVertVP, AStretch: single);
var
  LSql: string;
  LParams: TBafParams;
begin
  try
    LSql := 'delete from sys_map_rect where user_user_id = '
        + QuotedStr(FInter.NeedInfo('usrid', '')) + ' and cdefinition = '
        + QuotedStr(ADefinition);
    dataMain.QueryPrepare(dataMain.DefaultCon, 'SaveRect', LSql);
    dataMain.QueryExecute(dataMain.DefaultCon, 'SaveRect');
    LSql := 'insert into sys_map_rect (user_user_id, cdefinition, cleft, cright, '
        + 'ctop, cbottom, ahorzval, ahorzvp, avertval, avertvp, stretch)  values ('
        + QuotedStr(FInter.NeedInfo('usrid', '')) + ', ' + QuotedStr(ADefinition)
        + ', :cleft, :cright, :ctop, :cbottom, :ahorzval, :ahorzvp, :avertval, '
        + ':avertvp, :stretch)';
    LParams := dataMain.QueryPrepare(dataMain.DefaultCon, 'SaveRect', LSql);
    LParams.ParamAsFloat('cleft', ARect.Left);
    LParams.ParamAsFloat('cright', ARect.Right);
    LParams.ParamAsFloat('ctop', ARect.Top);
    LParams.ParamAsFloat('cbottom', ARect.Bottom);
    LParams.ParamAsFloat('ahorzval', AHorzVal);
    LParams.ParamAsFloat('ahorzvp', AHorzVP);
    LParams.ParamAsFloat('avertval', AVertVal);
    LParams.ParamAsFloat('avertvp', AVertVP);
    LParams.ParamAsFloat('stretch', AStretch);
    dataMain.QueryExecute(dataMain.DefaultCon, 'SaveRect');
  except

  end;
end;

procedure TBafFrmModule.GridSaveSGrid(ASegment: TBafPageSegment);
var
  LLineP, LKeyFieldName, LTableName, s, LBafConName: string;
  LIni: TStringIniFile;
  LRow, LColCount, LChangedFieldCount, LInsertedFieldCount, LSelectedRow,
      ix, LRowAffected: integer;
  LGrid: TBafSimpleGrid;
  LGridRow: TBafSgRow;
  LCell: TBafSgCell;
  LHist, LInsert, LLinkChanged: boolean;

  procedure lokWriteLine;
  var
//    LCol: integer;
    LCon: TBafDbCon;

    procedure lokWriteColumns;
    var
      LCol, LRow: integer;
    begin
      LRow := FGridSaveRow;
      while (LRow = FGridSaveRow) or LGridRow.IsAdditionalRow do begin
        for LCol := 0 to ASegment.Grid.Columns.Count - 1 do begin
          LCell := LGrid.DataCells[LCol, LRow];
          if (LCell.HasChanged or (LInsertedFieldCount > 0))
              and not LCell.NoData
              and (LCell.DataFieldName <> '') then begin
            case LCell.CellType of
              ctCurrInt: LIni.WriteString(SEC_DATA, LCell.DataFieldName,
                  BafCurrInt2Int(LCell.Text));
              else
                LIni.WriteString(SEC_DATA, LCell.DataFieldName, LCell.Text);
            end;
          end;

          // is it the key column? then save
          if (LCell.DataFieldName = LKeyFieldName) and not LCell.NoData then begin
            LIni.WriteString(SEC_DATA, LKeyFieldName, LCell.Text);
            LIni.WriteString(SEC_ADD, 'kv', LCell.Text);
          end;
        end;
        inc(LRow);
        LGridRow := LGrid.DataRow[LRow];
      end; // while
    end; // procedure lokWriteColumns

    procedure lokCount;
    var
      LRow, LCol: integer;
    begin
      LChangedFieldCount := 0;
      LInsertedFieldCount := 0;
      LRow := FGridSaveRow;
      while (LRow = FGridSaveRow) or LGridRow.IsAdditionalRow do begin
        for LCol := 0 to ASegment.Grid.Columns.Count - 1 do begin
          LCell := LGrid.DataCells[LCol, LRow];
          if (ix = 0) and not LCell.NoData then begin
            if LCell.HasChanged then
              inc(LChangedFieldCount);
            if LCell.Inserted then
              inc(LInsertedFieldCount);
          end;
        end;
        inc(LRow);
        LGridRow := LGrid.DataRow[LRow];
      end; // while
    end; // procedure lokCount

  begin
    lokCount;

    if LChangedFieldCount > 0 then begin
      LBafConName := FindParamStringReplaced(LLineP, 'db'+ s, DC_DEFAULT);
      LHist := FindParamBoolean(LLineP, 'hst' + s, true);
      LIni.AsString := '';
      LIni.WriteString(SEC_ADD, 't', LTableName);
      LIni.WriteString(SEC_ADD, 'k', LKeyFieldName);
      LIni.WriteBool(SEC_ADD, 'hst', LHist);
      LIni.WriteBool(SEC_ADD, 'ins', LInsertedFieldCount > 0);
      lokWriteColumns;

      LCon := dataMain.GetBafDbCon(LBafConName);
      dataMain.UpsertIni(LIni, LBafConName, LCon.ProgChg + FInter.CommandName, LRowAffected);
    end;
  end; // procedure lokWriteLine

begin
  LGrid := ASegment.Grid;
  LSelectedRow := LGrid.SelectedRow;
  LGrid.SelectedRow := -1;
  LIni := TStringIniFile.Create('');
  try
    for LRow := 0 to LGrid.RowCount - 1 do begin
      FGridSaveRow := LRow;
      LGridRow := LGrid.DataRow[LRow];
      if LGridRow.HasChanged and not LGridRow.IsAdditionalRow then begin
        LLineP := LGridRow.SGridZeile;
        for ix := 0 to 0 do begin        // change her for y functionality
          s := IfThen(ix = 0, '', IntToStr(ix));
          LTableName := FindParamStringReplaced(LLineP, 't' + s, '');
          LKeyFieldName := GetPrimaryKey(LLineP, s);
          if LTableName <> '' then
            lokWriteLine;
        end;
      end;
    end;
  finally
    LIni.Free;
  end;
  LGrid.SelectedRow := LSelectedRow;
// procedure TBafFrmModule.GridSaveSGrid
end;

procedure TBafFrmModule.GridSaveVL(ASegment: TBafPageSegment);
var
  LLineP, LKeyFieldValue, LKeyFieldName, LYTableName, s: string;
  LIni: TStringIniFile;
  LChangedFieldCount, LInsertedFieldCount, ix: integer;
  LHist, LLinkChanged: boolean;
  LGrid: TBafSimpleGrid;
  LGridRow: TBafSgRow;
  LCell: TBafSgCell;
  sl, sl2: TStringList;

  procedure lokCount(ACol, ARow: integer);
  begin
    LCell := ASegment.Grid.Cells[rtData, ACol, ARow];
    if (LCell.YFieldName <> '')
        and not LCell.NoData and (LCell.DataQIndex = ix) then begin
      if LCell.HasChanged then
        inc(LChangedFieldCount);
      if LCell.Inserted then
        inc(LInsertedFieldCount);
    end;
  end; // procedure lokCount


  procedure lokFindAndSet(ACol, ARow: integer);
  begin
    LCell := ASegment.Grid.Cells[rtData, ACol, ARow];
    if (LCell.HasChanged or (LInsertedFieldCount > 0))
        and (LCell.YFieldName <> '')
        and not LCell.NoData and (LCell.DataQIndex = ix) then begin
      if LCell.CellType = ctMemo then begin
        sl2 := TStringList.Create;
        sl2.Text := LCell.Text;
        sl.AddObject(LCell.DataFieldName, sl2);
      end
      else if LCell.CellType = ctCurrInt then
        LIni.WriteString(SEC_DATA, LCell.YFieldName, BafCurrInt2Int(LCell.Text))
      else
        LIni.WriteString(SEC_DATA, LCell.YFieldName, LCell.Text);
    end;

    // is it the key column? then get the value
    if LCell.DataFieldName = ASegment.DataKey[ix] then begin
      LKeyFieldValue := LCell.Text;
      LKeyFieldName := LCell.YFieldName;
      if ix = 0 then
        ASegment.DataKeyValue := LCell.Text; { TODO : Do we need this? }
      LIni.WriteString(SEC_DATA, LKeyFieldName, LKeyFieldValue);
    end;
  end; // procedure lokFindAndSet

  procedure lokSave;
  var
    LRow, LCol, LRowAffected: integer;
  begin
    LChangedFieldCount := 0;
    LInsertedFieldCount := 0;
    for LRow := 0 to LGrid.RowCount - 1 do begin
      for LCol := 0 to ASegment.Grid.Columns.Count - 1 do
        lokCount(LCol, LRow);
    end;

    if LChangedFieldCount > 0 then begin
      for LRow := 0 to LGrid.RowCount - 1 do begin
        for LCol := 0 to ASegment.Grid.Columns.Count - 1 do
          lokFindAndSet(LCol, LRow);
      end;
      LIni.WriteString(SEC_ADD, 'k', LKeyFieldName);
      LIni.WriteBool(SEC_ADD, 'ins', LInsertedFieldCount > 0);
      LIni.WriteString(SEC_ADD, 'kv', LKeyFieldValue);
      dataMain.UpsertIni(LIni, ASegment.BafConName[0],
          dataMain.GetBafDbCon(ASegment.BafConName[0]).ProgChg + FInter.CommandName,
          LRowAffected, sl);
    end;
  end; // procedure lokSave

  procedure lokCheckLinkedData;
  // linked memo-segments
  var
    LRow, LCol: integer;
  begin
    for LRow := 0 to ASegment.Grid.RowCount(rtData) - 1 do begin
      for LCol := 0 to ASegment.Grid.Columns.Count - 1 do begin
        LCell := ASegment.Grid.DataCells[LCol, LRow];
        if Assigned(LCell.LinkedSegment)
            and LCell.LinkedSegment.HasChanged then
          LCell.SetTextChange(Lcell.LinkedSegment.Lines.Text);
      end;
    end;
  end; // procedure lokCheckLinkedData

begin
  LLineP := ASegment.DataLineP;
  LGrid := ASegment.Grid;
  if (ASegment.DataTable[0] = '') or (ASegment.DataKey[0] = '') then
    FInter.DoLog('E', '#vldata, Parameter t or k not set')
  else begin
    lokCheckLinkedData;
    for ix := 0 to ASegment.GetDataTableCount - 1 do begin
      s := IfThen(ix = 0, '', IntToStr(ix));
      sl := TStringList.Create;
      if ASegment.DataTable[ix] <> '' then begin
        LHist := FindParamBoolean(LLineP, 'hst' + s, true);
        LIni := TStringIniFile.Create('');
        try
          LIni.WriteString(SEC_ADD, 't', ASegment.DataTable[ix]);
          LIni.WriteString(SEC_ADD, 'k', ASegment.DataKey[ix]);
          LIni.WriteBool(SEC_ADD, 'hst', LHist);
          lokSave;
        finally
          LIni.Free;
        end;
      end;
    end;
  end;
// procedure TBafFrmModule.GridSaveVL
end;

procedure TBafFrmModule.GridSaveVLJson(ASegment: TBafPageSegment);
var
  LLineP, LCmd, LSep, LMethod, LResponse, LUrl, LEncReq: string;
  LGrid: TBafSimpleGrid;
  LCell: TBafSgCell;
  LInserted, LIgnoreServerCertificate: boolean;
  LRoot, LNode: TBafJsonNode;

  procedure lokInsert;
  var
    LRow, LCol: integer;
    LIsString: boolean;
  begin
    LUrl := FindParamStringReplaced(LLineP, 'iurl', '');
    LMethod := FindParamStringReplaced(LLineP, 'mi', 'post');
    for LRow := 0 to LGrid.RowCount - 1 do begin
      for LCol := 0 to ASegment.Grid.Columns.Count - 1 do begin
        LCell := ASegment.Grid.Cells[rtData, LCol, LRow];
        if (LCell.HasChanged or LInserted) and not LCell.NoData then begin
          LIsString := not (LCell.CellType in [ctCurr, ctCurr4, ctInt,
              ctDate, ctDateMin, ctDateSek, ctCurrInt]);
          if LRoot.FindNode(LCell.DataFieldName, LSep, LNode, true) then
            LNode.SetValueText(LCell.Text, LIsString);
        end;
      end;
    end;
  end; // procedure lokInsert

  procedure lokUpdate;
  var
    LRow, LCol: integer;
    LIsString: boolean;
  begin
    LUrl := FindParamStringReplaced(LLineP, 'uurl', '');
    LMethod := FindParamStringReplaced(LLineP, 'mu', 'put');
    if LMethod = 'put' then begin
      TBafJsonModule.Parse(LRoot, LGrid.GridJson);
      for LRow := 0 to LGrid.RowCount - 1 do begin
        for LCol := 0 to ASegment.Grid.Columns.Count - 1 do begin
          LCell := ASegment.Grid.Cells[rtData, LCol, LRow];
          if (LCell.HasChanged or LInserted) and not LCell.NoData then begin
            LIsString := not (LCell.CellType in [ctCurr, ctCurr4, ctInt,
                ctDate, ctDateMin, ctDateSek, ctCurrInt]);
            if LRoot.FindNode(LCell.DataFieldName, LSep, LNode) then
              LNode.SetValueText(LCell.Text, LIsString);
          end;
        end;
      end;
    end
    else if LMethod = 'patch' then begin
      for LRow := 0 to LGrid.RowCount - 1 do begin
        for LCol := 0 to ASegment.Grid.Columns.Count - 1 do begin
          LCell := ASegment.Grid.Cells[rtData, LCol, LRow];
          if (LCell.HasChanged or LInserted) and not LCell.NoData then begin
            LIsString := not (LCell.CellType in [ctCurr, ctCurr4, ctInt,
                ctDate, ctDateMin, ctDateSek, ctCurrInt]);
            if LRoot.FindNode(LCell.DataFieldName, LSep, LNode, true) then
              LNode.SetValueText(LCell.Text, LIsString);
          end;
        end;
      end;
    end;
  end; // procedure lokUpdate

  procedure lokCheckInserted;
  var
    LRow, LCol: integer;
  begin
    LInserted := false;
    for LRow := 0 to LGrid.RowCount - 1 do begin
      for LCol := 0 to ASegment.Grid.Columns.Count - 1 do begin
        LCell := ASegment.Grid.Cells[rtData, LCol, LRow];
        if LCell.Inserted then begin
          LInserted := true;
          exit;
        end;
      end;
    end;
  end; // procedure lokCheckInserted;

begin
  LLineP := ASegment.DataLineP;
  LCmd := FindParamStringReplaced(LLineP, 'sc', '');
  if LCmd <> '' then
    TBafInterpreterLevel.ExecInNewLevel(LCmd, FExecInter, FInter)
  else begin
    LGrid := ASegment.Grid;
    LSep := FindParamStringReplaced(LLineP, 'sep', '.');
    LIgnoreServerCertificate := FindParamBooleanReplaced(LLineP, 'isc', false);
    LEncReq := FindParamStringReplaced(LLineP, 'e_req', 'utf8');
    LRoot := TBafJsonNode.Create(nil);
    try
      lokCheckInserted;
      if LInserted then
        lokInsert
      else
        lokUpdate;
      TBafWebModule.ExecHttp(LUrl, LMethod, LRoot.GetText(jmFormatted, 0),
          LEncReq, LIgnoreServerCertificate, FInter, LResponse);
    finally
      LRoot.Free;
    end;
  end;
// procedure TBafFrmModule.GridSaveVLJson
end;

procedure TBafFrmModule.GridSaveXGrid(ASegment: TBafPageSegment);
var
  s, LLineP, LTable, LKeyField, LKeyFieldValue, LKeyFieldName, LIniVorlage,
      LXKeyField, LXKeyValue, LYKeyName: string;
  LIni: TStringIniFile;
  LRow, LXGridIndex, LMaxXGridIndex, LChangedFieldCount, LInsertedFieldCount: integer;
  LInsert, LHist: boolean;
  LGridRow: TBafSgRow;
  LCell: TBafSgCell;
  LColumn: TBafSgColumn;
  LGrid: TBafSimpleGrid;

  function lokXRangeHasChanged: boolean;
  // checks, if XRange has changed and we have LKeyFieldValue
  var
    LCol: integer;
    LKeyFound, LChanged: boolean;
  begin
    result := false;
    LKeyFound := false;
    LChanged := false;
    LKeyFieldValue := '';
    for LCol := 0 to ASegment.Grid.Columns.Count - 1 do begin
      LColumn := ASegment.Grid.Columns.Items[LCol];
      if LColumn.XGridIndex = LXGridIndex then begin
        if ASegment.Grid.DataCells[LCol, LRow].HasChanged then
          LChanged := true;
        if AnsiCompareText(LColumn.CellFieldName, LKeyField) = 0 then begin
          LKeyFieldValue := ASegment.Grid.DataCells[LCol, LRow].Text;
          LKeyFound := true;
        end;
        if LChanged and LKeyFound then begin
          result := true;
          exit;
        end;
      end;
      if LColumn.XGridIndex > LXGridIndex then
        exit;
    end;
  end; // function lokXRangeHasChanged

  procedure lokWriteRange;
  var
    LCol, LColCount, LRowAffected: integer;
  begin
    LXKeyValue := '';
    LIni.AsString := LIniVorlage;
    LColCount := 0;

    // check Insert
    LInsert := Trim(LKeyFieldValue) = '';
    LIni.WriteBool(SEC_ADD, 'ins', LInsert);

    // go through the cols
    for LCol := 0 to ASegment.Grid.Columns.Count - 1 do begin
      LColumn := ASegment.Grid.Columns.Items[LCol];
      if LColumn.CellDataQuelle = dqSQL then begin
        if (LColumn.XGridIndex = -1)
            or (LColumn.XGridIndex = LXGridIndex) then begin
          LLineP := LColumn.LineP;
          LCell := ASegment.Grid.DataCells[LCol, LRow];

          if LInsert and (LXKeyValue = '') then begin  // X-Key
            s := FindParamStringLower(LLineP, 'f1', '');
            if (AnsiCompareText(s, LXKeyField) = 0)
                and not FindParamBoolean(LLineP, 'nd1', false) then
              LXKeyValue := ASegment.Grid.Cells[rtHeader, LCol, 0].Text;
          end;

          if (LCell.HasChanged or LInsert) and (LColumn.CellFieldName <> '')
              and not FindParamBoolean(LLineP, 'nd', false) then begin
            case LColumn.CellType of
              ctCurrInt: LIni.WriteString(SEC_DATA, LColumn.CellFieldName,
                  BafCurrInt2Int(LCell.Text));
              else
                LIni.WriteString(SEC_DATA, LColumn.CellFieldName, LCell.Text);
            end;
            inc(LColCount);
          end;

          // we have the key column? and insert? then create key
          if LInsert and (AnsiCompareText(LColumn.CellFieldName, LKeyField) = 0) then begin
            LKeyFieldValue := BafGetGuid;
            LCell.Text := LKeyFieldValue;
            LIni.WriteString(SEC_DATA, LColumn.CellFieldName, LCell.Text);
          end;
        end; // if (LColumn.XGridIndex = -1) or
      end; // if LColumn.CellDataSource = dqSQL
    end; // for LCol := 0 to

    if LColCount > 0 then begin
      LIni.WriteString(SEC_ADD, 'kv', LKeyFieldValue);
      if LInsert and (LXKeyValue <> '') then
        LIni.WriteString(SEC_DATA, LXKeyField, LXKeyValue);
      dataMain.UpsertIni(LIni, ASegment.BafConName[0],
          dataMain.GetBafDbCon(ASegment.BafConName[0]).ProgChg + FInter.CommandName, LRowAffected);
    end
    else
      LIni.Clear;
  end; // procedure lokWriteRange

  procedure lokWriteLine(AIx, ARow: integer);
  var
    LCol, LRowAffected: integer;
    LColumn: TBafSgColumn;

    procedure lokWriteColumns;
    var
      LCol: integer;
    begin
      LKeyFieldValue := '';
      LKeyFieldName := '';
      for LCol := 0 to LGrid.Columns.Count - 1 do begin
        LColumn := LGrid.Columns.Items[LCol];
        if (LColumn.CellDataQuelle in [dqSQL, dqY]) then begin
          LCell := LGrid.DataCells[LCol, ARow];
          if (LCell.HasChanged or (LInsertedFieldCount > 0))
              and (LColumn.CellYFieldName <> '')
              and not LColumn.CellNoData
              and (LColumn.CellDataQIndex = AIx) then begin
            LIni.WriteString(SEC_DATA, LColumn.CellFieldName, LCell.Text);
          end;

          // is it the key column? then save
          if LColumn.CellFieldName = ASegment.DataKey[AIx] then begin
            LKeyFieldValue := LCell.Text;
            LKeyFieldName := LColumn.CellYFieldName;
            if AIx = 0 then
              LGridRow.DataKeyValue := LCell.Text;    { TODO : Do we need this? }
            LIni.WriteString(SEC_DATA, LKeyFieldName, LKeyFieldValue);
          end;
        end;
      end;
    end; // procedure lokWriteColumns

    procedure lokCount;
    begin
      LCell := LGrid.DataCells[LCol, ARow];
      LColumn := LCell.Parents.Column;
      if (LColumn.CellYFieldName <> '')
          and not LColumn.CellNoData and (LColumn.CellDataQIndex = AIx) then begin
        if LCell.HasChanged then
          inc(LChangedFieldCount);
        if LCell.Inserted then
          inc(LInsertedFieldCount);
      end;
    end; // procedure lokCount

  begin
    LChangedFieldCount := 0;
    LInsertedFieldCount := 0;
    for LCol := 0 to ASegment.Grid.Columns.Count - 1 do
      lokCount;

    if LChangedFieldCount > 0 then begin
      LIni.AsString := LIniVorlage;
      LIni.WriteBool(SEC_ADD, 'ins', LInsertedFieldCount > 0);
      lokWriteColumns;

      LIni.WriteString(SEC_ADD, 'k', LKeyFieldName);
      LIni.WriteBool(SEC_ADD, 'ins', LInsertedFieldCount > 0);
      LIni.WriteString(SEC_ADD, 'kv', LKeyFieldValue);
      dataMain.UpsertIni(LIni, ASegment.BafConName[0],
          dataMain.GetBafDbCon(ASegment.BafConName[0]).ProgChg + FInter.CommandName, LRowAffected);
    end;
  end; // procedure lokWriteLine

  procedure lokY;
  var
    ix, LRow: integer;
  begin
    for ix := 1 to ASegment.GetDataTableCount - 1 do begin
      s := IfThen(ix = 0, '', IntToStr(ix));
      if ASegment.DataTable[ix] <> '' then begin
        LHist := FindParamBoolean(LLineP, 'hst' + s, true);
        LIni := TStringIniFile.Create('');
        try
          LIni.WriteString(SEC_ADD, 't', ASegment.DataTable[ix]);
          LIni.WriteString(SEC_ADD, 'k', ASegment.DataKey[ix]);
          LIni.WriteBool(SEC_ADD, 'hst', LHist);
          LIniVorlage := LIni.AsString;
          for LRow := 0 to ASegment.Grid.RowCount - 1 do begin
            LGridRow := ASegment.Grid.DataRow[LRow];
            if LGridRow.HasChanged then
              lokWriteLine(ix, LRow);      // <-------
          end;
        finally
          LIni.Free;
        end;
      end;
    end;
  end; // procedure lokY

begin
  LLineP := ASegment.DataLineP;
  LGrid := ASegment.Grid;
  LTable := FindParamStringLower(LLineP, 't', '');
  LKeyField := GetPrimaryKey(LLineP);
  LXKeyField := FindParamStringLower(LLineP, 'fcol', '');
  if (LTable = '') or (LKeyField = '') or (LXKeyField = '') then
    FInter.DoLog('E', '#xgriddata, Parameter t or k or lcol not set')
  else begin
    LHist := FindParamBoolean(LLineP, 'hst', true);
    LIni := TStringIniFile.Create('');
    try
      LIni.WriteString(SEC_ADD, 't', LTable);
      LIni.WriteString(SEC_ADD, 'k', LKeyField);
      LIni.WriteBool(SEC_ADD, 'hst', LHist);
      LIniVorlage := LIni.AsString;

      LMaxXGridIndex := ASegment.Grid.Columns.Items[
          ASegment.Grid.Columns.Count - 1].XGridIndex;

      for LRow := 0 to ASegment.Grid.RowCount - 1 do begin
        LGridRow := ASegment.Grid.DataRow[LRow];
        if LGridRow.HasChanged then begin
          for LXGridIndex := 0 to LMaxXGridIndex do begin   // write X-Data
            if lokXRangeHasChanged then
              lokWriteRange;
          end;
        end;
      end;

    finally
      LIni.Free;
    end;
    lokY;
  end;
// procedure TBafFrmModule.GridSaveXGrid
end;

procedure TBafFrmModule.GridSaveXXGrid(ASegment: TBafPageSegment);
var
  s, LLineP, LTable, LKeyField, LKeyFieldValue, LKeyFieldName, LIniVorlage,
      LXKeyField, LXXKeyField, LXKeyValue, LXXKeyValue, LYKeyName: string;
  LIni: TStringIniFile;
  LRow, LXGridIndex, LXXGridIndex, LMaxXXGridIndex, LMaxXGridIndex,
      LChangedFieldCount, LInsertedFieldCount: integer;
  LInsert, LHist: boolean;
  LGridRow: TBafSgRow;
  LCell: TBafSgCell;
  LColumn: TBafSgColumn;
  LGrid: TBafSimpleGrid;

  function lokXRangeHasChanged: boolean;
  // checks, if XRange has changed and we have LKeyFieldValue
  var
    LCol: integer;
    LKeyFound, LChanged: boolean;
  begin
    result := false;
    LKeyFound := false;
    LChanged := false;
    LKeyFieldValue := '';
    for LCol := 0 to ASegment.Grid.Columns.Count - 1 do begin
      LColumn := ASegment.Grid.Columns.Items[LCol];
      if (LColumn.XGridIndex = LXGridIndex)
          and (LColumn.XXGridIndex = LXXGridIndex) then begin
        if ASegment.Grid.DataCells[LCol, LRow].HasChanged then
          LChanged := true;
        if AnsiCompareText(LColumn.CellFieldName, LKeyField) = 0 then begin
          LKeyFieldValue := ASegment.Grid.DataCells[LCol, LRow].Text;
          LKeyFound := true;
        end;
        if LChanged and LKeyFound then begin
          result := true;
          exit;
        end;
      end;
      if LColumn.XGridIndex > LXGridIndex then
        exit;
    end;
  end; // function lokXRangeHasChanged

  procedure lokWriteRange;
  var
    LCol, LColCount, LRowAffected: integer;
  begin
    LXKeyValue := '';
    LXXKeyValue := '';
    LIni.AsString := LIniVorlage;
    LColCount := 0;

    // check Insert
    LInsert := Trim(LKeyFieldValue) = '';
    LIni.WriteBool(SEC_ADD, 'ins', LInsert);

    // go through the cols
    for LCol := 0 to ASegment.Grid.Columns.Count - 1 do begin
      LColumn := ASegment.Grid.Columns.Items[LCol];
      if LColumn.CellDataQuelle = dqSQL then begin
        if (LColumn.XGridIndex = -1)
            or (LColumn.XGridIndex = LXGridIndex) then begin
          LLineP := LColumn.LineP;
          LCell := ASegment.Grid.DataCells[LCol, LRow];

          if LInsert and (LXKeyValue = '') then begin  // X-Key
            s := FindParamStringLower(LLineP, 'f', '');
            if AnsiCompareText(s, LXKeyField) = 0 then
              LXKeyValue := LCell.Text;
          end;

          if LInsert and (LXXKeyValue = '') and (LColumn.XXGridIndex = LXXGridIndex) then begin  // XX-Key
            s := FindParamString(LLineP, 'f1', '');
            if AnsiCompareText(s, LXXKeyField) = 0 then
              LXXKeyValue := ASegment.Grid.Cells[rtHeader, LCol, 0].Text;
          end;

          if (LCell.HasChanged or LInsert) and (LColumn.CellFieldName <> '')
              and ((LColumn.XGridIndex = -1) or (LColumn.XXGridIndex = LXXGridIndex))
              and not FindParamBoolean(LLineP, 'nd', false)
              and not (LColumn.CellDataQuelle in [dqY]) then begin
            case LColumn.CellType of
              ctCurrInt: LIni.WriteString(SEC_DATA, LColumn.CellFieldName,
                  BafCurrInt2Int(LCell.Text));
              else
                LIni.WriteString(SEC_DATA, LColumn.CellFieldName, LCell.Text);
            end;
            inc(LColCount);
          end;

          // we have the key column? and insert? then create key
          if LInsert and (AnsiCompareText(LColumn.CellFieldName, LKeyField) = 0)
              and (LColumn.XXGridIndex = LXXGridIndex) then begin
            LKeyFieldValue := BafGetGuid;
            LCell.Text := LKeyFieldValue;
            LIni.WriteString(SEC_DATA, LColumn.CellFieldName, LCell.Text);
          end;
        end; // if (LColumn.XGridIndex = -1) or
      end; // if LColumn.CellDataSource = dqSQL
    end; // for LCol := 0 to

    if LColCount > 0 then begin
      LIni.WriteString(SEC_ADD, 'kv', LKeyFieldValue);
      if LInsert and (LXKeyValue <> '') then
        LIni.WriteString(SEC_DATA, LXKeyField, LXKeyValue);
      if LInsert and (LXXKeyValue <> '') then
        LIni.WriteString(SEC_DATA, LXXKeyField, LXXKeyValue);
      dataMain.UpsertIni(LIni, ASegment.BafConName[0],
          dataMain.GetBafDbCon(ASegment.BafConName[0]).ProgChg + FInter.CommandName, LRowAffected);
    end
    else
      LIni.Clear;
  end; // procedure lokWriteRange

begin
  LLineP := ASegment.DataLineP;
  LGrid := ASegment.Grid;
  LTable := FindParamStringLower(LLineP, 't', '');
  LKeyField := GetPrimaryKey(LLineP);
  LXKeyField := FindParamString(LLineP, 'fxcol', '');
  LXXKeyField := FindParamString(LLineP, 'fxxcol', '');
  if (LTable = '') or (LKeyField = '') or (LXKeyField = '') then
    FInter.DoLog('E', '#xgriddata, Parameter t or k or lcol not set')
  else begin
    LHist := FindParamBoolean(LLineP, 'hst', true);
    LIni := TStringIniFile.Create('');
    try
      LIni.WriteString(SEC_ADD, 't', LTable);
      LIni.WriteString(SEC_ADD, 'k', LKeyField);
      LIni.WriteBool(SEC_ADD, 'hst', LHist);
      LIniVorlage := LIni.AsString;

      LMaxXGridIndex := ASegment.Grid.Columns.Items[
          ASegment.Grid.Columns.Count - 1].XGridIndex;
      LMaxXXGridIndex := ASegment.Grid.Columns.Items[
          ASegment.Grid.Columns.Count - 1].XXGridIndex;

      for LRow := 0 to ASegment.Grid.RowCount - 1 do begin
        LGridRow := ASegment.Grid.DataRow[LRow];
        if LGridRow.HasChanged then begin
          for LXGridIndex := 0 to LMaxXGridIndex do begin   // write X-Data
            for LXXGridIndex := 0 to LMaxXXGridIndex do begin
              if lokXRangeHasChanged then
                lokWriteRange;
            end;
          end;
        end;
      end;

    finally
      LIni.Free;
    end;
//    lokY;
  end;
// procedure TBafFrmModule.GridSaveXXGrid
end;

procedure TBafFrmModule.GridSegButtonClick(const Sender: TBafPageParents);
var
  LLineP, LCommand, LConfirm: string;
begin
  LLineP := Sender.SegButton.LineP;
  LCommand := FindParamStringReplaced(LLineP, 'cmd', '');
  LConfirm := FindParamStringReplaced(LLineP, 'cnf', '');

  if Trim(LConfirm) = '' then
    FInter.CommandOnTimer(LCommand)
  else if TfrmBafDialog.DialogYesNow(dataMain.ProgName, LConfirm, nil) then
    FInter.CommandOnTimer(LCommand);
end;

procedure TBafFrmModule.PageStatus(ASender: TObject; AStatus: TBafGridStatus);
var
  i: integer;
begin
  if FExecInter = nil then begin
    FExecInter := FInter;
    try
      if (AStatus = gsChanged) and not DoPageCheck then
        AStatus := gsCheckFailed;
    finally
      FExecInter := nil;
    end;
  end
  else if (AStatus = gsChanged) and not DoPageCheck then
    AStatus := gsCheckFailed;

  for i := 0 to FCompList.Count - 1 do begin
    if FCompList[i] is TBafButton then
      TBafButton(FCompList[i]).OpEnabled(AStatus);
    if FCompList[i] is TBafTree then
      TBafTree(FCompList[i]).Enabled := (AStatus = gsBrowse);
    if FCompList[i] is TBafEdit then
      TBafEdit(FCompList[i]).Enabled := (AStatus = gsBrowse);
  end;
  for i := 0 to FPage.PageButtonList.Count - 1 do begin
    if FPage.PageButtonList[i] is TBafButton then
      TBafButton(FPage.PageButtonList[i]).OpEnabled(AStatus);

  end;
end;

procedure TBafFrmModule.HintMouseEnter(Sender: TObject);
var
  LHintControl: TControl;
  LBtnPos: TPointF;
  LCell: TBafSgCell;
  s: string;
  LMoveWidth: single;
begin
  LHintControl := (Sender as TControl);
  FHintText := LHintControl.Hint;
//  if (FHintText <> '') and (LHintControl.ShowHint) then begin
  if (FHintText <> '')  then begin
    if Sender is TBafSimpleGrid then begin
      LCell := TBafSimpleGrid(LHintControl).HoverCell;
      if not Assigned(LCell) then begin
        FPanelHint.Visible := false;
        exit;
      end;
      LBtnPos := LHintControl.LocalToAbsolute(LCell.DisplayRect.BottomRight);
      FPanelHint.Position.Point := FInter.ParentPanel.AbsoluteToLocal(LBtnPos);
      s := Format('X: %8.2f   Y: %8.2f', [LCell.DisplayRect.Location.X, LCell.DisplayRect.Location.Y]);
      LMoveWidth := LCell.DisplayRect.Width;
      FPanelHint.Position.X := FPanelHint.Position.X - 5;
      FPanelHint.Position.Y := FPanelHint.Position.Y - 5;
    end
    else begin
      LBtnPos := LHintControl.ParentControl.LocalToAbsolute(LHintControl.Position.Point);
      FPanelHint.Position.Point := FInter.ParentPanel.AbsoluteToLocal(LBtnPos);
      LMoveWidth := LHintControl.Width;
      FPanelHint.Position.X := FPanelHint.Position.X + LHintControl.Width - 5;
      FPanelHint.Position.Y := FPanelHint.Position.Y + LHintControl.Height - 5;
    end;

    FPanelHint.Visible := true;
    FPanelHint.ClipParent := false;
    FPanelHint.ClipChildren := false;
    FPanelHint.Width := FPanelHint.Canvas.TextWidth(FHintText) + 4;
    FPanelHint.Height := FPanelHint.Canvas.TextHeight(FHintText) + 4;
    if (FPanelHint.Position.X + FPanelHint.Width)
        > FInter.ParentPanel.Width then begin
      FPanelHint.Position.X := Max(FPanelHint.Position.X - LMoveWidth
          + 10 - FPanelHint.Width, 0);
    end;
    if (FPanelHint.Position.Y + FPanelHint.Height)
        > FInter.ParentPanel.Height then begin
      FPanelHint.Position.Y := Max(FPanelHint.Position.Y - LHintControl.Height
          + 10 - FPanelHint.Height, 0);
    end;
    FPanelHint.BringToFront;
  end;
end;

procedure TBafFrmModule.HintMouseLeave(Sender: TObject);
begin
  FPanelHint.Visible := false;
end;

procedure TBafFrmModule.HintPaint(Sender: TObject; Canvas: TCanvas;
  const ARect: TRectF);
begin
  Canvas.BeginScene;
  try
    Canvas.Fill.Color := TAlphaColorRec.Yellow;
    Canvas.FillRect(ARect, 0, 0, [], 1);
    ARect.SetLocation(2, -1);
    Canvas.Fill.Color := TAlphaColorRec.Black;
    Canvas.FillText(ARect, FHintText, false, 1, [], TTextAlign.Leading);
  finally
    Canvas.EndScene;
  end;
end;

function TBafFrmModule.InsertNewItem(AParent: TBafTreeNode; ACaption: string;
    ACheckDummy: boolean): TBafTreeNode;
begin
  result := TBafTreeNode.CreateInsertItem(FTree, AParent, ACaption);
end;

procedure TBafFrmModule.PrimaryDefs;
begin
  FLastPrim := FPage.PrimaryDivs.Add;
  FLastPrim.AutoSize := FindParamBooleanReplaced('as', true);
  FLastPrim.Size := FindParamSingleReplaced('sz', -1);
  FLastPrim.LineP := FExecInter.LineP;
end;

procedure TBafFrmModule.ProgressAbort(Sender: TObject);
begin
  FInter.ProgressAborted := true;
end;

procedure TBafFrmModule.ProgressDlg;
var
  LTitle, LCaption, LCmd: string;
begin
  if FindParamBooleanReplaced('cnd', true) then begin
    if FProgressDialog = nil then begin
      LTitle := FindParamStringReplaced('title', '');
      if LTitle = '' then
        LTitle := dataMain.ProgName;
      LCaption := FindParamStringReplaced('c', '');
      FProgressDialog := TfrmBafDialog.InitProgress(LTitle, LCaption, nil, ProgressAbort);
      try
        Application.ProcessMessages;
        FProgressValue := 0;
        FProgressMax := FindParamIntegerReplaced('max', 0);
        FInter.ProgressAborted := false;
        LCmd := FindParamStringReplaced('cmd', '');
        TBafInterpreterLevel.ExecInNewLevel(LCmd, FExecInter, FInter);
        if FInter.ProgressAborted then begin
          LCmd := FindParamStringReplaced('acmd', '');
          if LCmd <> '' then
            TBafInterpreterLevel.ExecInNewLevel(LCmd, FExecInter, FInter);
        end;
      finally
        FInter.ProgressAborted := false;
        FreeAndNil(FProgressDialog);
      end;
    end;
  end;
end;

procedure TBafFrmModule.ProgressInc;
var
  s: string;
begin
  if FindParamBooleanReplaced('cnd', true) then begin
    if FProgressDialog <> nil then begin
      inc(FProgressValue);
      if FProgressMax > 0 then begin
        s := Format('%d / %d', [FProgressValue, FProgressMax]);
        FProgressDialog.ProgressBar1.Max := FProgressMax;
        FProgressDialog.ProgressBar1.Value := FProgressValue;
      end
      else
        s := IntToStr(FProgressValue);
      FProgressDialog.Memo1.Lines.Text := FindParamStringReplaced('c', '') + ' ' + s;
      Application.ProcessMessages;
    end;
  end;
end;

procedure TBafFrmModule.SaveOrCancel(ASave: boolean);
var
  LBafSelected: TBafTreeNode;
  LSelect: string;
begin
  if ASave then begin
    if FPageBeforeSaveCommand <> '' then
      TBafInterpreterLevel.ExecInNewLevel(FPageBeforeSaveCommand, FExecInter, FInter);
    PageSave;
    if FPageAfterSaveCommand <> '' then
      TBafInterpreterLevel.ExecInNewLevel(FPageAfterSaveCommand, FExecInter, FInter);
    if FGridRefreshAfterSave then begin
      LSelect := FTree.Selected.GetSelectionCommand;
      if LSelect <> '' then
        TBafInterpreterLevel.ExecInNewLevel(LSelect, FExecInter, FInter);
    end;
  end
  else begin   // Cancel
    if Assigned(FTree) then begin
      LBafSelected := FTree.Selected;
      LBafSelected.DbCancel;
      FPageFailed.Clear;
      RefresCheckMemo;
      if LBafSelected.ObjectToRemove then begin
        FTree.Selected := FBeforeSelected;
//        FTree.RemoveObject(LBafSelected);
        FreeAndNil(LBafSelected);
      end;
      if Assigned(FTree.Selected) then begin
        FTree.Enabled := true;
        TreeViewChange(FTree, FTree.Root);
      end
      else
        ClearPage;
    end
    else begin
      DoFilter(nil);
      FPage.Resize;
    end;
  end;
  FPage.SetStatus;
end;

procedure TBafFrmModule.SegDiaCol;
var
  LColumn: TBafDiaColumn;
begin
  LColumn := FLastSeg.Dia.Columns.Add;
  LColumn.ParamLine := FExecInter.LineP;
  LColumn.FieldName := FindParamStringReplaced('f', '');
  LColumn.Caption := FindParamStringReplaced('c', '');
  LColumn.Color := BafColor2AlphaColor(FindParamColor('cl', TColorRec.Black));
  if FindParamStringReplacedLower('y', '') = 'date' then
    LColumn.ColumnType := dtDate;
  LColumn.Alpha := FindParamSingleReplaced('al', 1);
end;

procedure TBafFrmModule.SegDiaData;
var
  LSegment: TBafPageSegment;
  LItemName, LSourceName, LName: string;
  LIsDate: boolean;
  LDiaRow: TBafDiaRow;

  procedure lokDataFromGrid;
  var
    LCol, LRow, ix, LGridCol: integer;
    LFieldName, LGridSegmentName, s: string;
    LColumn: TBafDiaColumn;
    LGridSegment: TBafPageSegment;
    sl: TStringList;
    LDate: TDateTime;
  begin
    LGridSegmentName := FindParamStringReplacedLower('j', '');
    LGridSegment :=  FPage.PrimaryDivs.FindSegmentByName(LGridSegmentName);
    // Get the index of the columns
    for LCol:= 0 to LSegment.Dia.Columns.Count - 1 do begin
      LColumn := LSegment.Dia.Columns.Items[LCol];
      LFieldName := LColumn.FieldName;
      ix := StrToIntDef(LFieldName, -1);
      if ix >= 0 then
        LColumn.ColIx := ix
      else
        LColumn.ColIx := LGridSegment.Grid.Columns.FieldName2Ix(LFieldName);
    end;
    // Get the data
    for LRow := 0 to LGridSegment.Grid.RowCount(rtData) - 1 do begin
      for LCol := 0 to LSegment.Dia.Columns.Count - 1 do begin
        ix := LSegment.Dia.Columns.Items[LCol].ColIx;
        s := LGridSegment.Grid.Cells[rtData, ix, LRow].Text;
        if LCol = 0 then begin
          if LIsDate then begin
            LDate := StrToDateDef(s, 0);
            s := FormatDateTime('yyyy-mm-dd', LDate);
          end;
          LDiaRow := LSegment.Dia.AddRowSl(s);
          if LIsDate then
             LDiaRow.Cells[LCol] := LDate
          else
            LDiaRow.Cells[LCol] := StrToFloatDef(s, 0);
        end
        else
          LDiaRow.Cells[LCol] := StrToFloatDef(s, 0);
      end;
    end;
  end; // procedure lokDataFromGrid

  procedure lokDataFromsql;
  var
    s, LSql: string;
    LDataSet: TDataSet;
    LCol: integer;
    LField: TField;
  begin
    FLastSeg.BafConName[0] := FindParamStringReplacedLower('db', DC_DEFAULT);
    LName := FInter.Name + '~' + FLastSeg.BafConName[0];
    LSql := Trim(FInter.GetSqlAndClear(1));
    if LSql <> '' then begin
      SqlAndParams(FLastSeg.BafConName[0], LName, LSql);
      LDataSet := dataMain.QueryOpen(FLastSeg.BafConName[0], LName);
      while not LDataSet.Eof do begin
        FInter.DebugDbRow(LDataSet);

        for LCol := 0 to LSegment.Dia.Columns.Count - 1 do begin
          LField := LDataSet.FieldByName(LSegment.Dia.Columns.Items[LCol].FieldName);

          if LCol = 0 then begin
            if LIsDate then
              s := FormatDateTime('yyyy-mm-dd', LField.AsDateTime);
            LDiaRow := LSegment.Dia.AddRowSl(s);
            if LIsDate then
               LDiaRow.Cells[LCol] := LField.AsDateTime
            else
              LDiaRow.Cells[LCol] := LField.AsFloat;
          end
          else
            LDiaRow.Cells[LCol] := LField.AsFloat;
        end;

        LDataSet.Next;
      end;
    end
    else
      FInter.DoLog('E', 'empty SQL statement');
  end; // procedure lokDataFromsql

  procedure lokLinkedDias;
  var
    s: string;
    LLinkedSegment: TBafPageSegment;
  begin
    s := FindParamStringReplaced('ld', '');
    if s <> '' then begin
      LLinkedSegment := FPage.PrimaryDivs.FindSegmentByName(s);
      if Assigned(LLinkedSegment) and (LLinkedSegment.SegmentType = stDia)
          and (LLinkedSegment <> LSegment) then
        LSegment.Dia.JoinDiagram := LLinkedSegment.Dia;
    end;
  end; // procedure lokLinkedDias

begin
  LItemName := FindParamStringReplacedLower('i', '');
  if LItemName = '' then
    LSegment := FLastSeg
  else
    LSegment := FPage.PrimaryDivs.FindSegmentByName(LItemName);
  LIsDate := (LSegment.Dia.Columns.Items[0].ColumnType = dtDate);
  LSourceName := FindParamStringReplacedLower('q', '');
  if LSourceName = 'grid' then
    lokDataFromGrid
  else if LSourceName = 'sql' then
    lokDataFromsql

  ;
  lokLinkedDias;

  LSegment.Dia.Prepare;
  LSegment.GridCalcHeight;
// procedure TBafFrmModule.SegDiaData
end;

procedure TBafFrmModule.SegDia;
begin
  AddSegment(FExecInter.LineP, uBafTypes.stDia);
  FLastSeg.Dia.LegendPosName := FindParamStringReplacedLower('pos', 'none') ;
  FLastSeg.Dia.DiaTypeName := FindParamStringReplacedLower('y', 'line');
  FLastSeg.Dia.ScrollVertVisible := FindParamBooleanReplaced('sv', true);
  FLastSeg.Dia.ScrollHorzVisible := FindParamBooleanReplaced('sh', true);
  FLastSeg.HeightOpened := FindParamSingleReplaced('ho', 200);
  FLastSeg.HeightClosed := FindParamSingleReplaced('hc', 0);
end;

procedure TBafFrmModule.SegGrdThread;
var
  LThread: TBafLoadDataThread;
  LType, s, LSegName: string;
  i: integer;

  procedure lokXmlJson;
  begin
    if LType = 'gridxml' then
      LThread.ThreadType := ttGridXml
    else if LType = 'gridjson' then
      LThread.ThreadType := ttGridJson;
    LThread.Sql := FindParamStringReplaced('request', '');
    LThread.RequestUri := FindParamStringReplaced('url', '');
    LThread.ReadWriteTimeout := FindParamIntegerReplaced('to', 15);
    s := FindParamStringReplaced('method', '');
    LThread.Method := TBafWebModule.GetRequestMethode(s);
    LThread.ContentType := FindParamStringReplaced('cy', '');
    LThread.Accept := FindParamStringReplaced('acc', '');
    LThread.IgnoreServerCertificate := FindParamBooleanReplaced('isc', false);
    LThread.RequestEncoding := FindParamStringReplaced('e_req', 'utf8');
    LThread.Inter := FExecInter;
    LThread.LineP := FExecInter.LineP;
    LThread.WaitForData := FindParamIntegerReplaced('wait', 1000);
    LThread.ResultEncoding := FindParamStringReplaced('e_res', 'utf8');
    LThread.ConvertUtf8 := FindParamBooleanReplaced('cu8', false);
    LThread.Path := FindParamStringReplaced('path', '');
    LThread.Prefix := FindParamStringReplaced('pfx', '');
    LThread.Separator := FindParamStringReplaced('sep', '.');
  end; // procedure lokXmlJson

begin
  if FindParamBooleanReplaced('cnd', true) then begin
    LThread := TBafLoadDataThread.Create(true);
    LThread.FreeOnTerminate := true;
    LSegName := FindParamStringReplaced('i', '');
    if LSegName <> '' then
      LThread.Segment := FPage.PrimaryDivs.FindSegmentByName(LSegName)
    else
      LThread.Segment := FLastSeg;
    LThread.KeyName := FindParamStringReplaced('k', '');
    LThread.FieldPrefix := FindParamStringReplaced('fp', '');
    LType := FindParamStringReplacedLower('y', 'grid');
    if LType = 'grid' then begin
      LThread.ThreadType := ttGrid;
      LThread.Sql := FInter.GetSqlAndClear(1);
      LThread.BafConName := FindParamStringReplacedLower('db', DC_DEFAULT);
    end
    else if LType = 'gridbulk' then begin
      LThread.ThreadType := ttGridBulk;
      LThread.Sql := FInter.GetSqlAndClear(1);
      LThread.BafConName := FindParamStringReplacedLower('db', DC_DEFAULT);
    end
    else if LType = 'gridcache' then begin
      LThread.ThreadType := ttGridCache;
      LThread.Sql := FInter.GetSqlAndClear(1);
      LThread.BafConName := FindParamStringReplacedLower('db', DC_DEFAULT);
      for i := 1 to FindParamIntegerReplaced('cc', 1) do begin
        s := Trim(FindParamStringReplacedLower('cnd' + IntToStr(i), ''));
        if s <> '' then
          LThread.AddCacheFilterStatement(s);
      end;
    end
    else if LType = 'gridlist' then begin
      LThread.ThreadType := ttGridList;
      LThread.Sql := FInter.GetSqlAndClear(1);
      LThread.BafConName := FindParamStringReplacedLower('db', DC_DEFAULT);
      LThread.ListSeparator := FindParamStringReplaced('sep', ',');
    end
    else if (LType = 'gridxml') or (LType = 'gridjson') then
      lokXmlJson;
    LThread.ReadyCommand := FindParamStringReplaced('ready', '');
    LThread.RowNoInsert := FindParamBooleanReplaced('rni', false);
    if (LThread = nil) or LThread.IsTerminated then
      exit;
    if LThread.Init then begin
      LThread.OnTerminate := LoadDataThreadTerminate;
      FThreadList.Add(LThread);
      LThread.Suspended := false;
    end;
  end;
// procedure TBafFrmModule.SegGrdThread;
end;

procedure TBafFrmModule.SegGrid;
begin
  AddSegment(FExecInter.LineP, uBafTypes.stGrid);
  FHeaderRowCount := FindParamIntegerReplaced('hrc', 1);
  FFooterRowCount := FindParamIntegerReplaced('frc', 0);
  FLastSeg.Grid.Columns.FixedColsCount := FindParamIntegerReplaced('fcc', 0);
  FLastSeg.Grid.Columns.LineType := GetLineType;
  FLastSeg.Grid.SelectionColumn := FindParamIntegerReplaced('sc', -1);
  FLastSeg.Grid.WithoutHorizontalScrollBar := FindParamBooleanReplaced('whs', false);
  FLastSeg.Grid.AddCommand := FindParamStringReplaced('acmd', '');
//  FLastSeg.FilterColumnsRow := FindParamIntegerReplaced('fcr', 1) - 1;
end;

procedure TBafFrmModule.SegGridAdd;
// adds a new row to a grid
var
  LSegment: TBafPageSegment;
  i, LCol, LSelCol: integer;
  LRow: TBafSgRow;
  LLineP, LFieldName, LValue, LTyp, LSelColName: string;
  LChange, LPageCheckPause: boolean;
  LCell: TBafSgCell;
  LColumn: TBafSgColumn;
begin
  if FindSegment('#grd_add', LSegment) then begin
    LTyp := FindParamStringReplacedLower('y', 'bottom');
    if LTyp = 'bottom' then
      LRow := LSegment.Grid.InsertRow(MaxInt)
    else if LTyp = 'top' then
      LRow := LSegment.Grid.InsertRow(0)
    else if LTyp = 'bsel' then
      LRow := LSegment.Grid.InsertRow(Max(LSegment.Grid.SelectedRow, 0))
    else if LTyp = 'asel' then
      LRow := LSegment.Grid.InsertRow(Max(LSegment.Grid.SelectedRow, 0) + 1);
    LChange := FindParamBooleanReplaced('chg', false);
    LPageCheckPause := FPageCheckPause;
    FPageCheckPause := true;
    try
      for LCol := 0 to LSegment.Grid.Columns.Count - 1 do begin
        LColumn := LSegment.Grid.Columns.Items[LCol];
        LFieldName := LColumn.CellFieldName;
        if (LFieldName <> '') then
          LValue := FindParamStringReplaced('f_' + LFieldName, '')
        else begin
          LLineP := LColumn.LineP;
          LValue := FindParamStringReplaced(LLineP, 'z', '');
        end;
        LCell := LRow.Cells[LCol];
        if LColumn.CellType = ctLookupLive then begin
          LCell.LookupHelper := TBafComboHelper.Create(false);
          FGridObjectList.Add(LCell.LookupHelper);
        end;
        if LChange then
          LCell.SetTextChange(LValue)
        else
          LCell.Text := LValue;
        ColumnNullValue(LCell);
      end;
    finally
      FPageCheckPause := LPageCheckPause;
      if not FPageCheckPause then
        FPage.SetStatus;
    end;
    LSegment.GridCalcHeight;
    LSelColName := FindParamStringReplacedLower('sc', '');
    LSelCol := StrToIntDef(LSelColName, -1);
    if LSelCol = -1 then
      LSelCol := LSegment.Grid.Columns.FieldName2Ix(LSelColName);
    if LSelCol >= 0 then
      LSegment.Grid.SelectedCol := LSelCol;
    LSegment.Grid.SelectedRow := LRow.RowIndex;
    if LSegment.Grid.CanFocus then
      LSegment.Grid.SetFocus;
    LSegment.Grid.Repaint;
  end
  else
    FInter.DoLog('E', '#grd_add - Segment not found');
// procedure TBafFrmModule.SegGridAdd
end;

procedure TBafFrmModule.SegGridAddOrChange;
// Depending of the Value of some fields add an new row or change the values of an existing
var
  LSegment: TBafPageSegment;
  i, LRow, LMatchRow: integer;
  LIgnoreCase: boolean;
  LSgRow: TBafSgRow;
  LLineP, LFieldName, LValue, LTyp, LSelColName: string;
  LChange, LPageCheckPause: boolean;
  LCell: TBafSgCell;
  LColumn: TBafSgColumn;

  procedure lokFind;
  var
    i, LRow, LCol: integer;
    LFindFieldNames, LFindValues: array[1..9] of string;
    LValue: string;
    b: boolean;
  begin
    LMatchRow := -1;
    for i := 1 to 9 do begin
      LFindFieldNames[i] := FindParamStringReplacedLower('fnd_' + IntToStr(i), '');
      if LFindFieldNames[i] <> '' then
        LFindValues[i] := FindParamStringReplaced('f_' + LFindFieldNames[i], '');
    end;
    for LRow := 0 to LSegment.Grid.RowCount(rtData) - 1 do begin
      b := true;
      for i := 1 to 9 do begin
        if LFindFieldNames[i] <> '' then begin
          LCol := LSegment.Grid.Columns.FieldName2Ix(LFindFieldNames[i]);
          LValue := LSegment.Grid.Cells[rtData, LCol, LRow].Text;
          if LIgnoreCase then begin
            if AnsiCompareText(LValue, LFindValues[i]) <> 0 then begin
              b := false;
              Break;
            end;
          end
          else begin
            if AnsiCompareStr(LValue, LFindValues[i]) <> 0 then begin
              b := false;
              Break;
            end;
          end;
        end;
      end;
      if b then begin
        LMatchRow := LRow;
        Exit;
      end;
    end;
  end; // procedure lokFind

  procedure lokSetValues;
  var
    LCol: integer;
    LValueOld: string;
  begin
    LChange := FindParamBooleanReplaced('chg', false);
    LPageCheckPause := FPageCheckPause;
    FPageCheckPause := true;
    try
      for LCol := 0 to LSegment.Grid.Columns.Count - 1 do begin
        LColumn := LSegment.Grid.Columns.Items[LCol];
        LCell := LSgRow.Cells[LCol];
        LValueOld := LCell.Text;
        LFieldName := LColumn.CellFieldName;
        if (LFieldName <> '') then
          LValue := FindParamStringReplaced('f_' + LFieldName, LValueOld)
        else begin
          LLineP := LColumn.LineP;
          LValue := FindParamStringReplaced(LLineP, 'z', LValueOld);
        end;
        if LColumn.CellType = ctLookupLive then begin
          LCell.LookupHelper := TBafComboHelper.Create(false);
          FGridObjectList.Add(LCell.LookupHelper);
        end;
        if LChange or (LValueOld <> LValue) then
          LCell.SetTextChange(LValue)
        else
          LCell.Text := LValue;
        ColumnNullValue(LCell);
      end;
    finally
      FPageCheckPause := LPageCheckPause;
      if not FPageCheckPause then
        FPage.SetStatus;
    end;
  end; // procedure lokSetValues

begin
  if FindParamBooleanReplaced('cnd', true) then begin
    if FindSegment('#grd_ac', LSegment) then begin
      LIgnoreCase := FindParamBooleanReplaced('ic', true);
      lokFind;
      if LMatchRow = -1 then begin
        LTyp := FindParamStringReplacedLower('y', 'bottom');
        if LTyp = 'bottom' then
          LSgRow := LSegment.Grid.InsertRow(MaxInt)
        else if LTyp = 'top' then
          LSgRow := LSegment.Grid.InsertRow(0)
        else if LTyp = 'bsel' then
          LSgRow := LSegment.Grid.InsertRow(Max(LSegment.Grid.SelectedRow, 0))
        else if LTyp = 'asel' then
          LSgRow := LSegment.Grid.InsertRow(Max(LSegment.Grid.SelectedRow, 0) + 1);
      end
      else
        LSgRow := LSegment.Grid.Row[rtData, LMatchRow];
      lokSetValues;
      LSegment.GridCalcHeight;
      LSegment.Grid.Repaint;
    end
    else
      FInter.DoLog('E', '#grdadd - Segment not found');
  end;
// procedure TBafFrmModule.SegGridAddOrChange
end;

procedure TBafFrmModule.SegGridButton;
var
  LCaption, LHint, LStatusEnabled: string;
  LWidth: integer;
  LRight: TBafRight;
  LReadOnly, LNewLine: boolean;
begin
  LRight := FExecInter.GetRight;
  if FindParamBooleanReplaced('cnd', true) and (LRight > brNone) then begin
    LReadOnly := (LRight < brWrite) or FindParamBooleanReplaced('ro', false);
    LCaption := FindParamStringReplaced('c', '');
    LHint := FindParamStringReplaced('h', '');
    LWidth := FindParamInteger('w', 200);
    LStatusEnabled := FindParamStringReplacedLower('se', 'b');
    LNewLine := FindParamBooleanReplaced('nl', false);
    FLastSeg.AddButton(LCaption, LHint, FExecInter.LineP, LStatusEnabled,
        LWidth, LReadOnly, LNewLine, LRight);
  end;
end;

procedure TBafFrmModule.SegGridButtons;
begin
  if FindParamBooleanReplaced('cnd', true) then
    AddSegment(FExecInter.LineP, uBafTypes.stButtons);
end;

procedure TBafFrmModule.SegGridCol(ALineP: string; ADataSet: TDataSet;
    AXGridIndex, AXXGridIndex: integer);
var
  LColumn: TBafSgColumn;
  LCell: TBafSgCell;
  s, LParam, LCmd: string;
  LRight: TBafRight;
  ix: integer;

  procedure lokHeaderFooter;
  var
    LRow: integer;
    LField: TField;
  begin
    for LRow := 0 to FHeaderRowCount - 1 do begin
      LCell := FLastSeg.Grid.Cells[rtHeader, LColumn.Index, LRow];
      s := IntToStr(LRow + 1);
      LCell.Text := FindParamStringReplaced(ALineP, 'c' + s, '');
      LParam := FindParamStringLower(ALineP, 'f' + s, '');
      if Assigned(ADataSet) and (LParam <> '') then
        LCell.Text := LCell.Text + ADataSet.FieldByName(LParam).AsString;
      LCell.Hint := BafDecode1310(FindParamStringReplaced(ALineP, 'h' + s, ''));
      if Assigned(ADataSet) and FindParam(ALineP, 'h' + s, LParam) then begin
        LField := ADataSet.FindField(LParam);
        if Assigned(LField) then
          LCell.Hint := ADataSet.FieldByName(LParam).AsString;
      end;
      LCell.Alignment := FInter.FindParamAlignment(ALineP, 'a' + s, taLeftJustify);
      LCell.ShowSort := FindParamBooleanReplaced(ALineP, 'ss' + s, true);
      LCell.ColSpan := FindParamIntegerReplaced(ALineP, 'cs' + s, 1);
    end;
    for LRow := 0 to FFooterRowCount - 1 do begin
      LCell := FLastSeg.Grid.Cells[rtFooter, LColumn.Index, LRow];
      s := IntToStr(LRow + 1);
      LCell.Text := FindParamString(ALineP, 'fc' + s, '');
      LParam := FindParamStringLower(ALineP, 'ff' + s, '');
      if Assigned(ADataSet) and (LParam <> '') then
        LCell.Text := LCell.Text + ADataSet.FieldByName(LParam).AsString;
      LCell.Hint := BafDecode1310(FindParamStringReplaced(ALineP, 'fh' + s, ''));
      if Assigned(ADataSet) and FindParam(ALineP, 'fh' + s, LParam) then begin
        LField := ADataSet.FindField(LParam);
        if Assigned(LField) then
          LCell.Hint := ADataSet.FieldByName(LParam).AsString;
      end;
      LCell.Alignment := FInter.FindParamAlignment(ALineP, 'fa' + s, taLeftJustify);
      LCell.ColSpan := FindParamInteger(ALineP, 'fcs' + s, 1);
//      if Pos('$', LCell.Text) > 0 then
        LCell.Command := FindParamString(ALineP, 'fc' + s, '');
      LCell.CellType := FindParamCelltype(ALineP, 'fy' + s);
    end;
  end; // procedure lokHeaderFooter;

  procedure lokCellNullValue;
  // action on a an empty column
  var
    s: string;
  begin
    if FindParam('nvic', s) then
      LColumn.SetNullValue(nvInsertChange, s)
    else if FindParam('nvi', s) then
      LColumn.SetNullValue(nvInsert, s)
    else if FindParam('nvc', s) then
      LColumn.SetNullValue(nvChange, s)
    else if FindParam('nv', s) then
      LColumn.SetNullValue(nvValue, s)
    else
      LColumn.SetNullValue(nvNone, '');
  end; // procedure lokCellNullValue

  procedure lokCellType;
  var
    s: string;
  begin
    if FLastSeg.SegmentType = stMap then begin
      s := FindParamStringReplacedLower('y', '');
      if s = 'map_c' then begin
        FLastSeg.Map.Field_Caption := LColumn.CellFieldName;
        LColumn.CellType := ctText;
        LColumn.IsMapSpecCol := true;
      end
      else if s = 'map_h' then begin
        FLastSeg.Map.Field_Hint := LColumn.CellFieldName;
        LColumn.CellType := ctText;
        LColumn.IsMapSpecCol := true;
      end
      else if s = 'map_z' then begin
        FLastSeg.Map.Field_IsChecked := LColumn.CellFieldName;
        LColumn.CellType := ctBool;
        LColumn.IsMapSpecCol := true;
      end
      else if s = 'map_x' then begin
        FLastSeg.Map.Field_PosX := LColumn.CellFieldName;
        LColumn.CellType := ctInt;
        LColumn.IsMapSpecCol := true;
      end
      else if s = 'map_y' then begin
        FLastSeg.Map.Field_PosY := LColumn.CellFieldName;
        LColumn.CellType := ctInt;
        LColumn.IsMapSpecCol := true;
      end
      else if s = 'map_s' then begin
        FLastSeg.Map.Field_FontSize := LColumn.CellFieldName;
        LColumn.CellType := ctInt;
        LColumn.IsMapSpecCol := true;
      end
      else if s = 'map_b' then begin
        FLastSeg.Map.Field_FontBold := LColumn.CellFieldName;
        LColumn.CellType := ctBool;
        LColumn.IsMapSpecCol := true;
      end
      else if s = 'map_q' then begin
        FLastSeg.Map.Field_FontColor := LColumn.CellFieldName;
        LColumn.CellType := ctInt;
        LColumn.IsMapSpecCol := true;
      end
      else if s = 'map_p' then begin
        FLastSeg.Map.Field_CaptionPos := LColumn.CellFieldName;
        LColumn.CellType := ctText;
        LColumn.IsMapSpecCol := true;
      end
      else if s = 'map_e' then begin
        FLastSeg.Map.Field_CanCheck := LColumn.CellFieldName;
        LColumn.CellType := ctBool;
        LColumn.IsMapSpecCol := true;
      end
      else
         LColumn.CellType := FindParamCelltype(ALineP, 'y');
    end
    else
      LColumn.CellType := FindParamCelltype(ALineP, 'y');
  end; // procedure lokCellType

begin
  if FindParamBooleanReplaced('cnd', true) then begin
    if FLastSeg.SegmentType = stMap then
      LColumn := FLastSeg.Map.Columns.Add
    else
      LColumn := FLastSeg.Grid.Columns.Add;
    LColumn.LineP := ALineP;
    LColumn.Width := FindParamIntegerReplaced(ALineP, 'w', 100);
    LColumn.Stretch := FindParamIntegerReplaced(ALineP, 'wst', 0);
    LColumn.Caption := FindParamStringReplaced('c', '');  // for the filter form
    LColumn.CellFieldName := FindParamStringReplacedLower(ALineP, 'f', '');
    LColumn.CellYFieldName := FindParamStringReplacedLower(ALineP, 'yf', '');
    LColumn.CellHintFieldName := FindParamStringReplaced(ALineP, 'h', '');
    LColumn.CellDataQuelle := FExecInter.FindParamQuelle(ALineP, dqSQL, ix);
    LColumn.CellDataQIndex := ix;
    lokCellType;
    if LColumn.CellType in [ctLookup, ctLookupLive, ctLookupStatus, ctLookupText] then
      LColumn.CellLookupHelper := FindLookupHelper(ALineP, '', LCmd);
    LColumn.CellMaxLength := FindParamInteger(ALineP, 'l', 0);
    LRight := FExecInter.GetRight(ALineP, '');
    if FLastSeg.SegRight = brNone then
      LRight := brNone
    else if (LRight = brWrite) and (FLastSeg.SegRight = brRead) then
      LRight := brRead;
    LColumn.CellReadOnly := (LRight < brWrite);
    LColumn.CellVisible := (LRight > brNone) and (LColumn.GetWidthValue > 0);
    if not LColumn.CellVisible then begin
      LColumn.Width := 0;
      LColumn.Stretch := 0;
    end;
    LColumn.OpenClose := GetOpenClose(FindParamStringLower('oc', 'oc'));
    LColumn.CellReadOnlyFieldName := FindParamStringReplaced(ALineP, 'rof', '');
    LColumn.CellAlignment := FInter.FindParamAlignment(ALineP, 'a', taLeftJustify);
    LColumn.CellCommand := FindParamStringReplaced(ALineP, 'cmd', '');
    if LColumn.CellCommand = '' then
      LColumn.CellCommand := FindParamString(ALineP, 'cmdn', '');
    LColumn.CellLoadCommand := FindParamString(ALineP, 'lcmd', '');
    LColumn.CellColorCommand := FindParamString(ALineP, 'ccc', '');
    LColumn.CellChangeCommand := FindParamStringReplaced(ALineP, 'chg', '');
    LColumn.ColumnGroups := FindParamStringReplaced(ALineP, 'grp', '');
    LColumn.CellNoData := FindParamBoolean(ALineP, 'nd', false);
    LColumn.CellCharCase := FindParamCharCase('', 'cy', TEditCharCase.ecNormal);
    LColumn.CellCharsIgnore := FindParamStringReplaced(ALineP, 'ci', '');
    LColumn.CellCharsAllowed := FindParamStringReplaced(ALineP, 'ca', '');
    LColumn.CellArp := FindParamSingleReplaced(ALineP, 'arp', 0);
    if Assigned(FLastSeg.Grid) then
      lokHeaderFooter;
    lokCellNullValue;
    LColumn.XGridIndex := AXGridIndex;
    LColumn.XXGridIndex := AXXGridIndex;
    LColumn.SortColumn := FindParamInteger('soc', -1);
    s := FindParamStringLower('jy', '');
    if (s = 'sum') then
      LColumn.JoinType := jtSum;
  end;
// procedure TBafFrmModule.SegGridCol
end;

procedure TBafFrmModule.SegGridCalc;
var
  LSegment: TBafPageSegment;
  LCol, LSelCol, LCount: integer;
  LSum, LMin, LMax, LValue: currency;
  LColumns: TBafSgColumns;
  LCountCondition, LFunc, LFieldName, LResult, LResultType: string;
  LOnlyChangedRows: boolean;

  procedure lokResult;
  begin
    if LFunc = 'sum' then
      LValue := LSum
    else if LFunc = 'min' then
      LValue := LMin
    else if LFunc = 'max' then
      LValue := LMax
    else if LFunc = 'avg' then
      LValue := LSum / LCount
    else LValue := LCount;
    if (LResultType = 'curr') or (LResultType = 'curr2') then
      LResult := FormatFloat('0.00', LValue)
    else if (LResultType = 'curr4') then
      LResult := FormatFloat('0.0000', LValue)
    else
      LResult := FormatFloat('0', LValue);
    FExecInter.SetVarOrValue('n', LResult);
  end; // procedure lokResult

  procedure lokInit;
  var
    i: integer;
  begin
    LOnlyChangedRows := FindParamBooleanReplaced('ocr', false);
    LSelCol := FindParamIntegerReplaced('sc', -1);
    LCountCondition := FindParamString('ccnd', BAFYESCHAR);
    LFunc := FindParamStringReplacedLower('y', 'cnt');
    LResultType := FindParamStringReplacedLower('ry', 'int');
    LCol := FindParamInteger('col', -1);
    if LCol = -1 then begin
      LFieldName := FindParamStringReplaced('f', '');
      case LSegment.SegmentType of
        stMap: LColumns := LSegment.Map.Columns;
        else
          LColumns := LSegment.Grid.Columns;
      end; // case LSegment.SegmentType
      for i := 0 to LColumns.Count - 1 do begin
        if AnsiCompareText(LFieldName, LColumns.Items[i].CellFieldName) = 0 then begin
          LCol := i;
          Break;
        end;
      end;
    end;
    LCount := 0;
    LSum := 0;
    LMax := - MaxInt;
    LMin := MaxInt;
  end; // procedure lokInit

  procedure lokCalc(AText: string);
  begin
    LValue := StrToCurrDef(AText, 0);
    LSum := LSum + LValue;
    LMax := Max(LMax, LValue);
    LMin := Min(LMin, LValue);
  end; // procedure lokCalc

  procedure lokDoGrid;
  var
    LRow: integer;
  begin
    for LRow := 0 to LSegment.Grid.RowCount(rtData) - 1 do begin
      LSegment.CountRow := LRow;
      if (LOnlyChangedRows = false)
          or LSegment.Grid.Row[rtData, LRow].HasChanged then begin
        if (LSelCol = -1)
            or BafIsYesChar(LSegment.Grid.Cells[rtData, LSelCol, LRow].Text) then begin
          if BafIsYesChar(FExecInter.ReplaceFunctions(LCountCondition)) then begin
            inc(LCount);
            if LCol > -1 then
              lokCalc(LSegment.Grid.Cells[rtData, LCol, LRow].Text);
          end;
        end;
      end; // if (LOnlyChangedRows
    end;
  end; // lokDoGrid

  procedure lokDoMap;
  var
    LRow: integer;
  begin
    for LRow := 0 to LSegment.Map.DataRowCount - 1 do begin
      LSegment.CountRow := LRow;
      if (LSelCol = -1)
          or BafIsYesChar(LSegment.Map.Cells[rtData, LSelCol, LRow].Text) then begin
        if BafIsYesChar(FExecInter.ReplaceFunctions(LCountCondition)) then begin
          inc(LCount);
          if LCol > -1 then
            lokCalc(LSegment.Map.Cells[rtData, LCol, LRow].Text);
        end;
      end;
    end;
  end; // lokDoMap

begin
  LCount := -1;
  if FindSegment('#grd_calc', LSegment) then begin
    if LSegment.SegmentType in [stGrid, stXGrid, stXXGrid, stMap] then begin
      lokInit;
      if LSegment.CountRow = -1 then begin
        try
          case LSegment.SegmentType of
            stMap: lokDoMap;
            else
              lokDoGrid;
          end; // case LSegment.SegmentType
        finally
          LSegment.CountRow := -1;
        end;
      end;
    end;
  end;
  lokResult;
// procedure TBafFrmModule.SegGridCalc
end;

procedure TBafFrmModule.SegGridData;
var
  LSql, LName, LBafConName: string;
  LDataSet: TDataSet;
  ix, LMaxRow: integer;
  LInsertOneRow: boolean;

  procedure lokText;
  var
    LNum, LRow, LCol: integer;
    LText: TStringList;
    LColumn: TBafSgColumn;
    LColumns: TBafSgColumns;
    LCell: TBafSgCell;
    LFieldName, LValue: string;
  begin
    LColumns := FLastSeg.Grid.Columns;
    LNum := FindParamIntegerReplaced('n', 1);
    LText := FInter.GetTextStringList(LNum);
    for LRow := 0 to LText.Count - 1 do begin
      for LCol := 0 to LColumns.Count - 1 do begin
        LColumn := LColumns.Items[LCol];
        LCell := FLastSeg.Grid.Cells[rtData, LCol, LRow];
        LFieldName := AnsiLowerCase(LColumn.CellFieldName);
        if LFieldName = 'text' then
          LValue := LText[LRow]
        else if LFieldName = 'key' then
          LValue := LText.KeyNames[LRow]
        else if LFieldName = 'value' then
          LValue := LText.ValueFromIndex[LRow]
        else
          LValue := '';
        LCell.Text := CellCheckFormat(LValue, LColumn.CellCommand);
      end;
    end;
  end; // procedure lokText

  procedure lokOpenTable;
  // benötigt t, pk, und k_id
  begin
    FGridDataRow := 0;
    if (FLastSeg.DataTable[0] = '') then
      FInter.DoLog('E', '#grddata, Parameter t empty')
    else begin
      LSql := Format('select * from %s', [FLastSeg.DataTable[0]]);
      SqlAndParams(FLastSeg.BafConName[0], LName, LSql);
      LDataSet := dataMain.QueryOpen(FLastSeg.BafConName[0], LName);
      if not LDataSet.Eof then begin
        while not LDataSet.Eof do begin
          GridCheckCols(LDataSet);
          FInter.DebugDbRow(LDataSet);
          FetchGridFields(LDataSet, FGridDataRow, -1, -1, true);
          if FGridDataRow >= LMaxRow then
            Break;
          LDataSet.Next;
        end;
      end
      else if LInsertOneRow then begin
        // if empty and ior=Y, then we insert one row
        FetchGridFields(LDataSet, FGridDataRow, -1, -1, false);
      end;
    end;
  end; // procedure lokOpenTable

  procedure lokSql(AAdd: boolean);
  begin
    if AAdd then
      FGridDataRow := FLastSeg.Grid.RowCount(rtData)
    else
      FGridDataRow := 0;
    LSql := Trim(FInter.GetSqlAndClear(1));
    if LSql <> '' then begin
      SqlAndParams(LBafConName, LName, LSql);
      LDataSet := dataMain.QueryOpen(LBafConName, LName);
      case FLastSeg.DataQuelle of
        dqSQL, dqSqlAdd: begin
          if not LDataSet.Eof then begin
            while not LDataSet.Eof do begin
              GridCheckCols(LDataSet);
              FInter.DebugDbRow(LDataSet);
              FetchGridFields(LDataSet, FGridDataRow, -1, -1, true);
              if FGridDataRow >= LMaxRow then
                Break;
              LDataSet.Next;
            end;
          end
          else if LInsertOneRow then begin
            // if empty and ior=Y, then we insert one row
            FetchGridFields(LDataSet, FGridDataRow, -1, -1, false);
          end;
        end;
        dqJoin: GridOpenJoin(LDataSet, FGridDataRow);
      end;
    end
    else
      FInter.DoLog('E', 'empty SQL statement');
  end; // procedure lokSql

  procedure lokSqlMerge;
  var
    LKeyList: TStringList;
    LKeyName, LKeyValue: string;
    LCol, LRow: integer;
  begin
    FGridDataRow := FLastSeg.Grid.RowCount(rtData);
    LKeyList := TStringList.Create;
    try
      // make key list
      LKeyName := FindParamStringReplacedLower('mk', '');
      if LKeyName <> '' then begin
        LCol := FLastSeg.Grid.Columns.FieldName2Ix(LKeyName);
        for LRow := 0 to FLastSeg.Grid.RowCount(rtData) - 1 do
          LKeyList.Add(FLastSeg.Grid.Cells[rtData, LCol, LRow].Text);
        LKeyList.Duplicates := dupIgnore;
        LKeyList.Sorted := true;

        // request data
        LSql := Trim(FInter.GetSqlAndClear(1));
        if LSql <> '' then begin
          SqlAndParams(LBafConName, LName, LSql);
          LDataSet := dataMain.QueryOpen(LBafConName, LName);
          while not LDataSet.Eof do begin
            LKeyValue := LDataSet.FieldByName(LKeyName).AsString;
            if LKeyList.IndexOf(LKeyValue) < 0 then begin
              FInter.DebugDbRow(LDataSet);
              FetchGridFields(LDataSet, FGridDataRow, -1, -1, true);
            end;
            if FGridDataRow >= LMaxRow then
              Break;
            LDataSet.Next;
          end;
        end
        else
          FInter.DoLog('E', 'empty SQL statement');
      end
      else
        FInter.DoLog('E', 'missing mk parameter');
    finally
      LKeyList.Free;
    end;
  end; //  procedure lokSqlMerge

  procedure lokThread;
  var
    LThread: TBafLoadSegmentThread;
  begin
    FGridDataRow := 0;
    LSql := Trim(FInter.GetSqlAndClear(1));
    if LSql <> '' then begin
      LThread := TBafLoadSegmentThread.Create(true);
      LThread.ThreadType := ttSegGrd;
      LThread.Segment := FLastSeg;
      LThread.Sql := LSql;
      LThread.BafConName := FLastSeg.BafConName[0];
      LThread.FreeOnTerminate := true;
      LThread.OnTerminate := LoadSegmentThreadTerminate;
      LThread.MaxRow := LMaxRow;
      LThread.Inter := FExecInter;
      LThread.ReadyCaption := FindParamStringReplaced('c',
          'D1BB0CD6-48B4-47DA-8371-F51522EB8438');
      LThread.ReadyCommand := FindParamString('rc', '');
      LThread.Init;
      FThreadList.Add(LThread);
      LThread.Suspended := false;
    end
    else
      FInter.DoLog('E', 'empty SQL statement');
  end; // procedure lokThread

  procedure lokXmlThread;
  var
    LThread: TBafLoadSegmentThread;
  begin
    FGridDataRow := 0;
    LThread := TBafLoadSegmentThread.Create(true);
    LThread.ThreadType := ttSegGrdXml;
    LThread.Segment := FLastSeg;
    LThread.LineP := FExecInter.LineP;
    LThread.Sql := FindParamStringReplaced('request', '');
    LThread.BafConName := FLastSeg.BafConName[0];
    LThread.FreeOnTerminate := true;
    LThread.OnTerminate := LoadSegmentThreadTerminate;
    LThread.MaxRow := LMaxRow;
    LThread.Inter := FExecInter;
    LThread.ReadyCaption := FindParamStringReplaced('c',
        'D1BB0CD6-48B4-47DA-8371-F51522EB8438');
    LThread.Init;
    FThreadList.Add(LThread);
    LThread.Suspended := false;
  end; // procedure lokXmlThread

  procedure lokTableNames;
  var
    i: integer;
    s, LTableName, LConnection: string;
  begin
    for i := 0 to 9 do begin
      s := IfThen(i = 0, '', IntToStr(i));
      LTableName := FindParamStringReplaced('t' + s, '');
      if LTableName <> '' then begin
        FLastSeg.DataTable[i] := LTableName;
        FLastSeg.DataKey[i] := GetPrimaryKey(FExecInter.LineP, s);
      end;
      LConnection := FindParamStringReplaced('db' + s, '');
      if LConnection <> '' then
        FLastSeg.BafConName[i] := LConnection;
    end;
  end; // procedure lokTableNames;

begin
  FLastSeg.IsDataLoading := true;
  try
    LMaxRow := FindParamIntegerReplaced('m', MaxInt);
    LInsertOneRow := FindParamBooleanReplaced('ior', false);
    FLastSeg.DataQuelle := FExecInter.FindParamQuelle(FExecInter.LineP, dqTable, ix);
    LBafConName := FindParamStringReplacedLower('db', DC_DEFAULT);
    LName := FInter.Name + '~' + LBafConName;
    if not (FLastSeg.DataQuelle in [dqSqlAdd, dqSqlMerge]) then begin
      FLastSeg.DataLineP := FExecInter.LineP;
      FLastSeg.BafConName[0] := LBafConName;
    end;
    lokTableNames;
    FLastSeg.Grid.GridCache := FindParamStringReplaced('gc', '');
    if gvBafDataCache.LoadGridCache(FLastSeg) then begin
      case FLastSeg.DataQuelle of
        dqSQL, dqJoin: LSql := FInter.GetSqlAndClear(1);      // to clear the statement
      end;
    end
    else begin
      case FLastSeg.DataQuelle of
        dqTable: lokOpenTable;
        dqSQL, dqJoin: lokSql(false);
        dqSqlAdd: lokSql(true);
        dqSqlMerge: lokSqlMerge;
        dqThread: lokThread;
        dqXML: begin
          FGridDataRow := 0;
          (FInter.GetModule('xml') as TBafXmlModule).
            XmlLoopData(FLastSeg, FExecInter, FExecInter.LineP);
        end;
        dqXMLThread: lokXmlThread;
        dqJSON: begin
          FGridDataRow := 0;
          (FInter.GetModule('json') as TBafJsonModule).
            JsonLoopData(FLastSeg, FTree, FExecInter, FExecInter.LineP);
        end;
        dqText: lokText;
      end;
    end;
  finally
    FLastSeg.IsDataLoading := false;
  end;
  FLastSeg.Grid.Columns.GridHeaderLineBreak;
  if FindParamBooleanReplaced('ocd', false) and (FGridDataRow > 0) then    // OpenCatOnData
    FLastCat.Opened := true;
  FGridDataRow := -1;
// procedure TBafFrmModule.SegGridData
end;

procedure TBafFrmModule.SegGridDub;
var
  LSegment: TBafPageSegment;
  LGrid: TBafSimpleGrid;
  LList: TStringList;
  LOnlyChangedRows, LResult: boolean;
  LSelCol, LColCount: integer;
  LCheckCondition: string;
  LCols: array[1..9] of integer;

  procedure lokDoGrid;
  var
    i, LRow: integer;
    s: string;
  begin
    for LRow := 0 to LSegment.Grid.RowCount(rtData) - 1 do begin
      LSegment.CountRow := LRow;
      if (LOnlyChangedRows = false)
          or LSegment.Grid.Row[rtData, LRow].HasChanged then begin
        if (LSelCol = -1)
            or BafIsYesChar(LSegment.Grid.Cells[rtData, LSelCol, LRow].Text) then begin
          if BafIsYesChar(FExecInter.ReplaceFunctions(LCheckCondition)) then begin
            s := '';
            for i := 1 to LColCount do
              s := s + LGrid.Cells[rtData, LCols[i], LRow].Text + '~@~';
            try
              LList.Add(s);
            except
              LResult := true;
              for i := 1 to LColCount do
                FExecInter.SetVarOrValue('d' + IntToStr(i), LGrid.Cells[rtData, LCols[i], LRow].Text);
              exit;
            end;
          end;
        end;
      end; // if (LOnlyChangedRows
    end;
  end; // lokDoGrid

  procedure lokInit;
  var
    i: integer;
    s: string;
  begin
    LResult := false;
    LOnlyChangedRows := FindParamBooleanReplaced('ocr', false);
    LSelCol := FindParamIntegerReplaced('sc', -1);
    LCheckCondition := FindParamString('ccnd', BAFYESCHAR);
    LColCount := FindParamIntegerReplaced('cnt', 0);
    for i := 1 to LColCount do begin
      s := IntToStr(i);
      LCols[i] := FindParamIntegerReplaced('col' + s, -1);
      if LCols[i] = -1 then
        LCols[i] := LGrid.Columns.FieldName2Ix(FindParamStringReplaced('f' + s, ''));
    end;
  end; // lokInit;

begin
  if FindParamBooleanReplaced('cnd', true) then begin
    if FindSegment('#grd_dub', LSegment) then begin
      if LSegment.SegmentType in [stGrid, stXGrid, stXXGrid] then begin
        LGrid := LSegment.Grid;
        LList := TStringList.Create;
        try
          LList.Sorted := true;
          LList.Duplicates := dupError;
          lokInit;
          if LSegment.CountRow = -1 then begin
            try
              lokDoGrid;
            finally
              LSegment.CountRow := -1;
            end;
          end;
          FExecInter.SetVarOrValue('n', IfThen(LResult, BAFYESCHAR, BAFNOCHAR));
        finally
          LList.Free;
        end;
      end;
    end;
  end;
// procedure TBafFrmModule.SegGridDub
end;

procedure TBafFrmModule.SegGridLoop;
var
  LSegment: TBafPageSegment;
  LGrid: TBafSimpleGrid;
  LRow, LSelCol: integer;
  LBafConName, LEachRow, LKomma, LKommaName: string;
  LEachRowTrans, LNoException, LApplProc: boolean;
  LType: TBafGridRowType;
begin
  if FindSegment('#grd_loop', LSegment) then begin
    if LSegment.SegmentType in [stGrid, stXGrid, stXXGrid] then begin
      LGrid := LSegment.Grid;
      LApplProc := FindParamBooleanReplaced('ap', false);
      LEachRow := FindParamStringReplaced('er', '');
      LEachRowTrans := FindParamBooleanReplaced('ert', false);
      LNoException := FindParamBooleanReplaced('nex', false);
      LBafConName := FindParamStringReplacedLower('db', DC_DEFAULT);
      LSelCol := FindParamIntegerReplaced('sc', -1);
      LKommaName := FindParamStringReplaced('nkomma', '');
      if FindParamBooleanReplaced('ov', false) then
        LType := rtDisplayData
      else
        LType := rtData;
      if LGrid.LoopRow = -1 then begin
        try
          for LRow := 0 to LGrid.RowCount(LType) - 1 do begin
            LKomma := IfThen(LRow = LGrid.RowCount(LType) - 1, '', ',');
            FInter.Variable[LKommaName] := LKomma;
            LGrid.LoopRow := LRow;
            if (LSelCol = -1)
                or BafIsYesChar(LGrid.Cells[LType, LSelCol, LRow].Text) then
              FExecInter.EachRow(LBafConName, LEachRow, '#grd_loop',
                  LEachRowTrans, LNoException);
            if FInter.ProgressAborted then begin
              FInter.DoLog('I', '#grd_loop, loop by user aborted');
              Break;
            end;
            if LApplProc then
              Application.ProcessMessages;
          end;
        finally
          LGrid.LoopRow := -1;
        end;
      end;
    end;
  end;
// procedure TBafFrmModule.SegGridLoop
end;

procedure TBafFrmModule.SegGridPaste;
type
  TPasteType = (ptDirect, ptSearchRow, ptSearchCol);
var
  LSegment: TBafPageSegment;
  LZeile, LSep, s, LFxRow, LFirstRow, LRowPre, LRowPost: string;
  sl, slLine: TStringList;
  LCol, LRow, p, LFrow, LFxCol, LStart: integer;
  LAdd, LIgnoreEmpty, LLat, LTrim: boolean;
  LPasteType: TPasteType;

  procedure lokParams;
  begin
    LSep := FindParamStringReplaced('sep', #9);
    LAdd := FindParamBooleanReplaced('add', false);
    LLat := FindParamBooleanReplaced('lat', false);
    LFirstRow := Trim(FindParamStringReplaced('fr', ''));
    LIgnoreEmpty := FindParamBooleanReplaced('ige', false);
    s := FindParamStringLower('y', 'd');
    case s[1] of
      'd': LPasteType := ptDirect;
      'r': LPasteType := ptSearchRow;
      'c': LPasteType := ptSearchCol;
    end;
    LTrim := FindParamBooleanReplaced('trim', true);
    LRowPre := FindParamStringReplaced('frowpre', '');
    LRowPost := FindParamStringReplaced('frowpost', '');
  end; // procedure lokParams

  procedure lokPaste;
  // Insert Values from Clipboard
  begin
    LCol := 0;
    p := Pos(LSep, LZeile);
    while p > 0 do begin
      s := copy(LZeile, 1, p - 1);
      if LTrim then
        s := Trim(s);
      Delete(LZeile, 1, p);
      while (LSegment.Grid.Columns.Items[LCol].CellType in [ctLink, ctButton]) do begin
        inc(LCol);
        if LCol >= LSegment.Grid.Columns.Count then
          exit;
      end;
      if not LSegment.Grid.Columns.Items[LCol].CellReadOnly then
        LSegment.Grid.DataCells[LCol, LRow].SetTextChange(s, true, true, LLat);
      inc(LCol);
      p := Pos(LSep, LZeile);
    end;
  end; // procedure lokPaste

  procedure lokDisassembleLine;
  begin
    slLine.Clear;
    LZeile := LZeile + LSep;
    p := Pos(LSep, LZeile);
    while p > 0 do begin
      s := copy(LZeile, 1, p - 1);
      Delete(LZeile, 1, p);
      slLine.Add(s);
      p := Pos(LSep, LZeile);
    end;
  end; // procedure lokDisassembleLine

  procedure lokAllColumns;
  // goes through all columns of the grid, looking for data definitions
  var
    LCol, LIx: integer;
    LCell: TBafSgCell;
  begin
    for LCol := 0 to LSegment.Grid.Columns.Count - 1 do begin
      s := FindParamString('f_' + LSegment.Grid.Columns.Items[LCol].CellFieldName, '');
      if AnsiCompareText(copy(s, 1, 3), 'row') = 0 then begin
        s := copy(s, 4, MaxInt);
        LIx := StrToIntDef(s, -1);
        if (LIx >= 0) and (LIx < slLine.Count) then
          s := slLine[LIx];
      end
      else
        s := FInter.ReplaceFunctions(s);
      if Ltrim then
        s := Trim(s);
      LCell := LSegment.Grid.DataCells[LCol, LRow];
      if (LCell.CellType = ctBoolInv) and (s <> '') then
        s := IfThen(BafIsYesChar(s), 'N', 'Y');
      if s <> '' then
        LCell.SetTextChange(s, true, true, LLat);
      ColumnNullValue(LCell);
      if LPasteType = ptSearchCol then
        LSegment.Grid.DataRow[LRow].RowInserted := true;
    end; // for LCol
  end; // procedure lokAllColumns

  procedure lokSearchPaste;
  var
    LRow2, LCol2: integer;
    LFound: boolean;
    LSgRow: TBafSgRow;
    LCell: TBafSgCell;
  begin
    lokDisassembleLine;
    LFound := false;
    for LRow2 := 0 to LSegment.Grid.RowCount(rtData) - 1 do begin
      LRow := LRow2;
      if AnsiCompareText(LSegment.Grid.DataCells[LFxCol, LRow].Text,
          LRowPre + slLine[LFrow] + LRowPost) = 0 then begin
        // we've found the row, now inserting
        LFound := true;
        lokAllColumns;
        Break;
      end; // if AnsiCompareText
    end; // for LRow
    if not LFound and LAdd then begin
      LSgRow := LSegment.Grid.InsertRow(MaxInt);
      LRow := LSgRow.RowIndex;
      lokAllColumns;
    end;
  end; // procedure lokSearchPaste

  procedure lokCleanup;
  // Ergänzt die Werte für f_-Definitionen
  var
    LCol: integer;
  begin
    for LCol := 0 to LSegment.Grid.Columns.Count - 1 do begin
      if LSegment.Grid.DataCells[LCol, LRow].Text = '' then begin
        s := FindParamString('f_' + LSegment.Grid.Columns.Items[LCol].CellFieldName, '');
        if s <> '' then begin
          s := FInter.ReplaceFunctions(s);
          LSegment.Grid.DataCells[LCol, LRow].SetTextChange(s);
          LSegment.Grid.DataRow[LRow].RowInserted := true;
        end;
      end;
    end;
  end; // procedure lokCleanup

  procedure lokPrepareSearchRow;
  // we have something to prepare
  var
    i: integer;
  begin
    LFxRow := FindParamStringLower('fxrow', '');
    if LFxRow = '' then
      FInter.DoLog('E', '#grd_paste - fxrow has to be set');
    LFrow := FindParamInteger('frow', 0);
    LFxCol := -1;
    for i := 0 to LSegment.Grid.Columns.Count - 1 do begin
      if LSegment.Grid.Columns.Items[i].CellFieldName = LFxRow then begin
        LFxCol := i;
        Break;
      end;
    end;
    if LFxCol = -1 then
      FInter.DoLog('E', '#grd_paste - fxrow was not found in the grid');
  end; // procedure lokPrepareSearchRow

  procedure lokLoop;
  var
    i: integer;
  begin
    for i := LStart to sl.Count - 1 do begin
      LZeile := sl[i] + LSep;
      if (Trim(LZeile) <> '') or not LIgnoreEmpty then begin
        case LPasteType of
          ptDirect: begin
            lokPaste;
            lokCleanup;
          end;
          ptSearchRow: lokSearchPaste;
          ptSearchCol: begin
            lokDisassembleLine;
            lokAllColumns;
          end;
        end;
        inc(LRow);
      end; // if (Trim(LZeile) <> '')
    end;
  end; // procedure lokLoop

begin
  if FindParamBooleanReplaced('cnd', true) then begin
    if FindSegment('#grd_paste', LSegment) then begin
      if LSegment.SegmentType in [stGrid, stXGrid, stXXGrid] then begin
        lokParams;
        if LPasteType = ptSearchRow then
          lokPrepareSearchRow;
        LRow := IfThen(LAdd, LSegment.Grid.RowCount(rtData), 0);
        sl := TStringList.Create;
        slLine := TStringList.Create;
        try
          sl.Text := Clipboard.AsText;
          LStart := IfThen(LFirstRow = '', 0, 1);
          if FindParamBooleanReplaced('hhr', false) then
            inc(LStart);
          if (LFirstRow <> '') then begin
            if LFirstRow <> Trim(sl[0]) then
              FInter.DoLog('E', 'The value of the first data row has to be: ' + LFirstRow)
            else
              lokLoop;
          end
          else
            lokLoop;
        finally
          slLine.Free;
          sl.Free;
        end;
        LSegment.GridCalcHeight;  // rows could be added
        LSegment.Grid.Repaint;
      end
      else
        FInter.DoLog('E', '#grd_paste - Segment not found');
    end; // if FindSegment
  end;
// procedure TBafFrmModule.SegGridPaste
end;

procedure TBafFrmModule.SegGridPos;
// Finds the Position as a row num, by the given column values
const
  NODATA = 'CE4BAB6A-71C7-4D1D-B1B1-FF6FFFA91CE5';

var
  i, LRow, LNumCols: integer;
  LColumnIx: array[1..9] of integer;
  LColumnVal: array[1..9] of string;
  LSegment: TBafPageSegment;
  LColumn: TBafSgColumn;
  LFieldName, LValue, LValVar: string;
  LResult: boolean;

  procedure lokFindColumns;
  var
    LCol: integer;
  begin
    LNumCols := 0;
    for LCol := 0 to LSegment.Grid.Columns.Count - 1 do begin
      LColumn := LSegment.Grid.Columns.Items[LCol];
      LFieldName := LColumn.CellFieldName;
      if (LFieldName <> '') then begin
        LValue := FindParamStringReplaced('f_' + LFieldName, NODATA);
        if LValue <> NODATA then begin
          inc(LNumCols);
          if LNumCols > 9 then
            FInter.DoLog('E', '#grd_pos - more then 9 fields');
          LColumnIx[LNumCols] := LCol;
          LColumnVal[LNumCols] := LValue;
        end;
      end;
    end;
  end; // procedure lokFindColumns

begin
  if FindSegment('#grd_pos', LSegment) then begin
    lokFindColumns;
    for LRow := 0 to LSegment.Grid.RowCount(rtData) - 1 do begin
      LResult := true;
      for i := 1 to LNumCols do begin
        if AnsiCompareText(LSegment.Grid.Cells[rtData, LColumnIx[i], LRow].Text,
            LColumnVal[i])  <> 0 then begin
          LResult := false;
          Break;
        end;
      end;
      if LResult then
        Break;
    end;
    FExecInter.SetVarOrValue('res', IfThen(LResult, BAFYESCHAR, BAFNOCHAR));
    FExecInter.SetVarOrValue('row', IntToStr(LRow));
  end
  else
    FInter.DoLog('E', '#grd_pos - Segment not found');
// procedure TBafFrmModule.SegGridPos
end;

procedure TBafFrmModule.SegGridSort;
var
  ix: integer;
  LSegment: TBafPageSegment;
  LFieldName, LDirection: string;
begin
  if FindSegment('#grd_sort', LSegment) then begin
    LSegment.Grid.Columns.ClearSort;
    LFieldName := FindParamStringReplacedLower('f', '');
    LDirection := FindParamStringReplacedLower('y', 'u');
    if LFieldName <> '' then begin
      ix := LSegment.Grid.Columns.FieldName2Ix(LFieldName);
      if (ix > 0) and (copy(LDirection, 1, 1) = 'u') then
        LSegment.Grid.Columns.AddSortStack(ix, sdUp)
      else if (ix > 0) then
        LSegment.Grid.Columns.AddSortStack(ix, sdDown);
    end;
  end
  else
    FInter.DoLog('E', '#grd_sort - Segment not found');
// procedure TBafFrmModule.SegGridSort
end;

procedure TBafFrmModule.PageValue;
// writes data in the page
var
  LSegment: TBafPageSegment;
  LGrid: TBafSimpleGrid;
  LMap: TBafMap;
  LSegName, LValue, LFieldName, LTyp, s: string;
  LIfEmpty, LChange, LSetValue, LSetSelection, LIgnoreNextRow: boolean;

  procedure lokCell(ACol, ARow: integer);
  begin
    if not LIfEmpty or (LGrid.Cells[rtData, ACol, ARow].Text = '') then begin
      if LSetValue then begin
        if LChange then
          LGrid.Cells[rtData, ACol, ARow].SetTextChange(LValue)
        else
          LGrid.Cells[rtData, ACol, ARow].Text := LValue;
      end;
      if LSetSelection then begin
        LGrid.SelectedCol := ACol;
        LGrid.SelectedRow := ARow;
        if LGrid.CanFocus then
         LGrid.SetFocus;
        LGrid.IgnoreNextRow := LIgnoreNextRow;
      end;
    end;
  end;

  procedure lokGridCol(ACol, ASc: integer);
  var
    LRow: integer;
    LRowName: string;
  begin
    LRowName := FindParamStringLower('row', '');
    LRow := FindParamIntegerReplaced('row', -1);
    if (ACol > -1) and (LRow > -1) then
      lokCell(ACol, LRow)
    else if LRowName = 'sel' then
      lokCell(ACol, LGrid.SelectedRow)
    else if LRowName = 'looprow' then
      lokCell(ACol, LGrid.LoopRow)
    else if LRowName = 'checkrow' then
      lokCell(ACol, LGrid.CheckRow)
    else if (LRowName = 'calcrow') then
      lokCell(ACol, LGrid.Parents.Segment.CountRow)
    else if LRowName = 'all' then begin
      for LRow := 0 to LGrid.RowCount(rtData) - 1 do
        lokCell(ACol, LRow)
    end
    else if (LRowName = 'allsel') and (ASc >= 0) then begin
      for LRow := 0 to LGrid.RowCount(rtData) - 1 do begin
        if BafIsYesChar(LGrid.Cells[rtData, ASc, LRow].Text) then
          lokCell(ACol, LRow)
      end;
    end;
  end; // procedure lokGridCol

  procedure lokGrid;
  var
    LCol, LRow, i, LSc: integer;
    s, LRowName: string;
  begin
    LGrid := LSegment.Grid;
    LCol := FindParamIntegerReplaced('col', -1);
    LSc := LGrid.SelectionColumn;
    if LCol = -1 then begin
      s := FindParamStringReplaced('f', '');
      for i := 0 to LGrid.Columns.Count - 1 do begin
        if AnsiCompareText(s, LGrid.Columns.Items[i].CellFieldName) = 0 then begin
          lokGridCol(i, LSc);
          break;
        end;
      end;
    end
    else
      lokGridCol(LCol, LSc);
    LGrid.Repaint;
  end; // procedure lokGrid

  procedure lokMap;
  var
    LCol, LRow, i, LSc: integer;
    s, LRowName, LOldValue: string;
  begin
    LMap := LSegment.Map;
    LCol := FindParamIntegerReplaced('col', -1);
    LSc := 0;
    if LCol = -1 then begin
      s := FindParamStringReplaced('f', '');
      for i := 0 to LMap.Columns.Count - 1 do begin
        if AnsiCompareText(s, LMap.Columns.Items[i].CellFieldName) = 0 then begin
          LCol := i;
          break;
        end;
      end;
    end;
    LRowName := FindParamStringLower('row', '');
    LRow := FindParamIntegerReplaced('row', -1);
    if LRow > -1 then
    else if LRowName = 'click' then
      LRow := LMap.ClickRow;
    if (LCol > -1) and (LRow > -1) then begin
      LOldValue := LMap.Cells[rtData, LCol, LRow].Text;
      if (LOldValue = '') or not LIfEmpty then begin
        if LChange then
          LMap.Cells[rtData, LCol, LRow].SetTextChange(LValue)
        else
          LMap.Cells[rtData, LCol, LRow].Text := LValue;
      end;
    end;
    LMap.Repaint;
  end; // procedure lokMap

  procedure lokAllColGrid;
  var
    LCol, LRow, i, LSc: integer;
    s, LRowName: string;
  begin
    LSetValue := true;
    LGrid := LSegment.Grid;
    LCol := FindParamIntegerReplaced('col', -1);
    LSc := LGrid.SelectionColumn;
    if LCol = -1 then begin
      s := FindParamStringReplaced('f', '');
      for i := 0 to LGrid.Columns.Count - 1 do begin
        if AnsiCompareText(s, LGrid.Columns.Items[i].CellFieldName) = 0 then
          lokGridCol(i, LSc);
      end;
    end;
    LGrid.Repaint;
  end; // procedure lokAllColGrid

  procedure lokVL;
  var
    LCol, LRow: integer;
    LCell: TBafSgCell;
  begin
    LGrid := LSegment.Grid;
    LCol := FindParamIntegerReplaced('col', -1);
    LRow := FindParamIntegerReplaced('row', -1);
    if (LCol > -1) and (LRow > -1) then
      lokCell(LCol, LRow)
    else begin
      LFieldName := FindParamStringReplaced('f', '');
      for LRow := 0 to LSegment.Grid.RowCount(rtData) - 1 do begin
        for LCol := 0 to LSegment.Grid.Columns.Count - 1 do begin
          LCell := LSegment.Grid.Cells[rtData, LCol, LRow];
          if AnsiCompareText(LFieldName, LCell.DataFieldName) = 0 then begin
            lokCell(LCol, LRow);
            exit;
          end;
        end;
      end;
    end;
    LSegment.Grid.Repaint;
  end; // procedure lokVL

begin
  if FindParamBooleanReplaced('cnd', true) then begin
    LSegName := FindParamStringReplaced('i', '');
    LValue := FindParamStringReplaced('z', '');
    LIfEmpty := FindParamBooleanReplaced('ie', false);
    LChange := FindParamBooleanReplaced('chg', true);
    LTyp := FindParamStringReplacedLower('y', 'val');
    LIgnoreNextRow := FindParamBooleanReplaced('inr', false);
    LSetValue := (Pos('val', LTyp) > 0);
    LSetSelection := (Pos('sel', LTyp) > 0);
    LSegment := FPage.PrimaryDivs.FindSegmentByName(LSegName);
    if LSegment = nil then
      exit;
    case LSegment.SegmentType of
      stGrid, stXGrid, stXXGrid: if Pos('allcol', LTyp) > 0 then
          lokAllColGrid
        else
          lokGrid;
      stValueList: lokVL;
      stMemo, stText: if not LIfEmpty or (LSegment.Lines.Text = '') then begin
        if LSetValue then
          LSegment.Lines.Text := LValue;
      end;
      stPic: LSegment.Pic.PictureFileName := LValue;
      stMap: lokMap;
    end;
    s := FindParamStringReplaced('c', '3D847349-7B87-46F8-8616-B70938453A88');
    if s <> '3D847349-7B87-46F8-8616-B70938453A88' then
      LSegment.Header := s;
    if FindParamBooleanReplaced('sr', false) then begin
      case LSegment.SegmentType of
        stGrid, stXGrid, stXXGrid, stValueList: LSegment.Grid.Repaint;
        stDia: LSegment.Dia.Repaint;
        stMap: LSegment.Map.Repaint;
        stPic: LSegment.Pic.Repaint;
      end;
    end;
  end;
// procedure TBafFrmModule.PageValue
end;

procedure TBafFrmModule.PageXls;
begin
  FInter.ExportPageXls(FPage);
end;

procedure TBafFrmModule.SegMap;
begin
  AddSegment(FExecInter.LineP, uBafTypes.stMap);
  FLastSeg.Map.ScrollVertVisible := FindParamBooleanReplaced('sv', true);
  FLastSeg.Map.ScrollHorzVisible := FindParamBooleanReplaced('sh', true);
  FLastSeg.Map.PictureFileName := FindParamStringReplaced('fn', '');
  FLastSeg.Map.FontSizeScale := FindParamSingleReplaced('fss', 1);
  FLastSeg.Map.MonthCalendarSize := FindParamSingleReplaced('mcs', 1);
  FLastSeg.Map.MapTypeName := FindParamStringReplacedLower('y', 'check');
  FLastSeg.Map.Definition := FindParamStringReplaced('d', '');
  FLastSeg.Map.OnSaveRect := GridSaveRect;
  FLastSeg.HeightOpened := FindParamSingleReplaced('ho', 200);
  FLastSeg.HeightClosed := FindParamSingleReplaced('hc', 0);
end;

procedure TBafFrmModule.SegMapData;
var
  s, LSql, LFieldName, LJoinKeyField, LJoinKeyValue, LName: string;
  LDataSet: TDataSet;
  ix, LX, LY, Lix: integer;
  LIndex2Color: boolean;
  LMapRow: TBafSgRow;
  LField: TField;

  function lokFindInData(AFieldName, ADefault: string): string;
  begin
    LField := LDataSet.FindField(AFieldName);
    if Assigned(LField) then
      result := LField.AsString
    else
      result := LFieldName;
  end; // function lokFindInData

  procedure lokMapStandardFields;
  begin
    LMapRow := FLastSeg.Map.DataRow[FGridDataRow];
    LMapRow.Caption := lokFindInData(FLastSeg.Map.Field_Caption, '');
    LMapRow.Hint := lokFindInData(FLastSeg.Map.Field_Hint, '');
    LX := StrToIntDef(lokFindInData(FLastSeg.Map.Field_PosX, ''), 0);
    LY := StrToIntDef(lokFindInData(FLastSeg.Map.Field_PosY, ''), 0);
    LMapRow.Pos := Point(LX, LY);
    LMapRow.CanCheck := BafIsYesChar(lokFindInData(FLastSeg.Map.Field_CanCheck, ''));
    LMapRow.IsChecked := BafIsYesChar(lokFindInData(FLastSeg.Map.Field_IsChecked, ''));
    LMapRow.FontSize := StrToIntDef(lokFindInData(FLastSeg.Map.Field_FontSize, ''), 10);
    LMapRow.FontBold := BafIsYesChar(lokFindInData(FLastSeg.Map.Field_FontBold, ''));
    if LIndex2Color then begin
      LIx := StrToIntDef(lokFindInData(FLastSeg.Map.Field_FontColor, ''), -1);
      if Lix >= 0 then
        LMapRow.FontColor := BafIndex2Color(Lix)
      else
        LMapRow.FontColor := TAlphaColorRec.Black;
    end
    else
      LMapRow.FontColor := FindParamColor('cl', TColorRec.Black);
    s := AnsiLowerCase(lokFindInData(FLastSeg.Map.Field_CaptionPos, '')) + ' ';
    case s[1] of
      'l': LMapRow.CaptionPos := TBafDiaLegendPos.lpLeft;
      'r': LMapRow.CaptionPos := TBafDiaLegendPos.lpRight;
      't', 'o': LMapRow.CaptionPos := TBafDiaLegendPos.lpTop;
      'b', 'u': LMapRow.CaptionPos := TBafDiaLegendPos.lpBottom;
    else
      LMapRow.CaptionPos := lpNone;
    end;
  end; // procedure lokMapStandardFields

  procedure lokOpenTable;
  // benötigt t, pk, und k_id
  begin
    if (FLastSeg.DataTable[0] = '') then
      FInter.DoLog('E', '#grddata, Parameter t empty')
    else begin
      LSql := Format('select * from %s', [FLastSeg.DataTable[0]]);
      SqlAndParams(FLastSeg.BafConName[0], LName, LSql);
      LDataSet := dataMain.QueryOpen(FLastSeg.BafConName[0], LName);
      while not LDataSet.Eof do begin
        FInter.DebugDbRow(LDataSet);
        FetchGridFields(LDataSet, FGridDataRow, -1, -1, true);
        lokMapStandardFields;
        LDataSet.Next;
      end;
    end;
  end; // procedure lokOpenTable

  procedure lokSql;

    procedure lokJoin;
    begin
      LJoinKeyField := FindParamStringReplaced('jk', '');
      LJoinKeyValue := '';
      FGridDataRow := -1;
      while not LDataSet.Eof do begin
        FInter.DebugDbRow(LDataSet);
        if LDataSet.FieldByName(LJoinKeyField).AsString <> LJoinKeyValue then begin
          inc(FGridDataRow);
          lokMapStandardFields;
          LJoinKeyValue := LDataSet.FieldByName(LJoinKeyField).AsString;
        end;
        if not LDataSet.FieldByName('cdate').IsNull then begin
          s := FormatDateTime('dd.mm.yyyy', LDataSet.FieldByName('cdate').AsDateTime);
          LMapRow.JoinList.Add(s);
          LMapRow.Hint := LMapRow.Hint + #13#10 + s;
        end;
        LDataSet.Next;
      end;
    end; // procedure lokJoin

  begin
    LSql := Trim(FInter.GetSqlAndClear(1));
    if LSql <> '' then begin
      SqlAndParams(FLastSeg.BafConName[0], LName, LSql);
      LDataSet := dataMain.QueryOpen(FLastSeg.BafConName[0], LName);
      case FLastSeg.DataQuelle of
        dqSQL: begin
          while not LDataSet.Eof do begin
            FInter.DebugDbRow(LDataSet);
            lokMapStandardFields;
            FetchGridFields(LDataSet, FGridDataRow, -1, -1, true);
            LDataSet.Next;
          end;
        end;
        dqJoin: lokJoin;
      end;
    end
    else
      FInter.DoLog('E', 'empty SQL statement');
  end; // procedure lokSql

  procedure lokTableNames;
  var
    i: integer;
    s, LTableName: string;
  begin
    for i := 0 to 9 do begin
      s := IfThen(i = 0, '', IntToStr(i));
      LTableName := FindParamStringReplaced('t' + s, '');
      if LTableName <> '' then begin
        FLastSeg.DataTable[i] := LTableName;
        FLastSeg.DataKey[i] := GetPrimaryKey(FExecInter.LineP, s);
      end;
    end;
  end; // procedure lokTableNames;

  procedure lokLoadDefinition;
  var
    LSql: string;
    LDataset: TDataset;
  begin
    LSql := 'select * from sys_map_rect where user_user_id = '
        + QuotedStr(FInter.NeedInfo('usrid', '')) + ' and cdefinition = '
        + QuotedStr(FLastSeg.Map.Definition);
    dataMain.QueryPrepare(dataMain.DefaultCon, 'SaveRect', LSql);
    LDataset := dataMain.QueryOpen(dataMain.DefaultCon, 'SaveRect');
    if not LDataset.Eof then begin
      FLastSeg.Map.SetDestRect(LDataset.FieldByName('cleft').AsFloat,
          LDataset.FieldByName('ctop').AsFloat,
          LDataset.FieldByName('cright').AsFloat,
          LDataset.FieldByName('cbottom').AsFloat,
          LDataset.FieldByName('ahorzval').AsFloat,
          LDataset.FieldByName('ahorzvp').AsFloat,
          LDataset.FieldByName('avertval').AsFloat,
          LDataset.FieldByName('avertvp').AsFloat,
          LDataset.FieldByName('stretch').AsFloat);
    end;
  end; //  procedure lokLoadDefinition

begin
  FLastSeg.IsDataLoading := true;
  try
    LIndex2Color := FindParamBooleanReplaced('i2c', false);
    FLastSeg.BafConName[0] := FindParamStringReplacedLower('db', DC_DEFAULT);
    LName := FInter.Name + '~' + FLastSeg.BafConName[0];
    FLastSeg.DataLineP := FExecInter.LineP;
    FLastSeg.DataQuelle := FExecInter.FindParamQuelle(FExecInter.LineP, dqSql, ix);
    FLastSeg.MapStartDate := StrToDate(FindParamStringReplaced('sdt', '01.01.2000'));
    lokTableNames;
    FGridDataRow := 0;
    case FLastSeg.DataQuelle of
      dqTable: lokOpenTable;
      dqSQL, dqJoin: lokSql;
    end;
  finally
    FLastSeg.IsDataLoading := false;
  end;
  if FLastSeg.Map.Definition <> '' then
    lokLoadDefinition;
  FLastSeg.HasChanged := false;
  FLastSeg.Map.MapRefresh;
// procedure TBafFrmModule.SegMapData
end;

procedure TBafFrmModule.SegMemo;
var
  ix: integer;

  procedure lokOpenData;
  var
    LItem, LRef, LId, LText: string;
  begin
    FLastSeg.BafConName[0] := DC_DEFAULT;
    FLastSeg.DataKeyValue := '';
    FLastSeg.Lines.Clear;
    LItem := FindParamStringReplaced('i', '');
    LRef := FindParamStringReplaced('k', 'none');
    FLastSeg.DataItem := LItem;
    FLastSeg.DataRef := LRef;
    if (LItem <> '') and (LRef <> '') then begin
      if dataMain.LoadDataMemo(LItem, LRef, LId, LText) then begin
        FLastSeg.DataKeyValue := LId;
        FLastSeg.Lines.Text := LText;
        FLastSeg.HasChanged := false;
        FPage.SetStatus;
      end;
    end;
  end; // procedure lokOpenData

  procedure lokOpenFile;
  begin
    FLastSeg.DataTable[0] := FindParamStringReplaced('fn', '');
    if FileExists(FLastSeg.DataTable[0]) then begin
      FLastSeg.Lines.LoadFromFile(FLastSeg.DataTable[0]);
      FLastSeg.HasChanged := false;
      FPage.SetStatus;
    end;
  end; // procedure lokOpenFile

  procedure lokOpenLink;
  var
    LItem, LFieldname: string;
    LSegment: TBafPageSegment;
    LCell: TBafSgCell;
    LColumn: TBafSgColumn;
    LCol, LRow: integer;
  begin
    LItem := FindParamStringLower('i', '');
    LFieldname := FindParamStringReplacedLower('f', '');
    LSegment := FPage.PrimaryDivs.FindSegmentByName(LItem);
    FLastSeg.BafConName[0] := LSegment.BafConName[0];
    case LSegment.SegmentType of
      stValueList: begin
        for LRow := 0 to LSegment.Grid.RowCount(rtData) do begin
          for LCol := 0 to LSegment.Grid.Columns.Count - 1 do begin
            LCell := LSegment.Grid.DataCells[LCol, LRow];
            if LCell.DataFieldName = LFieldname then begin
              FLastSeg.Lines.Text := LCell.Text;
              LCell.LinkedSegment := FLastSeg;
              FLastSeg.LinkedCell := LCell;
              FLastSeg.HasChanged := false;
              FPage.SetStatus;
              exit;
            end;
          end;
        end;
      end;
      stGrid: begin
        for LCol := 0 to LSegment.Grid.Columns.Count - 1 do begin
          LColumn := LSegment.Grid.Columns.Items[LCol];
          if LColumn.CellFieldName = LFieldname then begin
            LColumn.LinkedSegment := FLastSeg;
            LSegment.Grid.Columns.HasLinkedColumns := true;
          end;
        end;
      end;
    end;
  end; // procedure lokOpenLink

begin
  AddSegment(FExecInter.LineP, uBafTypes.stMemo);
  FLastSeg.Lines.Text := FindParamStringReplaced('z', '');
  FLastSeg.LinesRefresh;

  FLastSeg.LineP := FExecInter.LineP;
  FLastSeg.WordWrap := FindParamBooleanReplaced('ww', true);
  FLastSeg.DataQuelle := FExecInter.FindParamQuelle(FExecInter.LineP, dqData, ix);
  case FLastSeg.DataQuelle of
    dqData: lokOpenData;
    dqLink: lokOpenLink;
    dqFile: lokOpenFile;
  end;
// procedure TBafFrmModule.SegMemo
end;

procedure TBafFrmModule.SegPic;
var
  ix: integer;

  procedure lokOpenLink;
  var
    LItem, LFieldname: string;
    LSegment: TBafPageSegment;
    LCell: TBafSgCell;
    LColumn: TBafSgColumn;
    LCol, LRow: integer;
  begin
    LItem := FindParamStringLower('i', '');
    LFieldname := FindParamStringReplacedLower('f', '');
    LSegment := FPage.PrimaryDivs.FindSegmentByName(LItem);
    FLastSeg.BafConName[0] := LSegment.BafConName[0];
    case LSegment.SegmentType of
      stValueList: begin
        for LRow := 0 to LSegment.Grid.RowCount(rtData) do begin
          for LCol := 0 to LSegment.Grid.Columns.Count - 1 do begin
            LCell := LSegment.Grid.DataCells[LCol, LRow];
            if LCell.DataFieldName = LFieldname then begin
              LCell.LinkedSegment := FLastSeg;
               FLastSeg.Pic.PictureFileName := LCell.Text;
//              FLastSeg.LinkedCell := LCell;
//              FLastSeg.HasChanged := false;
//              FPage.SetStatus;
              exit;
            end;
          end;
        end;
      end;
      stGrid: begin
        for LCol := 0 to LSegment.Grid.Columns.Count - 1 do begin
          LColumn := LSegment.Grid.Columns.Items[LCol];
          if LColumn.CellFieldName = LFieldname then begin
            LColumn.LinkedSegment := FLastSeg;
            LSegment.Grid.Columns.HasLinkedColumns := true;
          end;
        end;
      end;
    end;
  end; // procedure lokOpenLink

  procedure lokLoadDefinition;
  var
    LSql: string;
    LDataset: TDataset;
  begin
    LSql := 'select * from sys_map_rect where user_user_id = '
        + QuotedStr(FInter.NeedInfo('usrid', '')) + ' and cdefinition = '
        + QuotedStr(FLastSeg.Pic.Definition);
    dataMain.QueryPrepare(dataMain.DefaultCon, 'SaveRect', LSql);
    LDataset := dataMain.QueryOpen(dataMain.DefaultCon, 'SaveRect');
    if not LDataset.Eof then begin
      FLastSeg.Pic.SetDestRect(LDataset.FieldByName('cleft').AsFloat,
          LDataset.FieldByName('ctop').AsFloat,
          LDataset.FieldByName('cright').AsFloat,
          LDataset.FieldByName('cbottom').AsFloat,
          LDataset.FieldByName('ahorzval').AsFloat,
          LDataset.FieldByName('ahorzvp').AsFloat,
          LDataset.FieldByName('avertval').AsFloat,
          LDataset.FieldByName('avertvp').AsFloat,
          LDataset.FieldByName('stretch').AsFloat);
    end;
  end; //  procedure lokLoadDefinition

begin
  AddSegment(FExecInter.LineP, uBafTypes.stPic);
  FLastSeg.Pic.ScrollVertVisible := FindParamBooleanReplaced('sv', true);
  FLastSeg.Pic.ScrollHorzVisible := FindParamBooleanReplaced('sh', true);
  FLastSeg.HeightOpened := FindParamSingleReplaced('ho', 200);
  FLastSeg.HeightClosed := FindParamSingleReplaced('hc', 0);
  FLastSeg.Pic.DataQuelle := FExecInter.FindParamQuelle(FExecInter.LineP, dqFile, ix);
  FLastSeg.Pic.Definition := FindParamStringReplaced('d', '');
  FLastSeg.Pic.OnSaveRect := GridSaveRect;
  case FLastSeg.Pic.DataQuelle of
    dqFile: FLastSeg.Pic.PictureFileName := FindParamStringReplaced('fn', '');
    dqLink: lokOpenLink;
    dqHttp: FLastSeg.Pic.LoadFromHttp(FindParamStringReplaced('url', ''),
        FindParamBooleanReplaced('isc', false), FInter);
  end;
  if FLastSeg.Pic.Definition <> '' then
    lokLoadDefinition;
// procedure TBafFrmModule.SegPic
end;

procedure TBafFrmModule.SegSGrid;
var
  LColCount, i, LColWidth: integer;
  s: string;
  LColumn: TBafSgColumn;
begin
  AddSegment(FExecInter.LineP, uBafTypes.stSGrid);
  FLastSeg.Grid.Columns.FixedColsCount := FindParamInteger('fcc', 0);
  FHeaderRowCount := FindParamIntegerReplaced('hrc', 1);
  FFooterRowCount := FindParamIntegerReplaced('frc', 0);
  FLastSeg.Grid.Columns.LineType := GetLineType;
  FLastSeg.Grid.WithoutHorizontalScrollBar := FindParamBooleanReplaced('whs', false);
  LColCount := FindParamIntegerReplaced('cc', 2);
  for i := 1 to LColCount do begin
    s := IntToStr(i);
    LColWidth := FindParamIntegerReplaced('w' + s, -1);
    if LColWidth >= 0 then begin
      LColumn := FLastSeg.Grid.Columns.Add;
      LColumn.Width := LColWidth;
      LColumn.Visible := (LColumn.Width > 0);
      LColumn.Stretch := FindParamIntegerReplaced('wst' + s, -1);
      LColumn.CellVisible := true;
    end;
  end;
end;

procedure TBafFrmModule.SegText;
begin
  AddSegment(FExecInter.LineP, uBafTypes.stText);
end;

procedure TBafFrmModule.SegVl;
var
  LColCount, i, LColWidth: integer;
  s: string;
  LColumn: TBafSgColumn;
begin
  AddSegment(FExecInter.LineP, uBafTypes.stValueList);
  FLastSeg.Grid.Columns.FixedColsCount := FindParamInteger('fcc', 0);
  FLastSeg.Grid.Columns.LineType := GetLineType;
  FLastSeg.Grid.WithoutHorizontalScrollBar := FindParamBooleanReplaced('whs', false);
  LColCount := FindParamIntegerReplaced('cc', 2);
  for i := 1 to LColCount do begin
    s := IntToStr(i);
    LColWidth := FindParamIntegerReplaced('w' + s, -1);
    if LColWidth >= 0 then begin
      LColumn := FLastSeg.Grid.Columns.Add;
      LColumn.Width := LColWidth;
      LColumn.Visible := (LColumn.Width > 0);
      LColumn.Stretch := FindParamIntegerReplaced('wst' + s, -1);
      LColumn.CellVisible := true;
    end;
  end;
end;

procedure TBafFrmModule.SegVlData;
var
  LSql, LName: string;
  LDataSet: TDataSet;
  LHasData: boolean;
  ix: integer;

  procedure lokFindAndSet(ACol, ARow: integer; ALineP: string);
  var
    s, LValue: string;
    LCell: TBafSgCell;
    LField: TField;
    LIni: TStringIniFile;

    procedure lokVlInsert;
    begin
      LCell.Inserted := true;
      FLastSeg.Grid.VlInsert := true;
      if Assigned(FTree.Selected) and (LCell.DataQIndex = 0) then begin
        LIni := FTree.Selected.Ini;
        if (FTree.Selected.GetTableName = FLastSeg.DataTable[0])
            and (LCell.DataFieldName = LIni.ReadString(SEC_ADD, 'k', ''))
            and (LIni.ReadString(SEC_DATA, LCell.DataFieldName, '') = '') then
          LIni.WriteString(SEC_DATA, LCell.DataFieldName, LCell.Text);
        FInter.DebugNodeIni(FTree.Selected.Ini);
      end;
    end; // procedure lokVlInsert

  begin
    LCell := FLastSeg.Grid.Cells[rtData, ACol, ARow];
    s := IntToStr(ACol + 1);
    if LHasData then begin
      if LCell.DataFieldName <> '' then begin
        LField := LDataSet.FindField(LCell.DataFieldName);
        if Assigned(LField) then begin
          case LCell.CellType of
            ctDateMin: if  LField.AsDateTime < 1 then
                LValue := ''
              else
                LValue := FormatDateTime('dd.mm.yyyy hh:mm', LField.AsDateTime);
            ctDateSek: if  LField.AsDateTime < 1 then
                LValue := ''
              else
                LValue := FormatDateTime('dd.mm.yyyy hh:mm:ss', LField.AsDateTime);
            ctCurrInt: LValue := BafInt2CurrInt(LField.AsString)
          else
            LValue := LField.AsString;
          end;
          LCell.Text := LValue;
        end
        else if LCell.DataQuelle = dqThread then
        else
          FInter.DoLog('E', '#vldata, f "' + LCell.DataFieldName + '" not found');
      end;
      if LCell.DataHintFieldName <> '' then begin
        LField := LDataSet.FindField(LCell.DataHintFieldName);
        if Assigned(LField) then
          LCell.Hint := LField.AsString
        else
          LCell.Hint := LCell.DataHintFieldName;
      end;
      if LCell.ReadOnlyFieldName <> '' then begin
        LField := LDataSet.FindField(LCell.ReadOnlyFieldName);
        if Assigned(LField) then
          LCell.ReadOnly := BafIsYesChar(Trim(LField.AsString));
      end;
    end;
    if LCell.Text = '' then begin    // NullValue
      if FindParam(ALineP, 'nvic' + s, LValue) then begin
        LCell.SetTextChange(FExecInter.ReplaceFunctions(LValue), true, true);
        lokVlInsert;
      end
      else if FindParam(ALineP, 'nvc' + s, LValue) then
        LCell.SetTextChange(FExecInter.ReplaceFunctions(LValue), true, true)
      else if FindParam(ALineP, 'nvi' + s, LValue) then begin
        LCell.Text := FExecInter.ReplaceFunctions(LValue);
        lokVlInsert;
      end
      else if FindParam(ALineP, 'nv' + s, LValue) then
        LCell.Text := FExecInter.ReplaceFunctions(LValue);
    end;
  end; // procedure lokFindAndSet

  procedure lokFetchFields;
  // goes through the cells and fetches the values from database
  var
    LRow, LCol: integer;
    LLineP: string;
    LField: TField;
  begin
    // KeyValue
    FInter.DebugDbRow(LDataSet);
    if LHasData and (FLastSeg.DataKey[0] <> '') then begin
      LField := LDataSet.FindField(FLastSeg.DataKey[0]);
      if Assigned(LField) then
        FLastSeg.DataKeyValue := LField.AsString;
    end;
    // Daten
    for LRow := 0 to FLastSeg.Grid.RowCount - 1 do begin
      LLineP := FLastSeg.Grid.DataRow[LRow].LineP;
      for LCol := 0 to FLastSeg.Grid.Columns.Count - 1 do
        lokFindAndSet(LCol, LRow, LLineP);
    end;
  end; // procedure lokFetchFields

  procedure lokOpenTable;
  // needs t, k and k_id
  begin
    FLastSeg.DataTable[0] := FindParamStringLower('t', '');
    FLastSeg.DataKey[0] := GetPrimaryKey(FExecInter.LineP);
    if (FLastSeg.DataTable[0] = '') or (FLastSeg.DataKey[0] = '') then
      FInter.DoLog('E', '#vl_data, t or k has no value')
    else begin
      LSql := Format('select * from %s where %s = :kid',
          [FLastSeg.DataTable[0], FLastSeg.DataKey[0]]);
      SqlAndParams(FLastSeg.BafConName[0], LName, LSql);
      LDataSet := dataMain.QueryOpen(FLastSeg.BafConName[0], LName);
      LHasData := not LDataSet.Eof;
      if not LHasData then
        FLastSeg.Grid.VlInsert := true;
      lokFetchFields;
    end;
  end; // procedure lokOpenTable

  procedure lokSql;
  var
    i: integer;
    s, LTableName: string;
  begin
    for i := 0 to 9 do begin
      s := IfThen(i = 0, '', IntToStr(i));
      LTableName := FindParamStringReplaced('t' + s, '');
      if LTableName <> '' then begin
        FLastSeg.DataTable[i] := LTableName;
        FLastSeg.DataKey[i] := GetPrimaryKey(FExecInter.LineP, s);
      end;
    end;

    LSql := FInter.GetSqlAndClear(1);
    SqlAndParams(FLastSeg.BafConName[0], LName, LSql);
    LDataSet := dataMain.QueryOpen(FLastSeg.BafConName[0], LName);
    LHasData := not LDataSet.Eof;
    if not LHasData then
      FLastSeg.Grid.VlInsert := true;
    lokFetchFields;
  end; // procedure lokSql

begin
  FLastSeg.IsDataLoading := true;
  try
    FLastSeg.DataLineP := FExecInter.LineP;
    FLastSeg.BafConName[0] := FindParamStringReplacedLower('db', DC_DEFAULT);
    LName := FInter.Name + '~' + FLastSeg.BafConName[0];
    FLastSeg.DataQuelle := FExecInter.FindParamQuelle(FExecInter.LineP, dqTable, ix);
    case FLastSeg.DataQuelle of
      dqTable: lokOpenTable;
      dqSQL: lokSql;
      dqJSON: (FInter.GetModule('json') as TBafJsonModule).
          JsonLoopData(FLastSeg, FTree, FExecInter, FExecInter.LineP);
    end;
  finally
    FLastSeg.IsDataLoading := false;
  end;
  if not FLastSeg.HasChanged then
    FLastSeg.HasChanged := FindParamBooleanReplaced('chg', false);
// procedure TBafFrmModule.SegVlData
end;

procedure TBafFrmModule.SegVlHist;
var
  LRight: TBafRight;
begin
  LRight := FExecInter.GetRight();
  if FLastSeg.SegRight = brNone then
    LRight := brNone;
  if (LRight = brNone) then
    FLastSeg.HistoryNoShowFields.Add(FindParamStringReplacedLower('f', ''));
end;

procedure TBafFrmModule.SegVlLine;
var
  LRow: TBafSgRow;
  LCell: TBafSgCell;
  LCol, ix: integer;
  s, LValue, LCmd: string;
  LRight: TBafRight;
begin
  LRight := FExecInter.GetRight();
  if FLastSeg.SegRight = brNone then
    LRight := brNone
  else if (LRight = brWrite) and (FLastSeg.SegRight = brRead) then
    LRight := brRead;
  if FindParamBooleanReplaced('cnd', true) and (LRight > brNone) then begin
    LRow := FLastSeg.Grid.DataRow[FLastSeg.Grid.RowCount];
    LRow.LineP := FExecInter.LineP;
    for LCol := 1 to FLastSeg.Grid.Columns.Count do begin
      s := IntToStr(LCol);
      LCell := FLastSeg.Grid.Cells[rtData, LCol - 1, LRow.RowIndex];
      LValue := FindParamStringReplaced('c' + s, '');
      if LValue <> '' then begin
        LCell.Text := LValue;
        LCell.ReadOnly := true;
        LCell.Visible := true;
      end
      else begin
        LCell.CellType := FindParamCelltype(FExecInter.LineP, 'y' + s);
        if LCell.CellType in [ctLookup, ctLookupLive, ctLookupStatus, ctLookupText] then
          LCell.LookupHelper := FindLookupHelper(FExecInter.LineP, s, LCmd);
        LCell.Text := FindParamStringReplaced('z' + s, '');
        LRight := FExecInter.GetRight(s);
        LCell.ReadOnly := LRight in [brNone, brRead];   //  (LRight < brWrite);
        LCell.Visible := (LRight > brNone);
        LCell.Alignment := FInter.FindParamAlignment(FExecInter.LineP, 'a' + s, taLeftJustify);
        LCell.MaxLength := FindParamIntegerReplaced('l' + s, 0);
        LCell.CharCase := FindParamCharCase('', 'cy' + s, TEditCharCase.ecNormal);
        LCell.CharsIgnore := FindParamStringReplaced('ci' + s, '');
        LCell.CharsAllowed := FindParamStringReplaced('ca' + s, '');
        LCell.DataFieldName := FindParamStringReplacedLower('f' + s, '');
        LCell.YFieldName := FindParamStringReplacedLower('yf' + s, '');
        LCell.DataQuelle := FExecInter.FindParamQuelle(FExecInter.LineP, dqSQL, ix, s);
        LCell.DataQIndex := ix;
        LCell.NoData := FindParamBooleanReplaced('nd' + s, false);
        LCell.Password := FindParamBooleanReplaced('pw' + s, false);
        LCell.ChangeCommand := FindParamStringReplaced('chg' + s, '');
      end;
      LCell.CharCase := FindParamCharCase('', 'cy' + s, TEditCharCase.ecNormal);
      LCell.Alignment := FInter.FindParamAlignment(FExecInter.LineP, 'a' + s, taLeftJustify);
      LCell.DataHintFieldName := FindParamStringReplaced('h' + s, '');
      LCell.ColSpan := FindParamIntegerReplaced('cs' + s, 1);
      LCell.Command := FindParamStringReplaced('cmd' + s, '');
      LCell.CellColorCommand := FindParamString('ccc' + s, '');
      LCell.CellArp := FindParamSingleReplaced('arp' + s, 0);
      LCell.ReadOnlyFieldName := FindParamStringReplaced('rof' + s, '');
    end;
    FLastSeg.GridCalcHeight;
  end;
end;

procedure TBafFrmModule.SegVlThread;
var
  LThread: TBafLoadDataThread;
  LType, LSegName: string;
begin
  if FindParamBooleanReplaced('cnd', true) then begin
    LThread := TBafLoadDataThread.Create(true);
    LThread.FreeOnTerminate := true;
    LSegName := FindParamStringReplaced('i', '');
    if LSegName <> '' then
      LThread.Segment := FPage.PrimaryDivs.FindSegmentByName(LSegName)
    else
      LThread.Segment := FLastSeg;
    LThread.KeyName := FindParamStringReplaced('k', '');
  //  LThread.FieldPrefix := FindParamStringReplaced('fp', '');
    LThread.DoChange := FindParamBooleanReplaced('chg', false);
    LThread.DoChangeIfNotEmpty := FindParamBooleanReplaced('chgine', false);
    LType := FindParamStringReplacedLower('y', 'vl');
    if LType = 'vl' then begin
      LThread.ThreadType := ttVL;
      LThread.Sql := FInter.GetSqlAndClear(1);
      LThread.BafConName := FindParamStringReplacedLower('db', DC_DEFAULT);
      LThread.KeyValue := LThread.Segment.Grid.GetVlCellText(LThread.KeyName);
    end;
    if (LThread = nil) or LThread.IsTerminated then
      exit;
    LThread.OnTerminate := LoadDataThreadTerminate;
    FThreadList.Add(LThread);
    LThread.Suspended := false;
  end;
// procedure TBafFrmModule.SegVlThread
end;

procedure TBafFrmModule.SegXGrid;
begin
  FXGridCols.Clear;
  AddSegment(FExecInter.LineP, uBafTypes.stXGrid);
  FHeaderRowCount := FindParamInteger('hrc', 1);
  FFooterRowCount := FindParamInteger('frc', 0);
  FLastSeg.Grid.Columns.LineType := GetLineType;
  FLastSeg.Grid.Columns.FixedColsCount := FindParamInteger('fcc', 0);
  FLastSeg.Grid.SelectionColumn := FindParamIntegerReplaced('sc', -1);
  FLastSeg.Grid.WithoutHorizontalScrollBar := FindParamBooleanReplaced('whs', false);
  FLastSeg.Grid.AddCommand := FindParamStringReplaced('acmd', '');
end;

procedure TBafFrmModule.SegXGridCalc;
// Calculation in a xgrid
var
  LSegment: TBafPageSegment;
  LNum, LCount: integer;
  LTyp, LFieldName, L1Func, L2Func, LResultString: string;
  LResultType:  TBafPageCellType;
  LNull0: boolean;

begin
  if FindParamBooleanReplaced('cnd', true) then begin
    if FindSegment('xgrid_calc', LSegment) then begin
      LTyp := FindParamStringReplacedLower('y', 'xy');
      LFieldName := FindParamStringReplaced('f', '');
      L1Func := AnsiLowerCase(FindParamStringReplaced('fnc1', 'sum'));
      L2Func := AnsiLowerCase(FindParamStringReplaced('fnc2', 'sum'));
      LNull0 := FindParamBooleanReplaced('nv0', false);
      LResultType := FindParamCelltype(FExecInter.LineP, 'ry');
      LResultString := SegXGridCalcIntern(LSegment, LTyp, LFieldName, L1Func,
          L2Func, LNull0, LResultType);
    end
    else
      LResultString := '';
    FExecInter.SetVarOrValue('z', LResultString);
  end;
// procedure TBafFrmModule.SegXGridCalc
end;

function TBafFrmModule.SegXGridCalcIntern(ASegment: TBafPageSegment;
    ATyp, AFieldName, A1Func, A2Func: string; ANv0: boolean;
    AResultType: TBafPageCellType): string;
var
  sl: TStringList;
  LCount: integer;
  LResult, LCalcValue, LCellValue: double;
  LText: string;
  LColumn: TBafSgColumn;

  procedure lokCalc(ACol, ARow: integer);
  begin
    LCellValue := 0;
    LText := ASegment.Grid.Cells[rtDisplayData, ACol, ARow].Text;
    case LColumn.CellType of
      ctBool, ctBool2, ctRadio: LCellValue := integer(BafIsYesChar(LText));
      ctBoolInv: LCellValue := integer(not BafIsYesChar(LText));
      ctInt, ctCurr, ctCurr4, ctCurrInt: LCellValue := StrToFloatDef(LText, 0);
      ctDate, ctDateMin, ctDateSek: LCellValue := StrToDateTimeDef(LText, 0);
    end;
    if (A1Func = 'sum') or (A1Func = 'avg') then
      LCalcValue := LCalcValue + LCellValue
    else if A1Func = 'min' then
      LCalcValue := System.Math.Min(LCalcValue, LCellValue)
    else if A1Func = 'min' then
      LCalcValue := System.Math.Min(LCalcValue, LCellValue);
    if (LText <> '') or ANv0 then
      inc(LCount);
  end; // procedure lokCalc

  procedure lokCalcResult;
  var
    i: integer;
  begin
    LCount := 0;
    if (A2Func = 'sum') or (A2Func = 'avg') then
      LCalcValue := 0
    else if A2Func = 'min' then
      LCalcValue := MaxInt
    else if A2Func = 'max' then
      LCalcValue := - MaxInt;
    for i := 0 to sl.Count - 1 do begin
      LCellValue := StrToCurrDef(sl[i], 0);
      if (A2Func = 'sum') or (A2Func = 'avg') then
        LCalcValue := LCalcValue + LCellValue
      else if A2Func = 'min' then
        LCalcValue := System.Math.Min(LCalcValue, LCellValue)
      else if A2Func = 'max' then
        LCalcValue := System.Math.Max(LCalcValue, LCellValue);
      inc(LCount);
    end;
    if (A2Func = 'sum') or (A2Func = 'min') or (A2Func = 'max') then
      LResult := LCalcValue
    else if (A2Func = 'avg') and (LCount > 0) then
      LResult := LCalcValue / LCount
    else if A2Func = 'count' then
      LResult := LCount;
    case AResultType of
      ctInt: result := FormatFloat('0', LResult);
      ctCurr4: result := FormatFloat('0.0000', LResult);
      ctDate: result := FormatDateTime('dd.mm.yyyy', LResult);
      ctDateMin: result := FormatDateTime('dd.mm.yyyy hh:mm', LResult);
      ctDateSek: result := FormatDateTime('dd.mm.yyyy hh:mm:ss', LResult);
    else
      result := FormatFloat('0.00', LResult);
    end;
  end; // procedure lokCalcResult

  procedure lokXY;
  // direction first x then y
  var
    LCol, LRow: integer;
  begin
    for LRow := 0 to ASegment.Grid.RowCount(rtDisplayData) - 1 do begin
      LCount := 0;
      if (A1Func = 'sum') or (A1Func = 'avg') then
        LCalcValue := 0
      else if A1Func = 'min' then
        LCalcValue := MaxInt
      else if A1Func = 'min' then
        LCalcValue := - MaxInt;
      for LCol := 0 to ASegment.Grid.Columns.Count - 1 do begin      // <---
        LColumn := ASegment.Grid.Columns.Items[LCol];
        if AnsiCompareStr(LColumn.CellFieldName, AFieldName) = 0 then
          lokCalc(LCol, LRow);
      end; // for LCol
      if (A1Func = 'sum') or (A1Func = 'min') or (A1Func = 'max') then
        LResult := LCalcValue
      else if (A1Func = 'avg') and (LCount > 0) then
        LResult := LCalcValue / LCount
      else if A1Func = 'count' then
        LResult := LCount;
      if (LCount > 0) or (A1Func = 'sum') or (A1Func = 'count') then
        sl.Add(FloatToStr(LResult))
      else if ANv0 then
        sl.Add('0');
    end; // for LRow
    lokCalcResult;
  end; // procedure lokXY

  procedure lokYX;
  // direction first y then x
  var
    LCol, LRow: integer;
  begin
    for LCol := 0 to ASegment.Grid.Columns.Count - 1 do begin
      LColumn := ASegment.Grid.Columns.Items[LCol];
      if AnsiCompareStr(LColumn.CellFieldName, AFieldName) = 0 then begin
        LCount := 0;
        if (A1Func = 'sum') or (A1Func = 'avg') then
          LCalcValue := 0
        else if A1Func = 'min' then
          LCalcValue := MaxInt
        else if A1Func = 'min' then
          LCalcValue := - MaxInt;
        for LRow := 0 to ASegment.Grid.RowCount(rtDisplayData) - 1 do      // <---
            lokCalc(LCol, LRow);
        if (A1Func = 'sum') or (A1Func = 'min') or (A1Func = 'max') then
          LResult := LCalcValue
        else if (A1Func = 'avg') and (LCount > 0) then
          LResult := LCalcValue / LCount
        else if A1Func = 'count' then
          LResult := LCount;
        if (LCount > 0) or (A1Func = 'sum') or (A1Func = 'count') then
          sl.Add(FloatToStr(LResult))
        else if ANv0 then
          sl.Add('0');
      end;
    end; // for
    lokCalcResult;
  end; // procedure lokYX

begin
  sl := TStringList.Create;
  try
    if ATyp = 'xy' then
      lokXY
    else
      lokYX;
  finally
    sl.Free;
  end;
// function TBafFrmModule.SegXGridCalcIntern
end;

procedure TBafFrmModule.SegXGridData;
var
  LSql, LColName, LRowName, LLastYVal, LXVal, LName: string;
  LDataSet: TDataSet;
  LXGridIndex, LRow, LMaxRow: integer;

  procedure lokDataRow;
  var
    i: integer;
  begin
    FInter.DebugDbRow(LDataSet);
    // new LLastYVal? then start a new row
    if LDataSet.FindField(LRowName).AsString <> LLastYVal then begin
      LLastYVal := LDataSet.FindField(LRowName).AsString;
      inc(LRow);
    end;

    // find XGridIndex
    LXVal := LDataSet.FieldByName(LColName).AsString;
    LXGridIndex := -1;
    for i := 0 to FLastSeg.Grid.Columns.Count - 1 do begin
      if LXVal = FLastSeg.Grid.Cells[rtHeader, i, 0].Text then begin
        LXGridIndex := FLastSeg.Grid.Columns.Items[i].XGridIndex;
        Break;
      end;
    end;

    // Assign the values of the column
    FetchGridFields(LDataSet, LRow, LXGridIndex, -1, false);
  end; // procedure lokDataRow

  procedure lokTableNames;
  var
    i: integer;
    s, LTableName: string;
  begin
    for i := 0 to 9 do begin
      s := IfThen(i = 0, '', IntToStr(i));
      LTableName := FindParamStringReplaced('t' + s, '');
      if LTableName <> '' then begin
        FLastSeg.DataTable[i] := LTableName;
        FLastSeg.DataKey[i] := GetPrimaryKey(FExecInter.LineP, s);
      end;
    end;
  end; // procedure lokTableNames;

begin
  FLastSeg.IsDataLoading := true;
  try
    LMaxRow := FindParamIntegerReplaced('m', MaxInt);
    LXGridIndex := 0;
    FLastSeg.DataLineP := FExecInter.LineP;
    FLastSeg.BafConName[0] := FindParamStringReplacedLower('db', DC_DEFAULT);
    LName := FInter.Name + '~' + FLastSeg.BafConName[0];
    lokTableNames;
    if gvBafDataCache.LoadGridCache(FLastSeg) then
      LSql := FInter.GetSqlAndClear(1)      // to clear the sql statement
    else begin
      LColName := FindParamStringLower('fcol', '');
      LRowName := FindParamStringLower('frow', '');
      if (LColName = '') or (LRowName = '') then
        FInter.DoLog('E', Format('#xgrddata - fcol: %s     frow: %s', [LColName, LRowName]));
      LSql := FInter.GetSqlAndClear(1);
      SqlAndParams(FLastSeg.BafConName[0], LName, LSql);
      LDataSet := dataMain.QueryOpen(FLastSeg.BafConName[0], LName);
      LRow := -1;
      LLastYVal := 'DC0870E6-D280-4933-8750-465F147664CB';
      while not LDataSet.Eof do begin
        lokDataRow;     // <----
        if (LRow + 1) >= LMaxRow then
          Break;
        LDataSet.Next;
      end;
    end;
  finally
    FLastSeg.IsDataLoading := false;
  end;
  FLastSeg.Grid.Columns.GridHeaderLineBreak;
  if FindParamBoolean('ocd', false) and (LRow > 0) then    // OpenCatOnData
    FLastCat.Opened := true;
// procedure TBafFrmModule.SegXGridData
end;

procedure TBafFrmModule.SegXGridXCol(ADoubleX: boolean = false);
// defines a x column
begin
  if ADoubleX then
    FXXGridCols.Add(FExecInter.LineP)
  else
    FXGridCols.Add(FExecInter.LineP);
end;

procedure TBafFrmModule.SegXGridXData;
// creates the x-columns from a sql statement
var
  LBafConName, LSql, LLineP, LName: string;
  LDataSet: TDataSet;
  i, LXGridIndex: integer;
begin
  LBafConName := FindParamStringReplacedLower('db', DC_DEFAULT);
  LName := FInter.Name + '~' + LBafConName;
  LSql := FInter.GetSqlAndClear(1);
  SqlAndParams(LBafConName, LName, LSql);
  LDataSet := dataMain.QueryOpen(LBafConName, LName);
  LXGridIndex := 0;
  while not LDataSet.Eof do begin
    for i := 0 to FXGridCols.Count - 1 do begin
      LLineP := FXGridCols[i];
      SegGridCol(LLineP, LDataSet, LXGridIndex, -1);
    end;
    inc(LXGridIndex);
    LDataSet.Next;
  end;
  FLastSeg.Grid.Columns.GridHeaderLineBreak;
end;

procedure TBafFrmModule.SegXXGrid;
begin
  FXGridCols.Clear;     // obere Ebene
  FXXGridCols.Clear;    // untere Ebene
  AddSegment(FExecInter.LineP, uBafTypes.stXXGrid);
  FHeaderRowCount := FindParamInteger('hrc', 1);
  FFooterRowCount := FindParamInteger('frc', 0);
  FLastSeg.Grid.Columns.LineType := GetLineType;
  FLastSeg.Grid.Columns.FixedColsCount := FindParamInteger('fcc', 0);
  FLastSeg.Grid.SelectionColumn := FindParamIntegerReplaced('sc', -1);
end;

procedure TBafFrmModule.SegXXGridData;
var
  LSql, LColName, LXColName, LRowName, LLastYVal, LXVal, LXXVal, LName: string;
  LDataSet: TDataSet;
  LXGridIndex, LXXGridIndex, LRow, LMaxRow: integer;

  procedure lokTableNames;
  var
    i: integer;
    s, LTableName: string;
  begin
    for i := 0 to 9 do begin
      s := IfThen(i = 0, '', IntToStr(i));
      LTableName := FindParamStringReplaced('t' + s, '');
      if LTableName <> '' then begin
        FLastSeg.DataTable[i] := LTableName;
        FLastSeg.DataKey[i] := GetPrimaryKey(FExecInter.LineP, s);
      end;
    end;
  end; // procedure lokTableNames;

  procedure lokDataRow;
  var
    i: integer;
  begin
    FInter.DebugDbRow(LDataSet);
    // new LLastYVal? then start a new row
    if LDataSet.FindField(LRowName).AsString <> LLastYVal then begin
      LLastYVal := LDataSet.FindField(LRowName).AsString;
      inc(LRow);
    end;

    // find XGridIndex
    LXGridIndex := -2;
    LXVal := LDataSet.FieldByName(LColName).AsString;
    for i := 0 to FLastSeg.Grid.Columns.Count - 1 do begin
      if LXVal = FLastSeg.Grid.Cells[rtHeader, i, 0].Text then begin
        LXGridIndex := FLastSeg.Grid.Columns.Items[i].XGridIndex;
        Break;
      end;
    end;

    // find XXGridIndex
    LXXGridIndex := -2;
    LXXVal := LDataSet.FieldByName(LXColName).AsString;
    for i := LXGridIndex to FLastSeg.Grid.Columns.Count - 1 do begin
      if (LXXVal = FLastSeg.Grid.Cells[rtHeader, i, 0].Text)
          and (LXGridIndex = FLastSeg.Grid.Columns.Items[i].XGridIndex) then begin
        LXXGridIndex := FLastSeg.Grid.Columns.Items[i].XXGridIndex;
        Break;
      end;
    end;

    // Assign the values of the column
    FetchGridFields(LDataSet, LRow, LXGridIndex, LXXGridIndex, false);
  end; // procedure lokDataRow

begin
  FLastSeg.IsDataLoading := true;
  try
    LXGridIndex := 0;
    LXXGridIndex := 0;
    LMaxRow := FindParamIntegerReplaced('m', MaxInt);
    FLastSeg.DataLineP := FExecInter.LineP;
    FLastSeg.BafConName[0] := FindParamStringReplacedLower('db', DC_DEFAULT);
    LName := FInter.Name + '~' + FLastSeg.BafConName[0];
    lokTableNames;
    if gvBafDataCache.LoadGridCache(FLastSeg) then
      LSql := FInter.GetSqlAndClear(1)      // to clear the sql statement
    else begin
      LColName := FindParamStringLower('fcol', '');
      LXColName := FindParamStringLower('fxxcol', '');
      LRowName := FindParamStringLower('frow', '');
      if (LColName = '') or (LXColName = '') or (LRowName = '') then
        FInter.DoLog('E', Format('#xxgrddata - fcol: %s     fxcol: %s     frow: %s',
            [LColName, LXColName, LRowName]));
      LSql := FInter.GetSqlAndClear(1);
      SqlAndParams(FLastSeg.BafConName[0], LName, LSql);
      LDataSet := dataMain.QueryOpen(FLastSeg.BafConName[0], LName);
      LRow := -1;
      LLastYVal := 'DC0870E6-D280-4933-8750-465F147664CB';
      while not LDataSet.Eof do begin
        lokDataRow;     // <----
        if (LRow + 1) >= LMaxRow then
          Break;
        LDataSet.Next;
      end;
    end;
  finally
    FLastSeg.IsDataLoading := false;
  end;
  FLastSeg.Grid.Columns.GridHeaderLineBreak;
  if FindParamBoolean('ocd', false) and (LRow > 0) then    // OpenCatOnData
    FLastCat.Opened := true;
// procedure TBafFrmModule.SegXXGridData
end;

procedure TBafFrmModule.SegXXGridXData;
// Creates the x- and xx-columns out of a sql-statement
var
  LBafConName, LSql, LLineP, LKeyName, LKeyValue, LOldKeyValue, LName: string;
  LDataSet: TDataSet;
  i, LXGridIndex, LXXGridIndex: integer;
begin
  LBafConName := FindParamStringReplacedLower('db', DC_DEFAULT);
  LName := FInter.Name + '~' + LBafConName;
  LSql := FInter.GetSqlAndClear(1);
  SqlAndParams(LBafConName, LName, LSql);
  LKeyName := FindParamStringReplaced('k', '');
  LDataSet := dataMain.QueryOpen(LBafConName, LName);
  LXGridIndex := -1;
  LXXGridIndex := -1;
  while not LDataSet.Eof do begin
    LKeyValue := LDataSet.FieldByName(LKeyName).AsString;
    if LKeyValue <> LOldKeyValue then begin
      LOldKeyValue := LKeyValue;
      inc(LXGridIndex);
      for i := 0 to FXGridCols.Count - 1 do begin
        LLineP := FXGridCols[i];
        SegGridCol(LLineP, LDataSet, LXGridIndex, -1);
      end;
      LXXGridIndex := 0;
    end;
    for i := 0 to FXXGridCols.Count - 1 do begin
      LLineP := FXXGridCols[i];
      SegGridCol(LLineP, LDataSet, LXGridIndex, LXXGridIndex);
    end;
    inc(LXXGridIndex);
    LDataSet.Next;
  end;
  FLastSeg.Grid.Columns.GridHeaderLineBreak;
end;

procedure TBafFrmModule.ServerOperations;
var
  LCount: integer;
begin
  if FindParamBooleanReplaced('cnd', true) then begin
    LCount := FindParamIntegerReplaced('z', 1);
    FInter.DoLog('ops', IntToStr(LCount));
  end;
end;

procedure TBafFrmModule.SetFNode(ALineP: string);
var
  s: string;
begin
  s := AnsiLowerCase(FExecInter.FindParamString(ALineP, 'u', 's'));
  FNode := GetNode(s);
end;

procedure TBafFrmModule.SGridData;
var
  LRight: TBafRight;
  LSql, LKeyValue, LName, LZeileP, LClFieldName: string;
  LDataSet: TDataSet;
  ix: integer;
  LQuelle: TBafDataQuelle;
  LDef, LSubDef: TDefNodeObject;
  LClList: TStringList;

  procedure lokCreateCells;
  var
    LRow: TBafSgRow;
    LCell: TBafSgCell;
    LCol, ix: integer;
    s, LValue, LCmd, LCellColorCommand, LRowType: string;
    LRight: TBafRight;
    LField: TField;
    LReadOnly: boolean;

    procedure lokFetchData;
    begin
      if LCell.DataFieldName <> '' then begin
        LField := LDataSet.FindField(LCell.DataFieldName);
        if Assigned(LField) then begin
          case LCell.CellType of
            ctDateMin: if  LField.AsDateTime < 1 then
                LValue := ''
              else
                LValue := FormatDateTime('dd.mm.yyyy hh:mm', LField.AsDateTime);
            ctDateSek: if  LField.AsDateTime < 1 then
                LValue := ''
              else
                LValue := FormatDateTime('dd.mm.yyyy hh:mm:ss', LField.AsDateTime);
            ctCurrInt: LValue := BafInt2CurrInt(LField.AsString)
          else
            LValue := LField.AsString;
          end;
          LCell.Text := LValue;
        end
        else if LCell.DataQuelle = dqThread then
        else
          FInter.DoLog('E', '#sgrd_data, f "' + LCell.DataFieldName + '" not found');
      end;
      if LCell.DataHintFieldName <> '' then begin
        LField := LDataSet.FindField(LCell.DataHintFieldName);
        if Assigned(LField) then
          LCell.Hint := LField.AsString
        else
          LCell.Hint := LCell.DataHintFieldName;
      end;
    end; // procedure lokFetchData

    procedure lokCellSettings;
    begin
      LValue := FindParamStringReplaced(LZeileP, 'c' + s, '');
      if LValue <> '' then begin
        LCell.Text := LValue;
        LCell.ReadOnly := true;
        LCell.Visible := true;
      end
      else begin
        LCell.CellType := FindParamCelltype(LZeileP, 'y' + s);
        if LCell.CellType in [ctLookup, ctLookupLive, ctLookupStatus, ctLookupText] then
          LCell.LookupHelper := FindLookupHelper(LZeileP, s, LCmd);
        LCell.Text := FindParamStringReplaced(LZeileP,'z' + s, '');
        LRight := FExecInter.GetRight(LZeileP, s);
        LCell.ReadOnly := (LRight in [brNone, brRead]) or LReadOnly;   //  (LRight < brWrite);
        LCell.Visible := (LRight > brNone);
        LCell.Alignment := FInter.FindParamAlignment(LZeileP, 'a' + s, taLeftJustify);
        LCell.MaxLength := FindParamIntegerReplaced(LZeileP, 'l' + s, 0);
        LCell.CharsIgnore := FindParamStringReplaced(LZeileP, 'ci' + s, '');
        LCell.CharsAllowed := FindParamStringReplaced(LZeileP, 'ca' + s, '');
        LCell.DataFieldName := FindParamStringReplacedLower(LZeileP, 'f' + s, '');
        LCell.YFieldName := FindParamStringReplacedLower(LZeileP, 'yf' + s, '');
        LCell.DataQuelle := FExecInter.FindParamQuelle(LZeileP, dqSQL, ix, s);
        LCell.DataQIndex := ix;
        LCell.NoData := FindParamBooleanReplaced(LZeileP, 'nd' + s, false);
        LCell.Password := FindParamBooleanReplaced(LZeileP, 'pw' + s, false);
        LCell.ChangeCommand := FindParamStringReplaced(LZeileP, 'chg' + s, '');
      end;
      LCell.CharCase := FindParamCharCase(LZeileP, 'cy' + s, TEditCharCase.ecNormal);
      LCell.Alignment := FInter.FindParamAlignment(LZeileP, 'a' + s, taLeftJustify);
      LCell.DataHintFieldName := FindParamStringReplaced(LZeileP, 'h' + s, '');
      LCell.ColSpan := FindParamIntegerReplaced(LZeileP, 'cs' + s, 1);
      LCell.Command := FindParamStringReplaced(LZeileP, 'cmd' + s, '');
      LCell.CellColorCommand := FindParamStringReplaced(LZeileP, 'ccc' + s, LCellColorCommand);
    end; // procedure lokCellSettings

  begin
    LRow := FLastSeg.Grid.DataRow[FLastSeg.Grid.RowCount];
    LRow.LineP := LZeileP;
    LRowType := FindParamStringReplacedLower(LZeileP, 'u', '') + ' ';
    LReadOnly := FindParamBooleanReplaced(LZeileP, 'ro', false);
    LCellColorCommand := FindParamStringReplaced(LZeileP, 'ccc', '');
    for LCol := 1 to FLastSeg.Grid.Columns.Count do begin
      s := IntToStr(LCol);
      LCell := FLastSeg.Grid.Cells[rtData, LCol - 1, LRow.RowIndex];
      if (LCol = 1) then begin
        if CharInSet(LRowType[1], ['a', 'w']) then
          LCell.Parents.Row.IsAdditionalRow := true;
        LCell.Parents.Row.SGridZeile := LZeileP;
      end;
      lokCellSettings;
      lokFetchData;
    end;
  end; // lokCreateCells;

  procedure lokAddRow;
  var
    i, j: integer;
    LField: TField;
    LCondition: string;
  begin
    // we go through the DefNodes and create a node for each entry
    for i := 0 to FDefNodes.Count - 1 do begin
      LDef := TDefNodeObject(FDefNodes.Objects[i]);
      LField := LDataSet.FindField(LDef.FKeyColumn);
      if Assigned(LField) then
        LKeyValue := LDataSet.FindField(LDef.FKeyColumn).AsString
      else begin
        LKeyValue := FExecInter.FindParamString(FDefNodes[i], 'c', '');
        if LDef.FKeyColumn <> '' then
          FInter.DoLog('W', '#sgrid_data - can not find key field: ' + LDef.FKeyColumn);
      end;
      if LKeyValue <> LDef.FKeyColumnValue then begin
        LCondition := FindParamStringReplaced(FDefNodes[i], 'cnd', '');
        if (LCondition = '') or FExecInter.ParseBoolStatement(LCondition) then begin
          // If we start over on an upper level, we must also reset everything below it
          if FindParamBoolean(FDefNodes[i], 'nc', false) then begin
            for j := i + 1 to FDefNodes.Count - 1 do begin
              LSubDef := TDefNodeObject(FDefNodes.Objects[j]);
              LSubDef.FKeyColumnValue := BAF_OTHER;
            end;
          end;
          LZeileP := FDefNodes[i];
          lokCreateCells;
          LDef.FKeyColumnValue := LKeyValue;
        end;
      end;
      if LClFieldName <> '' then begin
        LField := LDataSet.FindField(LClFieldName);
        if Assigned(LField) and (Trim(LField.AsString) <> '') then
          LClList.Add(LField.AsString);
      end;
    end; // for i
    LDataSet.Next;
  end; // procedure lokAddRow

  procedure lokSql;
  begin
    LSql := FInter.GetSqlAndClear(1);
    if LSql <> '' then begin
      SqlAndParams(FLastSeg.BafConName[0], LName, LSql);
      LDataSet := dataMain.QueryOpen(FLastSeg.BafConName[0], LName);
      while not LDataSet.Eof do begin
        FInter.DebugDbRow(LDataSet);
        lokAddRow;
      end;
    end;
  end;

begin
  if FindParamBooleanReplaced('cnd', true) then begin
    LRight := FExecInter.GetRight;
    if (LRight > brNone) then begin
      LClList := TStringList.Create;
      try
        LClList.Sorted := true;
        LClList.Duplicates := dupIgnore;
        FLastSeg.IsDataLoading := true;
        try
          FLastSeg.DataLineP := FExecInter.LineP;
          FLastSeg.BafConName[0] := FindParamStringReplacedLower('db', DC_DEFAULT);
          LName := FInter.Name + '~' + FLastSeg.BafConName[0];
          FLastSeg.DataQuelle := FExecInter.FindParamQuelle(FExecInter.LineP, dqSQL, ix);
          LClFieldName := FindParamStringReplacedLower('clfn', '');
          case FLastSeg.DataQuelle of
            dqSQL: lokSql;
          end;
        finally
          FDefNodes.Clear;
          FLastSeg.IsDataLoading := false;
        end;
        if not FLastSeg.HasChanged then
          FLastSeg.HasChanged := FindParamBooleanReplaced('chg', false);
        if LClFieldName <> '' then
          FExecInter.SetVarOrValue('cln', LClList.DelimitedText);
        FLastSeg.GridCalcHeight;
      finally
        LClList.Free;
      end;
    end;
  end;
// procedure TBafFrmModule.SGridData
end;

procedure TBafFrmModule.SGridHeaderFooter;
var
  LRow, LColumn: integer;
  LCell: TBafSgCell;
  s: string;
begin
  if FindParamBooleanReplaced('cnd', true) then begin
    for LColumn := 0 to FLastSeg.Grid.Columns.Count - 1 do begin
      for LRow := 0 to FHeaderRowCount - 1 do begin
        LCell := FLastSeg.Grid.Cells[rtHeader, LColumn, LRow];
        s := IntToStr(LRow + 1) + IntToStr(LColumn + 1);
        LCell.Text := FindParamStringReplaced('c' + s, '');
        LCell.Hint := BafDecode1310(FindParamStringReplaced('h' + s, ''));
        LCell.Alignment := FInter.FindParamAlignment('', 'a' + s, taLeftJustify);
//        LCell.ShowSort := FindParamBooleanReplaced('ss' + s, true);
        LCell.ColSpan := FindParamIntegerReplaced('cs' + s, 1);
      end;
      for LRow := 0 to FFooterRowCount - 1 do begin
        LCell := FLastSeg.Grid.Cells[rtFooter, LColumn, LRow];
        s := IntToStr(LRow + 1) + IntToStr(LColumn + 1);
        LCell.Text := FindParamStringReplaced('fc' + s, '');
        LCell.Alignment := FInter.FindParamAlignment('', 'fa' + s, taLeftJustify);
        LCell.ColSpan := FindParamInteger('fcs' + s, 1);
        LCell.CellType := FindParamCelltype('fy' + s);
      end;
    end;
  end;
end;

procedure TBafFrmModule.SGridNode;
var
  LDef: TDefNodeObject;
begin
  if FindParamBooleanReplaced('cnd', true) then begin
    LDef := TDefNodeObject.Create;
    LDef.FKeyColumn := GetPrimaryKey;
    LDef.FKeyColumnValue := BAF_OTHER;
    FDefNodes.AddObject(FExecInter.LineP, LDef);
  end;
end;

procedure TBafFrmModule.ShowMsg;
var
  s: string;
begin
  if FindParamBooleanReplaced('cnd', true) then begin
    s := FindParamStringReplaced('c', '');
    TfrmBafDialog.ShowMessage(dataMain.ProgName, s, nil);
  end;
end;

procedure TBafFrmModule.SrvProcForm;
begin
  FInter.LogHideCommand := FindParamBooleanReplaced('lhc', false);
  FInter.SrvDebgLog := FindParamBooleanReplaced('dbg', false);
end;

procedure TBafFrmModule.TreeBack(ABackBack: boolean);
begin
  if FindParamBooleanReplaced('cnd', true) then begin
    if FTree.Enabled then begin
      FTreeNodeListInProcess := true;
      try
        if ABackBack then begin  // vorwärts
          if FTreeNodeListPointer < (FTreeNodeList.Count - 1) then begin
            FTree.Selected := (FTreeNodeList[FTreeNodeListPointer + 1] as TBafTreeNode);
            inc(FTreeNodeListPointer);
          end;
        end
        else begin  // zurück
          if FTreeNodeListPointer > 0 then begin
            FTree.Selected := (FTreeNodeList[FTreeNodeListPointer - 1] as TBafTreeNode);
            dec(FTreeNodeListPointer);
          end;
        end;
      finally
        FTreeNodeListInProcess := false;
      end;
    end;
  end;
end;

procedure TBafFrmModule.TreeItemFunction(var AText: string);
begin
  AText := FInter.ReplaceFunctions(AText);
end;

procedure TBafFrmModule.TreeItemLookup(ASpecial: boolean; AName, AKey: string;
  var AResult: string);
var
  LHelper: TBafComboHelper;
begin
  if ASpecial then
    LHelper := gvBafDataCache.Special[AName]
  else
    LHelper := gvBafDataCache.Lookup[AName];
  AResult := LHelper.GetCaptionGuid(AKey);
end;

procedure TBafFrmModule.TreeSelect;
var
  LType: string;
  LItem, LSelectedOld: TBafTreeNode;
  f1, f2, z1, z2: string;
  LSeachInChilds, b1, b2: boolean;


  procedure lokParent(ALevel: integer);
  begin
    LItem := LSelectedOld;
    while (ALevel > 0) and Assigned(LItem) do begin
      LItem := LItem.ParentNode as TBafTreeNode;
      dec(ALevel);
    end;
    if Assigned(LItem) then
      FTree.Selected := LItem;
  end; // procedure lokParent

  procedure lokChild(ALevel: integer);
  begin
    LItem := LSelectedOld;
    while (ALevel > 0) and (LItem.ChildCount > 0) do begin
      LItem := LItem.Childs[0];
      dec(ALevel);
    end;
    if Assigned(LItem) then
      FTree.Selected := LItem;
  end; // procedure lokParent

  function lokSearchInChilds(AParent: TBafTreeNode; AAnd: boolean): boolean;
  var
    i: integer;
    LChildItem: TBafTreeNode;
//    s: string;
  begin
    result := false;
    for i := 0 to AParent.ChildCount - 1 do begin
      LChildItem := AParent.Childs[i];
      b1 := LChildItem.IsValue(f1, z1);
      b2 := LChildItem.IsValue(f2, z2);
//      s := LChildItem.Ini.AsString;
      if (b1 and b2) or (not AAnd and (b1 or b2)) then begin
        FTree.Selected := LChildItem;
        result := true;
        exit;
      end;
      if LSeachInChilds then begin
        if lokSearchInChilds(LChildItem, AAnd) then begin
          result := true;
          exit;
        end;
      end;
    end;
  end; // function lokSearchInChilds

  procedure lokFindTree(AItem: TBafTreeNode; AAnd: boolean);
  var
    i: integer;
  begin
    LSeachInChilds := FindParamBooleanReplaced('sic', false);
    f1 := FindParamStringReplaced('f', '');
    f2 := FindParamStringReplaced('f2', '');
    z1 := FindParamStringReplaced('z', '');
    z2 := FindParamStringReplaced('z2', '');
    if Assigned(AItem) then
      lokSearchInChilds(AItem, AAnd)
    else begin
      for i := 0 to FTree.Root.ChildCount - 1 do begin
        LItem := FTree.Root.Childs[i];
        b1 := LItem.IsValue(f1, z1);
        b2 := LItem.IsValue(f2, z2);
        if (b1 and b2) or (not AAnd and (b1 or b2)) then begin
          FTree.Selected := LItem;
          exit;
        end;
        if LSeachInChilds then begin
          if lokSearchInChilds(LItem, AAnd) then
            exit;
        end;
      end;
    end;
  end; // lokFind

  procedure lokUpDown(AUp: boolean);
  var
    LParent: TBafTreeNode;
    ix: integer;
  begin
    ix := FTree.Selected.NodeIndex;
    if ix >= 0 then begin
      LItem := nil;
      LParent := FTree.Selected.ParentNode;
      if (ix > 0) and AUp then
        LItem := LParent.Childs[ix - 1]
      else if not AUp and (ix < (LParent.ChildCount - 1)) then
        LItem := LParent.Childs[ix + 1];
      if Assigned(LItem) then
        FTree.Selected := LItem;
    end;
  end; // procedure lokUpDown

  procedure lokOpenParents;
  begin
    LItem := FTree.Selected;
    while Assigned(LItem) do begin
      LItem := LItem.ParentNode as TBafTreeNode;
      if Assigned(LItem) then
        LItem.Opened := true;
    end;
  end;

begin
  if FindParamBooleanReplaced('cnd', true) then begin
    LSelectedOld := FTree.Selected;
    FTree.Selected := nil;
    LType := FindParamStringLower('y', '');
    if LType = 'p' then
      lokParent(1)
    else if LType = 'pp' then
      lokParent(2)
    else if LType = 'ppp' then
      lokParent(3)
    else if LType = 'pppp' then
      lokParent(4)
    else if LType = 'ppppp' then
      lokParent(5)
    else if LType = 'c' then
      lokChild(1)
    else if LType = 'cc' then
      lokChild(2)
    else if LType = 'ccc' then
      lokChild(3)
    else if LType = 'cccc' then
      lokChild(4)
    else if LType = 'ccccc' then
      lokChild(5)
    else if LType = 'rf' then
      lokFindTree(nil, false)
    else if LType = 'sf' then
      lokFindTree(LSelectedOld, false)
    else if LType = 'rfa' then
      lokFindTree(nil, true)
    else if LType = 's' then begin
      if Assigned(LSelectedOld.Ini) then
        FTree.Selected := LSelectedOld
    end
    else if LType = 'sfa' then
      lokFindTree(LSelectedOld, true)
    else if LType = 'sfo' then begin
      LSelectedOld.Opened := true;
      lokFindTree(LSelectedOld, false)
    end
    else if (LType = 'sfao') or (LType = 'sfoa') then begin
      LSelectedOld.Opened := true;
      lokFindTree(LSelectedOld, true)
    end
    else if (LType = 'd') or (LType = 'down') then
      lokUpDown(false)
    else if (LType = 'u') or (LType = 'up') then
      lokUpDown(true)
    ;
    if Assigned(FTree.Selected) then begin
      if FindParamBooleanReplaced('o', false) then
        FTree.Selected.Opened := true;
      if FindParamBooleanReplaced('op', false) then
        lokOpenParents;
    end
    else
      FTree.Selected := LSelectedOld;
  end;
// procedure TBafFrmModule.TreeSelect
end;

procedure TBafFrmModule.TreeSelect2;
var
  LType: string;
  LItem, LSelectedNew: TBafTreeNode;
  f1, f2, z1, z2, s: string;
  LSeachInChilds, b1, b2: boolean;


  procedure lokParent(ALevel: integer);
  begin
    LItem := FTree.Selected;
    while (ALevel > 0) and Assigned(LItem) do begin
      LItem := LItem.ParentNode as TBafTreeNode;
      dec(ALevel);
    end;
    if Assigned(LItem) then
      LSelectedNew := LItem;
  end; // procedure lokParent

  procedure lokChild(ALevel: integer);
  begin
    LItem := FTree.Selected;
    while (ALevel > 0) and (LItem.ChildCount > 0) do begin
      LItem := LItem.Childs[0];
      dec(ALevel);
    end;
    if Assigned(LItem) then
      LSelectedNew := LItem;
  end; // procedure lokParent

  function lokSearchInChilds(AParent: TBafTreeNode; AAnd: boolean): boolean;
  var
    i: integer;
    LChildItem: TBafTreeNode;
//    s: string;
  begin
    result := false;
    for i := 0 to AParent.ChildCount - 1 do begin
      LChildItem := AParent.Childs[i];
      b1 := LChildItem.IsValue(f1, z1);
      b2 := LChildItem.IsValue(f2, z2);
//      s := LChildItem.Ini.AsString;
      if (b1 and b2) or (not AAnd and (b1 or b2)) then begin
        LSelectedNew := LChildItem;
        result := true;
        exit;
      end;
      if LSeachInChilds then begin
        if lokSearchInChilds(LChildItem, AAnd) then begin
          result := true;
          exit;
        end;
      end;
    end;
  end; // function lokSearchInChilds

  procedure lokFindTree(AItem: TBafTreeNode; AAnd: boolean);
  var
    i: integer;
  begin
    LSeachInChilds := FindParamBooleanReplaced('sic', false);
    f1 := FindParamStringReplaced('f', '');
    f2 := FindParamStringReplaced('f2', '');
    z1 := FindParamStringReplaced('z', '');
    z2 := FindParamStringReplaced('z2', '');
    if Assigned(AItem) then
      lokSearchInChilds(AItem, AAnd)
    else
      lokSearchInChilds(FTree.Root, AAnd);
  end; // lokFind

  procedure lokUpDown(AUp: boolean);
  var
    LParent: TBafTreeNode;
    ix: integer;
  begin
    ix := FTree.Selected.NodeIndex;
    if ix >= 0 then begin
      LItem := nil;
      LParent := FTree.Selected.ParentNode;
      if (ix > 0) and AUp then
        LItem := LParent.Childs[ix - 1]
      else if not AUp and (ix < (LParent.ChildCount - 1)) then
        LItem := LParent.Childs[ix + 1];
      if Assigned(LItem) then
        LSelectedNew := LItem;
    end;
  end; // procedure lokUpDown

  procedure lokOpenParents;
  begin
    LItem := FTree.Selected;
    while Assigned(LItem) do begin
      LItem := LItem.ParentNode as TBafTreeNode;
      if Assigned(LItem) then
        LItem.Opened := true;
    end;
  end;

begin
  if FindParamBooleanReplaced('cnd', true) then begin
    LSelectedNew := nil;
    LType := FindParamStringLower('y', '');
    if LType = 'p' then
      lokParent(1)
    else if LType = 'pp' then
      lokParent(2)
    else if LType = 'ppp' then
      lokParent(3)
    else if LType = 'pppp' then
      lokParent(4)
    else if LType = 'ppppp' then
      lokParent(5)
    else if LType = 'c' then
      lokChild(1)
    else if LType = 'cc' then
      lokChild(2)
    else if LType = 'ccc' then
      lokChild(3)
    else if LType = 'cccc' then
      lokChild(4)
    else if LType = 'ccccc' then
      lokChild(5)
    else if (LType = 'rf') or (LType = 'rfo') then
      lokFindTree(nil, false)
    else if LType = 'sf' then
      lokFindTree(FTree.Selected, false)
    else if LType = 'rfa' then
      lokFindTree(nil, true)
    else if LType = 's' then begin
      if Assigned(FTree.Selected) and Assigned(FTree.Selected.Ini) then
        FTree.Selected := FTree.Selected
      else if Assigned(FTreeSelectNode) then
        FTree.Selected := FTreeSelectNode;
    end
    else if LType = 'sas' then begin
      s := '';
      if FindParamBooleanReplaced('op', false) then
        s := s + 'op=Y   ';
      if FindParamBooleanReplaced('o', false) then
        s := s + 'o=Y   ';
      if FindParamBooleanReplaced('wsc', false) then
        s := s + 'wsc=Y   ';
      FInter.CommandOnTimer('#tree_sel   y=s   ' + s);
    end
    else if LType = 'sfa' then
      lokFindTree(FTree.Selected, true)
    else if LType = 'sfo' then begin
      FTree.Selected.Opened := true;
      lokFindTree(FTree.Selected, false)
    end
    else if (LType = 'sfao') or (LType = 'sfoa') then begin
      FTree.Selected.Opened := true;
      lokFindTree(FTree.Selected, true)
    end
    else if (LType = 'd') or (LType = 'down') then
      lokUpDown(false)
    else if (LType = 'u') or (LType = 'up') then
      lokUpDown(true)
    ;
    if Assigned(LSelectedNew) then begin
      if FindParamBooleanReplaced('wsc', false) then
        FTree.SilentTreeSelect(LSelectedNew)
      else
        FTree.Selected := LSelectedNew;
    end;
      if Assigned( FTree.Selected) and FindParamBooleanReplaced('o', false) then
        FTree.Selected.Opened := true;
      if FindParamBooleanReplaced('op', false) then
        lokOpenParents;
  end;
// procedure TBafFrmModule.TreeSelect2
end;

procedure TBafFrmModule.TreeViewChange(ASender: TBafTree; ANode: TBafTreeNode);
begin
  if FTree.Enabled then begin
    if Assigned(FTree.Selected) then begin
      FInter.DebugNodeIni(FTree.Selected.Ini);
      if not FTreeNodeListInProcess then begin
        if FTreeNodeListPointer = (FTreeNodeList.Count - 1) then begin
          FTreeNodeListPointer := FTreeNodeList.Count;
          FTreeNodeList.Add(FTree.Selected);
        end
        else begin
          inc(FTreeNodeListPointer);
          FTreeNodeList[FTreeNodeListPointer] := FTree.Selected;
        end;
      end;
      if FTreeLock then
        exit;
      FCommand := FTree.Selected.GetSelectionCommand;
      if FCommand = '' then
        ClearPage
      else
        TBafInterpreterLevel.ExecInNewLevel(FCommand, FExecInter, FInter);
      FOldSelectedNode := FTree.Selected;
      if FTree.CanFocus and FTree.Selected.GetTreeFocus then
        FTree.SetFocus;
    end; // if Assigned(FTree.Selected)
  end
  else
    FTree.Selected := FOldSelectedNode;
end;

procedure TBafFrmModule.TreeViewExpanded(Sender: TBafTree; ANode: TBafTreeNode);
begin                            // Never select expanded nodes automatically !!!
  if not FTreeLock then begin
    if ANode.HasOpenCommand and (ANode.ChildCount = 0) then begin
      FTreeEmptyOpen := true;
      try
        FLastExpanded := ANode;
        if Assigned(ANode.Ini) then
          TBafInterpreterLevel.ExecInNewLevel(ANode.GetOpenCommand, FExecInter, FInter);
      finally
        FTreeEmptyOpen := false;
      end;
    end;
  end;
  frmMain.SpecialPageRefresh(true);
end;

procedure TBafFrmModule.WriteSrvLog(AText: string);
begin
  inherited;
  FSrvLog.Add(AText);
end;

function TBafFrmModule.XGridCalc(AParams: TStrings): string;
// Calculation in a xgrid
// 0 - Segment
// 1 - Typ
// 2 - FieldName
// 3 - Func 1
// 4 - Func 2
// 5 - NV0
// 6 - Result-Type
var
  LSegment: TBafPageSegment;
  LNum, LCount: integer;
  LTyp, LFieldName, L1Func, L2Func, LResultString: string;
  LResultType:  TBafPageCellType;
  LNull0: boolean;

begin
  if AParams.Count > 3 then begin
    LSegment := FPage.PrimaryDivs.FindSegmentByName(AParams[0]);
    if Assigned(LSegment) and (LSegment.SegmentType = stXGrid) then begin
      LTyp := AnsiLowerCase(AParams[1]);
      LFieldName := AnsiLowerCase(AParams[2]);
      L1Func := AnsiLowerCase(AParams[3]);
      L2Func := AnsiLowerCase(AParams[4]);
      LNull0 := BafIsYesChar(AParams[5]);
      LResultType := BafGetCellType(AnsiLowerCase(AParams[6]));
      result := SegXGridCalcIntern(LSegment, LTyp, LFieldName, L1Func,
          L2Func, LNull0, LResultType);
    end;
  end
  else
    FInter.DoLog('E', '$XGRD_CALC number of params less 4');
// function TBafFrmModule.XGridCalc
end;

function TBafFrmModule.InterpretLine(AExecInter: TBafCustomInterpreter): boolean;
var
  LInter: TBafCustomInterpreter;

  function lokCheck(ACmd: string; ACmd2: string = ''; ACmd3: string = ''): boolean;
  begin
    result := (FExecInter.LineF = ACmd)
      or ((ACmd2 <> '') and (FExecInter.LineF = ACmd2))
      or ((ACmd3 <> '') and (FExecInter.LineF = ACmd3));
  end;

  function lokClient: boolean;
  begin
    result := true;
    if lokCheck('#frm', '#form') then CreateForm                                 // create a form
    else if FExecInter.LineF = '#cout' then ConsoleOut                           // write into the console
    else if FExecInter.LineF = '#coutl' then ConsoleOutLine                      // write an info into the console
    else if FExecInter.LineF = '#csave' then ConsoleSaveToFile                   // Save the console into a file
    else if FExecInter.LineF = '#srv_ops' then ServerOperations                  //
    else if FExecInter.LineF = '#filter' then DoFilter(nil)                      // call the filter statement
    else if FExecInter.LineF = '#cancel' then SaveOrCancel(false)                // cancel data change
    else if FExecInter.LineF = '#save' then SaveOrCancel(true)                   // save data change
    else if FExecInter.LineF = '#xlive_F9' then CommandButtonClick(nil)          // executes a xlive

    else if lokCheck('#btn', '#button') then AddButton                           // insert a button
    else if lokCheck('#edt', '#edit') then AddEdit                               // insert a edit
    else if lokCheck('#lbl', '#label') then AddLabel                             // insert a label
    else if lokCheck('#chk', '#check', '#checkbox') then AddCheckbox             // insert a CheckBox
//
//
    else if lokCheck('#msg', '#message') then ShowMsg
    else if lokCheck('#progress') then ProgressInc
    else if lokCheck('#progress_dlg') then ProgressDlg
    else if lokCheck('#dlg') then BafDlg
    else if lokCheck('#dlg_close') then BafDlgClose
//
    // Tree
    else if lokCheck('#tree_clear', '#cleartree') then ClearTree
    else if lokCheck('#tree_add', '#addtree') then AddTreeItem
    else if lokCheck('#tree_fill', '#filltree') then FillTree
    else if lokCheck('#tree_node', '#defnode') then AddDefNode
    else if lokCheck('#tree_fillsql', '#filltreesql') then FillTreeSql
    else if lokCheck('#tree_fillpath', '#filltreepath') then FillTreePath
    else if lokCheck('#tree_fillthread', '#filltreethread') then FillThread
    else if lokCheck('#tree_sel', '#tree_select', '#treesel') then TreeSelect2
    else if lokCheck('#tree_back', '#treeback') then TreeBack(false)
    else if lokCheck('#tree_fwd', '#treebackback') then TreeBack(true)
    else if lokCheck('#tree_clearnode', '#treeclearnode') then ClearTreeNode


//
    // Page
    else if lokCheck('#page', '#grid') then PageDefs
    else if lokCheck('#page_fill', '#grid_fill', '#fillgrid') then FillPage
    else if lokCheck('#page_prim', '#prim') then PrimaryDefs
    else if lokCheck('#page_cat', '#cat') then CatDefs
    else if lokCheck('#page_val', '#grid_val', '#grd_val') then PageValue
    else if lokCheck('#page_check', '#grid_check', '#grd_check') then PageCheck
    else if lokCheck('#page_ready') then PageReady
    else if lokCheck('#page_xls') then PageXls
    else if lokCheck('#page_pdf') then PagePDF

    else if lokCheck('#text_seg', '#segtext') then SegText
    else if lokCheck('#text_line', '#textline') then
        FLastSeg.AddLineUnchanged(FExecInter.LineP)
    else if lokCheck('#memo_seg', '#segmemo') then SegMemo

    else if lokCheck('#vl_seg', '#segvl') then SegVl                               // VL-Segment
    else if lokCheck('#vl_line', '#vlline') then SegVlLine
    else if lokCheck('#vl_data', '#vldata') then SegVlData
    else if lokCheck('#vl_hist', '#vlhist') then SegVlHist
    else if lokCheck('#vl_thread', '#vlthread') then SegVlThread

    else if lokCheck('#grd_seg', '#seggrd') then SegGrid                               // Grid-Segment
    else if lokCheck('#grd_col', '#grdcol') then SegGridCol(FExecInter.LineP, nil, -1, -1)
    else if lokCheck('#grd_data', '#grddata') then SegGridData
    else if lokCheck('#grd_add', '#grdadd') then SegGridAdd
    else if lokCheck('#grd_loop', '#grid_loop') then SegGridLoop
    else if lokCheck('#grd_calc') then SegGridCalc
    else if lokCheck('#grd_lookuplivefill') then PageLookupLiveFill
    else if lokCheck('#grd_lookupliveclear') then PageLookupLiveClear
    else if lokCheck('#grd_thread', '#grdthread') then SegGrdThread
    else if lokCheck('#grd_paste', '#grid_paste') then SegGridPaste
    else if lokCheck('#grd_ac', '#grid_ac') then SegGridAddOrChange
    else if lokCheck('#grd_pos', '#grid_pos') then SegGridPos
    else if lokCheck('#grd_sort', '#grid_sort') then SegGridSort
    else if lokCheck('#grd_dub', '#grid_dub') then SegGridDub



    else if lokCheck('#xgrd_seg', '#segxgrd') then SegXGrid                            // XGrid-Segment
    else if lokCheck('#xgrd_col', '#xgrdcol') then SegGridCol(FExecInter.LineP, nil, -1, -1)
    else if lokCheck('#xgrd_xcol', '#xgrdxcol') then SegXGridXCol
    else if lokCheck('#xgrd_data', '#xgrddata') then SegXGridData
    else if lokCheck('#xgrd_xdata', '#xgrdxdata') then SegXGridXData
    else if lokCheck('#xgrd_calc', '#xgrdxcalc') then SegXGridCalc

    else if lokCheck('#xxgrd_seg', '#segxxgrd') then SegXXGrid                            // Double-X-Segment
    else if lokCheck('#xxgrd_col', '#xxgrdcol') then SegGridCol(FExecInter.LineP, nil, -1, -1)
    else if lokCheck('#xxgrd_xcol', '#xxgrdxcol') then SegXGridXCol
    else if lokCheck('#xxgrd_xxcol', '#xxgrdxxcol') then SegXGridXCol(true)
    else if lokCheck('#xxgrd_xdata', '#xxgrdxdata') then SegXXGridXData
    else if lokCheck('#xxgrd_data', '#xxgrddata') then SegXXGridData

    else if lokCheck('#sgrd_seg', '#sgrdseg') then SegSGrid                            // SGrid-Segment
    else if lokCheck('#sgrd_node', '#sgrdnode') then SGridNode
    else if lokCheck('#sgrd_data', '#sgrddata') then SGridData
    else if lokCheck('#sgrd_hf', '#sgrdhf') then SGridHeaderFooter




    else if lokCheck('#btns_seg', '#segbtns', '#segbuttons') then SegGridButtons                 // Buttons
    else if lokCheck('#btns_btn', '#segbtn', '#segbutton') then SegGridButton

    else if lokCheck('#dia_seg') then SegDia                                          // Diagram
    else if lokCheck('#dia_col') then SegDiaCol
    else if lokCheck('#dia_data') then SegDiaData

    else if lokCheck('#map_seg') then SegMap                                          // Maps
    else if lokCheck('#map_data') then SegMapData

    else if lokCheck('#pic_seg') then SegPic                                          // Pictures


    else result := false;

  end; // function lokClient

  function lokSrvProc: boolean;
  begin
    result := true;
    if lokCheck('#frm', '#form') then SrvProcForm
    else if FExecInter.LineF = '#cout' then ConsoleOutSrvProc(false)                           // write into the console
    else if FExecInter.LineF = '#coutl' then ConsoleOutSrvProc(true)                      // write an info into the console
    else if FExecInter.LineF = '#srv_ops' then ServerOperations

    else result := false;
  end; // function lokSrvProc


begin
  result := true;
  LInter := FExecInter;
  try
    FExecInter := AExecInter;
    case gvInterType of
      itClient: result := lokClient;
      itSrvProc: result := lokSrvProc;
    end;
  finally
    FExecInter := LInter;
  end;
// function TBafFrmModule.InterpretLine
end;


procedure TBafFrmModule.LoadDataThreadTerminate(Sender: TObject);
var
  LCmd: string;
begin
  if (Sender is TBafLoadDataThread) and TBafLoadDataThread(Sender).Finished then begin
    LCmd := TBafLoadDataThread(Sender).ReadyCommand;
    if StrToIntDef(LCmd, -1) = -1 then
      TBafInterpreterLevel.ExecInNewLevel(LCmd, FExecInter, FInter);
  end;
  FThreadList.Remove(Sender);  // prüft intern auf Existenz
end;

procedure TBafFrmModule.LoadSegmentThreadTerminate(Sender: TObject);
begin
  FThreadList.Remove(Sender);  // prüft intern auf Existenz
  Application.ProcessMessages;
  frmMain.SpecialPageRefresh;
end;

end.


