{$O-}
unit main;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, ExtCtrls, StdCtrls, ComCtrls, ToolWin, ActnMan, ActnCtrls,
  ActnMenus, ActnList, XPStyleActnCtrls, StdActns, ImgList, DBGrids, Grids,
  DBCtrls, DB, Registry, Menus, Buttons, ShellApi,WinHelpViewer,
  ABSMain,
  ABSTypes,
  ABSConst,
  ABSExcept,
  ABSConverts,
  ABSSecurity,
  ABSCompression,
  ABSLexer, System.Actions, System.ImageList;

const
  RegistryComponentAcePath = '\Software\ComponentAce';
  RegistryAbsoluteDatabasePath = RegistryComponentAcePath + '\Absolute Database';
  RegistryDBManagerPath = RegistryAbsoluteDatabasePath + '\DatabaseManager';
  HistoryOpenedFilesSize = 5;
  FormCaption = 'Absolute Database Manager';
  ShortFormCaption = 'AbsDb Manager';

type

  TAppSettings = class(TObject)
   private
    FFileNames:     array of String;
    FLastSQMFile: String;
    FLastTableName: String;
    FLastSql:       String;
    FLastPageIndex: Integer;
    FLastLeft:      Integer;
    FLastTop:       Integer;
    FLastWidth:     Integer;
    FLastHeight:    Integer;
    FLastPosition:  Integer;
    FLastWindowState: Integer;
		FLastTreeWidth: Integer;
		FLastQueryHeight: Integer;

    function GetFileName(Index: Integer): String;
    function GetFileNamesCount: Integer;
    procedure LoadSettings;
   public
    constructor Create(HistorySize: Integer);
    destructor Destroy; override;

    procedure ClearFileNames;
    procedure AddFileName(FileName: String);
    procedure SaveSettings;

    property FileNames[Index: Integer]: String read GetFileName;
    property FileNamesCount: Integer read GetFileNamesCount;
    property LastOpenedFileName: String index 0 read GetFileName;

    property LastSQMFile: String read FLastSQMFile write FLastSQMFile;
    property LastTableName: String read FLastTableName write FLastTableName;
    property LastSQL: String read FLastSQL write FLastSQL;
    property LastPageIndex: Integer read FLastPageIndex write FLastPageIndex;
    property LastPosition: Integer read FLastPosition write FLastPosition;
    property LastWindowState: Integer read FLastWindowState write FLastWindowState;
    property LastLeft: Integer read FLastLeft write FLastLeft;
    property LastTop: Integer read FLastTop write FLastTop;
    property LastWidth: Integer read FLastWidth write FLastWidth;
    property LastHeight: Integer read FLastHeight write FLastHeight;
		property LastTreeWidth: Integer read FLastTreeWidth write FLastTreeWidth;
		property LastQueryHeight: Integer read FLastQueryHeight write FLastQueryHeight;
  end;


  TQueryRec = record
    SQL:        String;
    Params:     array of String;
  end;

  TQueries = array of TQueryRec;

  TSqlHistory = class(TObject)
   private
    FDatabaseFileName:  String;
    FQueries:           TQueries;
    FSqlHistorySize:    Integer;
    FListBoxSqlHistory: TListBox;
    procedure Load;
    procedure SetSQL(Index: Integer; Value: String);
    function  GetSQL(Index: Integer): String;
    procedure RepaintListBoxLine(Index: Integer);
   public
    constructor Create(ListBoxSqlHistory: TListBox; SqlHistorySize: Integer=100);
    destructor Destroy; override;
    function FindSQL(SQL: String): integer;
    procedure Clear;
    procedure Save;
    procedure SetDatabaseFileName(Name: String);
    procedure AddQuery(SQL: String);
    procedure DeleteCurrentQuery;
   public
    property DatabaseFileName: String read FDatabaseFileName write FDatabaseFileName;
    property SqlHistorySize: Integer read FSqlHistorySize write FSqlHistorySize;
    property Queries: TQueries read FQueries write FQueries;
    property SQL[Index: Integer]: String read GetSQL write SetSQL; default;
  end;



  TfrmMain = class(TForm)
    StatusBar: TStatusBar;
    ilActions: TImageList;
    CoolBar1: TCoolBar;
    MainMenu: TActionMainMenuBar;
    db: TABSDatabase;
    tCurTable: TABSTable;
    qQuery: TABSQuery;
    odOpenDatabase: TOpenDialog;
    dsTable: TDataSource;
    dsQuery: TDataSource;
    sdSaveDatabase: TSaveDialog;
    tFields: TABSTable;
    dsFields: TDataSource;
    tIndexes: TABSTable;
    dsIndexes: TDataSource;
    tIndexColumns: TABSTable;
    pmTables: TPopupMenu;
    CreateTable1: TMenuItem;
    RenameTable1: TMenuItem;
    DeleteSelectedTables1: TMenuItem;
    RestuctureTable1: TMenuItem;
    N1: TMenuItem;
    RefreshTableList1: TMenuItem;
    dstIndexColumns: TDataSource;
    tFieldssortorder: TAutoIncField;
    tFieldsName: TStringField;
    tFieldsType: TStringField;
    tFieldsSize: TIntegerField;
    tFieldsRequired: TBooleanField;
    tFieldsDefault: TStringField;
    tIndexesid: TAutoIncField;
    tIndexessortorder: TAutoIncField;
    tIndexesType: TStringField;
    tIndexesName: TStringField;
    tIndexesColumns: TStringField;
    tIndexColumnsindexid: TIntegerField;
    tIndexColumnssortorder: TAutoIncField;
    tIndexColumnsCaseInsensitive: TBooleanField;
    tIndexColumnsAsc: TBooleanField;
    tIndexColumnsMaxIndexedSize: TIntegerField;
    tFieldsid: TAutoIncField;
    tIndexColumnsColumnID: TIntegerField;
    tIndexColumnsColumnName: TStringField;
    tFieldsMinValue: TStringField;
    tFieldsMaxValue: TStringField;
    tFieldsBLOBCompressionAlgorithm: TStringField;
    tFieldsBLOBBlockSize: TIntegerField;
    tFieldsBLOBCompressionMode: TIntegerField;
    pMainPanel: TPanel;
    pnlTree: TPanel;
    tvDatabase: TTreeView;
    Splitter1: TSplitter;
    ActionManager: TActionManager;
    aCreateDatabase: TAction;
    aOpenDatabase: TAction;
    aReopenFile: TAction;
    aCloseDatabase: TAction;
    aExit: TAction;
    aCreateTable: TAction;
    aDeleteTables: TAction;
    aRestructureTable: TAction;
    aRenameTable: TAction;
    aChangeDatabaseParams: TAction;
    aCompactDatabase: TAction;
    aRepairDatabase: TAction;
    aAbout: TAction;
    aRefreshTableList: TAction;
    aRefreshTableData: TAction;
    aRefreshTableStructure: TAction;
    aExecSQL: TAction;
    aRefreshDatabaseParams: TAction;
    aBorrowStructure: TAction;
    ilDbTree: TImageList;
    ActionToolBar2: TActionToolBar;
    aPrintStructure: TAction;
    pcTables: TPageControl;
    tsStructure: TTabSheet;
    Splitter2: TSplitter;
    Panel2: TPanel;
    Label3: TLabel;
    dbgFields: TDBGrid;
    Panel3: TPanel;
    Label4: TLabel;
    dbgIndexes: TDBGrid;
    tsTableData: TTabSheet;
    dbgTableData: TDBGrid;
    tsSQL: TTabSheet;
    Splitter3: TSplitter;
    pcSQL: TPageControl;
    tsData: TTabSheet;
    dbgQuery: TDBGrid;
    tsLog: TTabSheet;
    reLog: TRichEdit;
    pnlQuery: TPanel;
    tsDatabase: TTabSheet;
    Label1: TLabel;
    Label7: TLabel;
    Label10: TLabel;
    Label8: TLabel;
    Label9: TLabel;
    cbEncrypted: TCheckBox;
    ePageSize: TEdit;
    ePageCountInExtent: TEdit;
    eCryproAlgorithm: TEdit;
    eDatabaseFileName: TEdit;
    eDatabaseFileSize: TEdit;
    Label11: TLabel;
    eMaxConnections: TEdit;
    BorrowStructure1: TMenuItem;
    PrintTablesStructure1: TMenuItem;
    pmDataBase: TPopupMenu;
    ChangeDatabaseParams1: TMenuItem;
    CompactDatabase1: TMenuItem;
    RepairDatabase1: TMenuItem;
    aCopyTables: TAction;
    CopySelectedTables1: TMenuItem;
    aOpenSQLScript: TAction;
    aSaveSQLScript: TAction;
    sdSql: TSaveDialog;
    odSql: TOpenDialog;
    aSyntaxHighlighting: TAction;
    aEmptyTables: TAction;
    About1: TMenuItem;
    tsExportToSql: TTabSheet;
    reExportToSql: TRichEdit;
    sdExportToSql: TSaveDialog;
    aExportAsSqlToScreen: TAction;
    aExportAsSqlToFile: TAction;
    GroupBox1: TGroupBox;
    cbAddDropTable: TCheckBox;
    cbStructure: TCheckBox;
    cbData: TCheckBox;
    cbBlobSettings: TCheckBox;
    cbFieldNamesInInsert: TCheckBox;
    Button1: TButton;
    tFieldsObjectID: TIntegerField;
    aMakeExeDatabase: TAction;
    btnExpotrAsSqlToScreeen: TButton;
    btnExpotrAsSqlToFile: TButton;
    Panel7: TPanel;
    Label2: TLabel;
    lRecordCount: TLabel;
    DBNavigator1: TDBNavigator;
    Panel10: TPanel;
    Label6: TLabel;
    lSQLRecordCount: TLabel;
    lReadOnly: TLabel;
    DBNavigator2: TDBNavigator;
    aExecSQLScript: TAction;
    aExportToExcel: TAction;
    ToolBar2: TToolBar;
    ToolButton2: TToolButton;
    ToolBar1: TToolBar;
    ToolButton1: TToolButton;
    aHelp: TAction;
    aChangeLog: TAction;
    pmQuery: TPopupMenu;
    mSelectAll: TMenuItem;
    mSelectCount: TMenuItem;
    aCut: TEditCut;
    aCopy: TEditCopy;
    aPaste: TEditPaste;
    aSelectAll: TEditSelectAll;
    aUndo: TEditUndo;
    aDelete: TEditDelete;
    pmSQL: TPopupMenu;
    Cut1: TMenuItem;
    Undo1: TMenuItem;
    N2: TMenuItem;
    Copy1: TMenuItem;
    Paste1: TMenuItem;
    N3: TMenuItem;
    SelectAll1: TMenuItem;
    Delete1: TMenuItem;
    aGoToTableData: TAction;
    N4: TMenuItem;
    mCurrentTable: TMenuItem;
    mFieldList: TMenuItem;
    reSQL: TRichEdit;
    Panel9: TPanel;
    cbRequestLive: TCheckBox;
    cbHighlighting: TCheckBox;
    Panel6: TPanel;
    Label14: TLabel;
    lColumn: TLabel;
    Panel5: TPanel;
    Label12: TLabel;
    lLine: TLabel;
    Panel1: TPanel;
    ToolBar3: TToolBar;
    ToolButton13: TToolButton;
    ToolButton14: TToolButton;
    ToolButton5: TToolButton;
    ToolButton6: TToolButton;
    ToolButton7: TToolButton;
    ToolButton8: TToolButton;
    ToolButton9: TToolButton;
    ToolButton10: TToolButton;
    ToolButton11: TToolButton;
    ToolButton3: TToolButton;
    ToolButton15: TToolButton;
    ToolButton12: TToolButton;
    ToolBar4: TToolBar;
    ToolButton16: TToolButton;
    pSqlHistory: TPanel;
    Splitter4: TSplitter;
    aShowSqlHistory: TAction;
    lbSqlHistory: TListBox;
    Label5: TLabel;
    reTemp: TRichEdit;
    SpeedButton1: TSpeedButton;
    aSqlOptimization: TAction;
    aDelFromSqlHistory: TAction;
    pmSqlHistory: TPopupMenu;
    DeletefromSQLHistory1: TMenuItem;
    aQueryMaker: TAction;
    procedure aExitExecute(Sender: TObject);
    procedure aCloseDatabaseExecute(Sender: TObject);
    procedure aOpenDatabaseExecute(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure aRefreshTableListExecute(Sender: TObject);
    procedure aCreateDatabaseExecute(Sender: TObject);
    procedure aCreateTableExecute(Sender: TObject);
    procedure lbTableListClick(Sender: TObject);
    procedure aRefreshTableDataExecute(Sender: TObject);
    procedure tCurTableAfterPost(DataSet: TDataSet);
    procedure FormCreate(Sender: TObject);
    procedure aRefreshTableStructureExecute(Sender: TObject);
    procedure aRestructureTableExecute(Sender: TObject);
    procedure aDeleteTablesExecute(Sender: TObject);
    procedure aExecSQLExecute(Sender: TObject);
    procedure reSQLKeyDown(Sender: TObject; var Key: Word;
      Shift: TShiftState);
    procedure reSQLKeyPress(Sender: TObject; var Key: Char);
    procedure aCompactDatabaseExecute(Sender: TObject);
    procedure aRenameTableExecute(Sender: TObject);
    procedure aReopenFileExecute(Sender: TObject);
    procedure tFieldsAfterPost(DataSet: TDataSet);
    procedure tFieldsBeforeDelete(DataSet: TDataSet);
    procedure tIndexColumnsBeforeInsert(DataSet: TDataSet);
    procedure tIndexesBeforeInsert(DataSet: TDataSet);
    procedure tvDatabaseKeyDown(Sender: TObject; var Key: Word;
      Shift: TShiftState);
    procedure tvDatabaseCollapsing(Sender: TObject; Node: TTreeNode;
      var AllowCollapse: Boolean);
    procedure tvDatabaseChange(Sender: TObject; Node: TTreeNode);
    procedure aRefreshDatabaseParamsExecute(Sender: TObject);
    procedure aChangeDatabaseParamsExecute(Sender: TObject);
    procedure aRepairDatabaseExecute(Sender: TObject);
    procedure aBorrowStructureExecute(Sender: TObject);
    procedure dbgTableDataDblClick(Sender: TObject);
    procedure aAboutExecute(Sender: TObject);
    procedure aPrintStructureExecute(Sender: TObject);
    procedure tCurTableEditError(DataSet: TDataSet; E: EDatabaseError;
      var Action: TDataAction);
    procedure tCurTablePostError(DataSet: TDataSet; E: EDatabaseError;
      var Action: TDataAction);
    procedure tCurTableDeleteError(DataSet: TDataSet; E: EDatabaseError;
      var Action: TDataAction);
    procedure tCurTableAfterScroll(DataSet: TDataSet);
    procedure qQueryAfterScroll(DataSet: TDataSet);
    procedure tvDatabaseDblClick(Sender: TObject);
    procedure tvDatabaseMouseMove(Sender: TObject; Shift: TShiftState; X,
      Y: Integer);
    procedure aCopyTablesExecute(Sender: TObject);
    procedure aOpenSQLScriptExecute(Sender: TObject);
    procedure aSaveSQLScriptExecute(Sender: TObject);
    procedure reSQLSelectionChange(Sender: TObject);
    procedure reSQLChange(Sender: TObject);
    procedure aSyntaxHighlightingExecute(Sender: TObject);
    procedure aEmptyTablesExecute(Sender: TObject);
    procedure aExportAsSqlToScreenExecute(Sender: TObject);
    procedure aExportAsSqlToFileExecute(Sender: TObject);
    procedure cbStructureClick(Sender: TObject);
    procedure cbDataClick(Sender: TObject);
    procedure aMakeExeDatabaseExecute(Sender: TObject);
    procedure FormActivate(Sender: TObject);
    procedure aExecSQLScriptExecute(Sender: TObject);
    procedure aExportToExcelExecute(Sender: TObject);
    procedure pcTablesChange(Sender: TObject);
    procedure aHelpExecute(Sender: TObject);
    procedure pmQueryClick(Sender: TObject);
    procedure aGoToTableDataExecute(Sender: TObject);
    procedure aShowSqlHistoryExecute(Sender: TObject);
    procedure aSqlOptimizationExecute(Sender: TObject);
    procedure aChangeLogExecute(Sender: TObject);
    procedure lbSqlHistoryClick(Sender: TObject);
    procedure lbSqlHistoryDrawItem(Control: TWinControl; Index: Integer;
      Rect: TRect; State: TOwnerDrawState);
    procedure lbSqlHistoryKeyDown(Sender: TObject; var Key: Word;
      Shift: TShiftState);
    procedure aDelFromSqlHistoryExecute(Sender: TObject);
    procedure lbSqlHistoryMouseMove(Sender: TObject; Shift: TShiftState; X,
      Y: Integer);
    procedure aQueryMakerExecute(Sender: TObject);
    procedure Panel1Click(Sender: TObject);
  private
    { Private declarations }
    SqlHistory: TSqlHistory;
    FSQLExec: Boolean;
    FOldActilePage: TTabSheet;
    procedure OpenDatabaseFile(FileName: String);
    procedure RefreshLastOpenedFiles;
    procedure SelectTable(Name: String='');
    function GetCurrentTableName: String;
    procedure GetSelectedTableNames(List: TStrings);
    procedure FillFieldDefsAndIndexDefs(FieldDefs: TABSAdvFieldDefs;
                                        IndexDefs: TABSAdvIndexDefs);
    procedure AddToLog(Message: String = '');
    procedure ShowLog;
    procedure SetAllDatabaseActionsEnabled(Flag: Boolean);
    procedure SetAllTableActionsEnabled(Flag: Boolean);

    function ExtractNumber(Txt, Prefix: String): Integer;
    procedure SQLHiLight;

    procedure ExportToSql(ToFile: Boolean);
  public
    { Public declarations }
    Settings: TAppSettings;
  end;
    procedure MoveRow(Dataset: TDataSet; MoveUp: Boolean);

var
  frmMain:  TfrmMain;

implementation

uses uDatabase, uTable, ABSBase, Math, uTableName, StrUtils, uMemo,
  uFmtMemo, uBlob, uGraphic, uAbout, uTableStructRpt, uCopyTable,
  Types, uMakeExeDb, uExportExcel, uFieldList, uChangeLog, uQueryMaker;

{$R *.dfm}



{ TAppSettings }

constructor TAppSettings.Create(HistorySize: Integer);
begin
  SetLength(FFileNames, HistorySize);
  FLastSQMFile := '';
  FLastTableName := '';
  LoadSettings;
end;


destructor TAppSettings.Destroy;
begin
  //SaveSettings;
  inherited;
end;


procedure TAppSettings.ClearFileNames;
var
  i: Integer;
begin
  for i:=0 to Length(FFileNames)-1 do
    FFileNames[i] := '';
end;


function TAppSettings.GetFileName(Index: Integer): String;
begin
  Result := FFileNames[Index];
end;


function TAppSettings.GetFileNamesCount: Integer;
var i: Integer;
begin
  Result := 0;
  for i:=0 to Length(FFileNames)-1 do
   begin
    if (FFileNames[i] <> '') then
     Inc(Result)
    else
     Break;
   end;
end;


procedure TAppSettings.LoadSettings;
var
  reg:  TRegistry;
  i:    Integer;
begin
  reg := TRegistry.Create;
  try
   Reg.RootKey := HKEY_CURRENT_USER;

   // Read last openned files list
   if Reg.OpenKey(RegistryDBManagerPath + '\History', True) then
    begin
     for i:=0 to Length(FFileNames)-1 do
      begin
       FFileNames[i] := Reg.ReadString('File'+IntToStr((i+1)));
      end;
    end;
   Reg.CloseKey;

   // Read last openned table name
   if Reg.OpenKey(RegistryDBManagerPath, True) then
     begin
       FLastSQMFile := Reg.ReadString('SQMFile');
       FLastTableName := Reg.ReadString('TableName');
       //FLastSql := Reg.ReadString('SQL');
       if Reg.ValueExists('SQLLen') then
         begin
           SetLength(FLastSql, Reg.ReadInteger('SQLLen'));
           try
             Reg.ReadBinaryData('SQLBin', pchar(FLastSql)^, Reg.ReadInteger('SQLLen')*sizeof(char));
           except
             FLastSql := '';
           end;
         end;
       if Reg.ValueExists('LastPageIndex') then
         FLastPageIndex := Reg.ReadInteger('LastPageIndex')
       else
         FLastPageIndex := 3;
       if Reg.ValueExists('LastPosition') then
         FLastPosition := Reg.ReadInteger('LastPosition')
       else
         FLastPosition := Integer(poScreenCenter);
       if Reg.ValueExists('LastWindowState') then
         FLastWindowState := Reg.ReadInteger('LastWindowState')
       else
         FLastWindowState := Integer(wsMaximized);
       if Reg.ValueExists('LastLeft') then
         FLastLeft := Reg.ReadInteger('LastLeft')
       else
         FLastLeft := 0;
       if Reg.ValueExists('LastTop') then
         FLastTop := Reg.ReadInteger('LastTop')
       else
         FLastTop := 0;
       if Reg.ValueExists('LastWidth') then
         FLastWidth := Reg.ReadInteger('LastWidth')
       else
         FLastWidth := 640;
       if Reg.ValueExists('LastHeight') then
         FLastHeight := Reg.ReadInteger('LastHeight')
       else
         FLastHeight := 480;
			 if Reg.ValueExists('LastQueryHeight') then
				 FLastQueryHeight := Reg.ReadInteger('LastQueryHeight')
			 else
				 FLastQueryHeight := Round(FLastHeight / 2);
			 if Reg.ValueExists('LastTreeWidth') then
				 FLastTreeWidth := Reg.ReadInteger('LastTreeWidth')
			 else
				 FLastTreeWidth := 185;
     end;
   Reg.CloseKey;

  finally
   reg.Free;
  end;
end;


procedure TAppSettings.SaveSettings;
var
  reg:  TRegistry;
  i:    Integer;
begin
  reg := TRegistry.Create;
  try
   Reg.RootKey := HKEY_CURRENT_USER;

   // Save last openned files list
   if Reg.OpenKey(RegistryDBManagerPath + '\History', True) then
    begin
     for i:=0 to Length(FFileNames)-1 do
      begin
       Reg.WriteString('File'+IntToStr((i+1)), FFileNames[i]);
      end;
    end;
   Reg.CloseKey;

   if Reg.OpenKey(RegistryDBManagerPath, True) then
    begin
     // Save last openned table name
     Reg.WriteString('SQMFile', FLastSQMFile);
     Reg.WriteString('TableName', FLastTableName);
     // Save Last SQL text
     if (length(FLastSql) < 2048) then
       begin
         Reg.WriteBinaryData('SQLBin', pchar(FLastSql)^, length(FLastSql)*sizeof(char));
         Reg.WriteInteger('SQLLen', length(FLastSql));
       end;
     //Reg.WriteString('SQL', FLastSql);
     Reg.WriteInteger('LastPageIndex', FLastPageIndex);
     Reg.WriteInteger('LastPosition', FLastPosition);
     Reg.WriteInteger('LastWindowState', FLastWindowState);
     Reg.WriteInteger('LastLeft', FLastLeft);
     Reg.WriteInteger('LastTop', FLastTop);
     Reg.WriteInteger('LastWidth', FLastWidth);
     Reg.WriteInteger('LastHeight', FLastHeight);
		 Reg.WriteInteger('LastQueryHeight', FLastQueryHeight);
		 Reg.WriteInteger('LastTreeWidth', FLastTreeWidth);
    end;
   Reg.CloseKey;

  finally
   reg.Free;
  end;
end;


procedure TAppSettings.AddFileName(FileName: String);
var i,n: Integer;
begin
  if FileName = '' then
    Exit;
  // Find
  n := -1;
  for i:=0 to Length(FFileNames)-1 do
   if ( AnsiUpperCase(trim(FFileNames[i])) = AnsiUpperCase(trim(FileName))) then
    begin
     n := i;
     Break;
    end;
  // Delete
  if (n <> -1) then
   begin
    // Delete FFileNames[n]
    for i:=n downto 1 do FFileNames[i] := FFileNames[i-1];
   end
  else
   begin
    // Delete Last
    for i:=Length(FFileNames)-1 downto 1 do FFileNames[i] := FFileNames[i-1];
   end;
  FFileNames[0] := FileName;
  SaveSettings;
end;



//==============================================================================


{ TSqlHistory }

constructor TSqlHistory.Create(ListBoxSqlHistory: TListBox; SqlHistorySize: Integer);
begin
  self.FListBoxSqlHistory := ListBoxSqlHistory;
  self.FSqlHistorySize    := SqlHistorySize;
  Clear;
end;

destructor TSqlHistory.Destroy;
begin
  Save;
end;

procedure TSqlHistory.Clear;
begin
  SetLength(FQueries,0);
  FListBoxSqlHistory.Clear;
  //FListBoxSqlHistory.Items.Add('');
  FListBoxSqlHistory.ItemIndex := -1;
end;

procedure TSqlHistory.Load;
begin

end;

procedure TSqlHistory.Save;
begin

end;

function TSqlHistory.FindSQL(SQL: String): integer;
var i,n: Integer;
begin
  SQL := Trim(SQL);
  n := -1;
  for i:=0 to Length(FQueries)-1 do
   if (SQL = FQueries[i].SQL) then
    begin
     n:=i;
     Break;
    end;
  Result := n;
end;

procedure TSqlHistory.AddQuery(SQL: String);
var i,n: Integer;
begin
  SQL := Trim(SQL);
  if SQL = '' then Exit;
  n := FindSQL(SQL);
  if ( n = -1 ) then
   begin
    SetLength(FQueries,Length(FQueries)+1);
    for i:=Length(FQueries)-1 downto 1 do
      FQueries[i] := FQueries[i-1];
    FQueries[0].SQL := SQL;
    for i:=0 to Length(FQueries)-1 do
      RepaintListBoxLine(i);
    FListBoxSqlHistory.ItemIndex := 0;
   end
  else
   FListBoxSqlHistory.ItemIndex := n;
end;

procedure TSqlHistory.DeleteCurrentQuery;
var Index,i: Integer;
begin
  Index := FListBoxSqlHistory.ItemIndex;
  if (Index < 0) or (Index > Length(FQueries)-1) then Exit;
  for i:=Index to Length(FQueries)-2 do
   begin
    FQueries[i] := FQueries[i+1];
   end;
  SetLength(FQueries,Length(FQueries)-1);
  FListBoxSqlHistory.DeleteSelected;
  FListBoxSqlHistory.ItemIndex := Index;
end;


procedure TSqlHistory.SetSQL(Index: Integer; Value: String);
begin
  if Index >= Length(FQueries) then
    AddQuery(Value)
  else
    FQueries[Index].SQL := Value;
  RepaintListBoxLine(Index);
end;

function TSqlHistory.GetSQL(Index: Integer): String;
begin
  if (Index >= Length(FQueries)) or (Index < 0) then
    Result := ''
  else
    Result := FQueries[Index].SQL;
end;


procedure TSqlHistory.RepaintListBoxLine(Index: Integer);
var s: String;
const MaxLen=50;
begin
  s := GetSQL(Index);
  if Length(s) > MaxLen then
    s := copy(s, 1, 50) + '...';
  s := StringReplace(s, #13, ' ', [rfReplaceAll,rfIgnoreCase]);
  s := StringReplace(s, #10, ' ', [rfReplaceAll,rfIgnoreCase]);
  s := StringReplace(s, '  ', ' ', [rfReplaceAll,rfIgnoreCase]);
  s := StringReplace(s, '  ', ' ', [rfReplaceAll,rfIgnoreCase]);
  FListBoxSqlHistory.Items[Index] := s;
end;


procedure TSqlHistory.SetDatabaseFileName(Name: String);
begin
  if FDatabaseFileName <> '' then
   begin
    Save;
    Clear;
   end;
  FDatabaseFileName := Name;
  Load;
end;

//==============================================================================



procedure TfrmMain.aExitExecute(Sender: TObject);
begin
  Close;
end;


procedure TfrmMain.aCloseDatabaseExecute(Sender: TObject);
begin
 if tvDatabase.Selected <> nil then
   Settings.LastTableName := tvDatabase.Selected.Text;
   
 db.Close;
 db.CryptoAlgorithm := DefaultCryptoAgorithm;
 frmMain.Caption := FormCaption;
 aRefreshTableList.Execute;
 pMainPanel.Visible := False;

 SetAllDatabaseActionsEnabled(False);
 SetAllTableActionsEnabled(False);
end;


procedure TfrmMain.aOpenDatabaseExecute(Sender: TObject);
begin
  if odOpenDatabase.Execute then
   begin
      db.Password := '';
      OpenDatabaseFile(odOpenDatabase.FileName);
   end;
end;


procedure TfrmMain.FormDestroy(Sender: TObject);
begin
 aCloseDatabase.Execute;
 reSQL.PlainText := True;
 Settings.LastSQL := reSQL.Text;
 Settings.LastPageIndex := pcTables.ActivePageIndex;
 Settings.LastPosition := Integer(poDesigned);
 Settings.LastWindowState := Integer(WindowState);
 Settings.LastLeft := Left;
 Settings.LastTop := Top;
 Settings.LastWidth := Width;
 Settings.LastHeight := Height;
 Settings.LastQueryHeight := pnlQuery.Height;
 Settings.LastTreeWidth := pnlTree.Width;
 Settings.SaveSettings;
 Settings.ClearFileNames;
 RefreshLastOpenedFiles;
 Settings.Free;
 SqlHistory.Free;
end;


procedure TfrmMain.aRefreshTableListExecute(Sender: TObject);
var
  OldName: String;
  sl: TStringList;
  i: Integer;
  Root: TTreeNode;
begin
  OldName := GetCurrentTableName;

  if db.Connected then
   begin
    tvDatabase.Items.Clear;
    Root := tvDatabase.Items.Add(nil, ExtractFileName(db.DatabaseFileName));
    tvDatabase.Items[0].ImageIndex := 0;
    tvDatabase.Items[0].SelectedIndex := 1;
    sl := TStringList.Create;
    try
     db.GetTablesList(sl);
     sl.Sort;
     tvDatabase.Items.BeginUpdate;
     try
       for i:=0 to sl.Count-1 do
        begin
          with tvDatabase.Items.AddChild(Root,sl[i]) do
            begin
              ImageIndex := 2;
              SelectedIndex := 3;
            end;
        end;
     finally
       tvDatabase.Items.EndUpdate;
     end;
     if OldName = '' then
       begin
         if tvDatabase.Items.Count >=2 then
           tvDatabase.Selected := tvDatabase.Items[1]
         else
           tvDatabase.Selected := tvDatabase.Items[0]
       end;
    finally
      if OldName <> '' then
        begin
          if (sl.IndexOf(OldName) <> -1) then
            SelectTable(OldName)
          else
            if (sl.Count > 0) then
              SelectTable(sl[0])
        end
      else
        SelectTable(Settings.LastTableName);
     sl.Free;
    end;
   end;
end;


procedure TfrmMain.SelectTable(Name: String='');
var
 i: Integer;
begin
  if (name <> '') then
   for i:=1 to tvDatabase.Items.Count-1 do
    if AnsiUpperCase(tvDatabase.Items[i].Text) = AnsiUpperCase(Name) then
     begin
      tvDatabase.Selected := tvDatabase.Items[i];
      Break;
     end;
  aRefreshTableData.Execute;
  aRefreshTableStructure.Execute;
end;


procedure TfrmMain.aRefreshTableDataExecute(Sender: TObject);
var
 bookmark: TBookmark;
begin
 lRecordCount.Caption := '0';
 if ((db.Connected) and (GetCurrentTableName <> '')) then
  begin
   if ((tCurTable.Active) and
       (tCurTable.TableName = GetCurrentTableName)) then
    begin
     bookmark := tCurTable.GetBookmark;
     tCurTable.Refresh;
     tCurTable.GotoBookmark(bookmark);
     tCurTable.FreeBookmark(bookmark);
    end
   else
    begin
     tCurTable.Close;
     tCurTable.TableName := GetCurrentTableName;
     tCurTable.Open;
    end;
   lRecordCount.Caption := IntToStr(tCurTable.RecordCount);
  end
 else
  begin
   tCurTable.Close;
  end;
end;


procedure TfrmMain.aCreateDatabaseExecute(Sender: TObject);
begin
  aCloseDatabase.Execute;
  if sdSaveDatabase.Execute then
   if frmDatabase.ShowModal = mrOk then
    begin
     db.DatabaseFileName := sdSaveDatabase.FileName;
     db.PageSize := StrToInt(frmDatabase.cbPageSize.Text);
     db.PageCountInExtent := StrToInt(frmDatabase.cbPageCountInExtent.Text);
     db.MaxConnections := StrToInt(frmDatabase.eMaxConnections.Text);
     if frmDatabase.cbEncrypted.Checked then
      begin
       db.Password := frmDatabase.ePassword1.Text;
       db.CryptoAlgorithm := TABSCryptoAlgorithm(frmDatabase.cbCryptoAgorithm.ItemIndex);
      end
     else
      begin
       frmMain.db.Password := '';
      end;
     db.CreateDatabase;
     OpenDatabaseFile(db.DatabaseFileName);
     tvDatabase.Select(tvDatabase.Items[0],[]);
    end;
end;


procedure TfrmMain.aCreateTableExecute(Sender: TObject);
begin

 tIndexColumns.Close;
 tIndexColumns.EmptyTable;
 tIndexColumns.Open;

 tIndexes.Close;
 tIndexes.EmptyTable;
 tIndexes.Open;

 tFields.Close;
 tFields.EmptyTable;
 tFields.Open;

 frmTable.eTableName.text := '';

 repeat
   if frmTable.ShowModal = mrOk then
    begin
     tCurTable.Close;
     tCurTable.TableName := frmTable.eTableName.text;
     tCurTable.FieldDefs.Clear;
     tCurTable.IndexDefs.Clear;
     FillFieldDefsAndIndexDefs(tCurTable.AdvFieldDefs,
                               tCurTable.AdvIndexDefs);
     try
       tCurTable.CreateTable;
       aRefreshTableList.Execute;
       SelectTable(frmTable.eTableName.text);
       break;
     except
       on E: Exception do
         MessageDlg('Create table error: '+E.Message+#13#10'Try again.', mtError, [mbOK], 0)
       // show table creation window again
     end;
    end
   else
     begin
       SelectTable;
       break;
     end;
 until false;
end;


procedure TfrmMain.lbTableListClick(Sender: TObject);
begin
 if (tvDatabase.Items.Count > 1) and
    (tCurTable.TableName <> GetCurrentTableName) then
 SelectTable;
end;


procedure TfrmMain.tCurTableAfterPost(DataSet: TDataSet);
begin
 aRefreshTableData.Execute;
end;


procedure TfrmMain.FormCreate(Sender: TObject);
var OldSH: Boolean;
   tReg : TRegistry;
   sHelpFile : string;
   InstallDir: String;
begin
  // Help file
  tReg := TRegistry.Create;
  try
    tReg.RootKey := HKEY_CURRENT_USER;
    tReg.OpenKey('SOFTWARE\ComponentAce\Absolute Database', true);
    InstallDir := tReg.ReadString('InstallDir');
    sHelpFile := InstallDir+'\Help\AbsDbManual.hlp';
    if not(FileExists(sHelpFile)) then
      sHelpFile := ExtractFilePath(ParamStr(0))+'..\..\Help\AbsDbManual.hlp';
    if FileExists(sHelpFile) then
      Application.HelpFile := sHelpFile;
  finally
    tReg.Free;
  end;

  Application.HintPause := 0;
  Application.HintHidePause := 50000;
  Application.HintShortPause := 0;


  FOldActilePage := nil;
  Settings := TAppSettings.Create(HistoryOpenedFilesSize);
  SqlHistory := TSqlHistory.Create(lbSqlHistory);
  RefreshLastOpenedFiles;
  OldSH := aSyntaxHighlighting.Checked;
  aSyntaxHighlighting.Checked := False;
  reSQL.PlainText := True;
  reSQL.Text := Settings.LastSQL;
  if OldSH then aSyntaxHighlighting.Execute;
  //reSQL.OnChange(Sender);

  // Create Fields Table
  tFields.CreateTable;
  tFields.Open;
  tFields.Database.SilentMode := True;
  tFields.Close;
  tFields.RestructureFieldDefs.Find('Type').DefaultValue.AsAnsiString := 'String';
  tFields.RestructureFieldDefs.Find('Size').DefaultValue.AsInteger := 20;
  tFields.RestructureFieldDefs.Find('Required').DefaultValue.AsBoolean := False;
  tFields.RestructureFieldDefs.Find('BLOBCompressionAlgorithm').DefaultValue.AsAnsiString := 'None';
  tFields.RestructureFieldDefs.Find('BLOBCompressionMode').DefaultValue.AsInteger := 0;
  tFields.RestructureFieldDefs.Find('BLOBCompressionMode').MinValue.AsInteger := 0;
  tFields.RestructureFieldDefs.Find('BLOBCompressionMode').MaxValue.AsInteger := 9;
  tFields.RestructureFieldDefs.Find('BLOBBlockSize').DefaultValue.AsInteger := DefaultBLOBBlockSize;
  tFields.RestructureTable;
  tFields.Open;

  // Create Indexes Table
  tIndexes.CreateTable;
  tIndexes.Open;
  tIndexes.Database.SilentMode := True;
  tIndexes.Close;
  tIndexes.RestructureFieldDefs.Find('Name').DefaultValue.AsAnsiString := 'IndexName';
  tIndexes.RestructureTable;
  tIndexes.Open;

  // Create IndexesColumns Table
  tIndexColumns.CreateTable;
  tIndexColumns.Open;
  tIndexColumns.Database.SilentMode := True;
  tIndexColumns.Close;
  tIndexColumns.RestructureFieldDefs.Find('CaseInsensitive').DefaultValue.AsBoolean := False;
  tIndexColumns.RestructureFieldDefs.Find('Asc').DefaultValue.AsBoolean := True;
  tIndexColumns.RestructureFieldDefs.Find('MaxIndexedSize').DefaultValue.AsInteger := DEFAULT_MAX_INDEXED_SIZE;
  tIndexColumns.RestructureTable;
  tIndexColumns.Open;

  aCloseDatabaseExecute(nil);
  if ParamCount > 0 then
   begin
    OpenDatabaseFile(ParamStr(1));
   end
  else
   begin
    if FileExists(Settings.LastOpenedFileName) then
      OpenDatabaseFile(Settings.LastOpenedFileName);
   end;
   SqlHistory.SetDatabaseFileName(db.DatabaseFileName);
   //SelectTable(Settings.LastTableName);

   if (pcTables.Pages[Settings.LastPageIndex].TabVisible) then
     pcTables.ActivePageIndex := Settings.LastPageIndex;
   Left := Settings.LastLeft;
   Top := Settings.LastTop;
   Width := Settings.LastWidth;
   Height := Settings.LastHeight;
   if (Integer(Settings.LastPosition) <> 0) then
     Position := TPosition(Settings.LastPosition);
   WindowState := TWindowState(Settings.LastWindowState);
   pnlTree.Width := Settings.LastTreeWidth;
   pnlQuery.Height := Settings.LastQueryHeight;
   if Settings.LastPageIndex = 0 then
    begin
     FOldActilePage := nil;
     if (tvDatabase.Items.Count > 0) then
       tvDatabase.Select(tvDatabase.Items[0],[]);
     FOldActilePage := pcTables.ActivePage;
    end;
end;


procedure TfrmMain.aRefreshTableStructureExecute(Sender: TObject);
var
  i,j: Integer;
  s: String;
  FieldList, DescFieldList, CaseInsFieldList, MaxIndexedSizeList: TStringList;
begin
  tFields.Close;
  tFields.EmptyTable;
  tFields.Open;
  if tCurTable.Active then
   begin

    for i:=0 to tCurTable.AdvFieldDefs.Count-1 do
     with tFields do
      begin
       tFields.Insert;
       tFields.FieldByName('Name').AsString := tCurTable.AdvFieldDefs[i].Name;
       tFields.FieldByName('ObjectID').AsInteger := tCurTable.AdvFieldDefs[i].ObjectID;
       tFields.FieldByName('Type').AsString := AftToStr(tCurTable.AdvFieldDefs[i].DataType);
       tFields.FieldByName('Size').AsInteger := tCurTable.AdvFieldDefs[i].Size;
       tFields.FieldByName('Required').AsBoolean := tCurTable.AdvFieldDefs[i].Required;
       tFields.FieldByName('Default').AsVariant := tCurTable.AdvFieldDefs[i].DefaultValue.AsVariant;
       tFields.FieldByName('MinValue').AsVariant := tCurTable.AdvFieldDefs[i].MinValue.AsVariant;
       tFields.FieldByName('MaxValue').AsVariant := tCurTable.AdvFieldDefs[i].MaxValue.AsVariant;
       tFields.FieldByName('BLOBCompressionAlgorithm').AsString := ABSCompressionAlgorithmNames[Integer(tCurTable.AdvFieldDefs[i].BLOBCompressionAlgorithm)];
       tFields.FieldByName('BLOBCompressionMode').AsInteger := tCurTable.AdvFieldDefs[i].BLOBCompressionMode;
       tFields.FieldByName('BLOBBlockSize').AsInteger := tCurTable.AdvFieldDefs[i].BLOBBlockSize;
       tFields.Post;
      end;

    // Indexes
    tIndexes.Close;
    tIndexes.EmptyTable;
    tIndexes.Open;

    // IndexColumns
    tIndexColumns.Close;
    tIndexColumns.EmptyTable;
    tIndexColumns.Open;

    for i:=0 to tCurTable.IndexDefs.Count-1 do
     begin
      tIndexes.Insert;
      tIndexes.FieldByName('Name').AsString := tCurTable.IndexDefs[i].Name;

      if ixPrimary in tCurTable.IndexDefs[i].Options then
       s := 'Primary'
      else if ixUnique in tCurTable.IndexDefs[i].Options then
       s := 'Unique'
      else
       s := '';
      tIndexes.FieldByName('Type').AsString := s;
      s := tCurTable.AdvIndexDefs[i].Fields;
      s := '(' + AnsiReplaceStr(s,';',', ') + ')';
      tIndexes.FieldByName('Columns').AsString := s;
      tIndexes.Post;

      FieldList := TStringList.Create;
      DescFieldList := TStringList.Create;
      CaseInsFieldList := TStringList.Create;
      MaxIndexedSizeList := TStringList.Create;
      try
        GetNamesList(FieldList, tCurTable.AdvIndexDefs[i].Fields);
        GetNamesList(DescFieldList, tCurTable.AdvIndexDefs[i].DescFields);
        GetNamesList(CaseInsFieldList, tCurTable.AdvIndexDefs[i].CaseInsFields);
        GetNamesList(MaxIndexedSizeList, tCurTable.AdvIndexDefs[i].MaxIndexedSizes);
        for j:=0 to FieldList.Count-1 do
         begin
          tIndexColumns.Insert;
          tIndexColumns.FieldByName('indexid').AsInteger := tIndexes.LastAutoincValue('id');
          if tFields.Locate('name',FieldList[j],[loCaseInsensitive]) then
            tIndexColumns.FieldByName('ColumnID').AsInteger := tFields.FieldByName('id').AsInteger;
          tIndexColumns.FieldByName('Asc').AsBoolean := DescFieldList.IndexOf(FieldList[j]) = -1;
          tIndexColumns.FieldByName('CaseInSensitive').AsBoolean := CaseInsFieldList.IndexOf(FieldList[j]) <> -1;
          if (MaxIndexedSizeList[j] <> '') then
            tIndexColumns.FieldByName('MaxIndexedSize').AsInteger := StrToInt(MaxIndexedSizeList[j]);
          tIndexColumns.Post;
         end;
      finally
       FieldList.Free;
       DescFieldList.Free;
       CaseInsFieldList.Free;
       MaxIndexedSizeList.Free;
      end;
     end;
   end;//if tCurtable.Active
end;


procedure TfrmMain.aRestructureTableExecute(Sender: TObject);
var
  name: String;
  log: string;
begin
 frmTable.eTableName.text := tCurTable.TableName;
 name := frmTable.eTableName.text;
 while frmTable.ShowModal = mrOk do
  begin
   qQuery.Close;
   tCurTable.Close;
   name := frmTable.eTableName.text;
   if (tCurTable.TableName <> name) then
     tCurTable.RenameTable(name);
   FillFieldDefsAndIndexDefs(tCurTable.RestructureFieldDefs,
                             tCurTable.RestructureIndexDefs);
   tCurTable.DisableControls;
   try
     if (not tCurTable.RestructureTable(log)) then
       MessageDlg('Restructure failed. '+Log, mtError, [mbOK], 0)
     else
       break;
   finally
     tCurTable.EnableControls;
   end;
  end;
  aRefreshTableList.Execute;
  SelectTable(name);
end;


procedure TfrmMain.aDeleteTablesExecute(Sender: TObject);
var
  s: String;
  i: Integer;
  d: String;
  t: TABSTable;
  TableList: TStringList;
begin
  TableList := TStringList.Create;
  try
   GetSelectedTableNames(TableList);
   if TableList.Count <=0 then Exit;
    s := 'Delete (Drop) all selected tables: (';
    d := '';
    for i:=0 to TableList.Count-1 do
     begin
      s := s + d + TableList[i];
      d := ', ';
     end;
    s := s + '). Are you sure?';
    if (MessageDlg(s, mtConfirmation, [mbYes,mbNo],0) = mrYes) then
     begin
      qQuery.Close;
      tCurTable.Close;
      //qQuery.Close;
      t := TABSTable.Create(self);
      try
       t.DatabaseName := db.DatabaseName;
       for i:=0 to TableList.Count-1 do
         begin
          t.TableName := TableList[i];
          t.DeleteTable;
         end;
      finally
       t.Free;
      end;
     end;
  finally
   aRefreshTableList.Execute;
   TableList.Free;
   
  end;
end;


procedure TfrmMain.aExecSQLExecute(Sender: TObject);
var
  q: String;
  dtStart, dtEnd: TTime;
begin
  if ( pcTables.ActivePage <> tsSQL ) then Exit;

  frmQueryMaker.Hide;
  ToolButton3.Hint := 'Show Query Maker';
  Application.ProcessMessages;
  tCurTable.Close;
  Screen.Cursor := crHourGlass;
  try
    qQuery.Close;
    aExportToExcel.Enabled := False;
    lSQLRecordCount.Caption := '0';
    lReadOnly.Caption := '';
		if (reSQL.SelLength>0) then
      q := trim(reSQL.SelText)
		else
      q := trim(reSQL.Text);
    AddToLog;
    AddToLog('Executing Query: '#13#10 + q);
    StatusBar.SimpleText := ' Executing query, please wait...';
    Application.ProcessMessages;
    qQuery.SQL.Text := q;
    qQuery.ReadOnly := False;
    qQuery.RequestLive := cbRequestLive.Checked;
    dtStart := Time;
    try
     qQuery.Open;
     dtEnd := Time;
     lSQLRecordCount.Caption := IntToStr(qQuery.RecordCount);
     if qQuery.ReadOnly then
      lReadOnly.Caption := 'ReadOnly'
     else
      lReadOnly.Caption := 'LiveResult';
     AddToLog('Ok.');
     q := ' Execution time: '+FormatDateTime('hh:nn:ss.zzz', (dtEnd-dtStart));
		 AddToLog(q);
     SqlHistory.AddQuery(qQuery.SQL.Text);

     if ( qQuery.RowsAffected <> -1 ) then
      begin
       AddToLog(Format('%d rows affected.', [qQuery.RowsAffected]));
       q := Format(' %d rows affected,', [qQuery.RowsAffected])+q;
      end;
     StatusBar.SimpleText := q;
     Application.ProcessMessages;
     
     pcSQL.ActivePage := tsData;
     aExportToExcel.Enabled := True;
    except
     on e: EABSException do
      begin
       if (e.NativeError = 20001) then
        begin
         AddToLog('Ok.');
         if ( qQuery.RowsAffected <> -1 ) then
          begin
           AddToLog(Format(' %d rows affected.', [qQuery.RowsAffected]));
           ShowLog;
          end;
         SqlHistory.AddQuery(qQuery.SQL.Text);
        end
       else
        begin
         qQuery.Close;
         reSQL.CaretPos := Point(ExtractNumber(e.Message,'column')-1,
                                 ExtractNumber(e.Message,'line')-1);
         AddToLog(e.Message);
         ShowLog;
         reSQL.SetFocus;
        end;
      end;
     on e: Exception do
      begin
       qQuery.Close;
       AddToLog(e.Message);
       ShowLog;
      end;
    end;//try-except
  finally
    Screen.Cursor := crDefault;
    aRefreshTableList.Execute;
    if (tCurTable.Exists) then
      tCurTable.Open
    else
      SelectTable;
  end;
end;


procedure TfrmMain.reSQLKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
begin
  if (ssCtrl in Shift) and (Key = 13) then
   begin
    aExecSQL.Execute;
    FSQLExec := True;
   end
  else if (ssCtrl in Shift) and (Key = ord('T')) then
   pmQueryClick(mCurrentTable)
  else if (ssCtrl in Shift) and (Key = ord('F')) then
   pmQueryClick(mFieldList)
end;

procedure TfrmMain.reSQLKeyPress(Sender: TObject; var Key: Char);
begin
  if FSQLExec and (Key in [#10,#13]) then
   begin
    FSQLExec := False;
    Key := #0;
   end;
end;


procedure TfrmMain.aCompactDatabaseExecute(Sender: TObject);
begin
  if not db.Connected then Exit;
  aCloseDatabase.Execute;
  try
    AddToLog('Compact Database...');
    try
     db.CompactDatabase;
     AddToLog('Ok');
    except
      on e: Exception do
       begin
        AddToLog(e.Message);
        ShowLog;
        raise;
       end;
    end
  finally
   OpenDatabaseFile(db.DatabaseFileName);
  end;
end;

procedure TfrmMain.aRenameTableExecute(Sender: TObject);
var
  OldName, NewName: String;
begin
  OldName := GetCurrentTableName;
  if OldName = '' then Exit;
  frmTableName.Caption := 'Rename table ' + OldName;
  frmTableName.eTableName.Text := OldName;
  if frmTableName.ShowModal = mrOk then
   begin
    NewName := frmTableName.eTableName.Text;
    if (NewName = OldName) then Exit;
    qQuery.Close;
    tCurTable.Close;
    try
      tCurTable.RenameTable(NewName);
    finally
      tCurTable.Open;
      aRefreshTableList.Execute;
    end;
    SelectTable(NewName);
   end;
end;


procedure TfrmMain.RefreshLastOpenedFiles;
var i: Integer;
    a: TAction;
begin
  // Unsafe code, but... :(
  with ActionManager.ActionBars[13].Items[0].Items[2] do
   begin
     for i:=0 to items.Count-1 do
      begin
       Action.Free;
      end;
     items.Clear;     
     if Settings.FileNamesCount = 0 then
       Visible := False
     else
      begin
       Visible := True;
       for i:=0 to Settings.FileNamesCount-1 do
        with items.Add do
         begin
          a:= TAction.Create(self);
          a.Caption := Settings.FileNames[i];
          a.OnExecute := aReopenFileExecute;
          Action := a;
         end;
      end;
   end;
end;


procedure TfrmMain.aReopenFileExecute(Sender: TObject);
begin
 if Sender is TAction then
  begin
   OpenDatabaseFile(TAction(Sender).Caption);
  end;
end;

procedure TfrmMain.OpenDatabaseFile(FileName: String);
begin
  if FileName='' then Exit;
  aCloseDatabase.Execute;
  db.DatabaseFileName := FileName;
  db.Open;

  SetAllDatabaseActionsEnabled(True);
  SetAllTableActionsEnabled(True);

  Settings.AddFileName(FileName);
  aRefreshTableList.Execute;
  RefreshLastOpenedFiles;
  frmMain.Caption := ShortFormCaption + ' {' + FileName + '}';
  aRefreshDatabaseParams.Execute;
  pMainPanel.Visible := True;
end;

procedure TfrmMain.Panel1Click(Sender: TObject);
var
  ws1, ws2: WideString;
begin
  tCurTable.Locate('id','51325',[]);
  ws1 := tCurTable.FieldByName('noise').Value;
  tCurTable.Locate('id','4813',[]);
  ws2 := tCurTable.FieldByName('noise').Value;
  if (ws1 = ws2) then
    ShowMessage('=')
  else
    ShowMessage('<>');
end;

procedure TfrmMain.tFieldsAfterPost(DataSet: TDataSet);
begin
  tIndexColumns.Refresh;
end;

procedure TfrmMain.tFieldsBeforeDelete(DataSet: TDataSet);
begin
  while tIndexColumns.Locate('columnid', DataSet.FieldByName('id').AsInteger,[]) do
   tIndexColumns.Delete;
end;


procedure TfrmMain.tIndexColumnsBeforeInsert(DataSet: TDataSet);
begin
  if (tIndexes.State in [dsEdit,dsInsert]) then tIndexes.Post;
end;

procedure TfrmMain.tIndexesBeforeInsert(DataSet: TDataSet);
begin
  if (tFields.State in [dsEdit,dsInsert]) then tFields.Post;
end;

procedure TfrmMain.FillFieldDefsAndIndexDefs(FieldDefs: TABSAdvFieldDefs;
                                             IndexDefs: TABSAdvIndexDefs);
var
  i: Integer;
  s: String;
  FieldList, DescFieldList, CaseInsFieldList, MaxIndexedSizeList: String;
begin

   // Fields
   FieldDefs.Clear;
   tFields.First;
   for i:=0 to tFields.RecordCount-1 do
    begin
     with FieldDefs.AddFieldDef do
      begin
       Name := tFields.FieldByName('Name').AsString;
       ObjectID := tFields.FieldByName('ObjectID').AsInteger;
       DataType := StrToAft(tFields.FieldByName('Type').AsString);
       Size := tFields.FieldByName('Size').AsInteger;
       Required := tFields.FieldByName('Required').AsBoolean;
       DefaultValue.AsWideString := tFields.FieldByName('Default').AsWideString;
       MinValue.AsAnsiString := tFields.FieldByName('MinValue').AsAnsiString;
       MaxValue.AsAnsiString := tFields.FieldByName('MaxValue').AsAnsiString;
       BLOBCompressionAlgorithm := TCompressionAlgorithm(
                                     GetCompressionAlgorithm(
                                       tFields.FieldByName(
                                         'BLOBCompressionAlgorithm').AsString));
       BLOBCompressionMode := tFields.FieldByName('BLOBCompressionMode').AsInteger;
       BLOBBlockSize := tFields.FieldByName('BLOBBlockSize').AsInteger;
      end;
     tFields.Next;
    end;

   // Indexes
   IndexDefs.Clear;
   tIndexes.First;
   for i:=0 to tIndexes.RecordCount-1 do
    begin
     with IndexDefs.AddIndexDef do
      begin
       Name := tIndexes.FieldByName('Name').AsString;
       
       if UpperCase(Trim(tIndexes.FieldByName('Type').AsString)) = 'PRIMARY' then
         Options := [ixPrimary]
       else
       if UpperCase(Trim(tIndexes.FieldByName('Type').AsString)) = 'UNIQUE' then
         Options := [ixUnique];

       FieldList := '';
       DescFieldList := '';
       CaseInsFieldList := '';
       MaxIndexedSizeList := '';
       tIndexColumns.Filter := 'indexid='+tIndexes.FieldByName('id').AsString;
       tIndexColumns.Filtered := True;
       try
        tIndexColumns.First;
        while not tIndexColumns.Eof do
         begin
          s := tIndexColumns.FieldByName('columnname').AsString;
          FieldList := FieldList + s + ';';
          if not tIndexColumns.FieldByName('Asc').AsBoolean then
            DescFieldList := DescFieldList + s + ';';
          if tIndexColumns.FieldByName('CaseInsensitive').AsBoolean then
            CaseInsFieldList := CaseInsFieldList + s + ';';
          MaxIndexedSizeList := MaxIndexedSizeList + tIndexColumns.FieldByName('MaxIndexedSize').AsString + ';';
          tIndexColumns.Next;
         end;
       finally
        tIndexColumns.Filtered := False;
       end;
       if FieldList <> '' then SetLength(FieldList, Length(FieldList)-1);
       if DescFieldList <> '' then SetLength(DescFieldList, Length(DescFieldList)-1);
       if CaseInsFieldList <> '' then SetLength(CaseInsFieldList, Length(CaseInsFieldList)-1);
       if MaxIndexedSizeList <> '' then SetLength(MaxIndexedSizeList, Length(MaxIndexedSizeList)-1);
       Fields := FieldList;
       CaseInsFields := CaseInsFieldList;
       DescFields := DescFieldList;
       MaxIndexedSizes := MaxIndexedSizeList;
      end;
     tIndexes.Next;
    end;
end;


procedure MoveRow(Dataset: TDataSet; MoveUp: Boolean);
var
  bmSelected, bmOther: TBookmark;
  iSelected, iOther: Integer;
begin
  bmSelected:= Dataset.GetBookmark;
  try
    iSelected:= Dataset.FieldByName('SortOrder').AsInteger;
    if MoveUp then Dataset.Prior
    else Dataset.Next;
    if Dataset.Eof then Exit;
    bmOther:= Dataset.GetBookmark;
    iOther:= Dataset.FieldByName('SortOrder').AsInteger;
    Dataset.GotoBookmark(bmSelected);
    Dataset.Edit;
    Dataset.FieldByName('SortOrder').AsInteger:= -1;
    Dataset.Post;
    Dataset.GotoBookmark(bmOther);
    Dataset.Edit;
    Dataset.FieldByName('SortOrder').AsInteger:= iSelected;
    Dataset.Post;
    Dataset.GotoBookmark(bmSelected);
    Dataset.Edit;
    Dataset.FieldByName('SortOrder').AsInteger:= iOther;
    Dataset.Post;
  finally
    Dataset.GotoBookmark(bmSelected);
  end;
end;

function TfrmMain.GetCurrentTableName: String;
var
  n: TTreeNode;
begin
 Result := '';
 n := tvDatabase.Selected;
 if n = nil then Exit;
 if n.IsFirstNode then Exit;
 Result := n.Text;
end;


procedure TfrmMain.GetSelectedTableNames(List: TStrings);
var
  i: Integer;
begin
  List.Clear;
  for i:=0 to tvDatabase.SelectionCount-1 do
   if not tvDatabase.Selections[i].IsFirstNode then List.Add(tvDatabase.Selections[i].Text);
end;


procedure TfrmMain.tvDatabaseKeyDown(Sender: TObject; var Key: Word;
  Shift: TShiftState);
begin
 case Key of
   13:    aRestructureTable.Execute;
   VK_F2: aRenameTable.Execute;
 end;
end;

procedure TfrmMain.tvDatabaseCollapsing(Sender: TObject; Node: TTreeNode;
  var AllowCollapse: Boolean);
begin
 AllowCollapse := False;
end;


procedure TfrmMain.tvDatabaseChange(Sender: TObject; Node: TTreeNode);
begin
 if ((FOldActilePage <> nil) and
     (pcTables.ActivePage = tsDatabase) and
     (tvDatabase.Selected.IsFirstNode)) then Exit;

 if tvDatabase.Selected.IsFirstNode then
  begin
   FOldActilePage := pcTables.ActivePage;
   
   tsDatabase.TabVisible := True;
   tsStructure.TabVisible := False;
   tsExportToSql.TabVisible := False;
   tsTableData.TabVisible := False;
   pcTables.ActivePage := tsDatabase;

   aExportAsSqlToScreen.Enabled := False;
   aExportAsSqlToFile.Enabled := False;
   aEmptyTables.Enabled := False;
   aDeleteTables.Enabled := False;
   aRestructureTable.Enabled := False;
   aRenameTable.Enabled := False;
   aRefreshTableData.Enabled := False;
   aRefreshTableStructure.Enabled := False;
   aCopyTables.Enabled := False;
   aBorrowStructure.Enabled := False;
   aPrintStructure.Enabled := False;
   aExportToExcel.Enabled := False;
  end
 else
  begin
    if ((FOldActilePage = nil) or (tsDatabase.TabVisible)) then
      begin
       tsTableData.TabVisible := True;
       pcTables.ActivePage := tsTableData;

       tsDatabase.TabVisible := False;
       tsStructure.TabVisible := True;
       tsExportToSql.TabVisible := True;

       if ((FOldActilePage <> nil) and
           (pcTables.Pages[FOldActilePage.PageIndex].TabVisible)) then
         pcTables.ActivePage := FOldActilePage
       else
         pcTables.ActivePage := tsTableData;

       FOldActilePage := pcTables.ActivePage;
      end;

   SelectTable(tvDatabase.Selected.Text);

   aEmptyTables.Enabled := True;
   aDeleteTables.Enabled := True;
   aCopyTables.Enabled := True;
   aExportAsSqlToScreen.Enabled := True;
   aExportAsSqlToFile.Enabled := True;
   aExportToExcel.Enabled := True;

   if tvDatabase.SelectionCount = 1 then
    begin
     aRestructureTable.Enabled := True;
     aRenameTable.Enabled := True;
     aRefreshTableData.Enabled := True;
     aRefreshTableStructure.Enabled := True;
     aBorrowStructure.Enabled := True;
     aPrintStructure.Enabled := True;
    end
   else
    begin
     aRestructureTable.Enabled := False;
     aRenameTable.Enabled := False;
     aRefreshTableData.Enabled := False;
     aRefreshTableStructure.Enabled := False;
     aBorrowStructure.Enabled := False;
     aPrintStructure.Enabled := True;
    end;
  end;
end;

procedure TfrmMain.aRefreshDatabaseParamsExecute(Sender: TObject);
var
  fs: TFileStream;
begin
 if db.Connected then
  begin
    eDatabaseFileName.Text := db.DatabaseFileName;
    try
      fs := TFileStream.Create(db.DatabaseFileName, fmOpenRead + fmShareDenyNone);
      eDatabaseFileSize.Text := Format('%.3f Mb',[fs.Size / (1024*1000)]);
      fs.Free;
    except
    end;
    ePageSize.Text := IntToStr(db.PageSize);
    ePageCountInExtent.Text := IntToStr(db.PageCountInExtent);
    eMaxConnections.Text := IntToStr(db.MaxConnections);
    cbEncrypted.Checked := (db.Password <> '');
    eCryproAlgorithm.Text := CryptoAlgorithmNames[Integer(db.CryptoAlgorithm)];
  end;
end;

procedure TfrmMain.aChangeDatabaseParamsExecute(Sender: TObject);
var
 Log: String;
 pwd: String;
begin
  if frmDatabase.ShowModal = mrOk then
    begin
     Log := '';
     aCloseDatabase.Execute;
     if frmDatabase.cbEncrypted.Checked then
       pwd := frmDatabase.ePassword1.Text
     else
       pwd := '';
     db.ChangeDatabaseSettings(Log, False, pwd,
                               TABSCryptoAlgorithm(frmDatabase.cbCryptoAgorithm.ItemIndex),
                               StrToInt(frmDatabase.cbPageSize.Text),
                               StrToInt(frmDatabase.cbPageCountInExtent.Text),
                               StrToInt(frmDatabase.eMaxConnections.Text),);
     db.Password := pwd;
     OpenDatabaseFile(db.DatabaseFileName);
    end;
end;

procedure TfrmMain.aRepairDatabaseExecute(Sender: TObject);
var
  log: String;
begin
  aCloseDatabase.Execute;
  try
   AddToLog('Repair Database...');
   log := db.RepairDatabase;
   if (log <> '') then
    begin
     MessageDlg(log, mtError, [mbOK], 0);
     AddToLog(log);
     ShowLog;
    end
   else
    AddToLog('Ok.');
  finally
   OpenDatabaseFile(db.DatabaseFileName);
  end;
end;


procedure TfrmMain.aBorrowStructureExecute(Sender: TObject);
begin
 frmTable.eTableName.text := '';
 if frmTable.ShowModal = mrOk then
  begin
   tCurTable.Close;
   tCurTable.TableName := frmTable.eTableName.text;
   FillFieldDefsAndIndexDefs(tCurTable.AdvFieldDefs,
                             tCurTable.AdvIndexDefs);
   tCurTable.CreateTable;
   aRefreshTableList.Execute;
   SelectTable(name);
  end
 else
  SelectTable;
end;

procedure TfrmMain.dbgTableDataDblClick(Sender: TObject);
begin
if Sender is TDBGrid then
 case TDBGrid(Sender).SelectedField.DataType of
  ftMemo, ftWideMemo :
           begin
             fMemo.CurrentGrid := TDBGrid(Sender);
             fMemo.ShowModal;
           end;
  ftFmtMemo :
           begin
             fFmtMemo.CurrentGrid := TDBGrid(Sender);
             fFmtMemo.ShowModal;
           end;
  ftGraphic :
           begin
             fGraphic.CurrentGrid := TDBGrid(Sender);
             fGraphic.ShowModal;
           end;
  ftBlob : begin
             fBlob.CurrentGrid := TDBGrid(Sender);
             fBlob.ShowModal;
           end;
 end;
end;

procedure TfrmMain.aAboutExecute(Sender: TObject);
begin
  frmAbout.ShowModal;
end;

procedure TfrmMain.aPrintStructureExecute(Sender: TObject);
var
  TableNames: TStringList;
  i,j: Integer;
  curTable: TABSTable;
begin
  if (fTableStructureRpt = nil) then
    begin
      MessageDlg('Sorry, this function does not work as the default printer is offline',mtWarning,[mbOK],0);
      exit;
    end;
  TableNames := TStringList.Create;
  curTable := TABSTable.Create(nil);
  curTable.DatabaseName := tCurTable.DatabaseName;
  try
    with fTableStructureRpt do
      begin
        tblTableList.CreateTable;
        tblTableList.Open;
        tblTableStruct.CreateTable;
        tblTableStruct.Open;

        GetSelectedTableNames(TableNames);
        for i := 0 to TableNames.Count-1 do
          begin
            tblTableList.AppendRecord([TableNames.Strings[i]]);
            curTable.TableName := TableNames.Strings[i];
            curTable.Open;
            for j:=0 to curTable.AdvFieldDefs.Count-1 do
             with tblTableStruct do
              begin
               Insert;
               FieldByName('TableName').AsString := TableNames.Strings[i];
               FieldByName('FieldName').AsString := CurTable.AdvFieldDefs[j].Name;
               FieldByName('Type').AsString := AftToStr(CurTable.AdvFieldDefs[j].DataType);
               FieldByName('Size').AsInteger := CurTable.AdvFieldDefs[j].Size;
               FieldByName('Required').AsBoolean := CurTable.AdvFieldDefs[j].Required;
               FieldByName('Default').AsVariant := CurTable.AdvFieldDefs[j].DefaultValue.AsVariant;
               FieldByName('Min').AsVariant := CurTable.AdvFieldDefs[j].MinValue.AsVariant;
               FieldByName('Max').AsVariant := CurTable.AdvFieldDefs[j].MaxValue.AsVariant;
               FieldByName('BLOBCompressionAlgorithm').AsString := ABSCompressionAlgorithmNames[Integer(CurTable.AdvFieldDefs[j].BLOBCompressionAlgorithm)];
{               FieldByName('BLOBCompressionMode').AsInteger := CurTable.AdvFieldDefs[j].BLOBCompressionMode;
               FieldByName('BLOBBlockSize').AsInteger := CurTable.AdvFieldDefs[j].BLOBBlockSize;}
               Post;
              end;
            curTable.Close;
          end;

//        QuickRep1.PreviewModal;
//        RvProject1.Execute;
        frxReport1.ShowReport;
        tblTableList.Close;
        tblTableStruct.Close;
        tblTableList.DeleteTable;
        tblTableStruct.DeleteTable;
      end;
  finally
    TableNames.Free;
    curTable.Free;
  end;
end;

procedure TfrmMain.tCurTableEditError(DataSet: TDataSet; E: EDatabaseError;
  var Action: TDataAction);
begin
   Action:=daAbort;
   if (E is EABSEngineError) then
        case (EABSEngineError(E).ErrorCode) of
          ABS_ERR_RECORD_LOCKED:
           begin
             if MessageDlg('The record you are trying to edit is locked. '+
                           'Do you want to try again?',
                           mtWarning,[mbYes,mbNo],0)=mrYes then
                Action:=daRetry;
           end;
          ABS_ERR_TABLE_LOCKED:
           begin
             if MessageDlg('The table you are trying to edit is locked. '+
                           'Do you want to try again?',
                           mtWarning,[mbYes,mbNo],0)=mrYes then
                Action:=daRetry;
           end;
          ABS_ERR_UPDATE_RECORD_MODIFIED:
           begin
             MessageDlg('The record you are trying to edit has been modified by another user. '+
                        'The table will now be refreshed',
                        mtWarning,[mbOk],0);
             DataSet.Refresh;
             Action:=daRetry;
           end;
          ABS_ERR_UPDATE_RECORD_DELETED:
           begin
             MessageDlg('The record you are trying to edit has been deleted by another user '+
                        'The table will now be refreshed',
                        mtWarning,[mbOk],0);
             DataSet.Refresh;
             Action:=daRetry;
           end
        else
          MessageDlg(E.Message,mtError,[mbOK],0);
        end
   else
      MessageDlg(E.Message,mtError,[mbOK],0);
end;

procedure TfrmMain.tCurTablePostError(DataSet: TDataSet; E: EDatabaseError;
  var Action: TDataAction);
begin
   Action:=daAbort;
   if (E is EABSEngineError) then
      begin
      if (EABSEngineError(E).ErrorCode = ABS_ERR_CONSTRAINT_VIOLATED) then
         MessageDlg(EABSEngineError(E).ErrorMessage+
                    '. Please change the record to make the constraint satisfied '+
                    'and re-post the record.',mtError,[mbOK],0)
      else if (EABSEngineError(E).ErrorCode = ABS_ERR_TABLE_LOCKED) then
         begin
         if MessageDlg('The table is locked. '+
                       'Do you want to try to post this record again?',
                        mtWarning,[mbYes,mbNo],0)=mrYes then
            Action:=daRetry;
         end
      else
         MessageDlg(E.Message,mtError,[mbOK],0);
       end
   else
      MessageDlg(E.Message,mtError,[mbOK],0);
end;

procedure TfrmMain.tCurTableDeleteError(DataSet: TDataSet;
  E: EDatabaseError; var Action: TDataAction);
begin
   Action:=daAbort;
   if (E is EABSEngineError) then
        case (EABSEngineError(E).ErrorCode) of
          ABS_ERR_RECORD_LOCKED:
           begin
           if MessageDlg('The record is locked. '+
                         'Do you want to try to delete this record again?',
                          mtWarning,[mbYes,mbNo],0)=mrYes then
              Action:=daRetry;
           end;
          ABS_ERR_TABLE_LOCKED:
           begin
           if MessageDlg('The table is locked. '+
                         'Do you want to try to delete this record again?',
                          mtWarning,[mbYes,mbNo],0)=mrYes then
              Action:=daRetry;
           end;
          ABS_ERR_DELETE_RECORD_MODIFIED:
           begin
             MessageDlg('The record you are trying to delete has been modified by another user. '+
                        'The table will now be refreshed. If you want to delete this record, try again.',
                        mtWarning,[mbOk],0);
             DataSet.Refresh;
           end;
          ABS_ERR_DELETE_RECORD_DELETED:
           begin
             MessageDlg('The record you are trying to delete has been deleted by another user '+
                        'The table will now be refreshed',
                        mtWarning,[mbOk],0);
             DataSet.Refresh;
           end
      else
         MessageDlg(E.Message,mtError,[mbOK],0);
       end
   else
      MessageDlg(E.Message,mtError,[mbOK],0);
end;

procedure TfrmMain.tCurTableAfterScroll(DataSet: TDataSet);
begin
  lRecordCount.Caption := IntToStr(tCurTable.RecordCount);
end;

procedure TfrmMain.qQueryAfterScroll(DataSet: TDataSet);
begin
  lRecordCount.Caption := IntToStr(tCurTable.RecordCount);
end;

procedure TfrmMain.AddToLog(Message: String = '');
begin
  if Message = '' then
    reLog.Lines.Add('')
  else
    reLog.Lines.Add('['+DateTimeToStr(now)+'] ' + Message);

  //reLog.CaretPos := Point(-1,0);
  reLog.Perform(EM_SCROLLCARET, 0, 0);
end;

procedure TfrmMain.ShowLog;
begin
  StatusBar.SimpleText := '';
  pcTables.ActivePage := tsSQL;
  pcSQL.ActivePage := tsLog;
end;

procedure TfrmMain.SetAllDatabaseActionsEnabled(Flag: Boolean);
begin
  aCloseDatabase.Enabled := Flag;
  aChangeDatabaseParams.Enabled := Flag;
  aCompactDatabase.Enabled := Flag;
  aRepairDatabase.Enabled := Flag;
  aExecSQL.Enabled := Flag;
end;

procedure TfrmMain.SetAllTableActionsEnabled(Flag: Boolean);
begin
  aCreateTable.Enabled := Flag;
  aEmptyTables.Enabled := Flag;
  aDeleteTables.Enabled := Flag;
  aRestructureTable.Enabled := Flag;
  aRenameTable.Enabled := Flag;
  aRefreshTableList.Enabled := Flag;
  aRefreshTableData.Enabled := Flag;
  aRefreshTableStructure.Enabled := Flag;
  aCopyTables.Enabled := Flag;
  aBorrowStructure.Enabled := Flag;
  aPrintStructure.Enabled := Flag;
end;

procedure TfrmMain.tvDatabaseDblClick(Sender: TObject);
begin
 if tvDatabase.Selected.IsFirstNode then
   aChangeDatabaseParams.Execute
 else
   //aRestructureTable.Execute
   begin
     pcTables.ActivePage := tsSQL;
     if frmQueryMaker.Visible then
       frmQueryMaker.AddTable(tCurTable, Point(0, 0))
     else
       aGoToTableData.Execute
   end;
end;

procedure TfrmMain.tvDatabaseMouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer);
var n: TTreeNode;
begin
 n := tvDatabase.GetNodeAt(X,Y);
 {if n = nil then
   tvDatabase.PopupMenu := nil
 else }if n= tvDatabase.Items[0] then
   tvDatabase.PopupMenu := pmDataBase
 else
   tvDatabase.PopupMenu := pmTables;
end;

procedure TfrmMain.aCopyTablesExecute(Sender: TObject);
var
  s,d, newname: String;
  i: Integer;
  TableList: TStringList;
  t: TABSTable;
begin
  TableList := TStringList.Create;
  try
   GetSelectedTableNames(TableList);
   if TableList.Count <=0 then Exit;
   frmCopyTable.eDbFileName.Text := db.DatabaseFileName;
   if TableList.Count=1 then
    begin
     frmCopyTable.Caption := 'Copy table: ' + TableList[0];
     frmCopyTable.eTableName.Text := TableList[0];
     frmCopyTable.eTableName.Enabled := True;
     frmCopyTable.rbCurrentDb.Enabled := True;
     frmCopyTable.rbCurrentDbClick(Sender);
    end
   else
    begin
     s := '';
     d := '';
     for i:=0 to TableList.Count-1 do
      begin
       s := s + d + TableList[i];
       d := ', ';
      end;
     frmCopyTable.Caption := 'Copy tables: (' + s + ')';
     frmCopyTable.eTableName.Text := s;
     frmCopyTable.eTableName.Enabled := False;
     frmCopyTable.rbCurrentDb.Enabled := False;
     frmCopyTable.rbSelectedDbClick(Sender);
     frmCopyTable.rbSelectedDb.Checked := True;
    end;
  if frmCopyTable.ShowModal = mrOk then
   begin
     for i:=0 to TableList.Count-1 do
      begin
       t:=TABSTable.Create(nil);
       try
        t.DatabaseName := db.DatabaseName;
        t.TableName := TableList[i];
        if frmCopyTable.eTableName.Enabled then
          newname := frmCopyTable.eTableName.Text
        else
          newname := TableList[i];
        if frmCopyTable.rbCurrentDb.Checked then
          t.CopyTable(newname,'')
        else
          t.CopyTable(newname, frmCopyTable.eDbFileName.Text)
       finally
        t.Free;
       end;
      end;
   end;
  finally
   aRefreshTableList.Execute;
   TableList.Free;
  end;
end;

procedure TfrmMain.aOpenSQLScriptExecute(Sender: TObject);
var
  oldSH: Boolean;
  S: string;
begin
  if odSql.Execute then
   begin
    S := ChangeFileExt(odSql.FileName, sqmFileExt);
    with frmQueryMaker do begin
      Visible := False;
      if FileExists(S) then begin
        ReadFromFile(S);
        Caption := Format(sMakerCaption, [S]);
        FCurCaption := Caption;
        aQueryMakerExecute(nil);
      end else begin
        RemoveAllTables(True);
        Caption := Format(sMakerCaption, [sNewProject]);
        FCurCaption := Caption;
      end;
    end;
    oldSH := aSyntaxHighlighting.Checked;
    aSyntaxHighlighting.Checked := False;
    reSQL.PlainText := true;
    reSQL.Text := '';
    reSQL.Lines.LoadFromFile(odSql.FileName);
    pcTables.ActivePage := tsSQL;
    if oldSH then aSyntaxHighlighting.Execute;
   end;
end;

procedure TfrmMain.aSaveSQLScriptExecute(Sender: TObject);
var
  S: string;
begin
 if sdSql.Execute then
  begin
    reSQL.PlainText := true;
    reSQL.Lines.SaveToFile(sdSql.FileName);
    S := ChangeFileExt(sdSql.FileName, sqmFileExt);
    frmQueryMaker.WriteToFile(S);
    frmQueryMaker.Caption := S;
  end;
end;

procedure TfrmMain.reSQLSelectionChange(Sender: TObject);
begin
  lLine.Caption := IntToStr(reSQL.CaretPos.Y+1);
  lColumn.Caption := IntToStr(reSQL.CaretPos.X+1);
end;

function TfrmMain.ExtractNumber(Txt, Prefix: String): Integer;
var
  s,p: String;
  i: Integer;
  n: String;
begin
  Result := 0;
  s := AnsiUpperCase(Txt);
  p := AnsiUpperCase(Prefix);
  i := AnsiPos(p,s);
  if i = 0 then exit;
  i := i + Length(Prefix);
  while (not (s[i] in ['0'..'9'])) and (i<=Length(s)) do Inc(i);
  n:='';
  while (s[i] in ['0'..'9']) and (i<=Length(s)) do
   begin
    n:=n+s[i];
    Inc(i);
   end;
  if n <> '' then
    Result := StrToInt(n);

end;

procedure TfrmMain.reSQLChange(Sender: TObject);
begin
  SQLHiLight;
end;


procedure TfrmMain.SQLHiLight;
var i,j: Integer;
    OldCaretPos: TPoint;
    OldSelStart: Integer;
    OldSelLength: Integer;
    Lexer: TABSLexer;
    t: TToken;
    strStream: TStringStream;
begin
  if not aSyntaxHighlighting.Checked then Exit;

  try
    strStream := TStringStream.Create('');
    try
      //reSQL.Enabled := False;
      reSQL.PlainText := True;
      Lexer := TABSLexer.Create(reSQL.Text);

      OldCaretPos := reSQL.CaretPos;
      OldSelStart := reSQL.SelStart;
      OldSelLength := reSQL.SelLength;
      //OldOnSelectionChange := reSQL.OnSelectionChange;
      reSQL.OnSelectionChange := nil;
      //OldOnChange := reSQL.OnChange;
      reSQL.OnChange := nil;
      try
        reTemp.PlainText := True;
        reTemp.Text := '';
        reTemp.Text := reSQL.Text;

        //reSQL.SelectAll;
        //reSQL.SelAttributes.Style := [];
        //reSQL.SelAttributes.Color := clWindowText;

        for i:=0 to Lexer.NumCommands-1 do
         for j:=0 to Lexer.Commands[i].NumTokens-1 do
          begin
           t := Lexer.Commands[i].Tokens[j];
           reTemp.CaretPos := Point(t.ColumnNum-1, t.LineNum-1);
           reTemp.SelLength := Length(t.Text);
           case t.TokenType of
             tktString:           begin
                                    //reSQL.SelAttributes.Style := [fsBold];
                                  end;
             tktInt,
             tktFloat:            begin
                                    reTemp.SelAttributes.Style := [fsBold];
                                    reTemp.SelAttributes.Color := clMaroon;
                                  end;

             tktReservedWord:     begin
                                    reTemp.SelAttributes.Color := clBlue;
                                    reTemp.SelAttributes.Style := [fsBold];
                                  end;
             tktQuotedString,
             tktBracketedString:  begin
                                    reTemp.SelLength := reTemp.SelLength + 2;
                                    reTemp.SelAttributes.Color := clPurple;
                                    reTemp.SelAttributes.Style := [fsBold];
                                  end;
             tktLeftParenthesis,
             tktRightParenthesis: begin
                                    reTemp.SelAttributes.Color := clGreen;
                                    reTemp.SelAttributes.Style := [fsBold];
                                  end;
           end;
          end;
      finally
        reTemp.PlainText := False;
        reTemp.Lines.SaveToStream(strStream);
        strStream.Seek(0, soFromBeginning);
        reSQL.PlainText := False;
        reSQL.Lines.LoadFromStream(strStream);
        reSQL.PlainText := True;
  
        reSQL.CaretPos := OldCaretPos;
        reSQL.SelStart := OldSelStart;
        reSQL.SelLength := OldSelLength;

        SendMessage(reSQL.Handle, WM_KEYDOWN, VK_LEFT, 0);
        if (reSQL.SelStart <> OldSelStart) then
          SendMessage(reSQL.Handle, WM_KEYDOWN, VK_RIGHT, 0);

        reSQL.OnSelectionChange := reSQLSelectionChange;
        reSQL.OnChange := reSQLChange;

        Lexer.Free;
      end;
    finally
      strStream.Free;
    end;
  except
  end;
  reSQL.PlainText := True;
end;

procedure TfrmMain.aSyntaxHighlightingExecute(Sender: TObject);
var OldCaretPos: TPoint;
begin
  if aSyntaxHighlighting.Checked then
    SQLHiLight
  else
    begin
      OldCaretPos := reSQL.CaretPos;
      reSQL.PlainText := True;
      reSQL.SelectAll;
      reSQL.SelAttributes.Style := [];
      reSQL.SelAttributes.Color := clWindowText;
      reSQL.CaretPos := OldCaretPos;
    end;
  if (pcTables.ActivePage = tsSQL) then
    reSQL.SetFocus;
end;

procedure TfrmMain.aEmptyTablesExecute(Sender: TObject);
var
  s: String;
  i: Integer;
  d: String;
  t: TABSTable;
  TableList: TStringList;
begin
  TableList := TStringList.Create;
  try
   GetSelectedTableNames(TableList);
   if TableList.Count <=0 then Exit;
    s := 'Empty (truncate) all selected tables: (';
    d := '';
    for i:=0 to TableList.Count-1 do
     begin
      s := s + d + TableList[i];
      d := ', ';
     end;
    s := s + '). Are you sure?';
    if (MessageDlg(s, mtConfirmation, [mbYes,mbNo],0) = mrYes) then
     begin

      t := TABSTable.Create(self);
      try
       t.DatabaseName := db.DatabaseName;
       qQuery.Close;
       tCurTable.Close;
       for i:=0 to TableList.Count-1 do
         begin
          t.TableName := TableList[i];
          t.EmptyTable;
         end;
      finally
       t.Free;
      end;

     end;
  finally
   TableList.Free;
   aRefreshTableList.Execute;
  end;
end;

procedure TfrmMain.ExportToSql(ToFile: Boolean);
var
  TableList: TStringList;
  t: TABSTable;
  i: Integer;
  fs: TFileStream;
begin
  fs := nil;
  if (not ToFile) or sdExportToSql.Execute then
   begin
    if ToFile then
      fs := TFileStream.Create(sdExportToSql.FileName, fmCreate or fmOpenWrite or fmShareDenyWrite);
    try
      TableList := TStringList.Create;
      try
        GetSelectedTableNames(TableList);
        reExportToSql.Clear;
        t := TABSTable.Create(self);
        try
          t.DatabaseName := db.DatabaseName;
          with t.ExportToSqlOptions do
            begin
              Structure := cbStructure.Checked;
              AddDropTable := cbAddDropTable.Checked;
              BlobSettings := cbBlobSettings.Checked;
              Data := cbData.Checked;
              FieldNamesInInserts := cbFieldNamesInInsert.Checked;
            end;
          for i:=0 to TableList.Count-1 do
            begin
              t.TableName := TableList[i];
              if ToFile then
                t.ExportToSQL(fs)
              else
                reExportToSql.Lines.Add(t.ExportToSQL);
            end;
        finally
          if ToFile then t.Free;
        end;
      finally
       TableList.Free;
      end;
    finally
     fs.Free;
    end;
    if ToFile then reExportToSql.Text := 'Script saved to file: ' + sdExportToSql.FileName;
   end;
end;

procedure TfrmMain.aExportAsSqlToScreenExecute(Sender: TObject);
begin
  ExportToSql(False);
end;

procedure TfrmMain.aExportAsSqlToFileExecute(Sender: TObject);
begin
  ExportToSql(True);
end;

procedure TfrmMain.cbStructureClick(Sender: TObject);
begin
  cbAddDropTable.Enabled := cbStructure.Checked;
  cbBlobSettings.Enabled := cbStructure.Checked;
end;

procedure TfrmMain.cbDataClick(Sender: TObject);
begin
  cbFieldNamesInInsert.Enabled := cbData.Checked;
end;

procedure TfrmMain.aMakeExeDatabaseExecute(Sender: TObject);
begin
  if (frmMakeExeDB.ShowModal = mrOK) then
    begin
      aCloseDatabase.Execute;
      try
       db.MakeExecutableDatabase(frmMakeExeDB.edExeFileName.Text, frmMakeExeDB.edExeDBFileName.Text);
       ShowMessage('Executable database created: '+QuotedStr(frmMakeExeDB.edExeDBFileName.Text));
      finally
       OpenDatabaseFile(db.DatabaseFileName);
      end;
    end;
end;

procedure TfrmMain.FormActivate(Sender: TObject);
begin
   // display bug fix
   if (pcTables.Pages[Settings.LastPageIndex].TabVisible) then
     pcTables.ActivePageIndex := Settings.LastPageIndex;
   pcTablesChange(nil);
   Self.OnActivate := nil;
end;

procedure TfrmMain.aExecSQLScriptExecute(Sender: TObject);
var
  f: TFileStream;
  sql: AnsiString;
  processedCommandsCount: int64;

function GetNextChar(var c: AnsiChar): Boolean;
begin
  Result := (f.Read(c,1) = 1);
end;

function GetNextSQLCommand: Boolean;
type TParseMode = (pmCommandText,pmCommentBlock,pmLineComment,pmSingleQuotedText,pmDoubleQuotedText);
const SingleQuote = '''';
      DoubleQuote = '"';
var
  c: AnsiChar;
  PrevChar: AnsiChar;
  ParseMode: TParseMode;
  HasMeaningChars: Boolean;
begin
  sql := '';
  c := #0;
  PrevChar := #0;
  ParseMode := pmCommandText;
  HasMeaningChars := false;
  while GetNextChar(c) do
    begin
      case ParseMode of
        pmCommandText:
           begin
             if (PrevChar = '/') and (c = '*') then
               ParseMode := pmCommentBlock
             else
             if (PrevChar = '-') and (c = '-') then
               ParseMode := pmLineComment
             else
             if (c = SingleQuote) then
               ParseMode := pmSingleQuotedText
             else
             if (c = DoubleQuote) then
               ParseMode := pmDoubleQuotedText
             else
             if (c = ';') then
               break;
           end;
        pmCommentBlock:
           begin
             if (PrevChar = '*') and (c = '/') then
               ParseMode := pmCommandText
           end;
        pmLineComment:
           begin
             if (c in [#13,#10]) then
               ParseMode := pmCommandText
           end;
        pmSingleQuotedText:
           begin
             if (c = SingleQuote) then
               ParseMode := pmCommandText
           end;
        pmDoubleQuotedText:
           begin
             if (c = DoubleQuote) then
               ParseMode := pmCommandText
           end;
      end;
      sql := sql+c;
      PrevChar := c;
      if (not HasMeaningChars) then
        HasMeaningChars := (not (c in [' ',#13,#10,#9]));
    end;
  Result := HasMeaningChars;
end;

begin
  if odSql.Execute then
   begin
      f := TFileStream.Create(odSql.FileName, fmOpenRead or fmShareDenyNone);
      // execute large SQL script
      tCurTable.Close;
      Screen.Cursor := crHourGlass;
      try
        qQuery.Close;
        lSQLRecordCount.Caption := '0';
        lReadOnly.Caption := '';
        AddToLog;
        AddToLog('Executing SQL Script: ' + odSql.FileName);

        qQuery.ReadOnly := False;
        qQuery.RequestLive := cbRequestLive.Checked;
        processedCommandsCount := 0;
        try
          while GetNextSQLCommand do
            begin
              qQuery.SQL.Text := sql;
              if (processedCommandsCount mod 1000 = 0) then
                StatusBar.SimpleText := IntToStr(processedCommandsCount)+' commands performed.';
              inc(processedCommandsCount);
              try
               qQuery.ExecSQL;
              except
               on e: Exception do
                begin
                  AddToLog('Error running SQL command: '+sql+' Error: '+e.Message);
                end;
              end;
              Application.ProcessMessages;
            end;
          AddToLog('Finished.');
          if ( qQuery.RowsAffected <> -1 ) then
           AddToLog(Format(' %d rows affected by last SQL command.', [qQuery.RowsAffected]));
          ShowLog;
        except
         on e: EABSException do
          begin
             qQuery.Close;
             reSQL.Text := sql;
             reSQL.CaretPos := Point(ExtractNumber(e.Message,'column')-1,
                                     ExtractNumber(e.Message,'line')-1);
             AddToLog(e.Message);
             ShowLog;
             reSQL.SetFocus;
          end;
         on e: Exception do
          begin
           qQuery.Close;
           AddToLog(e.Message);
           ShowLog;
          end;
        end;//try-except

      finally
        f.free;
        Screen.Cursor := crDefault;
        aRefreshTableList.Execute;
        if (tCurTable.Exists) then
          tCurTable.Open
        else
          SelectTable;
      end;
   end;
end;

procedure TfrmMain.aExportToExcelExecute(Sender: TObject);
begin
  if ( pcTables.ActivePage = tsTableData ) then
    ExportToExcel(dsTable, True, StatusBar)
  else
    if qQuery.Active then
      ExportToExcel(dsQuery, True, StatusBar);
end;

procedure TfrmMain.pcTablesChange(Sender: TObject);
begin
  if ( pcTables.ActivePage = tsTableData ) then
    aExportToExcel.Enabled := True
  else
    if ( pcTables.ActivePage = tsSQL ) then
      aExportToExcel.Enabled := qQuery.Active;
end;

procedure TfrmMain.aHelpExecute(Sender: TObject);
{var
  tReg : TRegistry;
	sHelpFile : string;
  InstallDir: String;}
begin
  Application.HelpCommand(15, 0);
  {
  tReg := TRegistry.Create;
  try
    tReg.RootKey := HKEY_CURRENT_USER;
    tReg.OpenKey('SOFTWARE\ComponentAce\Absolute Database', true);
    InstallDir := tReg.ReadString('InstallDir');

    sHelpFile := InstallDir+'\Help\AbsDbManual.hlp';
    if not(FileExists(sHelpFile)) then
      sHelpFile := ExtractFilePath(ParamStr(0))+'..\..\Help\AbsDbManual.hlp';
    if FileExists(sHelpFile) then
      ShellExecute(Handle,'Open',PChar(sHelpFile),nil,nil,SW_SHOWNORMAL)
    else
      MessageDlg('Help file not found', mtInformation, [mbOK], 0);
  finally
    tReg.Free;
  end;}
end;

procedure TfrmMain.pmQueryClick(Sender: TObject);
var sql: String;
    mi: TMenuItem;
    i: Integer;
begin
  mi := TMenuItem(Sender);
  if (mi = mSelectAll) then
    sql := 'select * from '+ GetCurrentTableName + #13#10
  else if (mi = mSelectCount) then
    sql := 'select count(*) cnt from '+ GetCurrentTableName + #13#10
  else if (mi = mCurrentTable) then
    sql := GetCurrentTableName
  else if (mi = mFieldList) then
    begin
      // fill dialog
      frmFieldList.lbFields.ItemIndex := 0;
      frmFieldList.lbFields.Items.Clear;
      for i := 0 to dsTable.DataSet.Fields.Count-1 do
        if dsTable.DataSet.Fields[i].Visible then
          frmFieldList.lbFields.Items.Add(dsTable.DataSet.Fields[i].DisplayName);
      frmFieldList.lbFields.SelectAll;
      // show dialog
      if frmFieldList.ShowModal <> mrOk then Exit;
      // make query string
      sql := '';
      for i:=0 to frmFieldList.lbFields.Count-1 do
        if frmFieldList.lbFields.Selected[i] then
          if sql = '' then
            sql := sql + BracketFieldName(frmFieldList.lbFields.Items[i])
          else
            sql := sql + ', ' + BracketFieldName(frmFieldList.lbFields.Items[i])
    end
  ;

  reSQL.SelText := sql;
  reSQL.SetFocus;
end;

procedure TfrmMain.aGoToTableDataExecute(Sender: TObject);
begin
  pcTables.ActivePage := tsTableData;
  pcTablesChange(nil);
end;

procedure TfrmMain.aShowSqlHistoryExecute(Sender: TObject);
begin
  if aShowSqlHistory.Checked then
    begin
      pSqlHistory.Width := pSqlHistory.Tag;
    end
  else
    begin
      pSqlHistory.Tag := pSqlHistory.Width;
      pSqlHistory.Width := -1;
    end
end;

procedure TfrmMain.aSqlOptimizationExecute(Sender: TObject);
begin
 Application.HelpJump('IncreaseSQLPerformance');
end;

procedure TfrmMain.aChangeLogExecute(Sender: TObject);
begin
 frmChangeLog.ShowModal;
end;

procedure TfrmMain.lbSqlHistoryClick(Sender: TObject);
var i,n: Integer;
    s: String;
begin
  i := lbSqlHistory.ItemIndex;
  s := Trim(reSQL.Text);
  if s <> '' then
   begin
    n := SqlHistory.FindSQL(s);
    if ( n = -1 ) then
     begin
      SqlHistory.AddQuery(s);
      lbSqlHistory.ItemIndex := i+1;
     end;
   end;
  reSQL.Text := SqlHistory[lbSqlHistory.ItemIndex];
end;

procedure TfrmMain.lbSqlHistoryDrawItem(Control: TWinControl;
  Index: Integer; Rect: TRect; State: TOwnerDrawState);
var  c: TCanvas;
begin
  c := (Control as TListBox).Canvas;
  c.TextFlags := ETO_OPAQUE;
  c.FillRect(Rect);
  if odSelected in State then
    c.Font.Color := clWhite
  else
    if Index = 0 then
      c.Font.Color := clGreen
    else
      c.Font.Color := clBlack;
  c.TextOut(Rect.Left, Rect.Top, (Control as TListBox).Items[Index]);
end;


procedure TfrmMain.lbSqlHistoryKeyDown(Sender: TObject; var Key: Word;
  Shift: TShiftState);
begin
  if Key = VK_DELETE then
    aDelFromSqlHistory.Execute;
end;

procedure TfrmMain.aDelFromSqlHistoryExecute(Sender: TObject);
begin
  SqlHistory.DeleteCurrentQuery;
  reSQL.Text := SqlHistory[lbSqlHistory.ItemIndex];
end;

procedure TfrmMain.lbSqlHistoryMouseMove(Sender: TObject;
  Shift: TShiftState; X, Y: Integer);
var
  APoint: TPoint;
  Index: Integer;
begin
  APoint.X := X;
  APoint.Y := Y;
  Index := lbSqlHistory.ItemAtPos(APoint, True);
  if (Index <> -1) then
   if (SqlHistory[Index] <> lbSqlHistory.Hint) then
     begin
      lbSqlHistory.ShowHint := False;
      Application.HideHint;
      lbSqlHistory.Hint := SqlHistory[Index];
      Application.ProcessMessages;
      lbSqlHistory.ShowHint := True;
      Application.ShowHint := True;
     end;
end;

procedure TfrmMain.aQueryMakerExecute(Sender: TObject);
begin

  frmQueryMaker.Visible := not frmQueryMaker.Visible;
  case frmQueryMaker.Visible of    //
    False: ToolButton3.Hint := sShowQueryMaker;
    True: ToolButton3.Hint := sHideQueryMaker;
  end;    // case
end;

initialization
//  DecimalSeparator := '.'; // SQL requires '.' as a decimal separator


end.

