unit foBafCode;

// this code is under the BAF fair use license (BFUL) - https://bafbal.de/index.php?title=Bful
// the code editor

interface

uses
  System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants,
  FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs, FMX.TabControl,
  FMX.Controls.Presentation, FMX.StdCtrls, FMX.ScrollBox, FMX.Memo, FMX.Layouts,
  FMX.TreeView, System.Rtti, FMX.Edit, System.StrUtils, Data.DB, System.Math,
  FMX.DialogService, FMX.Memo.Types, FMX.Grid.Style, FMX.Grid, FMX.ListBox,
  System.Hash, uBafControls, contnrs, uOsStuff, uBafFmxUtils, FMX.EditBox,
  FMX.SpinBox, FMX.ComboEdit, Winapi.Windows, foBafSqlEditor, System.NetEncoding,
  foBafSqlOpen, foBafSearch;

type
//  TCmd = class
//    FId: string;
//    FName: string;
//    FParent: string;
//    FCode: string;
//    FTimestamp: TDateTime;
//  end;

  TCodeItemData = class(TFmxObject)
  private
    FName: string;
    FCode: string;
    FParent: string;
    FID: string;
    FInserted: Char;
    FChanged: boolean;
    FConflicted: boolean;
    FItemList: TObjectList;
    FViewportY: single;
    procedure SetChanged(const Value: boolean);
    procedure SetCode(const Value: string);
    procedure SetID(const Value: string);
    procedure SetName(const Value: string);
    procedure SetParent(const Value: string);
    procedure SetInserted(const Value: Char);
    procedure SetConflicted(const Value: boolean);
    procedure RefreshItemNames;
  public
    FTimestamp: TDateTime;
    FTimestampDest: TDateTime;
    FNameOld: string;
    FCodeOld: string;
    FParentOld: string;
    constructor Create(AOwner: TComponent);
    destructor Destroy; override;
    procedure Revert;
    function Save: boolean;
    property Name: string read FName write SetName;
    property ID: string read FID write SetID;
    property Code: string read FCode write SetCode;
    property Parent: string read FParent write SetParent;
    property Changed: boolean read FChanged write SetChanged;
    property Conflicted: boolean read FConflicted write SetConflicted;
    property Inserted: Char read FInserted write SetInserted;
    property ItemList: TObjectList read FItemList;
    property ViewportY: single read FViewportY write FViewportY;
  end;


  TfrmBafCode = class(TForm)
    btnSave: TButton;
    btnCancel: TButton;
    pnlDesk: TPanel;
    btnClose: TButton;
    cmbCodeFontSize: TComboBox;
    sdExport: TSaveDialog;
    odImport: TOpenDialog;
    btnSaveRefresh: TButton;
    TabControl1: TTabControl;
    tabCode: TTabItem;
    Splitter1: TSplitter;
    pnlCodeLeft: TPanel;
    btnRoot: TButton;
    btnSub: TButton;
    btnCodeRefresh: TButton;
    btnCodeFullCollapse: TButton;
    btnCodeFullExpand: TButton;
    btnExport: TButton;
    btnImport: TButton;
    btnProject: TButton;
    pnlCodeRight: TPanel;
    memCode: TMemo;
    Label2: TLabel;
    edtCodeId: TEdit;
    edtCodeName: TEdit;
    Label1: TLabel;
    btnRevert: TButton;
    edtCodeParent: TEdit;
    Label3: TLabel;
    btnGUID: TButton;
    pnlCodeButtons: TPanel;
    btnCodeSave: TButton;
    tabHistory: TTabItem;
    Panel1: TPanel;
    sgHist: TStringGrid;
    scHistDateChg: TStringColumn;
    scHistUserChg: TStringColumn;
    scHistParent: TStringColumn;
    scHistName: TStringColumn;
    scHistCode: TStringColumn;
    Panel2: TPanel;
    memHist: TMemo;
    Label4: TLabel;
    edtHistUser: TEdit;
    Label5: TLabel;
    edtHistName: TEdit;
    Label6: TLabel;
    edtHistParent: TEdit;
    splitHist: TSplitter;
    tabSearch: TTabItem;
    tabSimpleGridWizard: TTabItem;
    tcWizard: TTabControl;
    tabWizSql: TTabItem;
    memWizSql: TMemo;
    btnWizSqlPrepare: TButton;
    btnWizRestart: TButton;
    tabWizParams: TTabItem;
    btnWizRestart2: TButton;
    sgWizParams: TStringGrid;
    StringColumn1: TStringColumn;
    scWizValue: TStringColumn;
    btnWizOpen: TButton;
    lblWizCon: TLabel;
    cmbWizDatabase: TComboBox;
    tabWizData: TTabItem;
    btnWizRestart3: TButton;
    btnWizConfig: TButton;
    sgWizData: TStringGrid;
    tabWizConfig: TTabItem;
    sgWizConfig: TStringGrid;
    gcWizConfigAktiv: TCheckColumn;
    gcWizConfigFieldname: TStringColumn;
    gcWizConfigCaption: TStringColumn;
    gcWizConfigWidth: TIntegerColumn;
    gcWizConfigStretch: TIntegerColumn;
    gcWizConfigLength: TIntegerColumn;
    gcWizConfigType: TStringColumn;
    gcWizConfigAlign: TStringColumn;
    gcWizConfigReadOnly: TCheckColumn;
    gcWizConfigData: TStringColumn;
    btnWizRestart4: TButton;
    btnWizCreateGrid: TButton;
    btnWizCreateVL: TButton;
    btnWizWizardGrid: TButton;
    btnWizWizardVl: TButton;
    btnWizCreateCsv: TButton;
    btnWizCreateXls: TButton;
    edtWizCreateCsvSep: TEdit;
    tabWizCode: TTabItem;
    btnWizRestart5: TButton;
    memWizCode: TMemo;
    Label14: TLabel;
    tabWizGridWiz: TTabItem;
    tabWizVlWiz: TTabItem;
    tabSqlExec: TTabItem;
    pnlSqlExecTools: TPanel;
    cbSqlExpedite: TCheckBox;
    cbSqlReplaceColumnNames: TCheckBox;
    pnlSqlExecDesk: TPanel;
    pnlSqlExecOben: TPanel;
    memSqlExec1: TMemo;
    btnSqlExec1: TButton;
    btnSqlGuid1: TButton;
    lblSqlExec1: TLabel;
    cbExecOnly: TCheckBox;
    Splitter2: TSplitter;
    Panel5: TPanel;
    memSqlExec2: TMemo;
    btnSqlExec2: TButton;
    btnSqlGuid2: TButton;
    lblSqlExec2: TLabel;
    tabSqlOpen: TTabItem;
    tabSqlOpen2: TTabItem;
    tabSqlOpen3: TTabItem;
    tabSqlSelect: TTabItem;
    tabTools: TTabItem;
    BafVertScrollBox1: TBafVertScrollBox;
    btnColumnConvertGo: TButton;
    btnCreateGuid: TButton;
    btnCryptCreateUnit: TButton;
    btnCryptGo: TButton;
    btnHashGo: TButton;
    cbCreateGuidCopy: TCheckBox;
    cbCryptCopy: TCheckBox;
    cmbColumnConvertConvertType: TComboBox;
    cmbHash: TComboBox;
    edtColumnConvertColumnName: TEdit;
    edtColumnConvertIndexName: TEdit;
    edtColumnConvertTableName: TEdit;
    edtCreateGuid: TEdit;
    edtCryptDest: TEdit;
    edtCryptSource: TEdit;
    edtHashDest: TEdit;
    edtHashSource: TEdit;
    Hash: TLabel;
    Label10: TLabel;
    Label11: TLabel;
    Label12: TLabel;
    Label13: TLabel;
    Label8: TLabel;
    Label9: TLabel;
    lblCryptDecrypt: TLabel;
    sbCryptNumber: TSpinBox;
    Label15: TLabel;
    Label16: TLabel;
    edtBasicAuthUser: TEdit;
    Label17: TLabel;
    edtBasicAuthPassword: TEdit;
    Label18: TLabel;
    edtBasicAuthResult: TEdit;
    btnBasicAuthGo: TButton;
    cbBasicAuthFormat: TCheckBox;
    cbBasicAuthCopy: TCheckBox;
    tabLists: TTabItem;
    Panel8: TPanel;
    btnListsFlip: TButton;
    cbListsRes: TCheckBox;
    tcList: TTabControl;
    tabListsLL: TTabItem;
    btnListLs2Ln: TButton;
    btnListLn2Ls: TButton;
    Label19: TLabel;
    Label21: TLabel;
    cbListLLIgnore: TCheckBox;
    edtListLLSeparator: TEdit;
    edtListLLQuote: TEdit;
    TabItem2: TTabItem;
    btnListsDb1: TButton;
    btnListsDbUpsert: TButton;
    btnListsDbUpsertData: TButton;
    Label20: TLabel;
    btnListsDbUpsertCsv: TButton;
    edtListsDbUpsertData: TEdit;
    sbListsDbUpsertCsv: TSpinBox;
    btnListsDbSelect: TButton;
    btnListsDbSelectCR: TButton;
    TabItem3: TTabItem;
    btnListSortInvert: TButton;
    btnListSortAlpha: TButton;
    btnListSortNumber: TButton;
    btnListSortDate: TButton;
    TabItem4: TTabItem;
    cbListTrimLeft: TCheckBox;
    cbListTrimRight: TCheckBox;
    btnListCrop: TButton;
    Label22: TLabel;
    cmbListVon: TComboBox;
    edtListVon: TEdit;
    Label23: TLabel;
    cmbListBis: TComboBox;
    edtListBis: TEdit;
    cbListEmpty: TCheckBox;
    TabItem1: TTabItem;
    memListCompose: TMemo;
    btnListCombine: TButton;
    TabItem5: TTabItem;
    btnListBase64E: TButton;
    btnListBase64D: TButton;
    TabItem6: TTabItem;
    btnListsUpper: TButton;
    btnListsLower: TButton;
    cmbList: TComboBox;
    Panel9: TPanel;
    memListsTop: TMemo;
    memListsBottom: TMemo;
    spltLists: TSplitter;
    cbCodeFav: TCheckBox;
    tabFav: TTabItem;
    pnlFavLeft: TPanel;
    btnFavSub: TButton;
    btnFavRefresh: TButton;
    Splitter3: TSplitter;
    tabLastChanges: TTabItem;
    sgLastChanges: TStringGrid;
    StringColumn2: TStringColumn;
    StringColumn3: TStringColumn;
    StringColumn4: TStringColumn;
    lblLastChanges: TLabel;
    tabSearch2: TTabItem;
    TabItem7: TTabItem;
    lblCodeZeile: TLabel;
    procedure FormDestroy(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure btnRootClick(Sender: TObject);
    procedure tvCodeChange(Sender: TObject);
    procedure memCodeChangeTracking(Sender: TObject);
    procedure edtCodeNameChangeTracking(Sender: TObject);
    procedure btnRevertClick(Sender: TObject);
    procedure btnCodeSaveClick(Sender: TObject);
    procedure btnSubClick(Sender: TObject);
    procedure edtCodeParentChangeTracking(Sender: TObject);
    procedure btnCodeRefreshClick(Sender: TObject);
    procedure btnGUIDClick(Sender: TObject);
    procedure btnCodeSql1Click(Sender: TObject);
    procedure TabControl1Change(Sender: TObject);
    procedure sgHistSelectCell(Sender: TObject; const ACol, ARow: Integer;
      var CanSelect: Boolean);
    procedure btnCodeFullExpandClick(Sender: TObject);
    procedure btnCodeFullCollapseClick(Sender: TObject);
    procedure btnSaveClick(Sender: TObject);
    procedure btnSqlExec1Click(Sender: TObject);
    procedure btnSqlGuid1Click(Sender: TObject);
    procedure btnHashGoClick(Sender: TObject);
    procedure cmbCodeFontSizeChange(Sender: TObject);
    procedure btnColumnConvertGoClick(Sender: TObject);
    procedure btnCreateGuidClick(Sender: TObject);
    procedure btnWizRestartClick(Sender: TObject);
    procedure btnWizSqlPrepareClick(Sender: TObject);
    procedure FormShow(Sender: TObject);
    procedure btnWizOpenClick(Sender: TObject);
    procedure btnWizConfigClick(Sender: TObject);
    procedure btnWizCreateGridClick(Sender: TObject);
    procedure btnWizCreateVLClick(Sender: TObject);
    procedure btnExportClick(Sender: TObject);
    procedure btnImportClick(Sender: TObject);
    procedure btnCryptCreateUnitClick(Sender: TObject);
    procedure btnCryptGoClick(Sender: TObject);
    procedure pnlCodeButtonsResized(Sender: TObject);
    procedure btnProjectClick(Sender: TObject);
    procedure btnSaveRefreshClick(Sender: TObject);
    procedure btnListsFlipClick(Sender: TObject);
    procedure btnListsDb1Click(Sender: TObject);
    procedure btnListsDbUpsertClick(Sender: TObject);
    procedure btnListsDbUpsertDataClick(Sender: TObject);
    procedure btnWizWizardGridClick(Sender: TObject);
    procedure btnWizWizardVlClick(Sender: TObject);
    procedure btnBasicAuthGoClick(Sender: TObject);
    procedure cmbListChange(Sender: TObject);
    procedure tcListChange(Sender: TObject);
    procedure btnListLs2LnClick(Sender: TObject);
    procedure btnListsDbUpsertCsvClick(Sender: TObject);
    procedure btnListSortInvertClick(Sender: TObject);
    procedure btnListSortAlphaClick(Sender: TObject);
    procedure btnListCropClick(Sender: TObject);
    procedure btnListLn2LsClick(Sender: TObject);
    procedure btnListCombineClick(Sender: TObject);
    procedure btnListBase64EClick(Sender: TObject);
    procedure btnListBase64DClick(Sender: TObject);
    procedure btnWizCreateCsvClick(Sender: TObject);
    procedure btnWizCreateXlsClick(Sender: TObject);
    procedure btnListsUpperClick(Sender: TObject);
    procedure btnListsDbSelectClick(Sender: TObject);
    procedure cbCodeFavChange(Sender: TObject);
    procedure btnFavSubClick(Sender: TObject);
    procedure sgLastChangesCellDblClick(const Column: TColumn;
      const Row: Integer);
    procedure lblLastChangesDblClick(Sender: TObject);
    procedure memCodeViewportPositionChange(Sender: TObject;
      const OldViewportPosition, NewViewportPosition: TPointF;
      const ContentSizeChanged: Boolean);
  private
    FCodeBtnsDef: TStringList;
    procedure CodeBtnsInit;
    function ListsCreate2Upsert(AList: TStrings; APost: string): string;
    function ListsCreate2Select(AList: TStrings; ACrLf: boolean): string;
    function GetMemo1Text: string;
  private
    FCodeLoaded: boolean;
    FSelectedData: TCodeItemData;
    FWizColumns: TObjectList;
    FWizSql: string;
    FSqlExecButttonList: TObjectList;
    FInit: boolean;
    FPosAusIni: boolean;
    FBafSqlEditor: TfrmSqlEditor;
    FBafSqlOpen1: TfrmSqlOpen;
    FBafSqlOpen2: TfrmSqlOpen;
    FBafSqlOpen3: TfrmSqlOpen;
    FBafSearch1: TfrmBafSearch;
    FBafSearch2: TfrmBafSearch;
    FUserFav: TStringList;
    FParentList: TStringList;
    FNameList: TStringList;
    FLastChangesDate: TDateTime;
    procedure LoadCode2;
    procedure LoadHistory;
    procedure LoadLastChanges;
    procedure WizAddSql;
    function GetWizCon: string;
    procedure ExportNode(AFilename: string; AItem: TBafTreeViewItem);
    procedure InitSqlExecButtons;
    procedure SqlExecButttonClick(Sender: TObject);
    procedure RefreshFav;
  public
    tvCode, tvFav: TBafTreeView;
    procedure Execute(ACommand: string = '');
    function FindCommand(ACommand: string): boolean;
    class function GetData(AItem: TBafTreeViewItem; var AData: TCodeItemData): boolean;
  end;

var
  frmBafCode: TfrmBafCode;

implementation

{$R *.fmx}

uses dmMain, uBafTypes, udataHistSql, uBafCrypt, uBafCodeUtils, foBafDialog,
  foBafCommandWizard, foBafCodeWizGridWiz, foBafCodeWizVlWiz, uBafDataCache,
  uBafCsvXlsGenerator;

var
  mvCodeUpdating: boolean;

{ TfrmBafCode }

procedure TfrmBafCode.btnCodeSaveClick(Sender: TObject);
begin
  if Assigned(FSelectedData) then begin
    if not FSelectedData.Save then
      TfrmBafDialog.ShowMessage(dataMain.ProgName, 'Kommando '
          + FSelectedData.Name + ' konnte nicht gespeichert werden, da es '
          + 'zwischenteitlich geändert wurde.', frmBafCode);
  end;
end;

procedure TfrmBafCode.btnCodeSql1Click(Sender: TObject);
begin
  AddRemovePrefix((Sender as TButton).Text + ' ', memCode);
end;

procedure TfrmBafCode.btnColumnConvertGoClick(Sender: TObject);
var
  LCount: integer;
begin
  LCount := dataMain.ColumnConvert(edtColumnConvertTableName.Text,
      edtColumnConvertIndexName.Text, edtColumnConvertColumnName.Text,
      cmbColumnConvertConvertType.ItemIndex);
  ShowMessage(Format('Conversion read, %d rows changed', [LCount]));
end;

procedure TfrmBafCode.btnCreateGuidClick(Sender: TObject);
begin
  edtCreateGuid.Text := BafGetGuid;
  edtCreateGuid.SelectAll;
  if cbCreateGuidCopy.IsChecked then
    Clipboard.AsText := edtCreateGuid.Text;
end;

procedure TfrmBafCode.btnCryptCreateUnitClick(Sender: TObject);
begin
  Clipboard.AsText := BafGetXorValues;
end;

procedure TfrmBafCode.btnCryptGoClick(Sender: TObject);
begin
  edtCryptDest.Text := BafEncrypt(round(sbCryptNumber.Value), edtCryptSource.Text);
  lblCryptDecrypt.Text := BafDecrypt(edtCryptDest.Text);
  if cbCryptCopy.IsChecked then
    Clipboard.AsText := edtCryptDest.Text;
end;

procedure TfrmBafCode.btnExportClick(Sender: TObject);
var
  fn: string;
  sl: TStringList;

begin
  if sdExport.Execute then begin
    fn := sdExport.FileName;
    if FileExists(fn) then begin
      TDialogService.MessageDialog('File ' + fn + ' already exists. Overwrite?',
          TMsgDlgType.mtConfirmation, mbYesNo, TMsgDlgBtn.mbNo, 0,
        procedure(const aResult: TModalResult)
        begin
          if aResult = mrYes then
            ExportNode(fn, tvCode.BafSelected);
        end);
    end
    else
      ExportNode(fn, tvCode.BafSelected);
  end;
// procedure TfrmBafCode.btnExportClick
end;

procedure TfrmBafCode.btnFavSubClick(Sender: TObject);
var
  LData: TCodeItemData;
  LParent, LItem, LSubItem: TBafTreeViewItem;
  i: integer;
  s, t: string;
begin
  LParent := tvFav.BafSelected;
  LParent.Expand;
  if Assigned(tvFav.Selected) and GetData(LParent, FSelectedData) then begin
    LItem := TBafTreeViewItem.Create(tvFav);
    t := LParent.Text;
    if t[Length(t)] = '*' then
      Delete(t, Length(t), 1);
    s := Trim(t) + '_';
    LItem.Parent := LParent;
    LItem.Text := s;
    LData := TCodeItemData.Create(LItem);
    LData.Inserted := 'I';
    LData.ID := BafGetGuid;
    LData.Name := s;
    LData.Parent := FSelectedData.ID;
    LData.FItemList.Add(LItem);
    LData.Changed := false;
    LItem.BafData := LData;
    tvFav.Selected := LItem;
  end;
end;

procedure TfrmBafCode.btnGUIDClick(Sender: TObject);
begin
  memCode.InsertAfter(memCode.CaretPosition, '  d=' + BafGetGuid,
      [TInsertOption.MoveCaret, TInsertOption.CanUndo]);
end;

procedure TfrmBafCode.btnHashGoClick(Sender: TObject);
begin
  case cmbHash.ItemIndex of
    0: edtHashDest.Text := THashSHA2.GetHashString(edtHashSource.Text,
          THashSHA2.TSHA2Version.SHA512);
    1: edtHashDest.Text := THashSHA2.GetHashString(edtHashSource.Text,
          THashSHA2.TSHA2Version.SHA512).ToUpper;
    2: edtHashDest.Text := THashSHA2.GetHashString(edtHashSource.Text,
          THashSHA2.TSHA2Version.SHA512_256);
    3: edtHashDest.Text := THashSHA2.GetHashString(edtHashSource.Text,
          THashSHA2.TSHA2Version.SHA512_256).ToUpper;
    4: edtHashDest.Text := THashSHA2.GetHashString(edtHashSource.Text,
          THashSHA2.TSHA2Version.SHA512_224);
    5: edtHashDest.Text := THashSHA2.GetHashString(edtHashSource.Text,
          THashSHA2.TSHA2Version.SHA512_224).ToUpper;
    6: edtHashDest.Text := THashSHA2.GetHashString(edtHashSource.Text,
          THashSHA2.TSHA2Version.SHA384);
    7: edtHashDest.Text := THashSHA2.GetHashString(edtHashSource.Text,
          THashSHA2.TSHA2Version.SHA384).ToUpper;
    8: edtHashDest.Text := THashSHA2.GetHashString(edtHashSource.Text,
          THashSHA2.TSHA2Version.SHA256);
    9: edtHashDest.Text := THashSHA2.GetHashString(edtHashSource.Text,
          THashSHA2.TSHA2Version.SHA256).ToUpper;
    10: edtHashDest.Text := THashSHA2.GetHashString(edtHashSource.Text,
          THashSHA2.TSHA2Version.SHA224);
    11: edtHashDest.Text := THashSHA2.GetHashString(edtHashSource.Text,
          THashSHA2.TSHA2Version.SHA224).ToUpper;

    12: edtHashDest.Text := THashSHA1.GetHashString(edtHashSource.Text);
    13: edtHashDest.Text := THashSHA1.GetHashString(edtHashSource.Text).ToUpper;

    14: edtHashDest.Text := THashMD5.GetHashString(edtHashSource.Text);
    15: edtHashDest.Text := THashMD5.GetHashString(edtHashSource.Text).ToUpper;
  end;
end;

procedure TfrmBafCode.btnImportClick(Sender: TObject);
var
  sl, slLog, slCode: TStringList;
  LCounter: integer;
  LStart: boolean;
  s, LId, LName, LParent: string;

  procedure lokLine;
  begin
    case LCounter of
      0: LId := s;
      1: LName := s;
      2: if not LStart then
        LParent := s
        else
          LStart := false;
      else
        slCode.Add(s);
    end;
    inc(LCounter);
  end; // procedure lokLine;

  procedure lokImport;
  var
    i: integer;
  begin
    dataMain.StartTransaction(DC_DEFAULT);
    try
      for i := 0 to sl.Count - 1 do begin
        s := sl[i];
        if s = SEG_CODE then begin
          if not LStart then begin
            slCode.Clear;
          end;
          LCounter := 0;
        end
        else          { TODO : Import wieder gängig machen }
          lokLine;
      end;
      if LId <> '' then
      dataMain.Commit(DC_DEFAULT);
    except
      on E: Exception do begin
        ShowMessage(E.Message);
        dataMain.Rollback(DC_DEFAULT);
      end;
    end;
  end; // procedure lokImport;

begin
  if odImport.Execute then begin
    sl := TStringList.Create;
    slLog := TStringList.Create;
    slCode := TStringList.Create;
    try
      sl.LoadFromFile(odImport.FileName);
      LStart := true;
      LCounter := -1;
      lokImport;
    finally
      FreeAndNil(slCode);
      FreeAndNil(slLog);
      FreeAndNil(sl);
    end;
  end;
  LoadCode2;
end;

procedure TfrmBafCode.btnListBase64DClick(Sender: TObject);
begin
  memListsBottom.Lines.Text := TNetEncoding.Base64String.Decode(memListsTop.Lines.Text);
end;

procedure TfrmBafCode.btnListBase64EClick(Sender: TObject);
begin
  memListsBottom.Lines.Text := TNetEncoding.Base64String.Encode(memListsTop.Lines.Text);
end;

procedure TfrmBafCode.btnListCombineClick(Sender: TObject);
var
  i: integer;
  sl: TStringList;
  s, t, LTemp: string;
begin
  LTemp := memListCompose.Lines.Text;
  sl := TStringList.Create;
  try
    for i := 0 to memListsTop.Lines.Count - 1 do begin
      s := memListsTop.Lines[i];
      t := StringReplace(LTemp, '%s', s, [rfReplaceAll]);
      sl.Add(t);
    end;
    memListsBottom.Lines.Assign(sl);
    if cbListsRes.IsChecked then
      Clipboard.AsText := sl.Text;
  finally
    sl.Free;
  end;
end;

procedure TfrmBafCode.btnListCropClick(Sender: TObject);
var
  i: integer;
  s: string;

  procedure lokVon;
  var
    p: integer;
    t: string;
  begin
    t := edtListVon.Text;
    p := Pos(t, s);
    case cmbListVon.ItemIndex of
      1: s := copy(s, StrToIntDef(t, 1), MaxInt);
      2: s := copy(s, System.Math.Max(1, Length(s) - StrToIntDef(t, 1)), MaxInt);
      3: s := IfThen(p = 0, '', copy(s, p, MaxInt));
      4: s := IfThen(p = 0, s, copy(s, p, MaxInt));
      5: s := IfThen(p = 0, '', copy(s, p + Length(t), MaxInt));
      6: s := IfThen(p = 0, s, copy(s, p + Length(t), MaxInt));
    end;
  end; // procedure lokVon

  procedure lokBis;
  var
    p: integer;
    t: string;
  begin
    t := edtListBis.Text;
    p := Pos(t, s);
    case cmbListBis.ItemIndex of
      1: s := copy(s, 1, StrToIntDef(t, 1));
      2: s := copy(s, 1, Length(s) - StrToIntDef(t, 1));
      3: s := IfThen(p = 0, '', copy(s, 1, p + Length(t) - 1));
      4: s := IfThen(p = 0, s, copy(s, 1, p + Length(t) - 1));
      5: s := IfThen(p = 0, '', copy(s, 1, p - 1));
      6: s := IfThen(p = 0, s, copy(s, 1, p - 1));
    end;
  end; // procedure lokBis

begin
  memListsBottom.Lines.Clear;
  for i := 0 to memListsTop.Lines.Count - 1 do begin
    s := memListsTop.Lines[i];
    if cbListTrimLeft.IsChecked then
      s := TrimLeft(s);
    if cbListTrimRight.IsChecked then
      s := TrimRight(s);
    lokVon;
    lokBis;
    if (s <> '') or cbListEmpty.IsChecked then
      memListsBottom.Lines.Add(s);
  end;
  if cbListsRes.IsChecked then
    Clipboard.AsText := memListsBottom.Lines.Text;
end;

procedure TfrmBafCode.btnListLn2LsClick(Sender: TObject);
var
  LLine, s, LSep, LQuote: string;
  sl: TStringList;
  p: integer;

  procedure lokUnquote;
  begin
    if LQuote <> '' then begin
      p := Pos(LQuote, s);
      if p > 0 then begin
        Delete(s, 1, Length(LQuote));
        p := Pos(LQuote, s);
        if p > 0 then
          sl.Add(copy(s, 1, p - 1));
      end;
    end
    else
      sl.Add(s);
  end; // procedure lokUnquote

begin
  LSep := edtListLLSeparator.Text;
  if AnsiCompareText(LSep, 'tab') = 0 then
    LSep := #9;
  LQuote := edtListLLQuote.Text;
  sl := TStringList.Create;
  try
    LLine := memListsTop.Lines.Text;
    p := Pos(LSep, LLine);
    while p > 0 do begin
      s := copy(LLine, 1, p - 1);
      Delete(LLine, 1, p + Length(LSep) - 1);
      lokUnquote;
      p := Pos(LSep, LLine);
    end;
    if Trim(LLine) <> '' then begin
      s := LLine;
      lokUnquote;
    end;
    memListsBottom.Lines.Text := sl.Text;
    if cbListsRes.IsChecked then
      Clipboard.AsText := sl.Text;
  finally
    sl.Free;
  end;
// procedure TfrmBafCode.btnListLn2LsClick
end;

procedure TfrmBafCode.btnListLs2LnClick(Sender: TObject);
// List -> Line
var
  i: integer;
  s, LResult, LSep, LQuote: string;
begin
  LSep := edtListLLSeparator.Text;
  if AnsiCompareText(LSep, 'tab') = 0 then
    LSep := #9;
  LQuote := edtListLLQuote.Text;
  for i := 0 to memListsTop.Lines.Count - 1 do begin
    s := Trim(memListsTop.Lines[i]);
    if (s <> '') or not cbListLLIgnore.IsChecked then
      LResult := LResult + LSep + LQuote + memListsTop.Lines[i] + LQuote;
  end;
  LResult := copy(LResult, Length(LSep) + 1, MaxInt);
  memListsBottom.Lines.Text := LResult;
  if cbListsRes.IsChecked then
    Clipboard.AsText := LResult;
end;

procedure TfrmBafCode.btnListsDb1Click(Sender: TObject);
// List -> ('item1','item2',...)
var
  i: integer;
  s, LResult: string;
begin
  for i := 0 to memListsTop.Lines.Count - 1 do begin
    s := Trim(memListsTop.Lines[i]);
    if s <> '' then
      LResult := LResult + ', ' + QuotedStr(s);
  end;
  LResult := '(' + copy(LResult, 3, MaxInt) + ')';
  memListsBottom.Lines.Text := LResult;
  if cbListsRes.IsChecked then
    Clipboard.AsText := LResult;
end;

procedure TfrmBafCode.btnListsDbSelectClick(Sender: TObject);
// create -> select
begin
  memListsBottom.Lines.Text := ListsCreate2Select(memListsTop.Lines,
      Sender = btnListsDbSelectCR);
  if cbListsRes.IsChecked then
    Clipboard.AsText := memListsBottom.Lines.Text;
end;

procedure TfrmBafCode.btnListsDbUpsertClick(Sender: TObject);
// create -> upsert
begin
  memListsBottom.Lines.Text := ListsCreate2Upsert(memListsTop.Lines, '');
  if cbListsRes.IsChecked then
    Clipboard.AsText := memListsBottom.Lines.Text;
end;

procedure TfrmBafCode.btnListsDbUpsertCsvClick(Sender: TObject);
// create -> upsert
begin
  memListsBottom.Lines.Text := ListsCreate2Upsert(memListsTop.Lines,
      '$CSV(' + sbListsDbUpsertCsv.Text + ',%s)');
  if cbListsRes.IsChecked then
    Clipboard.AsText := memListsBottom.Lines.Text;
end;

procedure TfrmBafCode.btnListsDbUpsertDataClick(Sender: TObject);
// create -> upsert
begin
  memListsBottom.Lines.Text := ListsCreate2Upsert(memListsTop.Lines,
      '$DATA(' + edtListsDbUpsertData.Text + ',%s)');
  if cbListsRes.IsChecked then
    Clipboard.AsText := memListsBottom.Lines.Text;
end;

procedure TfrmBafCode.btnListsFlipClick(Sender: TObject);
var
  s: string;
begin
  s := memListsTop.Lines.Text;
  memListsTop.Lines.Text := memListsBottom.Lines.Text;
  memListsBottom.Lines.Text := s;
  if cbListsRes.IsChecked then
    Clipboard.AsText := s;
end;

procedure TfrmBafCode.btnProjectClick(Sender: TObject);
var
  LCmdWiz, LCmdName, LSql: string;
  LData: TCodeItemData;
  LRoot: TBafTreeViewItem;

  function lokInsertCode(AName, AParentId: string; AParent: TFmxObject): TBafTreeViewItem;
  var
    LList: TStringList;
    LDataset: TDataSet;
    LCode, LId, LName, LParent: string;
    i: integer;
    LItem, LSubItem: TBafTreeViewItem;
    LParams: TBafParams;
  begin
    LSql := dataMain.GetSqlTextFromDevtext('_code_wizard_byname', '');
    if LSql = '' then
      LSql := 'select * from sys_devtext where name = :kname';
    LParams := dataMain.QueryPrepare(dataMain.DefaultCon, CMD_WIZ, LSql);
    LParams.ParamAsString('kname', AName);
    LDataset := dataMain.QueryOpen(dataMain.DefaultCon, CMD_WIZ);
    if not LDataset.Eof then begin
      LCode := LDataset.FieldByName('ctext').AsString;
      LId := LDataset.FieldByName('sys_devtext_id').AsString;
    end
    else begin
      LCode := '';
      LId := '';
    end;

    if LId <> '' then begin
      LName := StringReplace(AName, LCmdWiz, LCmdName,
          [rfReplaceAll, rfIgnoreCase]);

      LItem := TBafTreeViewItem.Create(tvCode);
      LItem.Parent := AParent;
      LItem.Text := LName;
      LData := TCodeItemData.Create(LItem);
      LData.Inserted := 'I';
      LData.ID := BafGetGuid;
      LData.Name := LName;
      LData.FItemList.Add(LItem);
      LData.Code := StringReplace(LCode, LCmdWiz, LCmdName,
          [rfReplaceAll, rfIgnoreCase]);
      LData.Parent := AParentId;
      LItem.BafData := LData;
      LData.Changed := false;
      LData.Changed := true;

      LList := TStringList.Create;
      try
        LSql := dataMain.GetSqlTextFromDevtext('_code_wizard_byparent', '');
        if LSql = '' then
          LSql := 'select name from sys_devtext where parent = :kparent';
        LParams := dataMain.QueryPrepare(dataMain.DefaultCon, CMD_WIZ2, LSql);
        LParams.ParamAsString('kparent', LId);
        LDataset := dataMain.QueryOpen(dataMain.DefaultCon, CMD_WIZ2);
        while not LDataset.Eof do begin
          LList.Add(LDataset.Fields[0].AsString);
          LDataset.Next;
        end;
        LParent := LData.ID;
        for i := 0 to LList.Count - 1 do
          lokInsertCode(LList[i], LParent, LItem);
      finally
        LList.Free;
      end;
    end; // if LId <> ''
    result := LItem;
  end; // procedure lokInsertCode


begin
  if TfrmCommandWizard.Execute(LCmdWiz, LCmdName) then begin
    LRoot := lokInsertCode(LCmdWiz, '', tvCode);
    tvCode.Selected := LRoot;
  end;
// procedure TfrmBafCode.btnProjectClick
end;


procedure TfrmBafCode.btnRevertClick(Sender: TObject);
begin
  if Assigned(FSelectedData) then begin
    FSelectedData.Revert;
    if TabControl1.ActiveTab = tabCode then
      tvCodeChange(tvCode);
    if TabControl1.ActiveTab = tabFav then
      tvCodeChange(tvFav);
  end;
end;

procedure TfrmBafCode.btnRootClick(Sender: TObject);
var
  LData: TCodeItemData;
  LItem, LSubItem: TBafTreeViewItem;
begin
  LItem := TBafTreeViewItem.Create(tvCode);
  LItem.Parent := tvCode;
  LItem.Text := 'Neue Routine';
  LData := TCodeItemData.Create(LItem);
  LData.Inserted := 'I';
  LData.ID := BafGetGuid;
  LData.Name := 'Neue Routine';
  LData.FItemList.Add(LItem);
  LData.Changed := false;
  LItem.BafData := LData;
  tvCode.Selected := LItem;
end;

procedure TfrmBafCode.btnSaveClick(Sender: TObject);
// save or revert all (and close)
var
  i: integer;
  LData: TCodeItemData;
  LError: boolean;
  LErrorList: string;

  procedure lokSave(AItem: TBafTreeViewItem);
  var
    i: integer;
  begin
    if GetData(AItem, LData) then begin
      if LData.Changed then begin
        if Sender = btnSave then begin
          if not LData.Save then begin
            LError := true;
            LErrorList := LErrorList + LData.Name + #13#10;
          end;
        end
        else if Sender = btnCancel then
          LData.Revert;
      end;
    end;
    // all sub items
    for i := 0 to AItem.Count - 1 do
      lokSave(AItem.Items[i]);
  end;

begin
  LError := false;
  for i := 0 to tvCode.Count - 1 do
    lokSave(tvCode.BafItems[i]);
  for i := 0 to tvFav.Count - 1 do
    lokSave(tvFav.BafItems[i]);
  if TabControl1.ActiveTab = tabCode then
    tvCodeChange(tvCode)
  else
    tvCodeChange(tvFav);
  if LError then begin
    TfrmBafDialog.ShowMessage(dataMain.ProgName, 'Die folgenden Kommandos '
        + 'konnte nicht gespeichert werden, ' + #13#10
        + 'da sie zwischenteitlich geändert wurden:' + #13#10#13#10
        + LErrorList, frmBafCode);
  end
  else
    Close;
end;

procedure TfrmBafCode.btnSaveRefreshClick(Sender: TObject);
var
  i: integer;
  LData: TCodeItemData;

  procedure lokSave(AItem: TBafTreeViewItem);
  var
    i: integer;
  begin
    if GetData(AItem, LData) then begin
      if LData.Changed then
        LData.Save;
    end;
    // all sub items
    for i := 0 to AItem.Count - 1 do
      lokSave(AItem.Items[i]);
  end;

begin
  for i := 0 to tvCode.Count - 1 do
    lokSave(tvCode.BafItems[i]);
  for i := 0 to tvFav.Count - 1 do
    lokSave(tvFav.BafItems[i]);
  tvCodeChange(tvCode);
  tvCodeChange(tvFav);
  btnCodeRefreshClick(Sender);
end;

procedure TfrmBafCode.btnSqlExec1Click(Sender: TObject);
var
  LSql, s: string;
  LParams: TParams;
  LCount, LStart, LLength: integer;
begin
  lblSqlExec1.Text := '';
  lblSqlExec2.Text := '';
  LStart := memSqlExec1.SelStart;
  LLength := memSqlExec1.SelLength;
  Application.ProcessMessages;
  if Sender = btnSqlExec1 then
    LSql := GetMemo1Text
  else if Sender = btnSqlExec2 then
    LSql := memSqlExec2.Lines.Text;
  if LSql <> '' then begin
    dataMain.QueryPrepare(dataMain.DefaultCon, QEN_COMMAND, LSql);
    LCount := dataMain.QueryExecute(dataMain.DefaultCon, QEN_COMMAND);
    if LCount < 1000 then
      s := Format('Rows affected: %d', [LCount])
    else
      s := Format('Rows aff.: %d', [LCount]);
    if Sender = btnSqlExec1 then
      lblSqlExec1.Text := s
    else if Sender = btnSqlExec2 then
      lblSqlExec2.Text := s;
  end
  else
    ShowMessage('no statement');
  if cbExecOnly.IsChecked then begin
    if memSqlExec1.CanFocus then
      memSqlExec1.SetFocus;
    memSqlExec1.SelStart := LStart;
    memSqlExec1.SelLength := LLength;
  end;
end;

procedure TfrmBafCode.btnSqlGuid1Click(Sender: TObject);
begin
  if Sender = btnSqlGuid1 then
    memSqlExec1.InsertAfter(memSqlExec1.CaretPosition, BafGetGuid,
        [TInsertOption.MoveCaret, TInsertOption.CanUndo])
  else if Sender = btnSqlGuid2 then
    memSqlExec2.InsertAfter(memSqlExec2.CaretPosition, BafGetGuid,
        [TInsertOption.MoveCaret, TInsertOption.CanUndo]);
end;

procedure TfrmBafCode.btnSubClick(Sender: TObject);
var
  LData: TCodeItemData;
  LParent, LItem, LSubItem: TBafTreeViewItem;
  i: integer;
  s, t: string;
begin
  LParent := tvCode.BafSelected;
  LParent.Expand;
  if Assigned(tvCode.Selected) and GetData(LParent, FSelectedData) then begin
    LItem := TBafTreeViewItem.Create(tvCode);
    t := LParent.Text;
    if t[Length(t)] = '*' then
      Delete(t, Length(t), 1);
    s := Trim(t) + '_';
    LItem.Parent := LParent;
    LItem.Text := s;
    LData := TCodeItemData.Create(LItem);
    LData.Inserted := 'I';
    LData.ID := BafGetGuid;
    LData.Name := s;
    LData.Parent := FSelectedData.ID;
    LData.FItemList.Add(LItem);
    LData.Changed := false;
    LItem.BafData := LData;
    tvCode.Selected := LItem;
  end;
end;

procedure TfrmBafCode.btnWizConfigClick(Sender: TObject);
var
  LDCol, LRow: integer;
  s: string;

  function lokGenerateCaption(AText: string): string;
  var
    i: integer;
  begin
    result := AText;
    for i := 1 to Length(AText) - 1 do begin
      if i = 1 then
        result[i] := AnsiUpperCase(AText[i])[1]
      else if AText[i] = '_' then begin
        result[i] := ' ';
        result[i + 1] := AnsiUpperCase(AText[i + 1])[1];
      end;
    end;
  end;

begin
  sgWizConfig.RowCount := 0;
  for LDCol := 0 to sgWizData.ColumnCount - 1 do begin
    LRow := sgWizConfig.RowCount;
    sgWizConfig.RowCount := sgWizConfig.RowCount + 1;
    s := AnsiLowerCase(sgWizData.Columns[LDCol].Header);
    sgWizConfig.Cells[0, LRow] := IfThen((s = 'usrchg') or (s = 'datechg')
       or (s = 'progchg'), 'False', 'True');
    sgWizConfig.Cells[1, LRow] := s;
    s := lokGenerateCaption(s);
    sgWizConfig.Cells[2, LRow] := s;
    sgWizConfig.Cells[8, LRow] := IfThen(LDCol = 0, 'True', 'False');
    if sgWizData.Row >= 0 then
      sgWizConfig.Cells[9, LRow] := sgWizData.Cells[LDCol, sgWizData.Row];

  end;
  tcWizard.ActiveTab := tabWizConfig;
  gcWizConfigData.Width := 300;
end;

procedure TfrmBafCode.btnWizOpenClick(Sender: TObject);
var
  LParams: TBafParams;
  LData: TDataSet;
  LText, LSql: string;
  LColumn: TStringColumn;

  procedure lokStatement;
  var
    i: integer;
    LInBrackets: boolean;
  begin
    FWizSql := '';
    LInBrackets := false;
    LSql := memWizSql.Lines.Text;
    for i := 1 to Length(LSql) do begin
      if LSql[i] = '{' then
        LInBrackets := true
      else  if LSql[i] = '}' then
        LInBrackets := false
      else if not LInBrackets then
        FWizSql := FWizSql + LSQL[i];


    end;
    LParams := dataMain.QueryPrepare(GetWizCon, QEN_COMMAND, FWizSql);
    for i := 0 to sgWizParams.RowCount - 1 do
      LParams.ParamAsString(sgWizParams.Cells[0, i], sgWizParams.Cells[1, i]);
    LData := dataMain.QueryOpen(GetWizCon, QEN_COMMAND);
  end; // procedure lokStatement;

  procedure lokColumns;
  var
    i, w: integer;
  begin
    FWizColumns.Clear;
    for i := 0 to LData.FieldCount - 1 do begin
      LText := LData.Fields[i].FieldName;
      LColumn := TStringColumn.Create(sgWizData);
      FWizColumns.Add(LColumn);
      LColumn.Parent := sgWizData;
      LColumn.Header := LText;
      LColumn.Width := sgWizData.Canvas.TextWidth(LText) + 10;
      if LData.Fields[i].DataType in [ftString, ftWideString] then begin
        w := System.Math.Min(40, LData.Fields[i].DataSize) * 7;
        if LColumn.Width < w then
          LColumn.Width := w;
      end;
    end;
  end; // procedure lokColumns

  procedure lokData;
  var
    i, LCount, LRow: integer;
  begin
    LCount := 0;
    while not LData.Eof do begin
      LRow := sgWizData.RowCount;
      sgWizData.RowCount := sgWizData.RowCount + 1;
      for i := 0 to LData.FieldCount - 1 do
        sgWizData.Cells[i, LRow] := LData.Fields[i].AsString;

      inc(LCount);
      if LCount >= 42 then
        exit;
      LData.Next;
    end;
  end; // procedure lokData

begin
  sgWizData.RowCount := 0;
  lokStatement;
  lokColumns;
  lokData;
  tcWizard.ActiveTab := tabWizData;
  if sgWizData.RowCount > 0 then
    sgWizData.Row := 0;
// procedure TfrmBafCode.btnWizOpenClick
end;

procedure TfrmBafCode.btnWizRestartClick(Sender: TObject);
begin
  tcWizard.ActiveTab := tabWizSql;
end;

procedure TfrmBafCode.btnWizSqlPrepareClick(Sender: TObject);
type
  TStat = (sNone, sInParam, sInValue);
var
  LSql, LParam, LValue: string;
  i, LParamStart, LValueStart: integer;
  LStat: TStat;

  procedure lokWrite(AParam, AValue: string);
  begin
    sgWizParams.RowCount := sgWizParams.RowCount + 1;
    sgWizParams.Cells[0, sgWizParams.RowCount - 1] := AParam;
    sgWizParams.Cells[1, sgWizParams.RowCount - 1] := AValue;
    LStat := sNone;
  end;

begin
  sgWizParams.RowCount := 0;
  LSql := memWizSql.Lines.Text;
  LStat := sNone;
  for i := 1 to Length(LSql) do begin
    if (LSql[i] = ':') and (i < Length(LSql))
        and not (LSql[i - 1] in [':'])
        and not (LSql[i + 1] in [' ', '{', ':']) then begin
      LStat := sInParam;
      LParamStart := i;
    end
    else if (LStat = sInParam) and (LSql[i] in [' ']) then begin
      LParam := copy(LSql, LParamStart + 1, i - LParamStart);
      lokWrite(LParam, '');
    end
    else if (LStat = sInParam) and (LSql[i] in ['{']) then begin
      LParam := copy(LSql, LParamStart + 1, i - LParamStart - 1);
      LValueStart := i;
      LStat := sInValue;
    end
    else if (LStat = sInValue) and (LSql[i] in ['}']) then begin
      LValue := copy(LSql, LValueStart + 1, i - LValueStart - 1);
      LValue := StringReplace(LValue, #39, '', [rfReplaceAll]);
      lokWrite(LParam, LValue);
    end
  end;
  tcWizard.ActiveTab := tabWizParams;
  scWizValue.Width := 800;
end;

procedure TfrmBafCode.btnWizWizardGridClick(Sender: TObject);
begin
  if frmWizGridWiz = nil then
    Application.CreateForm(TfrmWizGridWiz, frmWizGridWiz);
  frmWizGridWiz.pnlMain.Parent := tabWizGridWiz;
  frmWizGridWiz.Init(sgWizConfig, sgWizParams, FWizSql, GetWizCon);
  tcWizard.ActiveTab := tabWizGridWiz;
end;

procedure TfrmBafCode.btnWizWizardVlClick(Sender: TObject);
begin
  if frmWizVlWiz = nil then
    Application.CreateForm(TfrmWizVlWiz, frmWizVlWiz);
  frmWizVlWiz.pnlMain.Parent := tabWizVlWiz;
  frmWizVlWiz.Init(sgWizConfig, sgWizParams, FWizSql, GetWizCon);
  tcWizard.ActiveTab := tabWizVlWiz;
end;

function SortNumber(List: TStringList; Index1, Index2: Integer): Integer;
var
  LValue1, LValue2, LResult: double;
begin
  LValue1 := StrToFloatDef(List.Strings[Index1], 0);
  LValue2 := StrToFloatDef(List.Strings[Index2], 0);
  LResult := (LValue1 - LValue2);
  if Abs(LResult) < 1 then
    LResult := LResult * 1000000000;
  result := round(LResult);
  if result = 0 then
    result := AnsiCompareStr(List.Strings[Index1], List.Strings[Index2]);
end;

function SortDate(List: TStringList; Index1, Index2: Integer): Integer;
var
  LValue1, LValue2, LResult: double;
begin
  LValue1 := StrToDateTimeDef(List.Strings[Index1], 0);
  LValue2 := StrToDateTimeDef(List.Strings[Index2], 0);
  LResult := (LValue1 - LValue2);
  if Abs(LResult) < 1 then
    LResult := LResult * 1000000000;
  result := round(LResult);
  if result = 0 then
    result := AnsiCompareStr(List.Strings[Index1], List.Strings[Index2]);
end;



procedure TfrmBafCode.btnListSortAlphaClick(Sender: TObject);
var
  i: integer;
  sl: TStringList;
begin
  sl := TStringList.Create;
  try
    sl.Assign(memListsTop.Lines);
    if Sender = btnListSortAlpha then
      sl.Sort
    else if Sender = btnListSortNumber then
      sl.CustomSort(SortNumber)
    else if Sender = btnListSortDate then
      sl.CustomSort(SortDate)
    ;
    memListsBottom.Lines.ASsign(sl);
    if cbListsRes.IsChecked then
      Clipboard.AsText := sl.Text;
  finally
    sl.Free;
  end;
end;

procedure TfrmBafCode.btnListSortInvertClick(Sender: TObject);
var
  i: integer;
begin
  memListsBottom.Lines.Clear;
  for i := memListsTop.Lines.Count - 1 downto 0 do
    memListsBottom.Lines.Add(memListsTop.Lines[i]);
  if cbListsRes.IsChecked then
    Clipboard.AsText := memListsBottom.Lines.Text;
end;

procedure TfrmBafCode.btnListsUpperClick(Sender: TObject);
var
  i: integer;
begin
  memListsBottom.Lines.Clear;
  for i := 0 to memListsTop.Lines.Count - 1 do
    memListsBottom.Lines.Add(IfThen(Sender = btnListsUpper,
        AnsiUpperCase(memListsTop.Lines[i]), AnsiLowerCase(memListsTop.Lines[i])));
  if cbListsRes.IsChecked then
    Clipboard.AsText := memListsBottom.Lines.Text;
end;

procedure TfrmBafCode.btnWizCreateCsvClick(Sender: TObject);
begin
  memWizCode.Lines.Text := TBafCsvXlsGenerator.CreateCsv(sgWizConfig, sgWizParams,
      FWizSql, IfThen(GetWizCon = 'default', '', GetWizCon),
      edtWizCreateCsvSep.Text);
  Clipboard.AsText := memWizCode.Lines.Text;
  tcWizard.ActiveTab := tabWizCode;
end;

procedure TfrmBafCode.btnWizCreateGridClick(Sender: TObject);
begin
  memWizCode.Lines.Text := TfrmWizGridWiz.GetGridCode(sgWizConfig, sgWizParams,
      FWizSql, GetWizCon, '');
  Clipboard.AsText := memWizCode.Lines.Text;
  tcWizard.ActiveTab := tabWizCode;
end;

procedure TfrmBafCode.btnWizCreateVLClick(Sender: TObject);
begin
  memWizCode.Lines.Text := TfrmWizVlWiz.GetVlCode(sgWizConfig, sgWizParams,
      FWizSql, GetWizCon);
  Clipboard.AsText := memWizCode.Lines.Text;
  tcWizard.ActiveTab := tabWizCode;
end;

procedure TfrmBafCode.btnWizCreateXlsClick(Sender: TObject);
begin
  memWizCode.Lines.Text := TBafCsvXlsGenerator.CreateXlsx(sgWizConfig, sgWizParams,
      FWizSql, IfThen(GetWizCon = 'default', '', GetWizCon));
  Clipboard.AsText := memWizCode.Lines.Text;
  tcWizard.ActiveTab := tabWizCode;
end;

procedure TfrmBafCode.cbCodeFavChange(Sender: TObject);
var
  LSql, LName, LUser: string;
  LParams: TBafParams;
  ix: integer;
begin
  if Assigned(FSelectedData) then begin
    LName := FSelectedData.Name;
    LUser := dataMain.UserGuid;
    if (LName <> '') and (LUser <> '') then begin
      if (Sender as TCheckBox).IsChecked then begin
        LSql := Format('insert into sys_userfav (name, user_user_id) '
            + ' values (%s, %s)', [QuotedStr(LName), QuotedStr(LUser)]);
        LParams := dataMain.QueryPrepare(dataMain.DefaultCon, 'Fav', LSql);
        dataMain.QueryExecute(dataMain.DefaultCon, 'Fav');
        FUserFav.Add(LName);
      end
      else begin
        LSql := Format('delete from sys_userfav where name = %s '
            + ' and user_user_id = %s', [QuotedStr(LName), QuotedStr(LUser)]);
        LParams := dataMain.QueryPrepare(dataMain.DefaultCon, 'Fav', LSql);
        dataMain.QueryExecute(dataMain.DefaultCon, 'Fav');
        ix := FUserFav.IndexOf(LName);
        if ix > -1 then
          FUserFav.Delete(ix);
      end;
      RefreshFav;
    end; // if (LId <> '')
  end; // if Assigned(FSelectedData)
// procedure TfrmBafCode.cbCodeFavChange
end;

procedure TfrmBafCode.cmbCodeFontSizeChange(Sender: TObject);
var
  LSize: Single;
begin
  LSize := StrToIntDef(cmbCodeFontSize.Items[cmbCodeFontSize.ItemIndex], 12);
  memCode.TextSettings.Font.Size := LSize;
  memHist.TextSettings.Font.Size := LSize;
  memSqlExec1.TextSettings.Font.Size := LSize;
  memSqlExec2.TextSettings.Font.Size := LSize;
  memWizSql.TextSettings.Font.Size := LSize;
  memWizCode.TextSettings.Font.Size := LSize;
  memHist.TextSettings.Font.Size := LSize;
  memListsTop.TextSettings.Font.Size := LSize;
  memListsBottom.TextSettings.Font.Size := LSize;
  FBafSqlEditor.memSql.TextSettings.Font.Size := LSize;
  FBafSqlOpen1.SetFontSize(LSize);
  FBafSqlOpen2.SetFontSize(LSize);
  FBafSqlOpen3.SetFontSize(LSize);
end;

procedure TfrmBafCode.cmbListChange(Sender: TObject);
var
  LTab: TTabItem;
begin
  LTab := tcList.Tabs[cmbList.ItemIndex];
  if LTab <> tcList.ActiveTab then
    tcList.ActiveTab := LTab;
end;

procedure TfrmBafCode.CodeBtnsInit;
var
  i: integer;
  LBtn: TButton;
begin
  FCodeBtnsDef := TStringList.Create;
  FCodeBtnsDef.Add('--=5');
  FCodeBtnsDef.Add('#sql=1');
  FCodeBtnsDef.Add('#sql2=7');
  FCodeBtnsDef.Add('#text=2');
  FCodeBtnsDef.Add('#text2=8');
  FCodeBtnsDef.Add('#text3=10');
  FCodeBtnsDef.Add('#cmd=3');
  FCodeBtnsDef.Add('#cmd2=6');
  FCodeBtnsDef.Add('#cmd3=9');
  FCodeBtnsDef.Add('#code=4');
//  FCodeBtnsDef.Add('#=');
  for i := 0 to FCodeBtnsDef.Count - 1 do begin
    LBtn := TButton.Create(Self);
    LBtn.Parent := pnlCodeButtons;
    LBtn.Position.X := 9;
    LBtn.Height := 26;
    LBtn.Width := 60;
    LBtn.Text := FCodeBtnsDef.Names[i];
    LBtn.OnClick := btnCodeSql1Click;
    FCodeBtnsDef.Objects[i] := LBtn;
  end;
end;

procedure TfrmBafCode.btnBasicAuthGoClick(Sender: TObject);
var
  s: string;
begin
  s := 'Basic ' +  TNetEncoding.Base64String.Encode(edtBasicAuthUser.Text + ':'
      + edtBasicAuthPassword.Text);
  if cbBasicAuthFormat.IsChecked then
    s := 'f_Authorization="' + s + '"';
  edtBasicAuthResult.Text := s;
  if cbBasicAuthCopy.IsChecked then
    Clipboard.AsText := s;
end;

procedure TfrmBafCode.btnCodeFullCollapseClick(Sender: TObject);
begin
  tvCode.CollapseAll;
end;

procedure TfrmBafCode.btnCodeFullExpandClick(Sender: TObject);
begin
  tvCode.ExpandAll;
end;

procedure TfrmBafCode.btnCodeRefreshClick(Sender: TObject);
begin
  if Sender = btnSaveRefresh then
    LoadCode2
  else begin
    TDialogService.MessageDialog('Revert all unsavedchanges?',
        TMsgDlgType.mtConfirmation, mbYesNo, TMsgDlgBtn.mbNo, 0,
      procedure(const aResult: TModalResult)
      begin
        if aResult = mrYes then
          LoadCode2;
      end);
  end;
end;

procedure TfrmBafCode.edtCodeParentChangeTracking(Sender: TObject);
begin
  if Assigned(FSelectedData) then
    FSelectedData.Parent := edtCodeParent.Text;
end;

procedure TfrmBafCode.edtCodeNameChangeTracking(Sender: TObject);
begin
  if Assigned(FSelectedData) then
    FSelectedData.Name := edtCodeName.Text;
end;

procedure TfrmBafCode.Execute(ACommand: string = '');
begin
  if not FCodeLoaded then
    LoadCode2;
  if ACommand <> '' then
    FindCommand(ACommand);
  ShowModal;
end;

procedure TfrmBafCode.ExportNode(AFilename: string; AItem: TBafTreeViewItem);
var
  sl: TStringList;

  procedure SaveNode(AItem: TBafTreeViewItem);
  var
    LData: TCodeItemData;
    i: integer;
  begin
    // save data
    if GetData(AItem, LData) then begin
      sl.Add(SEG_CODE);
      sl.Add(LData.ID);
      sl.Add(LData.Name);
      sl.Add(LData.Parent);
      sl.Add(LData.Code);
    end;
    // go through all sub items and save them too
    for i := 0 to AItem.Count - 1 do
      SaveNode(AItem.Items[i]);
  end; // procedure SaveNode

begin
  sl := TStringList.Create;
  try
    SaveNode(AItem);
    sl.SaveToFile(AFilename);
  finally
    sl.Free;
  end;
end;

function TfrmBafCode.FindCommand(ACommand: string): boolean;
var
  LData: TCodeItemData;
  i: integer;

  procedure lokSearch(AItem: TBafTreeViewItem);
  var
    i: integer;
  begin
    if GetData(AItem, LData) then begin
      if LData.Name = ACommand then begin
        TabControl1.ActiveTab := tabCode;
        tvCode.Selected := AItem;
        if memCode.CanFocus then
          memCode.SetFocus;
        result := true;
        exit;
      end;
    end;
    // all sub items
    for i := 0 to AItem.Count - 1 do
      lokSearch(AItem.Items[i]);
  end;

begin
  result := false;
  for i := 0 to tvCode.Count - 1 do
    lokSearch(tvCode.BafItems[i]);
end;

procedure TfrmBafCode.FormCreate(Sender: TObject);
begin
  FUserFav := TStringList.Create;
  FParentList := TStringList.Create;
  FParentList.OwnsObjects := true;
  FParentList.Sorted := true;
  FParentList.Duplicates := dupError;
  FNameList := TStringList.Create;
  FNameList.OwnsObjects := true;
  FNameList.Sorted := true;
  FNameList.Duplicates := dupError;

  FUserFav.Sorted := true;
  FSqlExecButttonList := TObjectList.Create;
  CodeBtnsInit;
  tvCode := TBafTreeView.Place(Self, pnlCodeLeft);
  tvCode.OnChange := tvCodeChange;
  btnRoot.BringToFront;
  btnSub.BringToFront;
  btnProject.BringToFront;
  btnCodeFullCollapse.BringToFront;
  btnCodeFullExpand.BringToFront;
  btnCodeRefresh.BringToFront;
  btnExport.BringToFront;
  btnImport.BringToFront;
  FWizColumns := TObjectList.Create(true);

  btnRoot.Text := #$f067;
  btnSub.Text := #$f107;
  btnProject.Text := #$f1ad;
  btnCodeRefresh.Text := #$f01e;
  btnCodeFullExpand.Text := #$f101;
  btnCodeFullCollapse.Text := #$f100;
  btnExport.Text := #$f56e;
  btnImport.Text := #$f56f;

  tvFav := TBafTreeView.Place(Self, pnlFavLeft);
  tvFav.OnChange := tvCodeChange;
  btnFavSub.BringToFront;
  btnFavRefresh.BringToFront;
  btnFavSub.Text := #$f107;
  btnFavRefresh.Text := #$f01e;




  FBafSqlEditor := TFrmSqlEditor.Create(Self);
  FBafSqlEditor.pnlDesk.Parent := tabSqlSelect;

  FBafSqlOpen1 := TfrmSqlOpen.Create(Self);
  FBafSqlOpen1.pnlDesk.Parent := tabSqlOpen;
  FBafSqlOpen2 := TfrmSqlOpen.Create(Self);
  FBafSqlOpen2.pnlDesk.Parent := tabSqlOpen2;
  FBafSqlOpen3 := TfrmSqlOpen.Create(Self);
  FBafSqlOpen3.pnlDesk.Parent := tabSqlOpen3;

  FBafSearch1 := TfrmBafSearch.Create(Self);
  FBafSearch1.pnlDesk.Parent := tabSearch;
  FBafSearch2 := TfrmBafSearch.Create(Self);
  FBafSearch2.pnlDesk.Parent := tabSearch2;


  TabControl1.ActiveTab := tabCode;
end;

procedure TfrmBafCode.FormDestroy(Sender: TObject);
begin
  FWizColumns.Free;
  BafFormPos2Ini(Self, dataMain.UserIni, CAT_CODE);
  dataMain.UserIni.WriteInteger(CAT_CODE, 'FontSize', cmbCodeFontSize.ItemIndex);
  FreeAndNil(FSqlExecButttonList);
  FreeAndNil(FCodeBtnsDef);
  FreeAndNil(FNameList);
  FreeAndNil(FParentList);
  FreeAndNil(FUserFav);
end;

procedure TfrmBafCode.FormShow(Sender: TObject);
begin
  if not FPosAusIni then begin
    BafIni2FormPos(Self, dataMain.UserIni, CAT_CODE, 10, 10, 400, 300);
    FPosAusIni := true;
  end;
  tcWizard.ActiveTab := tabWizSql;
  if FInit = false then begin
    InitSqlExecButtons;
    pnlCodeButtonsResized(nil);

//    FBafSqlEditor := TFrmSqlEditor.Create(Self);
//    FBafSqlEditor.pnlDesk.Parent := tabSqlSelect;
    cmbWizDatabase.Items.Assign(dataMain.GetDbConnectionList);
    cmbWizDatabase.ItemIndex := 0;
    FBafSqlEditor.Init;
    FBafSqlOpen1.Init;
    FBafSqlOpen2.Init;
    FBafSqlOpen3.Init;

    tcList.ActiveTab := tabListsLL;
    FInit := true;
  end;

end;

class function TfrmBafCode.GetData(AItem: TBafTreeViewItem;
    var AData: TCodeItemData): boolean;
var
  i: integer;
begin
  AData := AItem.BafData as TCodeItemData;
  result := Assigned(AData);
end;

function TfrmBafCode.GetMemo1Text: string;
begin
  if cbExecOnly.IsChecked then
    result := copy(memSqlExec1.Lines.Text,
        memSqlExec1.SelStart + 1, memSqlExec1.SelLength)
  else
    result := memSqlExec1.Lines.Text;
end;

function TfrmBafCode.GetWizCon: string;
begin
  if cmbWizDatabase.ItemIndex >= 0 then
    result := cmbWizDatabase.Items[cmbWizDatabase.ItemIndex];
  if result = '' then
    result := 'default';
end;

procedure TfrmBafCode.InitSqlExecButtons;
var
  LCaption: string;
  i: integer;
  LCommandIndex: TDataHistCmdIx;
  LButton: TButton;
begin
  FSqlExecButttonList.Clear;
  for i := 1 to TDataHistSql.GetButtonCount(dataMain.Driver) do begin
    if TDataHistSql.GetButton(dataMain.Driver, i, LCaption, LCommandIndex) then begin
      LButton := TButton.Create(Self);
      LButton.Parent := pnlSqlExecTools;
      LButton.Width := 105;
      LButton.Height := 24;
      LButton.Position.X := 8;
      LButton.Position.Y := i * 30 + 34;
      LButton.Tag := integer(LCommandIndex);
      LButton.Text := LCaption;
      LButton.OnClick := SqlExecButttonClick;
      FSqlExecButttonList.Add(LButton);
    end;
  end;
end;

procedure TfrmBafCode.lblLastChangesDblClick(Sender: TObject);
begin
  LoadLastChanges;
end;

function TfrmBafCode.ListsCreate2Select(AList: TStrings; ACrLf: boolean): string;
var
  s, LColumn, LNew, LTableName, LPost: string;
  i, LLine: integer;
  LFirst: boolean;
begin
  if TDataHistSql.ExtractTableName(AList, LLine, LTableName) then begin
    LNew := '';
    i := LLine + 1;
    LFirst := true;
    while (i < AList.Count) do begin
      s := TDataHistSql.lokStart(AList[i]);
      if s[1] = ')' then
        Break;
      if (s <> 'datechg') and (s <> 'usrchg') and (s <> 'progchg') then begin
        if ACrLf then
          result := result + IfThen(LFirst, '', ',')  + #13#10 + '    ' + s
        else
          result := result + IfThen(LFirst, '', ', ') + s;
      end;
      inc(i);
      LFirst := false;
    end;
    if ACrLf then
      result := result + #13#10;
    result := Format('select %s from %s', [result, Trim(LTableName)]);
  end; // if ExtractTableName then begin
end;

function TfrmBafCode.ListsCreate2Upsert(AList: TStrings; APost: string): string;
var
  s, LColumn, LNew, LTableName, LPost: string;
  i, LLine: integer;
begin
  if TDataHistSql.ExtractTableName(memListsTop.Lines, LLine, LTableName) then begin
    LNew := '';
    i := LLine + 1;
    while (i < memListsTop.Lines.Count) do begin
      s := TDataHistSql.lokStart(memListsTop.Lines[i]);
      if s[1] = ')' then
        Break;
      if (s <> (LTablename + '_id')) and (s <> 'datechg')
          and (s <> 'usrchg') and (s <> 'progchg') then
      result := result + '#code   f_' + s + '=' + Format(APost, [s]) + #13#10;
      inc(i);
    end;
    result := result + '#upsert   y=I   t=' + LTableName + '   f_'
        + LTablename + '_id=$GUID()   $CODE$';
  end; // if ExtractTableName then begin
end;

procedure TfrmBafCode.LoadCode2;
var
  LList: TStringList;
  s, LSql: string;
  LDataSet: TDataSet;
  LData: TCodeItemData;
  LItem, LSubItem: TBafTreeViewItem;
  ix: integer;
  c, t1, t2, t: int64;

  procedure lokData;
  begin
    LData := TCodeItemData.Create(nil);
    LData.Inserted := 'U';
    LData.ID := LDataSet.FieldByName(dataMain.DefaultCon.CommandTableId).AsString;
    LData.Name := LDataSet.FieldByName('name').AsString;
    LData.FNameOld := LDataSet.FieldByName('name').AsString;
    LData.Code := LDataSet.FieldByName('code').AsString;
    LData.FCodeOld := LDataSet.FieldByName('code').AsString;
    LData.Parent := LDataSet.FieldByName('parent').AsString;
    LData.FParentOld := LDataSet.FieldByName('parent').AsString;
    LData.Changed := false;
    LData.FTimestamp := LDataSet.FieldByName('datechg').AsDateTime;
    FNameList.AddObject(LData.Name, LData);
    ix := FParentList.IndexOf(LData.Parent);
    if ix = -1 then begin
      LList := TStringList.Create;
      ix := FParentList.AddObject(LData.Parent, LList);
    end;
    (FParentList.Objects[ix] as TStringList).AddObject(LData.Name, LData);
  end; // procedure lokData

  function lokAddItem(AData: TCodeItemData; AParent: TFmxObject): TBafTreeViewItem;
  begin
    LItem := TBafTreeViewItem.Create(tvCode);
    LItem.Parent := AParent;
    LItem.BafData := AData;
    LItem.Text := AData.Name;
    AData.FItemList.Add(LItem);
    result := LItem;
  end; // procedure lokAddItem

  procedure lokTree(AParent: string; AParentObject: TFmxObject);
  var
    i: integer;
    LList: TStringList;
    LItem: TBafTreeViewItem;
  begin
    ix := FParentList.IndexOf(AParent);
    if ix > -1 then begin
      LList := (FParentList.Objects[ix] as TStringList);
      LList.Sort;
      for i := 0 to LList.Count - 1 do begin
        LData := (LList.Objects[i] as TCodeItemData);
        LItem := lokAddItem(LData, AParentObject);
        lokTree(LData.Id, LItem);
      end;
    end;
  end; // procedure lokTree

  procedure lokFav;
  begin
    try
      FUserFav.Clear;
      LSql := Format('select name from sys_userfav where user_user_id = %s ',
        [QuotedStr(dataMain.UserGuid)]);
      dataMain.QueryPrepare(dataMain.DefaultCon, QEN_COMMAND, LSql);
      LDataSet := dataMain.QueryOpen(dataMain.DefaultCon, QEN_COMMAND);
      while not LDataSet.Eof do begin
        FUserFav.Add(LDataSet.FieldByName('name').AsString);
        LDataSet.Next;
      end;
      RefreshFav;
    except

    end;
  end; // procedure lokFav

begin
  QueryPerformanceFrequency(c);
  QueryPerformanceCounter(t1);
  tvCode.Clear;
  FNameList.Clear;
  FParentList.Clear;

  LSql := Format('select * from %s ', [dataMain.DefaultCon.CommandTable]);
  dataMain.QueryPrepare(dataMain.DefaultCon, QEN_COMMAND, LSql);
  LDataSet := dataMain.QueryOpen(dataMain.DefaultCon, QEN_COMMAND);
  while not LDataSet.Eof do begin
    lokData;
    LDataSet.Next;
  end;

  lokTree('', tvCode);
  lokFav;

  FCodeLoaded := true;
  QueryPerformanceCounter(t2);
  t := 1000000 * (t2 - t1) div c;
// procedure TfrmBafCode.LoadCode2
end;

procedure TfrmBafCode.LoadHistory;
var
  LSql: string;
  LParams: TBafParams;
  LDataSet: TDataSet;
  LRow: integer;
  LBafGen: TBafGeneration;
begin
  LBafGen := dataMain.DefaultCon.BafGen;
  LRow := 0;
  sgHist.RowCount := 100;
  if edtCodeId.Text <> '' then begin
    LSql := TDataHistSql.GetSqlCommandHistStatement(DC_DEFAULT, dataMain.Driver,
        LBafGen);
//    Clipboard.AsText := LSql;
    LParams := dataMain.QueryPrepare(dataMain.DefaultCon, QEN_COMMAND, LSql);
    LParams.ParamAsString('k_id', edtCodeId.Text);
    LDataSet := dataMain.QueryOpen(dataMain.DefaultCon, QEN_COMMAND);
    while not LDataSet.Eof do begin
      sgHist.Cells[0, LRow] := LDataSet.FieldByName('baf_datechg').AsString;
      sgHist.Cells[1, LRow] := LDataSet.FieldByName('baf_shortname').AsString;
      sgHist.Cells[2, LRow] := LDataSet.FieldByName('parent').AsString;
      sgHist.Cells[3, LRow] := LDataSet.FieldByName('name').AsString;
      sgHist.Cells[4, LRow] := LDataSet.FieldByName('code').AsString;
      inc(LRow);
      if LRow >= sgHist.RowCount then
        sgHist.RowCount := sgHist.RowCount + 100;
      LDataSet.Next;
    end;
  end;
  sgHist.RowCount := LRow;
end;

procedure TfrmBafCode.LoadLastChanges;
var
  LSql, s: string;
  LDataSet: TDataSet;
  i: integer;
begin
  i := 0;
  s := sgLastChanges.Cells[0, sgLastChanges.Row];
  LSql := Format('select name, to_char(c.datechg, ''dd.mm.yyyy hh24:mi:ss'') as datechg '
      + ' , u.login from %s c '
      + ' left outer join user_user u   on u.user_user_id = c.usrchg '
      + ' order by c.datechg desc limit 1000', [dataMain.DefaultCon.CommandTable]);
  dataMain.QueryPrepare(dataMain.DefaultCon, QEN_COMMAND, LSql);
  LDataSet := dataMain.QueryOpen(dataMain.DefaultCon, QEN_COMMAND);
  while not LDataSet.Eof do begin
    sgLastChanges.RowCount := i + 1;
    sgLastChanges.Cells[0, i] := LDataSet.FieldByName('name').AsString;
    sgLastChanges.Cells[1, i] := LDataSet.FieldByName('datechg').AsString;
    sgLastChanges.Cells[2, i] := LDataSet.FieldByName('login').AsString;
    inc(i);
    LDataSet.Next;
  end;
  FLastChangesDate := now;
  lblLastChanges.Text := 'Letzte Änderung ' + DateTimeToStr(now);
  for i := 0 to sgLastChanges.RowCount - 1 do begin
    if s = sgLastChanges.Cells[0, i] then begin
      sgLastChanges.Row := i;
      Break;
    end;
  end;
end;

procedure TfrmBafCode.memCodeChangeTracking(Sender: TObject);
begin
  if Assigned(FSelectedData) then
    FSelectedData.Code := memCode.Lines.Text;
end;

procedure TfrmBafCode.memCodeViewportPositionChange(Sender: TObject;
  const OldViewportPosition, NewViewportPosition: TPointF;
  const ContentSizeChanged: Boolean);
begin
  if Assigned(FSelectedData) and not mvCodeUpdating then
    FSelectedData.ViewportY := NewViewportPosition.Y;
  lblCodeZeile.Text := IntToStr(memCode.CaretPosition.Line + 1);
end;

procedure TfrmBafCode.pnlCodeButtonsResized(Sender: TObject);
var
  LCount, i, LTop, LBtnPrio: integer;
  LBtn: TButton;
begin
  if frmBafCode.Visible then begin
    LCount := trunc(pnlCodeButtons.Height - 30) div 34;
    LTop := 16;
    for i := 0 to FCodeBtnsDef.Count - 1 do begin
      LBtn := (FCodeBtnsDef.Objects[i] as TButton);
      LBtnPrio := StrToIntDef(FCodeBtnsDef.ValueFromIndex[i], MaxInt);
      if LBtnPrio <= LCount then begin
        LBtn.Visible := true;
        LBtn.Position.Y := LTop;
        LTop := LTop + 32;
      end
      else
        LBtn.Visible := false;

      if (LCount > 5) and (i in [0, 2, 5, 8]) then
        LTop := LTop + 6;

    end;
  end;
end;

procedure TfrmBafCode.RefreshFav;
var
  i, ix: integer;
  LItem, LSubItem: TBafTreeViewItem;
  LData: TCodeItemData;

  function lokAddItem(AData: TCodeItemData; AParent: TFmxObject): TBafTreeViewItem;
  begin
    LItem := TBafTreeViewItem.Create(tvFav);
    LItem.Parent := AParent;
    LItem.BafData := AData;
    LItem.Text := AData.Name;
    AData.FItemList.Add(LItem);
    result := LItem;
  end; // procedure lokAddItem

  procedure lokTree(AParent: string; AParentObject: TFmxObject);
  var
    i: integer;
    LList: TStringList;
    LItem: TBafTreeViewItem;
  begin
    ix := FParentList.IndexOf(AParent);
    if ix > -1 then begin
      LList := (FParentList.Objects[ix] as TStringList);
      LList.Sort;
      for i := 0 to LList.Count - 1 do begin
        LData := (LList.Objects[i] as TCodeItemData);
        LItem := lokAddItem(LData, AParentObject);
        lokTree(LData.Id, LItem);
      end;
    end;
  end; // procedure lokTree

begin
  for i := 0 to FNameList.Count - 1 do begin
    LData := (FNameList.Objects[i] as TCodeItemData);
    if (LData.FItemList.Count > 1) then
      LData.FItemList.Delete(1);
  end;
  tvFav.Clear;
  for i := 0 to FUserFav.Count - 1 do begin
    ix := FNameList.IndexOf(FUserFav[i]);
    if ix >= 0 then begin
      LData := (FNameList.Objects[ix] as TCodeItemData);
      LItem := lokAddItem(LData, tvFav);
      lokTree(LData.Id, LItem);
    end;
  end;
end;

procedure TfrmBafCode.sgHistSelectCell(Sender: TObject; const ACol,
  ARow: Integer; var CanSelect: Boolean);
begin
  edtHistUser.Text := sgHist.Cells[1, ARow];
  edtHistName.Text := sgHist.Cells[3, ARow];
  edtHistParent.Text := sgHist.Cells[2, ARow];
  memHist.Lines.Text := sgHist.Cells[4, ARow];
end;

procedure TfrmBafCode.sgLastChangesCellDblClick(const Column: TColumn;
  const Row: Integer);
begin
  FindCommand(sgLastChanges.Cells[0, Row])
end;

procedure TfrmBafCode.SqlExecButttonClick(Sender: TObject);
var
  LCommandIndex: TDataHistCmdIx;
  s: string;
begin
  LCommandIndex := TDataHistCmdIx((Sender as TButton).Tag);
  memSqlExec2.Lines.Text := TDataHistSql.GetText(DC_DEFAULT, dataMain.Driver,
      memSqlExec1.Lines.Text, LCommandIndex, false,
      cbSqlReplaceColumnNames.IsChecked);
  if cbSqlExpedite.IsChecked
      and not (LCommandIndex in [ciGeneralScript]) then begin
    s := Trim(AnsiLowerCase(copy(memSqlExec2.Lines.Text, 1, 20)));
    if (Pos('drop table', s) = 1) or (Pos('drop  table', s) = 1) then
      TfrmBafDialog.ShowMessage(dataMain.ProgName, 'Drop Operation' + #13#10
          + 'Please Execute manually', Self)
    else
      btnSqlExec1Click(btnSqlExec2);
  end;
end;

procedure TfrmBafCode.TabControl1Change(Sender: TObject);

  procedure lokCodeFav(AParent: TFmxObject; ATree: TBafTreeView);
  begin
    pnlCodeRight.Parent := AParent;
    tvCodeChange(ATree);
  end;

begin
  if FInit then begin
    if TabControl1.ActiveTab = tabHistory then
      LoadHistory
    else if TabControl1.ActiveTab = tabCode then
      lokCodeFav(tabCode, tvCode)
    else if TabControl1.ActiveTab = tabLastChanges then begin
      if FLastChangesDate < (now - (1 / 288)) then
        LoadLastChanges;
    end
    else if TabControl1.ActiveTab = tabFav then
      lokCodeFav(tabFav, tvFav);
  end;
end;

procedure TfrmBafCode.tcListChange(Sender: TObject);
var
  LIx: integer;
begin
  LIx := tcList.ActiveTab.Index;
  if LIx <> cmbList.ItemIndex then
    cmbList.ItemIndex := LIx;
end;

procedure TfrmBafCode.tvCodeChange(Sender: TObject);
var
  LItem: TBafTreeViewItem;
  i, ix: integer;
begin
  if (tvCode = nil) then
    exit;
  mvCodeUpdating := true;
  try
    LItem := (Sender as TBafTreeView).BafSelected;
    if Assigned(LItem) and GetData(LItem, FSelectedData) then begin
      edtCodeId.Text := FSelectedData.ID;
      edtCodeName.Text := FSelectedData.Name;
      edtCodeParent.Text := FSelectedData.Parent;
      memCode.Lines.Text := FSelectedData.Code;
      ix := FUserFav.IndexOf(FSelectedData.Name);
      if cbCodeFav.IsChecked <> (ix > -1) then begin
        cbCodeFav.OnChange := nil;
        try
          cbCodeFav.IsChecked := (ix > -1);
        finally
          cbCodeFav.OnChange := cbCodeFavChange;
        end;
      end;
      memCode.ScrollTo(0, FSelectedData.ViewportY, false);
    end
    else begin
      FSelectedData := nil;
      edtCodeId.Text := '';
      edtCodeName.Text := '';
      edtCodeParent.Text := '';
      cbCodeFav.IsChecked := false;
      memCode.Lines.Clear;
    end;
  finally
    mvCodeUpdating := false;
  end;
end;

procedure TfrmBafCode.WizAddSql;
var
  sl: TStringList;
  i: integer;
begin
  sl := TStringList.Create;
  try
    sl.Text := FWizSql;
    for i := 0 to sl.Count - 1 do
      memWizCode.Lines.Add('#sql ' + sl[i]);
  finally
    sl.Free;
  end;
end;

{ TCodeItemData }

constructor TCodeItemData.Create(AOwner: TComponent);
begin
  inherited;
  FItemList := TObjectList.Create(false);
end;

destructor TCodeItemData.Destroy;
begin
  FreeAndNil(FItemList);
  inherited;
end;

procedure TCodeItemData.RefreshItemNames;
var
  i: integer;
  LItem: TBafTreeViewItem;
begin
  for i := 0 to FItemList.Count - 1 do begin
    if FItemList[i] is TBafTreeViewItem then begin
      LItem := (FItemList[i] as TBafTreeViewItem);
      if FConflicted and FChanged then
        LItem.Text := FName + '!'
      else if FChanged then
        LItem.Text := FName + '*'
      else
        LItem.Text := FName;
    end;
  end;
end;

procedure TCodeItemData.Revert;
begin
  FCode := FCodeOld;
  if FNameOld <> '' then
    FName := FNameOld;
  if FParentOld <> '' then
    FParent := FParentOld;
  FChanged := false;
  RefreshItemNames;
end;

function TCodeItemData.Save: boolean;
begin
  result := dataMain.WriteCommand(DC_DEFAULT, FId, FName, FCode, FParent,
      FInserted, FTimestamp);
  if result then begin
    gvBafCommandCache.RefreshCommand(FName, FCode);
    FCodeOld := FCode;
    FInserted := 'U';
    FChanged := false;
    RefreshItemNames;
  end
  else
    Conflicted := true;
end;

procedure TCodeItemData.SetChanged(const Value: boolean);
begin
  if FChanged <> Value then begin
    FChanged := Value;
    RefreshItemNames;
  end;
end;

procedure TCodeItemData.SetCode(const Value: string);
begin
  if not mvCodeUpdating then begin
    FCode := Value;
    Changed := true;
  end;
end;

procedure TCodeItemData.SetConflicted(const Value: boolean);
begin
  FConflicted := Value;
  RefreshItemNames;
end;

procedure TCodeItemData.SetID(const Value: string);
begin
  FID := Value;
end;

procedure TCodeItemData.SetInserted(const Value: Char);
begin
  FInserted := Value;
end;

procedure TCodeItemData.SetName(const Value: string);
begin
  if not mvCodeUpdating then begin
    FName := Value;
    Changed := true;
    RefreshItemNames;
  end;
end;

procedure TCodeItemData.SetParent(const Value: string);
begin
  if not mvCodeUpdating then begin
    FParent := Value;
    Changed := true;
  end;
end;

end.

