unit uQueryMaker;

interface

uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, DB, ABSMain, Grids, ExtCtrls, Menus, ABSTypes,
  ComCtrls, ToolWin, StdCtrls;

const
  ClSelectedField = $00F3D9D9;
  ClBasicTable = $00D76544;
  ClFieldType = $00D76544;
  ClIndexField = $00FAE8E6;
  MaxHorz = 1000;
  MaxVert = 1000;

const
  TJoinColors: array[TABSJoinType] of TColor = (ClBlack, ClPurple, ClBlue, ClGreen, ClRed);
  TJoinDescrUpper: array[TABSJoinType] of string[20] =
   (#13#10' CROSS JOIN', #13#10' INNER JOIN', #13#10'  LEFT OUTER JOIN',
    #13#10' RIGHT OUTER JOIN', #13#10'  FULL OUTER JOIN');
  TJoinDescrLower: array[TABSJoinType] of string[20] =
   (#13#10' cross join', #13#10' inner join', #13#10'  left outer join',
    #13#10' right outer join', #13#10'  full outer join');
  SNameField = 'Field name';
  SSort      = 'Order by';
  SOrCond    = 'Or condition';
  SAgregate  = 'Agregate function';
  STypeField = 'Field type';
  SCondition = 'Where condition';
  SGroup     = 'Group by';
  SHaving    = 'Having';
  SAliasTable = 'Table alias';
  SNameTable = 'Table name';
  SVisible   = 'Displaying';
  SDatabase  = 'Database file';
  SCalcField = 'calc';
  sNewProject = 'New project';
  sMakerCaption = 'Query maker {%s}';
  SDelCalcField = 'Delete ''%s'' defined as:'#13#10 + '%s';
  SAddEditCalcField = 'Definition of ''%s'' is empty.'#13#10 + 'Operation cancelled';
  SAddCalcField = 'Calc field name is empty.'#13#10 + 'Operation cancelled';
  SDefCalcField = 'Calc field ''%s'' defined as:'#13#10 + ' %s';
  SWhereCondition = 'First define ''Where condition'' for this field';
  SHavingCondition = 'First define ''Agregate function'' or ''Group'' for this field';

  sfRemove = 'Remove ''%s''';
  sEditCalcFld = 'Edit Calc Field';
  sDelCalcFld = 'Del Calc Field';
  sfEditCalcFld = 'Edit Calc Field ''%s''';
  sfDelCalcFld = 'Del Calc Field ''%s''';
  sRemoveAllCols = 'Remove All Cols';
  sFileNotFound = 'File "%s" not found';
  ChmHelpFileName  = 'QueryMaker.chm';
  sIntoTable = 'NewTable';
  sShowQueryMaker = 'Show Query Maker';
  sHideQueryMaker = 'Hide Query Maker';
  sqmFileExt = '.sqm';

  SFAgregate: array[0..5] of string[10] =
    ('None', 'Average', 'Count', 'Max', 'Min', 'Sum');
  SFSort: array[0..2] of string[10] =
    ('None', 'Ascending', 'Descending');
  SFShow: array[0..2] of string[11] = ('Show', 'Hide', 'On new line');
  sDelimiter = #149;

type
  TCols = class(TComponent)
  Private
    FSel: TStringList;
    FObj: TStringList;
  Public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
  Published
    property Sel: TStringList Read FSel Write FSel;
    property Obj: TStringList Read FObj Write FObj;
  end;

  TItem = class(TObject)
  Private
    FItem: string;
  Public
    constructor Create(Value: string);
  Published
    property Item: string Read FItem Write FItem;
  end;

  TJoinImage = class(TGraphicControl)
  Private
    FFromTableJoin: longint;
    FToTableJoin: longint;
    FFromFieldJoin: integer;
    FToFieldJoin: integer;
    FABSJoinType: TABSJoinType;
    FDescription: string;
    FFromPoint: TPoint;
    FToPoint: TPoint;
    procedure WriteToFile(Fs: TFileStream);
    procedure ReadFromFile(Fs: TFileStream);
  Public
    constructor Create(AOwner: TComponent); override;
    procedure Paint; override;
  Published
    property FromTableJoin: longint Read FFromTableJoin Write FFromTableJoin;
    property ToTableJoin: longint Read FToTableJoin Write FToTableJoin;
    property FromFieldJoin: integer Read FFromFieldJoin Write FFromFieldJoin;
    property ToFieldJoin: integer Read FToFieldJoin Write FToFieldJoin;
    property ABSJoinType: TABSJoinType Read FABSJoinType Write FABSJoinType;
    property Description: string Read FDescription Write FDescription;
    property FromPointX: integer Read FFromPoint.X Write FFromPoint.X;
    property FromPointY: integer Read FFromPoint.Y Write FFromPoint.Y;
    property ToPointX: integer Read FToPoint.X Write FToPoint.X;
    property ToPointY: integer Read FToPoint.Y Write FToPoint.Y;
  end;

  TColumnsParams = class(TComponent)
  Private
    FType: string;
    FIsIndex: boolean;
    FFormula: string;
    FIsCalc: boolean;
    FCoord: TPoint;
    FNameWidth: integer;
    FTypeWidth: integer;
    FNameColor: TColor;
    FTypeColor: TColor;
    procedure WriteToFile(Fs: TFileStream);
    procedure ReadFromFile(Fs: TFileStream);
  Public
    constructor Create(AOwner: TComponent); override;
  Published
    property IsIndex: boolean Read FIsIndex Write FIsIndex;
    property Formula: string Read FFormula Write FFormula;
    property IsCalc: boolean Read FIsCalc Write FIsCalc;
    property FieldType: string Read FType Write FType;
    property XCoord: integer Read FCoord.X Write FCoord.X;
    property YCoord: integer Read FCoord.Y Write FCoord.Y;
    property NameWidth: integer Read FNameWidth Write FNameWidth;
    property TypeWidth: integer Read FTypeWidth Write FTypeWidth;
    property NameColor: TColor Read FNameColor Write FNameColor;
    property TypeColor: TColor Read FTypeColor Write FTypeColor;
  end;

  TPosChange = procedure(Sender: TObject; Top, Left: integer) of object;

  TTableImage = class(TCustomPanel)
  Private
    FTableNumber: integer;
    FColumns: TStrings;
    FDatabaseFileName: string;
    FMouseIn: boolean;
    FlastPosYTop: integer;
    FlastPosYBottom: integer;
    FSelItem: integer;
    FClearSel: boolean;
    FOnPosChange: TPosChange;
    FHeaderHeight: Integer;
    procedure SetCaptionTable(const Value: TCaption);
    function GetCaptionTable: TCaption;
    procedure ColumnsChange(Sender: TObject);
    function GetTableAlias: string;
    procedure WriteToFile(Fs: TFileStream);
    procedure ReadFromFile(Fs: TFileStream);
  Protected
    procedure Paint; override;
    procedure MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: integer); override;
    procedure MouseMove(Shift: TShiftState; X, Y: integer); override;
    procedure MouseEnter(var Msg: Tmessage); message CM_MOUSEENTER;
    procedure MouseLeave(var Msg: TMessage); message CM_MOUSELEAVE;
    procedure WMWINDOWPOSCHANGED(var Msg: Tmessage); message WM_WINDOWPOSCHANGED;
  Public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
    function GetMaxWidth: integer;
    function GetMaxHeight: integer;
    function GetSelItem(Y: integer): integer;
    procedure SetWidthHeight;
  Published
    property OnPosChange: TPosChange Read FOnPosChange Write FOnPosChange;
    property CaptionTable: TCaption Read GetCaptionTable Write SetCaptionTable;
    property SelectedItem: integer Read FSelItem Write FSelItem; 
    property Columns: TStrings Read FColumns Write FColumns;
    property TableAlias: string Read GetTableAlias;
    property DatabaseFileName: string Read FDatabaseFileName Write FDatabaseFileName;
    property TableNumber: integer Read FTableNumber Write FTableNumber;
    property HeaderHeight: Integer read FHeaderHeight write FHeaderHeight;
  end;

  TfrmQueryMaker = class(TForm)
    SgColumns: TStringGrid;
    PmGrid: TPopupMenu;
    PmTable: TPopupMenu;
    RemoveTable: TMenuItem;
    AddCalcFld: TMenuItem;
    PmJoin: TPopupMenu;
    Crossjoin: TMenuItem;
    Innerjoin: TMenuItem;
    Leftouterjoin: TMenuItem;
    Rightouterjoin: TMenuItem;
    Fullouterjoin: TMenuItem;
    N1: TMenuItem;
    Removejoin: TMenuItem;
    EditCalcFld: TMenuItem;
    DelCalcFld: TMenuItem;
    N2: TMenuItem;
    RemoveAllTbls: TMenuItem;
    ToolBar1: TToolBar;
    CbDistinct: TCheckBox;
    ToolButton1: TToolButton;
    RemoveAllCols: TMenuItem;
    RemoveSelCol: TMenuItem;
    cbLowerCase: TCheckBox;
    cbDoubleQuotes: TCheckBox;
    Label1: TLabel;
    cbIntoTable: TCheckBox;
    cbSaveComments: TCheckBox;
    cbMemory: TCheckBox;
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);

    procedure PmNoSortClick(Sender: TObject);
    procedure PmASCClick(Sender: TObject);
    procedure PmDESCClick(Sender: TObject);

    procedure PmJoinPopup(Sender: TObject);
    procedure CrossjoinClick(Sender: TObject);

    procedure PmTablePopup(Sender: TObject);
    procedure AddCalcFldClick(Sender: TObject);
    procedure EditCalcFldClick(Sender: TObject);
    procedure DelCalcFldClick(Sender: TObject);
    procedure RemoveTableClick(Sender: TObject);
    procedure RemoveAllTblsClick(Sender: TObject);
    procedure CbDistinctClick(Sender: TObject);

    procedure RemoveSelColClick(Sender: TObject);
    procedure RemoveAllColsClick(Sender: TObject);
    procedure SgColumnsDrawCell(Sender: TObject; ACol, ARow: integer; Rect: TRect; State: TGridDrawState);
    procedure SgColumnsSelectCell(Sender: TObject; ACol, ARow: integer; var CanSelect: boolean);
    procedure SgColumnsColumnMoved(Sender: TObject; FromIndex, ToIndex: integer);
    procedure SgColumnsContextPopup(Sender: TObject; MousePos: TPoint; var Handled: boolean);
    procedure SgColumnsMouseWheelDown(Sender: TObject; Shift: TShiftState; MousePos: TPoint; var Handled: boolean);
    procedure SgColumnsDragOver(Sender, Source: TObject; X, Y: integer; State: TDragState; var Accept: boolean);
    procedure SgColumnsDragDrop(Sender, Source: TObject; X, Y: integer);
    procedure SgColumnsClick(Sender: TObject);
    procedure Label1Click(Sender: TObject);
    procedure FormResize(Sender: TObject);
    procedure Label1MouseEnter(Sender: TObject);
    procedure Label1MouseLeave(Sender: TObject);
    procedure FormShow(Sender: TObject);
    procedure FormHide(Sender: TObject);
  Protected
    procedure CreateParams(var Params: TCreateParams); override;
  Private
    FTablesBoard: TForm;
    FTableCounter: integer;
    FSelectedJoin: integer;
    FTablesList: TList;
    FJoinsList: TList;
    IsWheel: boolean;
    procedure WheelWndProc(var Msg: TMsg; var Handled: boolean);
    procedure AgregateClick(Sender: TObject);
    procedure VisibleClick(Sender: TObject);
    procedure tbMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: integer);
    procedure tbDragOver(Sender, Source: TObject; X, Y: integer; State: TDragState; var Accept: boolean);
    procedure tbDragDrop(Sender, Source: TObject; X, Y: integer);
    procedure SetGrouped;
    procedure ResetGrouped(Col: integer);
    procedure ResetSort(Col: integer);
    procedure SgColumnsInit;
    procedure SelectColumn(ACol: longint);
    procedure DeleteColumn(ColNo: integer);
    procedure SetDescrJoin(IndJoin: integer);
    procedure ExecJoin(IndMenu, IndJoin: integer);
    procedure AjustPosWidthTables(var APos: TPoint; var AWidth: integer);
    procedure AddColumn(T: TTableImage);
    procedure TableImageDragOver(Sender, Source: TObject; X, Y: integer; State: TDragState; var Accept: boolean);
    procedure TableImageDragDrop(Sender, Source: TObject; X, Y: integer);
    procedure TableImagePosChange(Sender: TObject; Top, Left: integer);
    procedure TableImageDblClick(Sender: TObject);
    function  IsValidColRow(ACol, ARow: integer): boolean;
    procedure GenerateSQL(Re: TRichEdit);
    function AreTypesCompatible(T1, T2: TABSAdvancedFieldType): boolean;
  Public
    FCurCaption: TCaption;
    procedure RemoveAllTables(LblShow: boolean);
    procedure AddTable(Table: TABSTable; APos: TPoint);
    procedure WriteToFile(const FileName: string);
    procedure ReadFromFile(const FileName: string);
  end;

var
  FrmQueryMaker: TfrmQueryMaker;

implementation

uses Main, ABSConverts, ABSLexer, UCondition, ShellApi;

{$R *.dfm}

function TfrmQueryMaker.AreTypesCompatible(T1, T2: TABSAdvancedFieldType): boolean;
const
  BadTypes1: set of TABSAdvancedFieldType =
    [AftUnknown, AftBytes, AftVarBytes, AftBlob, AftGraphic, AftMemo, AftFormattedMemo, AftWideMemo, AftGuid];
  IntTypes1: set of TABSAdvancedFieldType =
    [AftShortint, AftSmallint, AftInteger, AftLargeint, AftByte, AftWord,
    AftCardinal, AftAutoInc, AftAutoIncShortint, AftAutoIncSmallint, AftAutoIncInteger,
    AftAutoIncLargeint, AftAutoIncByte, AftAutoIncWord, AftAutoIncCardinal];
  NumTypes1: set of TABSAdvancedFieldType = [AftSingle, AftDouble, AftExtended, AftCurrency];
  DateTypes1: set of TABSAdvancedFieldType = [AftDate, AftTime, AftDateTime, AftTimeStamp];
begin
  Result := False;
  if (T1 in BadTypes1) or (T2 in Badtypes1) then begin
    Exit
  end;
  case T1 of
    AftChar: begin
      Result := T2 = AftChar
    end;
    AftString: begin
      Result := T2 = AftString
    end;
    AftWideChar: begin
      Result := T2 = AftWideChar
    end;
    AftWideString: begin
      Result := T2 = AftWideString
    end;
    AftShortint, AftSmallint, AftInteger, AftLargeint, AftByte, AftWord, AftCardinal,
    AftAutoInc, AftAutoIncShortint, AftAutoIncSmallint, AftAutoIncInteger, AftAutoIncLargeint,
    AftAutoIncByte, AftAutoIncWord, AftAutoIncCardinal: begin
      Result := T2 in IntTypes1
    end;
    AftBoolean: begin
      Result := T2 = AftBoolean
    end;
    AftSingle, AftDouble, AftExtended, AftCurrency: begin
      Result := T2 in NumTypes1
    end;
    AftDate, AftTime, AftDateTime, AftTimeStamp: begin
      Result := T2 in DateTypes1
    end;
  end;
end;

procedure TfrmQueryMaker.CreateParams(var Params: TCreateParams);
begin
  inherited;
  with Params do begin
    Style     := Style or WS_OVERLAPPED;
    WndParent := FrmMain.Handle;
  end;
end;

procedure TfrmQueryMaker.AddTable(Table: TABSTable; APos: TPoint);

  function IsItIndex(Table: TABSTable; FieldName: string): boolean;
  var
    I: integer;
  begin
    Result := False;
    for I := 0 to Table.IndexDefs.Count - 1 do begin
      if Pos(FieldName, Table.IndexDefs[I].Fields) <> 0 then begin
        Result := True;
        Break;
      end;
    end;
  end;

var
  I, AWidth: integer;
  S: string;
  T: TTableImage;
  PosTable: TPoint;
begin
  Label1.Visible := False;
  Inc(FTableCounter);
  T := TTableImage.Create(Self);
  T.TableNumber := FTableCounter;
  T.DatabaseFileName := ExtractFileName(Table.Database.DatabaseFileName);
  T.Parent := FTablesBoard;
  T.HeaderHeight := Canvas.TextHeight('Wj') + 4;
  T.Caption := Table.TableName;

  S := '*';
  T.FColumns.AddObject(S, TColumnsParams.Create(T));
  with(T.FColumns.Objects[T.FColumns.Count - 1] as TColumnsParams) do begin
    FNameWidth := Canvas.TextWidth(S);
    FIsIndex := IsItIndex(Table, Trim(S));
    FType := 'all fields';
    FTypeWidth := Canvas.TextWidth(FType);
    FNameColor := ClBlack;
    FTypeColor := ClFieldType;
  end;

  for I := 0 to Table.AdvFieldDefs.Count - 1 do begin
    S := Table.AdvFieldDefs[I].Name;
    T.FColumns.AddObject(S, TColumnsParams.Create(T));
    with(T.FColumns.Objects[T.FColumns.Count - 1] as TColumnsParams) do begin
      FNameWidth := Canvas.TextWidth(S);
      FIsIndex := IsItIndex(Table, Trim(S));
      S    := LowerCase(AftToStr(Table.AdvFieldDefs[I].DataType));
      if Table.AdvFieldDefs[I].Size <> 0 then begin
        S := S + '(' + IntToStr(Table.AdvFieldDefs[I].Size) + ')'
      end;
      FType := S;
      FTypeWidth := Canvas.TextWidth(FType);
      FNameColor := ClBlack;
      FTypeColor := ClFieldType;
    end;
  end;
  T.Tag := longint(T);
  FTablesList.Add(T);
  AWidth := T.Width;
  AjustPosWidthTables(PosTable, AWidth);
  if (APos.Y = 0) and (APos.X = 0) then begin
    T.Top  := PosTable.Y;
    T.Left := PosTable.X;
  end
  else begin
    T.Top  := APos.Y;
    T.Left := APos.X;
  end;
  T.OnDblClick  := TableImageDblClick;
  T.OnDragOver  := TableImageDragOver;
  T.OnDragDrop  := TableImageDragDrop;
  T.OnPosChange := TableImagePosChange;
  T.PopupMenu   := PmTable;
  GenerateSQL(FrmMain.ReSQL);
end;

procedure TfrmQueryMaker.AjustPosWidthTables(var APos: TPoint; var AWidth: integer);
var
  I, MaxLeft, MaxTop: integer;
  MaxTopLeft: boolean;
begin
  MaxLeft := 0;
  MaxTop  := 0;
  AWidth  := 0;
  for I := 0 to FTablesList.Count - 1 do begin
    TTableImage(FTablesList[I]).SetWidthHeight;
  end;
  for I := 0 to FTablesList.Count - 1 do begin
    Application.ProcessMessages;
    if TTableImage(FTablesList[I]).Width > AWidth then begin
      AWidth := TTableImage(FTablesList[I]).Width
    end;
    MaxTopLeft := TTableImage(FTablesList[I]).Left > MaxLeft;
    if MaxTopLeft then begin
      MaxLeft := TTableImage(FTablesList[I]).Left
    end;
    if (TTableImage(FTablesList[I]).Top > MaxTop) or MaxTopLeft then begin
      MaxTop := TTableImage(FTablesList[I]).Top
    end;
  end;
  APos.X := MaxLeft + 5;
  APos.Y := MaxTop + 20;
  for I := 0 to FTablesList.Count - 1 do begin
    Application.ProcessMessages;
    TTableImage(FTablesList[I]).Width := AWidth;
  end;
  if FTablesList.Count = 1 then begin
    APos.Y := 5;
    APos.X := 5;
  end;
end;

procedure TfrmQueryMaker.FormCreate(Sender: TObject);
var
  S: string;
begin
  FCurCaption := Caption;
  FTablesBoard      := TForm.Create(Self);
  FTablesBoard.Name := 'frmBoard';
  FTablesBoard.Parent := FrmQueryMaker;
  FTablesBoard.Align := AlClient;
  FTablesBoard.BorderStyle := BsNone;
  FTablesBoard.HorzScrollBar.Range := MaxHorz;
  FTablesBoard.HorzScrollBar.Tracking := True;
  FTablesBoard.VertScrollBar.Range := MaxVert;
  FTablesBoard.VertScrollBar.Tracking := True;
  FTablesBoard.Show;
  FTablesBoard.OnDragOver := tbDragOver;
  FTablesBoard.OnDragDrop := tbDragDrop;
  FTablesList := TList.Create;
  FJoinsList  := TList.Create;
  SgColumnsInit;
  FTableCounter := 0;
  Label1.Parent := FTablesBoard;
  Label1.Left := (FTablesBoard.ClientWidth - Label1.Width) div 2;
  Label1.Top := (FTablesBoard.ClientHeight - Label1.Height) div 2;
  Label1.OnDragOver := tbDragOver;
  Label1.OnDragDrop := tbDragDrop;
  Application.OnMessage := WheelWndProc;
  cbMemory.Enabled := False;

  S := ChangeFileExt(frmMain.Settings.LastSQMFile, sqmFileExt);
  if FileExists(S) then begin
    ReadFromFile(S);
    Caption := Format(sMakerCaption, [S]);
    FCurCaption := Caption;
    GenerateSQL(FrmMain.ReSQL);
//    Visible := True;
  end
end;

procedure TfrmQueryMaker.FormDestroy(Sender: TObject);
var
  J: integer;
begin
  for J := 0 to FTablesList.Count - 1 do begin
    TTableImage(FTablesList[J]).Free
  end;
  FTablesList.Free;
  for J := 0 to FJoinsList.Count - 1 do begin
    TJoinImage(FJoinsList[J]).Free
  end;
  FJoinsList.Free;
end;

procedure TfrmQueryMaker.SgColumnsDragOver(Sender, Source: TObject; X, Y: integer; State: TDragState;
  var Accept: boolean);
begin
  Accept := (Source is TTableImage);
end;

procedure TfrmQueryMaker.SgColumnsDragDrop(Sender, Source: TObject; X, Y: integer);
begin
  AddColumn(TTableImage(Source));
end;

procedure TfrmQueryMaker.AddColumn(T: TTableImage);
var
  I, Ii, J: integer;

procedure AddSgColumn(ii, j: Integer);
begin
  SgColumns.Cells[Ii, 0] := TrimLeft(T.FColumns[J]);
  SgColumns.Cells[Ii, 1] := T.GetCaptionTable;
  SgColumns.Objects[Ii, 1] := TObject(T);
  SgColumns.Cells[Ii, 2] := T.DatabaseFileName;
  SgColumns.Cells[Ii, 3] := '';
  SgColumns.Objects[Ii, 3] := TObject(0);
  SgColumns.Cells[Ii, 4] := '';
  SgColumns.Cells[Ii, 5] := '';
  SgColumns.Cells[Ii, 6] := '';
  SgColumns.Objects[Ii, 6] := TObject(0);
  SgColumns.Cells[Ii, 7] := (T.FColumns.Objects[J] as TColumnsParams).FType;
  SgColumns.Objects[Ii, 7] := TObject(J);
  SgColumns.Cells[Ii, 8] := SFShow[0];
  SgColumns.Cells[Ii, 9] := '';
  SgColumns.Objects[Ii, 9] := TObject(0);
  SgColumns.Cells[Ii, 10] := '';
  SgColumns.Cells[Ii, 11] := T.TableAlias;
  SgColumns.Objects[Ii, 11] := TObject(T.FTableNumber);
end;

begin
  Ii := -1;
  for I := 0 to SgColumns.Colcount - 1 do begin
    if Length(SgColumns.Cells[I, 0]) = 0 then begin
      Ii := I;
      Break;
    end
  end;
  if Ii = -1 then begin
    Ii := SgColumns.Colcount;
    SgColumns.ColCount := SgColumns.Colcount + 1;
  end;
  if (T.SelectedItem < 0) then begin
    for J := 1 to T.Columns.Count - 1 do begin
      Application.ProcessMessages;
      AddSgColumn(Ii, J);
        Ii := SgColumns.Colcount;
        SgColumns.ColCount := SgColumns.Colcount + 1;
    end;
    SgColumns.ColCount := SgColumns.Colcount - 1;
  end else
  begin
    J := longint(T.SelectedItem);
    AddSgColumn(Ii, J);
  end;
  SelectColumn(Ii);
  GenerateSQL(FrmMain.ReSQL);
end;

procedure TfrmQueryMaker.TableImageDblClick(Sender: TObject);
begin
  AddColumn(Sender as TTableImage);
end;

procedure TfrmQueryMaker.SgColumnsInit;
var
  I: integer;
begin
  SelectColumn(1);
  for I := 0 to SgColumns.Rowcount - 1 do begin
    SgColumns.Cells[1, I] := ''
  end;
  SgColumns.Cells[0, 0] := SNameField;
  SgColumns.Objects[0, 0] := TObject(0);
  SgColumns.Cells[0, 1] := SNameTable;
  SgColumns.Objects[0, 1] := TObject(1);
  SgColumns.Cells[0, 2] := SDatabase;
  SgColumns.Objects[0, 2] := TObject(1);
  SgColumns.Cells[0, 3] := SSort;
  SgColumns.Objects[0, 3] := TObject(0);
  SgColumns.Cells[0, 4] := SCondition;
  SgColumns.Objects[0, 4] := TObject(0);
  SgColumns.Cells[0, 5] := SOrCond;
  SgColumns.Objects[0, 5] := TObject(1);
  SgColumns.Cells[0, 6] := SAgregate;
  SgColumns.Objects[0, 6] := TObject(0);
  SgColumns.Cells[0, 7] := STypeField;
  SgColumns.Cells[0, 8] := SVisible;
  SgColumns.Cells[0, 9] := SGroup;
  SgColumns.Objects[0, 9] := TObject(0);
  SgColumns.Cells[0, 10] := SHaving;
  SgColumns.Cells[0, 11] := SAliasTable;
  SelectColumn(SgColumns.Colcount - 1);
end;

function TfrmQueryMaker.IsValidColRow(ACol, ARow: integer): boolean;
begin
  Result := (ACol > 0) and (ARow in [3..6, 8..10]) and (SgColumns.Cells[ACol, 0] <> '');
end;

procedure TfrmQueryMaker.ResetGrouped(Col: integer);
var
  I, J, K: integer;
begin
  K := longint(SgColumns.Objects[Col, 9]);
  if K = 0 then begin
    Exit
  end;
  SgColumns.Cells[Col, 9] := '';
  SgColumns.Cells[Col, 10] := '';
  SgColumns.Objects[Col, 9] := TObject(0);
  for I := 1 to SgColumns.Colcount - 1 do begin
    Application.ProcessMessages;
    J := longint(SgColumns.Objects[I, 9]);
    if (J > K) and (J > 0) then begin
      Dec(J);
      SgColumns.Objects[I, 9] := TObject(J);
      SgColumns.Cells[I, 9]   := Format('%s_%d', [SGroup, J]);
    end;
  end;
  J := longint(SgColumns.Objects[0, 9]);
  Dec(J);
  SgColumns.Objects[0, 9] := TObject(J);
end;

procedure TfrmQueryMaker.SetGrouped;
var
  J: integer;
begin
  J := longint(SgColumns.Objects[0, 9]);
  Inc(J);
  SgColumns.Objects[0, 9] := TObject(J);
  SgColumns.Objects[SgColumns.Col, 9] := TObject(J);
  SgColumns.Cells[SgColumns.Col, 9] := Format('%s_%d', [SGroup, J]);
  GenerateSQL(FrmMain.ReSQL);
end;

procedure TfrmQueryMaker.GenerateSQL(Re: TRichEdit);
var
  I, J, K:      integer;
  TableList: TStringList;
  GroupList: TStringList;
  OrderList: TStringList;
  TableS: string;
  FieldS: string;
  WhereS: string;
  HavingS: string;
  GroupS: string;
  OrderS: string;
  CurNum: string;
  FirstWhere: boolean;
  FirstHaving: boolean;
  Ti:     TTableImage;
  SAgregate: string;
  S: string;
  Smem: string;
  Comment: string;
  StartComment: boolean;
  Lexer: TABSLexer;
  t: TToken;
  SelPos: integer;
  reText: string;
begin
  Comment := '';
  if cbSaveComments.Checked then begin
  StartComment := False;
  for I := 0 to Re.Lines.Count - 1 do begin
    if StartComment then begin
      Comment := Comment + Re.Lines[I] + #13#10;
    end else
    if (Pos('/*', Re.Lines[I]) <> 0) then begin
      StartComment := True;
      Comment := Comment + Copy(Re.Lines[I], Pos('/*', Re.Lines[I]), Length(Re.Lines[I])) + #13#10;
    end;
    if (Pos('*/', Re.Lines[I]) <> 0) then begin
      StartComment := False;
    end;
  end;
  if Length(Trim(Comment)) <> 0 then
    Comment := Comment + #13#10;
  end;

  if FTablesList.Count = 0 then begin
    if CbDistinct.Checked then begin
      S := 'SELECT DISTINCT *'#13#10
    end
    else begin
      S := 'SELECT *'#13#10
    end;
    if cbIntoTable.Checked then
    if cbMemory.Checked then begin
      S := Format('DROP TABLE IF EXISTS MEMORY %s;'#13#10, [sIntoTable]) + S;
      S := S + Format('  INTO MEMORY %s'#13#10, [sIntoTable]);
    end  else begin
      S := Format('DROP TABLE IF EXISTS %s;'#13#10, [sIntoTable]) + S;
      S := S + Format('  INTO %s'#13#10, [sIntoTable]);
    end;
    reText := Comment + S +
      Format('  FROM "%s" %s1'#13#10, [FrmMain.TCurTable.TableName, FrmMain.TCurTable.TableName[1]]);
  end else begin

  TableList := TStringList.Create;
  TableList.Sorted := True;
  TableList.Duplicates := DupIgnore;
  GroupList := TStringList.Create;
  GroupList.Sorted := True;
  OrderList := TStringList.Create;
  OrderList.Sorted := True;
  try
    WhereS      := '';
    HavingS     := '';
    FirstWhere  := True;
    FirstHaving := True;
    S      := 'SELECT ';
    if CbDistinct.Checked then begin
      S := S + 'DISTINCT '
    end;
      FieldS := S;
    for
      I := 1 to SgColumns.ColCount - 1 do begin
      if Length(SgColumns.Cells[I, 10]) <> 0 then begin
        case FirstHaving of
          False: begin
              HavingS := HavingS + Format(' AND'#13#10 + '        (%s)', [SgColumns.Cells[I, 10]])
          end;
          True: begin
            FirstHaving := False;
            HavingS     := HavingS + Format('(%s)', [SgColumns.Cells[I, 10]]);
          end;
        end
      end;
      if (Length(SgColumns.Cells[I, 4]) <> 0) and (Length(SgColumns.Cells[I, 5]) <> 0) then begin
        case FirstWhere of
          False: begin
            WhereS := WhereS + Format(' AND'#13#10 +
              '       ((%s."%s" %s) OR (%s."%s" %s))'#13#10,
              [SgColumns.Cells[I, 11], SgColumns.Cells[I, 0], SgColumns.Cells[I, 4],
              SgColumns.Cells[I, 11], SgColumns.Cells[I, 0], SgColumns.Cells[I, 5]])
          end;
          True: begin
            FirstWhere := False;
            WhereS     := WhereS + Format('((%s."%s" %s) OR (%s."%s" %s))',
              [SgColumns.Cells[I, 11], SgColumns.Cells[I, 0], SgColumns.Cells[I, 4],
              SgColumns.Cells[I, 11], SgColumns.Cells[I, 0], SgColumns.Cells[I, 5]])
          end
        end
      end
      else if Length(SgColumns.Cells[I, 4]) <> 0 then begin
        case FirstWhere of
          False: begin
            WhereS := WhereS + Format(' AND'#13#10 + '       (%s."%s" %s)',
              [SgColumns.Cells[I, 11], SgColumns.Cells[I, 0], SgColumns.Cells[I, 4]])
          end;
          True: begin
            FirstWhere := False;
            WhereS     := WhereS + Format('(%s."%s" %s)',
              [SgColumns.Cells[I, 11], SgColumns.Cells[I, 0], SgColumns.Cells[I, 4]]);
          end;
        end
      end;

      if SgColumns.Cells[I, 8] = SFShow[0] then begin
        if SgColumns.Cells[I, 7] = SCalcField then begin
          Ti     := (SgColumns.Objects[I, 1] as TTableImage);
          CurNum := (Ti.FColumns.Objects[longint(SgColumns.Objects[I, 7])] as TColumnsParams).FFormula;
          if Pos('CASE', UpperCase(CurNum)) <> 0 then begin
            FieldS := FieldS + Format('%s%sAS "%s"',
              [SgColumns.Cells[I, 6], CurNum, SgColumns.Cells[I, 0]]) + ', '
          end else
            FieldS := FieldS + Format('%s(%s) AS "%s"',
              [SgColumns.Cells[I, 6], CurNum, SgColumns.Cells[I, 0]]) + ', '
        end
        else begin
          if Length(SgColumns.Cells[I, 6]) <> 0 then begin
            case longint(SgColumns.Objects[I, 6]) of
              1: begin
                SAgregate := 'avg'
              end;
              2: begin
                SAgregate := 'cnt'
              end;
              3: begin
                SAgregate := 'max'
              end;
              4: begin
                SAgregate := 'min'
              end;
              5: begin
                SAgregate := 'sum'
              end;
            end;
            FieldS := FieldS + Format('%s(%s."%s") AS "%s%2:s"',
              [SgColumns.Cells[I, 6], SgColumns.Cells[I, 11], SgColumns.Cells[I, 0], SAgregate]) + ', '
          end
          else begin
            FieldS := FieldS + Format('%s."%s"', [SgColumns.Cells[I, 11], SgColumns.Cells[I, 0]]) + ', '
          end;
        end;
        TableList.Add((Format('"%s" %s', [SgColumns.Cells[I, 1], SgColumns.Cells[I, 11]])));
      end;

      if SgColumns.Cells[I, 8] = SFShow[2] then begin
        if SgColumns.Cells[I, 7] = SCalcField then begin
          Ti     := (SgColumns.Objects[I, 1] as TTableImage);
          CurNum := (Ti.FColumns.Objects[longint(SgColumns.Objects[I, 7])] as TColumnsParams).FFormula;
          if Pos('CASE', UpperCase(CurNum)) <> 0 then begin
            FieldS := FieldS + Format(#13#10'  %s%s AS "%s"',
              [SgColumns.Cells[I, 6], CurNum, SgColumns.Cells[I, 0]]) + ', ';
          end else begin
            FieldS := FieldS + Format(#13#10'  %s(%s) AS "%s"',
              [SgColumns.Cells[I, 6], CurNum, SgColumns.Cells[I, 0]]) + ', ';
          end;
        end
        else begin
          if Length(SgColumns.Cells[I, 6]) <> 0 then begin
            case longint(SgColumns.Objects[I, 6]) of
              1: begin
                SAgregate := 'avg'
              end;
              2: begin
                SAgregate := 'cnt'
              end;
              3: begin
                SAgregate := 'max'
              end;
              4: begin
                SAgregate := 'min'
              end;
              5: begin
                SAgregate := 'sum'
              end;
            end;
            FieldS := FieldS + Format(#13#10'  %s(%s."%s") AS "%s%2:s"',
              [SgColumns.Cells[I, 6], SgColumns.Cells[I, 11], SgColumns.Cells[I, 0], SAgregate]) + ', '
          end
          else begin
            FieldS := FieldS + Format(#13#10'  %s."%s"', [SgColumns.Cells[I, 11], SgColumns.Cells[I, 0]]) + ', '
          end;
        end;
        TableList.Add((Format('"%s" %s', [SgColumns.Cells[I, 1], SgColumns.Cells[I, 11]])));
      end;

      if longint(SgColumns.Objects[I, 9]) > 0 then begin
        CurNum := Format('%.5d', [longint(SgColumns.Objects[I, 9])]);
        GroupList.AddObject(CurNum, TItem.Create(Format('%s."%s"', [SgColumns.Cells[I, 11], SgColumns.Cells[I, 0]])));
      end;
      if (Copy(SgColumns.Cells[I, 3], 1, 1) = 'A') then begin
        CurNum := Format('%.5d', [longint(SgColumns.Objects[I, 3])]);
        if SgColumns.Cells[I, 7] = SCalcField then begin
          OrderList.AddObject(CurNum, TItem.Create(Format('"%s" ASC', [SgColumns.Cells[I, 0]])))
        end
        else if Length(SgColumns.Cells[I, 6]) <> 0 then begin
          OrderList.AddObject(CurNum, TItem.Create(Format('"%s%s" ASC', [SAgregate, SgColumns.Cells[I, 0]])));
        end
        else begin
          OrderList.AddObject(CurNum, TItem.Create(Format('%s."%s" ASC', [SgColumns.Cells[I, 11], SgColumns.Cells[I, 0]])))
        end;
      end;
      if (Copy(SgColumns.Cells[I, 3], 1, 1) = 'D') then begin
        CurNum := Format('%.5d', [longint(SgColumns.Objects[I, 3])]);
        if SgColumns.Cells[I, 7] = SCalcField then begin
          OrderList.AddObject(CurNum, TItem.Create(Format('"%s" DESC', [SgColumns.Cells[I, 0]])))
        end
        else if Length(SgColumns.Cells[I, 6]) <> 0 then begin
          OrderList.AddObject(CurNum, TItem.Create(Format('"%s%s" DESC', [SAgregate, SgColumns.Cells[I, 0]])));
        end
        else begin
          OrderList.AddObject(CurNum, TItem.Create(Format('%s."%s" DESC', [SgColumns.Cells[I, 11], SgColumns.Cells[I, 0]])))
        end;
      end;
    end;
    if UpperCase(FieldS) = 'SELECT ' then begin
      FieldS := Trim(FieldS) + #13#10;
    end;
    GroupList.Sort;
    OrderList.Sort;
    HavingS := Trim(HavingS);
    if Length(HavingS) <> 0 then begin
      HavingS := ' HAVING (' + HavingS + ')'#13#10
    end;
    WhereS := Trim(WhereS);
    if Length(WhereS) <> 0 then begin
      WhereS := ' WHERE ' + WhereS + #13#10
    end;
    FieldS := TrimRight(FieldS);
    if Pos(',', FieldS) <> 0 then begin
      SetLength(FieldS, Length(FieldS) - 1)
    end;
    FieldS := FieldS + #13#10;
    TableS := '';
    if FJoinsList.Count > 0 then begin
      TableS := '  FROM ' + TJoinImage(FJoinsList[0]).FDescription + ','#13#10;
      for I := 1 to FJoinsList.Count - 1 do begin
        TableS := TableS + '      ' + TJoinImage(FJoinsList[I]).FDescription + ','#13#10;
      end;
      TableS := TrimRight(TableS);
      if Pos(',', TableS) <> 0 then begin
        SetLength(TableS, Length(TableS) - 1)
      end;
      TableS := TableS + #13#10;
    end
    else if TableList.Count > 0 then begin
      TableS := '  FROM ';
      for I := 0 to TableList.Count - 1 do begin
        TableS := TableS + TableList[I] + ', '
      end;
      TableS := TrimRight(TableS);
      if Pos(',', TableS) <> 0 then begin
        SetLength(TableS, Length(TableS) - 1)
      end;
      TableS := TableS + #13#10;
    end;
    GroupS := '';
    if GroupList.Count > 0 then begin
      GroupS := ' GROUP BY ';
      for I := 0 to GroupList.Count - 1 do begin
        GroupS := GroupS + (GroupList.Objects[I] as TItem).Item + ', '
      end;
      GroupS := TrimRight(GroupS);
      if Pos(',', GroupS) <> 0 then begin
        SetLength(GroupS, Length(GroupS) - 1)
      end;
      GroupS := GroupS + #13#10;
    end;
    OrderS := '';
    if OrderList.Count > 0 then begin
      OrderS := ' ORDER BY ';
      for I := 0 to OrderList.Count - 1 do begin
        OrderS := OrderS + (OrderList.Objects[I] as TItem).Item + ', '
      end;
      OrderS := TrimRight(OrderS);
      if Pos(',', OrderS) <> 0 then begin
        SetLength(OrderS, Length(OrderS) - 1)
      end;
      OrderS := OrderS + #13#10;
    end;
    if (UpperCase(FieldS) = 'SELECT'#13#10) or (UpperCase(FieldS) = 'SELECT DISTINCT'#13#10) then begin
      if CbDistinct.Checked then begin
        FieldS := 'SELECT DISTINCT *'#13#10
      end
      else begin
        FieldS := 'SELECT *'#13#10
      end;
      begin
        if FJoinsList.Count = 0 then begin
          TableS := '  FROM ';
          for I := 0 to FTablesList.Count - 1 do begin
            with TTableImage(FTablesList.Items[I]) do begin
              TableS := TableS + Format('"%s" %s', [CaptionTable, TableAlias]) + ', ';
            end
          end;
          TableS := TrimRight(TableS);
          if Pos(',', TableS) <> 0 then begin
            SetLength(TableS, Length(TableS) - 1)
          end;
          TableS := TableS + #13#10;
        end;
      end;
    end;
    if cbIntoTable.Checked then begin
      if cbMemory.Checked then begin
        Smem := Format('DROP TABLE IF EXISTS MEMORY %s;'#13#10, [sIntoTable]);
        S := Format('  INTO MEMORY %s'#13#10, [sIntoTable]);
      end else begin
        Smem := Format('DROP TABLE IF EXISTS %s;'#13#10, [sIntoTable]);
        S := Format('  INTO %s'#13#10, [sIntoTable]);
      end;
    end else begin
      Smem := '';
      S := '';
    end;
    reText := (Comment + Smem + FieldS + S + TableS + WhereS + GroupS + HavingS + OrderS);
  finally
    for I := 0 to GroupList.Count - 1 do begin
      Application.ProcessMessages;
      GroupList.Objects[I].Free
    end;
    for I := 0 to OrderList.Count - 1 do begin
      Application.ProcessMessages;
      OrderList.Objects[I].Free
    end;
    TableList.Free;
    GroupList.Free;
    OrderList.Free;
  end;
  end;

    if cbDoubleQuotes.Checked then
    for I := 1 to Length(reText) do begin
      Application.ProcessMessages;
      if reText[I] = '''' then reText[I] := '"'
    end else
    for I := 1 to Length(reText) do begin
      Application.ProcessMessages;
      if reText[I] = '"' then reText[I] := ''''
    end;

    re.Text := reText;
    Lexer := TABSLexer.Create(reText);
    try
    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];
        if t.TokenType = tktReservedWord then begin
          SelPos := t.ColumnNum;
          for K := 1 to t.LineNum - 1 do begin
            SelPos := SelPos + Length(re.Lines[K - 1]) + 2;
          end;
          Delete(reText, SelPos, Length(t.Text));
          if cbLowerCase.Checked then
            Insert(LowerCase(t.Text), reText, SelPos)
          else
            Insert(UpperCase(t.Text), reText, SelPos);
        end;
      end;
    finally
      Lexer.Free;
    end;
    re.Text := reText;
end;

procedure TfrmQueryMaker.TableImageDragDrop(Sender, Source: TObject; X, Y: integer);
var
  Td, Ta: TTableImage;
  Id, Ia: integer;
  S1:     string;
  S2:     string;
  JI:     TJoinImage;
  I:      integer;
begin
  if Source is TTreeView then begin
    Ta := Sender as TTableImage;
    tbDragDrop(Sender, Source, X + Ta.Left, Y + Ta.Top);
    Exit;
  end;
  Td := Source as TTableImage;
  Ta := Sender as TTableImage;
  Id := Td.SelectedItem;
  Ia := Ta.GetSelItem(Y);
  if (Id < 0) or (Ia < 0) or (Id > Td.Columns.Count - 1) or (Ia > Ta.Columns.Count - 1) then begin
    Exit
  end;
  if (Td.Columns.Objects[Id] as TColumnsParams).IsCalc or (Ta.Columns.Objects[Ia] as TColumnsParams).IsCalc then begin
    Exit;
  end;
  S1 := (Td.FColumns.Objects[Id] as TColumnsParams).FType;
  S2 := (Ta.FColumns.Objects[Ia] as TColumnsParams).FType;
  I  := Pos('(', S1);
  if I <> 0 then begin
    S1 := Copy(S1, 1, I - 1)
  end;
  I := Pos('(', S2);
  if I <> 0 then begin
    S2 := Copy(S2, 1, I - 1)
  end;
  if AreTypesCompatible(StrToAft(S1), StrToAft(S2)) then begin
    JI := TJoinImage.Create(Self);
    with JI do begin
      PopupMenu   := PmJoin;
      FromTableJoin := longint(Td);
      ToTableJoin := longint(Ta);
      FromFieldJoin := Id;
      ToFieldJoin := Ia;
      ABSJoinType := AjtInner;
      Parent      := FTablesBoard;
      OnMouseDown := tbMouseDown;
      OnDragOver  := tbDragOver;
      OnDragDrop  := tbDragDrop;
    end;
    FJoinsList.Add(JI);
    ExecJoin(1, FJoinsList.Count - 1);
    FTablesBoard.Refresh;
  end;
end;

procedure TfrmQueryMaker.TableImageDragOver(Sender, Source: TObject;
  X, Y: integer; State: TDragState; var Accept: boolean);
begin
  Accept := (Sender <> Source) and (Sender is TTableImage);
end;

procedure TfrmQueryMaker.TableImagePosChange(Sender: TObject; Top, Left: integer);
begin
  FTablesBoard.Refresh;
end;

procedure TfrmQueryMaker.ResetSort(Col: integer);
var
  I, J, K: integer;
begin
  K := longint(SgColumns.Objects[Col, 3]);
  if K = 0 then begin
    Exit
  end;
  SgColumns.Cells[Col, 3]   := '';
  SgColumns.Objects[Col, 3] := TObject(0);
  for I := 1 to SgColumns.Colcount - 1 do begin
    Application.ProcessMessages;
    J := longint(SgColumns.Objects[I, 3]);
    if (J > K) and (J > 0) then begin
      Dec(J);
      SgColumns.Objects[I, 3] := TObject(J);
      if Copy(SgColumns.Cells[I, 3], 1, 1) = 'A' then begin
        SgColumns.Cells[I, 3] := Format('Asc_%d', [J])
      end
      else begin
        SgColumns.Cells[I, 3] := Format('Desc_%d', [J])
      end
    end;
  end;
  J := longint(SgColumns.Objects[0, 3]);
  Dec(J);
  SgColumns.Objects[0, 3] := TObject(J);
end;

procedure TfrmQueryMaker.WriteToFile(const FileName: string);
var
  J:  integer;
  I:  integer;
  Fs: TFileStream;
  Cs: TCols;
begin
  Fs := TFileStream.Create(FileName, FmCreate or FmOpenWrite);
  Cs := TCols.Create(Self);
  try
    I := SgColumns.ColCount - 1;
    Fs.Write(I, 4);
    for I := 0 to SgColumns.Colcount - 1 do begin
      Cs.FSel.Clear;
      Cs.FObj.Clear;
      for J := 0 to SgColumns.Rowcount - 1 do begin
        Cs.FSel.Add(SgColumns.Cells[I, J]);
        Cs.FObj.Add(IntToStr(longint(SgColumns.Objects[I, J])));
      end;
      Fs.WriteComponent(Cs);
    end;
    I := 0;
    Fs.Write(I, 1);
    I := FJoinsList.Count - 1;
    Fs.Write(I, 4);
    for I := 0 to FJoinsList.Count - 1 do begin
      TJoinImage(FJoinsList[I]).WriteToFile(Fs);
    end;
    I := 0;
    Fs.Write(I, 1);
    I := FTablesList.Count - 1;
    Fs.Write(I, 4);
    for I := 0 to FTablesList.Count - 1 do begin
      TTableImage(FTablesList[I]).WriteToFile(Fs);
    end;
  finally
    Fs.Free;
    Cs.Free;
  end;
end;

procedure TfrmQueryMaker.ReadFromFile(const FileName: string);
var
  J:  integer;
  I:  integer;
  K:  integer;
  Fs: TFileStream;
  Ti: TTableImage;
  Ji: TJoinImage;
  Cs: TCols;
  Flag: integer;
  arS: array[0..11] of Integer;
begin
  RemoveAllTables(False);
  frmMain.Settings.LastSQMFile := FileName;
  Fs := TFileStream.Create(FileName, FmOpenRead);
  Cs := TCols.Create(Self);
  try
    Fs.Read(K, 4);
    SgColumns.ColCount := K + 1;
    for I := 0 to K do begin
      Fs.ReadComponent(Cs);
      for J := 0 to SgColumns.Rowcount - 1 do begin
        SgColumns.Cells[I, J]   := Cs.FSel[J];
        Flag := StrToIntDef(Cs.FObj[J], 0);
        SgColumns.Objects[I, J] := TObject(Flag);
        if I = 0 then
          arS[J] := Flag;
      end;
    end;
    CbDistinct.Checked := arS[0] = 1;
    cbLowerCase.Checked := arS[1] = 1;
    cbDoubleQuotes.Checked := arS[2] = 1;
    cbIntoTable.Checked := arS[4] = 1;
    cbSaveComments.Checked := arS[5] = 1;
    cbMemory.Enabled := cbIntoTable.Checked;
    cbMemory.Checked := arS[6] = 1;
    Fs.Position := Fs.Position + 1;
    Fs.Read(K, 4);
    for I := 0 to K do begin
      Ji := TJoinImage.Create(Self);
      with Ji do begin
        ReadFromFile(Fs);
        Parent      := FTablesBoard;
        OnMouseDown := tbMouseDown;
        OnDragOver  := tbDragOver;
        OnDragDrop  := tbDragDrop;
        PopupMenu   := PmJoin;
      end;
      FJoinsList.Add(Ji);
    end;
    Fs.Position := Fs.Position + 1;
    Fs.Read(K, 4);
    for J := 0 to K do begin
      Ti := TTableImage.Create(Self);
      Ti.ReadFromFile(Fs);
      Inc(FTableCounter);
      for I := 1 to SgColumns.ColCount - 1 do begin
        if longint(SgColumns.Objects[I, 1]) = Ti.Tag then begin
          SgColumns.Objects[I, 1] := TObject(longint(Ti))
        end;
      end;
      for I := 0 to FJoinsList.Count - 1 do begin
        if TJoinImage(FJoinsList[I]).FFromTableJoin = Ti.Tag then begin
          TJoinImage(FJoinsList[I]).FFromTableJoin := longint(Ti)
        end;
        if TJoinImage(FJoinsList[I]).FToTableJoin = Ti.Tag then begin
          TJoinImage(FJoinsList[I]).FToTableJoin := longint(Ti)
        end;
      end;
      with Ti do begin
        Tag    := longint(Ti);
        Parent := FTablesBoard;
        OnDblClick := TableImageDblClick;
        OnDragOver := TableImageDragOver;
        OnDragDrop := TableImageDragDrop;
        OnPosChange := TableImagePosChange;
        PopupMenu := PmTable;
      end;
      FTablesList.Add(Ti);
    end;
  finally
    Fs.Free;
    Cs.Free;
    SelectColumn(1);
  end;
end;

procedure TfrmQueryMaker.RemoveAllTables(LblShow: boolean);
var
  I: integer;
begin
  frmMain.Settings.LastSQMFile := '';
  frmQueryMaker.Caption := Format(sMakerCaption, [sNewProject]);
  CbDistinct.Checked := False;
  SgColumns.Objects[0, 0] := TObject(0);
  cbLowerCase.Checked := True;
  SgColumns.Objects[0, 1] := TObject(1);
  cbDoubleQuotes.Checked := True;
  SgColumns.Objects[0, 2] := TObject(1);
  cbIntoTable.Checked := False;
  SgColumns.Objects[0, 4] := TObject(0);
  cbMemory.Checked := False;
  cbMemory.Enabled := False;
  SgColumns.Objects[0, 6] := TObject(0);
  cbSaveComments.Checked := True;
  SgColumns.Objects[0, 5] := TObject(1);
  for I := SgColumns.ColCount - 1 downto 1 do begin
    Application.ProcessMessages;
    DeleteColumn(I);
  end;
  for I := FJoinsList.Count - 1 downto 0 do begin
    Application.ProcessMessages;
    TJoinImage(FJoinsList[I]).Free;
    FJoinsList.Delete(I);
  end;
  for I := FTablesList.Count - 1 downto 0 do begin
    Application.ProcessMessages;
    TTableImage(FTablesList[I]).Free;
    FTablesList.Delete(I);
  end;
  FTableCounter := 0;
  FTablesBoard.HorzScrollBar.Position := 0;
  FTablesBoard.VertScrollBar.Position := 0;
  GenerateSQL(FrmMain.ReSQL);
  Label1.Visible := LblShow;
end;

procedure TTableImage.ColumnsChange(Sender: TObject);
begin
  Invalidate;
end;

constructor TTableImage.Create(AOwner: TComponent);
begin
  inherited;
  FColumns := TStringList.Create;
  TStringList(FColumns).OnChange := ColumnsChange;
  FSelItem := -1;
end;

destructor TTableImage.Destroy;
var
  I: integer;
begin
  for I := 0 to FColumns.Count - 1 do begin
    FColumns.Objects[I].Free
  end;
  FColumns.Free;
  inherited;
end;

function TTableImage.GetCaptionTable: TCaption;
begin
  Result := Caption;
end;

function TTableImage.GetSelItem(Y: integer): integer;
var
  Item: integer;
begin
  if Y <= FHeaderHeight then begin
    Result := -1;
    Exit;
  end;
  Item := Y - FHeaderHeight;
  try
    Item := Item div (Canvas.Textheight('Wj')+2);
  except
    Item := -1;
  end;
  if (Item > FColumns.Count - 1) or (Item < 0) then begin
    Item := -1
  end;
  Result := Item;
end;

function TTableImage.GetMaxHeight: integer;
begin
  Result := (FColumns.Count + 1) * (Canvas.TextHeight('Wj') + 2) + 3;
end;

function TTableImage.GetMaxWidth: integer;
var
  I, NameWidth, TypeWidth: integer;
  T1, T2: integer;
begin
  NameWidth := 0;
  TypeWidth := 0;
  for I := 0 to FColumns.Count - 1 do begin
    T1 := (FColumns.Objects[I] as TColumnsParams).FNameWidth;
    if T1 > NameWidth then begin
      NameWidth := T1
    end;
    T2 := (FColumns.Objects[I] as TColumnsParams).FTypeWidth;
    if T2 > TypeWidth then begin
      TypeWidth := T2
    end;
  end;
  Result := NameWidth + TypeWidth + 12;
  if result < Canvas.TextWidth(Caption) then
    result := Canvas.TextWidth(Caption) + 6;
end;

function TTableImage.GetTableAlias: string;
begin
  Result := CaptionTable[1] + IntToStr(FTableNumber);
end;

procedure TTableImage.MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: integer);
const
  SC_DragMove = $F012;
begin
  inherited MouseDown(Button, Shift, X, Y);
  if (Y <= FHeaderHeight) then begin
    ReleaseCapture;
    Perform(WM_SysCommand, SC_DragMove, 0);
    Exit;
  end;
  if (Y > FHeaderHeight) and (GetSelItem(Y) > -1) and (Button = Mbleft) then begin
    BeginDrag(True);
  end;
end;

procedure TTableImage.MouseEnter(var Msg: Tmessage);
begin
  FMouseIn      := True;
end;

procedure TTableImage.MouseLeave(var Msg: TMessage);
begin
  FMouseIn := False;
  if FClearSel then begin
    Canvas.Pen.Color   := ClSelectedField;
    Canvas.Brush.Color := ClSelectedField;
    Canvas.Pen.Mode    := PmNotXor;
    Canvas.Rectangle(1, FlastPosYTop, Width - 1, FlastPosYBottom);
    FClearSel := False;
  end;
end;

procedure TTableImage.MouseMove(Shift: TShiftState; X, Y: integer);
var
  Msg: TMessage;
begin
  inherited MouseMove(Shift, X, Y);
  if FmouseIn then begin
    if Y <= FHeaderHeight then begin
      MouseLeave(Msg);
      FmouseIn := True;
      FSelItem := -1;
    end else begin
      FSelItem := GetSelItem(Y);
      if FSelItem > - 1 then begin
        Canvas.Pen.Color   := ClSelectedField;
        Canvas.Brush.Color := ClSelectedField;
        Canvas.Pen.Mode    := PmNotXor;
        if FClearSel then
          Canvas.Rectangle(1, FlastPosYTop, Width - 1, FlastPosYBottom);
        FlastPosYBottom := (FColumns.Objects[FSelItem] as TColumnsParams).FCoord.Y + Canvas.TextHeight('Wj') + 2;
        FlastPosYTop   := (FColumns.Objects[FSelItem] as TColumnsParams).FCoord.Y;
        Canvas.Rectangle(1, FlastPosYTop, Width - 1, FlastPosYBottom);
        FClearSel := True;
      end
    end;
  end;
end;

procedure TTableImage.SetWidthHeight;
var
  I: integer;
begin
  for I := 0 to FColumns.Count - 1 do begin
    Application.ProcessMessages;
    (FColumns.Objects[I] as TColumnsParams).FCoord.X := 3;
    (FColumns.Objects[I] as TColumnsParams).FCoord.Y := (I + 1) * (Self.Canvas.TextHeight('Wj') + 2) + 2;
  end;
  Width  := GetMaxWidth;
  Height := GetMaxHeight;
end;

procedure TTableImage.Paint;
var
  Image: TBitmap;
  Hdr, Tx, Ty, I, Mxl, Mxl2: integer;
  Txt1:  string;
begin
  FClearSel := False;
  Image    := TBitmap.Create;
  try
    with Image, Image.Canvas do begin
      Width  := Self.Width;
      Height := Self.Height;
      Brush.Color := Self.Color;
      FillRect(Self.ClientRect);
      Pen.Color := ClBasicTable;
      Brush.Color := ClWhite;
      Hdr := FHeaderHeight - 1;
      RoundRect(0, 0, Self.Width, Self.Height, 8, 8);
      MoveTo(1, Hdr);
      LineTo(Width-1, Hdr);
      Brush.Color := ClBasicTable;
      Tx := 3;
      Ty := 2;
      FloodFill(2, 2, Pen.Color, FsBorder);
      Brush.Style := BsClear;
      Font.Color  := ClWhite;
      Font.Style  := [FsBold];
      TextOut(Tx, Ty, Caption);
      Font.Color := ClBlack;
      Font.Style := [];
      Mxl2 := 0;
      for I := 0 to FColumns.Count - 1 do begin
        Mxl := (FColumns.Objects[I] as TColumnsParams).FTypeWidth;
        if Mxl > Mxl2 then begin
          Mxl2 := Mxl
        end;
      end;
      for I := 0 to FColumns.Count - 1 do begin
        Ty   := (FColumns.Objects[I] as TColumnsParams).FCoord.Y;
        Txt1 := FColumns[I];
        if Ty < (Height - TextHeight(Txt1) - 1) then begin
          Tx := (FColumns.Objects[I] as TColumnsParams).FCoord.X;
          Font.Color := (FColumns.Objects[I] as TColumnsParams).FNameColor;
          if (FColumns.Objects[I] as TColumnsParams).FIsIndex then begin
            Brush.Color := ClIndexField;
            Pen.Style   := PsClear;
            RoundRect(1, Ty, Width, Ty + Hdr, 6, 4);
          end;
          Brush.Style := BsClear;
          TextOut(Tx, Ty, Txt1);
          Tx := Width - Mxl2 - 4;
          Font.Color := (FColumns.Objects[I] as TColumnsParams).FTypeColor;
          TextOut(Tx, Ty, (FColumns.Objects[I] as TColumnsParams).FType);
        end;
      end;
      Pen.Style   := PsSolid;
      MoveTo(Width - Mxl2 - 7, FHeaderHeight);
      Lineto(Width - Mxl2 - 7, Height-1);
      Self.Canvas.Draw(0, 0, Image);
    end;
  finally
    Image.Free;
  end;
end;

procedure TTableImage.ReadFromFile(Fs: TFileStream);
var
  I:  integer;
  Cp: TColumnsParams;
begin
  Fs.ReadComponent(Self);
  for I := 0 to FColumns.Count - 1 do begin
    Cp := TColumnsParams.Create(Self);
    Cp.ReadFromFile(Fs);
    FColumns.Objects[I] := Cp;
  end;
end;

procedure TTableImage.SetCaptionTable(const Value: TCaption);
begin
  if Value <> Caption then begin
    Caption := Value;
    Invalidate;
  end;
end;

procedure TTableImage.WMWINDOWPOSCHANGED(var Msg: Tmessage);
begin
  inherited;
  if Top < 0 then begin
    Top := 1
  end;
  if Left < 0 then begin
    Left := 1
  end;
  if Assigned(FonPosChange) then begin
    FOnPosChange(Self, Top, Left)
  end;
end;

procedure TTableImage.WriteToFile(Fs: TFileStream);
var
  I: integer;
begin
  Fs.WriteComponent(Self);
  for I := 0 to FColumns.Count - 1 do begin
    TColumnsParams(FColumns.Objects[I]).WriteToFile(Fs);
  end;
end;

constructor TColumnsParams.Create;
begin
  inherited;
  FIsIndex := False;
  FFormula := '';
  FIsCalc  := False;
end;

procedure TfrmQueryMaker.SgColumnsSelectCell(Sender: TObject; ACol, ARow: integer; var CanSelect: boolean);
begin
  CanSelect := IsValidColRow(ACol, ARow);
end;

procedure TfrmQueryMaker.DeleteColumn(ColNo: integer);
var
  I, Ii: integer;
begin
  ResetSort(ColNo);
  ResetGrouped(ColNo);
  Application.ProcessMessages;
  for I := 0 to SgColumns.Rowcount - 1 do begin
    SgColumns.Cells[Colno, I]   := '';
    SgColumns.Objects[Colno, I] := nil;
  end;
  if Colno < SgColumns.Colcount - 1 then begin
    for I := Colno to SgColumns.Colcount - 2 do begin
      for Ii := 0 to SgColumns.Rowcount - 1 do begin
        SgColumns.Cells[I, Ii]   := SgColumns.Cells[I + 1, Ii];
        SgColumns.Objects[I, Ii] := SgColumns.Objects[I + 1, Ii];
      end
    end;
  end;
  if SgColumns.Colcount < 3 then begin
    GenerateSQL(FrmMain.ReSQL);
    Exit;
  end;
  SgColumns.Colcount := SgColumns.Colcount - 1;
  if ColNo < SgColumns.Colcount then begin
    SelectColumn(ColNo)
  end
  else begin
    SelectColumn(SgColumns.Colcount - 1)
  end;
end;

procedure TfrmQueryMaker.AgregateClick(Sender: TObject);
begin
  SgColumns.Objects[SgColumns.Col, 6] := TObject((Sender as TmenuItem).Tag);
  case (Sender as TmenuItem).Tag of
    0: begin
      SgColumns.Cells[SgColumns.Col, 6]  := '';
      SgColumns.Cells[SgColumns.Col, 10] := '';
    end;
    1: begin
      SgColumns.Cells[SgColumns.Col, 6] := 'AVG'
    end;
    2: begin
      SgColumns.Cells[SgColumns.Col, 6] := 'COUNT'
    end;
    3: begin
      SgColumns.Cells[SgColumns.Col, 6] := 'MAX'
    end;
    4: begin
      SgColumns.Cells[SgColumns.Col, 6] := 'MIN'
    end;
    5: begin
      SgColumns.Cells[SgColumns.Col, 6] := 'SUM'
    end;
  end;
  GenerateSQL(FrmMain.ReSQL);
end;

procedure TfrmQueryMaker.PmNoSortClick(Sender: TObject);
begin
  ResetSort(SgColumns.Col);
  GenerateSQL(FrmMain.ReSQL);
end;

procedure TfrmQueryMaker.PmASCClick(Sender: TObject);
var
  J: integer;
begin
  if Length(Trim(SgColumns.Cells[SgColumns.Col, 3])) = 0 then begin
    J := longint(SgColumns.Objects[0, 3]);
    Inc(J);
    SgColumns.Objects[0, 3] := TObject(J);
    SgColumns.Objects[SgColumns.Col, 3] := TObject(J);
  end
  else begin
    J := longint(SgColumns.Objects[SgColumns.Col, 3])
  end;
  SgColumns.Cells[SgColumns.Col, 3] := Format('Asc_%d', [J]);
  GenerateSQL(FrmMain.ReSQL);
end;

procedure TfrmQueryMaker.PmDESCClick(Sender: TObject);
var
  J: integer;
begin
  if Length(Trim(SgColumns.Cells[SgColumns.Col, 3])) = 0 then begin
    J := longint(SgColumns.Objects[0, 3]);
    Inc(J);
    SgColumns.Objects[0, 3] := TObject(J);
    SgColumns.Objects[SgColumns.Col, 3] := TObject(J);
  end
  else begin
    J := longint(SgColumns.Objects[SgColumns.Col, 3])
  end;
  SgColumns.Cells[SgColumns.Col, 3] := Format('Desc_%d', [J]);
  GenerateSQL(FrmMain.ReSQL);
end;

procedure TfrmQueryMaker.SgColumnsDrawCell(Sender: TObject;
  ACol, ARow: integer; Rect: TRect; State: TGridDrawState);
begin
  if (ACol > 0) then begin
    with SgColumns.Canvas do begin
      Font.Color := ClFieldType;
      if (ARow in [3..6, 8..10]) and (GdSelected in State) then begin
        Brush.Color := ClIndexField;
        Font.Color  := ClWindowText;
        TextRect(Rect, Rect.Left + 2, Rect.Top + 2, SgColumns.Cells[ACol, ARow]);
      end;
      if ARow in [0..2, 7, 11] then begin
        Brush.Color := ClBtnFace;
        Font.Style  := [FsBold];
        TextRect(Rect, Rect.Left + 2, Rect.Top + 2, SgColumns.Cells[ACol, ARow]);
      end;
    end
  end;
end;

procedure TfrmQueryMaker.AddCalcFldClick(Sender: TObject);
var
  Ti:    TTableImage;
  S, NameField: string;
  PosTable: TPoint;
  AWidth: integer;
begin
  Ti := TTableImage(PmTable.PopupComponent.Tag);
      S := '';
      NameField := 'Enter Calc Field Name Here';
      if ExecuteCondition(FTablesList, S, NameField, CoCalc) then begin
        if (Length(S) <> 0) and (Length(NameField) <> 0) then begin
          NameField := Trim(NameField);
          Ti.FColumns.AddObject(NameField, TColumnsParams.Create(Ti));
          with(Ti.FColumns.Objects[Ti.FColumns.Count - 1] as TColumnsParams) do begin
            FType      := SCalcField;
            FFormula   := S;
            FIsCalc    := True;
            FNameWidth := Canvas.TextWidth(NameField);
            FTypeWidth := Canvas.TextWidth(FType);
            FNameColor := ClRed;
            FTypeColor := ClFieldType;
          end;
          AjustPosWidthTables(PosTable, AWidth);
        end
    else begin
      if Length(S) = 0 then
        MessageDlg(Format(SAddEditCalcField, [NameField]), mtInformation, [mbOK], 0)
      else
        MessageDlg(SAddCalcField, mtInformation, [mbOK], 0)
    end
  end;
end;

procedure TfrmQueryMaker.RemoveTableClick(Sender: TObject);
var
  I, J: integer;
  Ti:   TTableImage;
  PosTable: TPoint;
  AWidth: integer;

  procedure ReNumberTables(CurAliasTable: integer);
  var
    I, J: integer;
  begin
    for I := 0 to FTablesList.Count - 1 do begin
      Application.ProcessMessages;
      J := TTableImage(FTablesList[I]).FTableNumber;
      if J > CurAliasTable then begin
        Dec(J);
        TTableImage(FTablesList[I]).FTableNumber := J;
      end;
    end;
    for I := 1 to SgColumns.Colcount - 1 do begin
      Application.ProcessMessages;
      J := longint(SgColumns.Objects[I, 11]);
      if J > CurAliasTable then begin
        Dec(J);
        SgColumns.Objects[I, 11] := TObject(J);
        SgColumns.Cells[I, 11] := (SgColumns.Objects[I, 1] as TTableImage).TableAlias;
      end;
    end;
    Dec(FTableCounter);
  end;

begin
  Ti := TTableImage(PmTable.PopupComponent.Tag);
  for I := SgColumns.ColCount - 1 downto 1 do begin
    Application.ProcessMessages;
    if Ti = (SgColumns.Objects[I, 1] as TTableImage) then begin
      DeleteColumn(I);
    end;
  end;
  for I := FJoinsList.Count - 1 downto 0 do begin
    Application.ProcessMessages;
    if (longint(Ti) = TJoinImage(FJoinsList[I]).FFromTableJoin) or
      (longint(Ti) = TJoinImage(FJoinsList[I]).FToTableJoin) then begin
      ExecJoin(5, I);
    end;
  end;
  for I := FTablesList.Count - 1 downto 0 do begin
    Application.ProcessMessages;
    if Ti = TTableImage(FTablesList[I]) then begin
      J := TTableImage(FTablesList[I]).FTableNumber;
      TTableImage(FTablesList[I]).Free;
      FTablesList.Delete(I);
      ReNumberTables(J);
    end;
  end;
  for I := 0 to FJoinsList.Count - 1 do begin
    Application.ProcessMessages;
    ExecJoin(1, I);
  end;
  if FTablesList.Count = 0 then begin
    frmMain.Settings.LastSQMFile := '';
    frmQueryMaker.Caption := Format(sMakerCaption, [sNewProject]);
    CbDistinct.Checked := False;
    SgColumns.Objects[0, 0] := TObject(0);
    cbLowerCase.Checked := True;
    SgColumns.Objects[0, 1] := TObject(1);
    cbDoubleQuotes.Checked := True;
    SgColumns.Objects[0, 2] := TObject(1);
    cbIntoTable.Checked := False;
    SgColumns.Objects[0, 4] := TObject(0);
    cbMemory.Checked := False;
    cbMemory.Enabled := False;
    SgColumns.Objects[0, 6] := TObject(0);
    cbSaveComments.Checked := True;
    SgColumns.Objects[0, 5] := TObject(1);
    Label1.Visible := True;
  end;
  AjustPosWidthTables(PosTable, AWidth);
  GenerateSQL(FrmMain.ReSQL);
end;

procedure TColumnsParams.ReadFromFile(Fs: TFileStream);
begin
  Fs.ReadComponent(Self);
end;

procedure TColumnsParams.WriteToFile(Fs: TFileStream);
begin
  Fs.WriteComponent(Self);
end;

constructor TItem.Create(Value: string);
begin
  FItem := Value;
end;

procedure TfrmQueryMaker.tbMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: integer);

  function FindJoin(X, Y: integer): integer;
  var
    I:    integer;
    Join: TJoinImage;
    AMin, AMinNumber: integer;
    Distance: longint;
  begin
    Result := -1;
    AMin   := MaxInt;
    AMinNumber := -1;
    for I := 0 to FJoinsList.Count - 1 do begin
      Join := TJoinImage(FJoinsList[I]);
      with
        Join do begin
        Distance := Abs((X - FromPointX) * (ToPointY - FromPointY) - (Y - FromPointY) * (ToPointX - FromPointX));
        if Distance <= AMin then begin
          AMin := Distance;
          AMinNumber := I;
        end;
      end;
      if AMin < 500 then begin
        Result := AMinNumber
      end;
    end;
  end;

begin
  if (Button = MbRight) then begin
    FSelectedJoin := FindJoin(X, Y);
    if FSelectedJoin < 0 then begin
      PmJoin.AutoPopup := False;
      Exit;
    end;
    PmJoin.AutoPopup := True;
  end;
end;

procedure TfrmQueryMaker.tbDragOver(Sender, Source: TObject; X, Y: integer; State: TDragState; var Accept: boolean);
begin
  if Source is TTreeView then begin
    Accept := True;
    if State = DsDragLeave then begin
      (Source as TTreeView).DragCursor := CrDrag
    end;
    if (State = DsDragEnter) and ((Source as TTreeView).SelectionCount > 1) then begin
      (Source as TTreeView).DragCursor := CrMultiDrag
    end;
  end
end;

procedure TfrmQueryMaker.tbDragDrop(Sender, Source: TObject; X, Y: integer);
var
  I: integer;
  TmpList: TStringList;
  TmpTable: TABSTable;
begin
  if Source is TTreeView then begin
    FTablesBoard.Hide;
    TmpList  := TStringList.Create;
    TmpTable := TABSTable.Create(Application);
    TmpTable.DatabaseName := FrmMain.TCurTable.DatabaseName;
    try
      for I := 0 to FrmMain.TvDatabase.Items.Count - 1 do begin
        if FrmMain.TvDatabase.Items[I].Selected then begin
          TmpList.Add(FrmMain.TvDatabase.Items[I].Text)
        end
      end;
      FrmMain.TvDatabase.ClearSelection(True);
      for I := 0 to TmpList.Count - 1 do begin
        TmpTable.TableName := TmpList[I];
        TmpTable.Open;
        try
          if TmpList.Count = 1 then begin
            AddTable(TmpTable, Point(X, Y))
          end
          else begin
            AddTable(TmpTable, Point(0, 0))
          end;
        finally
          TmpTable.Close;
        end;
      end;
    finally
      FTablesBoard.Show;
      TmpList.Free;
      TmpTable.Free;
    end;
  end;
end;

constructor TJoinImage.Create(AOwner: TComponent);
begin
  inherited;
  Align := AlClient;
end;

procedure TJoinImage.Paint;
var
  X1, Y1, X2, Y2, D1, D2: longint;
  A1: longint;
begin
  A1 := Canvas.TextHeight('Wj') div 2;
  D1 := 1;
  D2 := 1;
  if (TTableImage(FFromTableJoin) <> nil) and (TTableImage(FToTableJoin) <> nil) then begin
    with(TTableImage(FFromTableJoin).FColumns.Objects[FFromFieldJoin] as TColumnsParams) do begin
      Y1 := FCoord.Y + TTableImage(FFromTableJoin).Top + A1;
      Y2 := (TTableImage(FToTableJoin).FColumns.Objects[FToFieldJoin] as TColumnsParams).FCoord.Y + TTableImage(FToTableJoin).Top + A1;
      X1 := 0;
      X2 := Maxint;
      if Abs(TTableImage(FFromTableJoin).Left - TTableImage(FToTableJoin).Left) < Abs(X1 - X2) then begin
        X1 := TTableImage(FFromTableJoin).Left;
        X2 := TTableImage(FToTableJoin).Left;
        D1 := -1;
        D2 := -1;
      end;
      if Abs(TTableImage(FFromTableJoin).Left - (TTableImage(FToTableJoin).Left + TTableImage(FToTableJoin).Width - 1)) < Abs(X1 - X2) then begin
        X1 := TTableImage(FFromTableJoin).Left;
        X2 := TTableImage(FToTableJoin).Left + TTableImage(FToTableJoin).Width - 1;
        D1 := -1;
        D2 := 1;
      end;
      if Abs((TTableImage(FFromTableJoin).Left + TTableImage(FFromTableJoin).Width - 1) - TTableImage(FToTableJoin).Left) < Abs(X1 - X2) then begin
        X1 := TTableImage(FFromTableJoin).Left + TTableImage(FFromTableJoin).Width - 1;
        X2 := TTableImage(FToTableJoin).Left;
        D1 := 1;
        D2 := -1;
      end;
      if Abs((TTableImage(FFromTableJoin).Left + TTableImage(FFromTableJoin).Width - 1) - (TTableImage(FToTableJoin).Left + TTableImage(FToTableJoin).Width - 1)) < Abs(X1 - X2) then begin
        X1 := TTableImage(FFromTableJoin).Left + TTableImage(FFromTableJoin).Width - 1;
        X2 := TTableImage(FToTableJoin).Left + TTableImage(FToTableJoin).Width - 1;
        D1 := 1;
        D2 := 1;
      end;
      inc(Y1);
      inc(Y2);
      FromPointX := X1;
      FromPointY := Y1;
      ToPointX   := X2;
      ToPointY   := Y2;
      if (Y1 <= TTableImage(FFromTableJoin).Top + TTableImage(FFromTableJoin).Height - 1) and
        (Y2 <= TTableImage(FToTableJoin).Top + TTableImage(FToTableJoin).Height - 1) then begin
        with Canvas do begin
          Pen.Mode  := PmCopy;
          Pen.Style := Pssolid;
          Pen.Color := TJoinColors[FABSJoinType];
          Moveto(X1 + TForm(Parent).HorzScrollBar.Position, Y1 + TForm(Parent).VertScrollBar.Position);
          Lineto(X1 + (D1 * 8) + TForm(Parent).HorzScrollBar.Position, Y1 + TForm(Parent).VertScrollBar.Position);
          Lineto(X2 + (D2 * 8) + TForm(Parent).HorzScrollBar.Position, Y2 + TForm(Parent).VertScrollBar.Position);
          Lineto(X2 + TForm(Parent).HorzScrollBar.Position, Y2 + TForm(Parent).VertScrollBar.Position);
          Moveto(X2 + (D2 * 4) + TForm(Parent).HorzScrollBar.Position, Y2 - 4 + TForm(Parent).VertScrollBar.Position);
          Lineto(X2 + TForm(Parent).HorzScrollBar.Position, Y2 + TForm(Parent).VertScrollBar.Position);
          Moveto(X2 + (D2 * 4) + TForm(Parent).HorzScrollBar.Position, Y2 + 4 + TForm(Parent).VertScrollBar.Position);
          Lineto(X2 + TForm(Parent).HorzScrollBar.Position, Y2 + TForm(Parent).VertScrollBar.Position);
        end
      end;
    end
  end;
end;

procedure TfrmQueryMaker.SetDescrJoin(IndJoin: integer);
var
  Sto, Sfrom: string;
begin
  with TJoinImage(FJoinsList[IndJoin]) do begin
      Sto   := TrimLeft(TTableImage(ToTableJoin).FColumns[ToFieldJoin]);
      Sfrom := TrimLeft(TTableImage(FromTableJoin).FColumns[FromFieldJoin]);
      Description := Format('"%s" %s ',
        [TTableImage(FromTableJoin).CaptionTable, TTableImage(FromTableJoin).TableAlias]) + TJoinDescrUpper[FABSJoinType] +
        Format(' "%s" %s', [TTableImage(ToTableJoin).CaptionTable, TTableImage(ToTableJoin).TableAlias]) +
        Format(#13#10'    ON (%s."%s" = %s."%s")', [TTableImage(FromTableJoin).TableAlias, Sfrom, TTableImage(ToTableJoin).TableAlias, Sto]);
  end;
end;

procedure TfrmQueryMaker.ExecJoin(IndMenu, IndJoin: integer);
var
  Sto, Sfrom: string;
begin
  with TJoinImage(FJoinsList[IndJoin]) do begin
    if IndMenu in [0..4] then begin
      Sto   := TrimLeft(TTableImage(ToTableJoin).FColumns[ToFieldJoin]);
      Sfrom := TrimLeft(TTableImage(FromTableJoin).FColumns[FromFieldJoin]);
      FABSJoinType := TABSJoinType(IndMenu);
      Description := Format('"%s" %s ',
        [TTableImage(FromTableJoin).CaptionTable, TTableImage(FromTableJoin).TableAlias]) + TJoinDescrUpper[FABSJoinType] +
        Format(' "%s" %s', [TTableImage(ToTableJoin).CaptionTable, TTableImage(ToTableJoin).TableAlias]) +
        Format(#13#10'    ON (%s."%s" = %s."%s")', [TTableImage(FromTableJoin).TableAlias, Sfrom, TTableImage(ToTableJoin).TableAlias, Sto]);
    end
    else begin
      Free;
      FJoinsList.Delete(IndJoin);
    end
  end;
  FTablesBoard.Invalidate;
  GenerateSQL(FrmMain.ReSQL);
end;

procedure TfrmQueryMaker.CrossjoinClick(Sender: TObject);
begin
  ExecJoin((Sender as TmenuItem).Tag, FSelectedJoin);
end;

procedure TfrmQueryMaker.PmJoinPopup(Sender: TObject);
begin
  PmJoin.Items[Ord(TJoinImage(FJoinsList[FSelectedJoin]).FABSJoinType)].Checked := True;
end;

procedure TfrmQueryMaker.SgColumnsColumnMoved(Sender: TObject;
  FromIndex, ToIndex: integer);
begin
  GenerateSQL(FrmMain.ReSQL);
end;

procedure TfrmQueryMaker.EditCalcFldClick(Sender: TObject);
var
  Ti: TTableImage;
  I, J:  integer;
  PosTable: TPoint;
  AWidth: integer;
  S, NameField:  string;
begin
  Ti := TTableImage(PmTable.PopupComponent.Tag);
  I  := Ti.SelectedItem;
  if (I < 0) or (I > Ti.Columns.Count - 1) then begin
    Exit
  end;
  S := (Ti.FColumns.Objects[I] as TColumnsParams).FFormula;
  NameField := Trim(Ti.Columns[I]);
  if ExecuteCondition(FTablesList, S, NameField, CoCalc) then begin
    if (Length(S) <> 0) and (Length(NameField) <> 0) then begin
      (Ti.FColumns.Objects[I] as TColumnsParams).FFormula := S;
      for J := 1 to SgColumns.ColCount - 1 do begin
        if (SgColumns.Cells[J, 7] = SCalcField) and (Trim(Ti.FColumns[I]) = SgColumns.Cells[J, 0]) then begin
          SgColumns.Cells[J, 0] := NameField;
        end;
      end;
      Ti.FColumns[I] := Trim(NameField);
      (Ti.FColumns.Objects[I] as TColumnsParams).FNameWidth := Canvas.TextWidth(Ti.FColumns[I]);
      AjustPosWidthTables(PosTable, AWidth);
      GenerateSQL(FrmMain.ReSQL);
    end
    else begin
    if Length(S) = 0 then
      MessageDlg(Format(SAddEditCalcField, [Ti.FColumns[I]]), mtInformation, [mbOK], 0)
    else
      MessageDlg(SAddCalcField, mtInformation, [mbOK], 0)
    end;
  end;
end;

procedure TfrmQueryMaker.DelCalcFldClick(Sender: TObject);
var
  Ti:   TTableImage;
  I, J: integer;
  PosTable: TPoint;
  AWidth: integer;
  S:    string;
begin
  Ti := TTableImage(PmTable.PopupComponent.Tag);
  I  := Ti.SelectedItem;
  if (I < 0) or (I > Ti.Columns.Count - 1) or not (Ti.Columns.Objects[I] as TColumnsParams).FIsCalc then begin
    Exit
  end;
  S := Format(SDelCalcField, [Ti.Columns[I], (Ti.Columns.Objects[I] as TColumnsParams).FFormula]);
  if MessageDlg(S, mtConfirmation, [mbYes, mbNo], 0) = mrNo then begin
    Exit
  end;
  for J := SgColumns.ColCount - 1 downto 1 do begin
    Application.ProcessMessages;
    if SgColumns.Cells[J, 7] = SCalcField then begin
      if Ti = (SgColumns.Objects[J, 1] as TTableImage) then begin
        DeleteColumn(J);
      end
    end;
  end;
  Ti.FColumns.Objects[I].Free;
  Ti.FColumns.Delete(I);
  AjustPosWidthTables(PosTable, AWidth);
  GenerateSQL(FrmMain.ReSQL);
end;

procedure TfrmQueryMaker.PmTablePopup(Sender: TObject);
var
  Ti: TTableImage;
  I: Integer;
begin
  Ti := TTableImage(PmTable.PopupComponent.Tag);
  I  := Ti.SelectedItem;
  RemoveTable.Caption := Format(sfRemove, [Ti.GetCaptionTable]);
  if (Ti.SelectedItem < 0) or (not (Ti.Columns.Objects[Ti.SelectedItem] as TColumnsParams).FIsCalc) then begin
    EditCalcFld.Enabled := False;
    EditCalcFld.Caption := sEditCalcFld;
    DelCalcFld.Enabled  := False;
    DelCalcFld.Caption := sDelCalcFld;
  end
  else begin
    EditCalcFld.Enabled := True;
    EditCalcFld.Caption := Format(sfEditCalcFld, [Trim(Ti.Columns[I])]);
    DelCalcFld.Enabled  := True;
    DelCalcFld.Caption := Format(sfDelCalcFld, [Trim(Ti.Columns[I])]);
  end;
end;

procedure TfrmQueryMaker.RemoveAllTblsClick(Sender: TObject);
begin
  RemoveAllTables(True);
end;

procedure TfrmQueryMaker.CbDistinctClick(Sender: TObject);
var
  I: Integer;
begin
  for I := 0 to FJoinsList.Count - 1 do begin
    SetDescrJoin(I);
  end;
  GenerateSQL(FrmMain.ReSQL);
  if CbDistinct.Checked then begin
    SgColumns.Objects[0, 0] := TObject(1)
  end
  else begin
    SgColumns.Objects[0, 0] := TObject(0)
  end;
  if cbLowerCase.Checked then begin
    SgColumns.Objects[0, 1] := TObject(1)
  end
  else begin
    SgColumns.Objects[0, 1] := TObject(0)
  end;
  if cbDoubleQuotes.Checked then begin
    SgColumns.Objects[0, 2] := TObject(1)
  end
  else begin
    SgColumns.Objects[0, 2] := TObject(0)
  end;
  if cbIntoTable.Checked then begin
    SgColumns.Objects[0, 4] := TObject(1)
  end
  else begin
    SgColumns.Objects[0, 4] := TObject(0);
    cbMemory.Checked := False;
  end;
  if cbSaveComments.Checked then begin
    SgColumns.Objects[0, 5] := TObject(1)
  end
  else begin
    SgColumns.Objects[0, 5] := TObject(0)
  end;
  cbMemory.Enabled := cbIntoTable.Checked;
  if cbMemory.Checked then begin
    SgColumns.Objects[0, 6] := TObject(1)
  end
  else begin
    SgColumns.Objects[0, 6] := TObject(0)
  end;
end;

procedure TfrmQueryMaker.SelectColumn(ACol: longint);
var
  FocuseRect: TGridRect;
begin
  FocuseRect.Left     := ACol;
  FocuseRect.Top      := 1;
  FocuseRect.Right    := ACol;
  FocuseRect.Bottom   := 11;
  SgColumns.Selection := FocuseRect;
end;

procedure TfrmQueryMaker.SgColumnsClick(Sender: TObject);
var
  ACol, ARow, I: longint;
  ClientPoint, ScreenPoint: TPoint;
  Index: integer;
  NewItem: TMenuItem;
  P:     Tpoint;
  Tr:    Trect;
  S, S1: string;
  Ti: TTableImage;
begin
  GetCursorPos(ScreenPoint);
  ClientPoint := SgColumns.ScreenToClient(ScreenPoint);
  SgColumns.MouseToCell(ClientPoint.X, ClientPoint.Y, ACol, ARow);
  if IsWheel then begin
    IsWheel := False;
    SelectColumn(ACol);
    Exit;
  end;
  if not ((ACol > 0) and (ARow > 0)) then begin
    SelectColumn(SgColumns.Col);
    Exit;
  end;
  if Length(SgColumns.Cells[1, 0]) = 0 then begin
    Exit;
  end;
  for Index := PmGrid.Items.Count - 1 downto 0 do begin
    PmGrid.Items[Index].Free
  end;
  case ARow of
    3: begin
      for Index := 0 to 2 do begin
        Application.ProcessMessages;
        NewItem := TMenuItem.Create(PmGrid);
        PmGrid.Items.Add(NewItem);
        NewItem.Caption := SFSort[Index];
        NewItem.Tag     := Index;
        NewItem.GroupIndex := 1;
        NewItem.RadioItem := True;
        case Index of
          0: begin
            NewItem.OnClick := PmNoSortClick;
            NewItem.Checked := SgColumns.Cells[SgColumns.Col, 3] = '';
          end;
          1: begin
            NewItem.OnClick := PmASCClick;
            NewItem.Checked := Copy(SgColumns.Cells[SgColumns.Col, 3], 1, 1) = 'A';
          end;
          2: begin
            NewItem.OnClick := PmDESCClick;
            NewItem.Checked := Copy(SgColumns.Cells[SgColumns.Col, 3], 1, 1) = 'D';
          end;
        end;
      end;
      Tr  := SgColumns.Cellrect(SgColumns.Col, SgColumns.Row);
      P.X := Tr.Left;
      P.Y := Tr.Top;
      P   := SgColumns.ClientToScreen(P);
      PmGrid.Popup(P.X, P.Y);
    end;
    4: begin
      S  := SgColumns.Cells[SgColumns.Col, SgColumns.Row];
      S1 := Format('"%s" %s %s', [SgColumns.Cells[SgColumns.Col, 0], sDelimiter, SgColumns.Cells[SgColumns.Col, 7]]);
      if ExecuteCondition(FTablesList, S, S1, CoWhere) then begin
        S := Trim(S);
        SgColumns.Cells[SgColumns.Col, SgColumns.Row] := S;
        GenerateSQL(FrmMain.ReSQL);
      end;
    end;
    5: begin
      if Length(SgColumns.Cells[SgColumns.Col, SgColumns.Row - 1]) <> 0 then begin
        S  := SgColumns.Cells[SgColumns.Col, SgColumns.Row];
        S1 := Format('"%s" %s %s', [SgColumns.Cells[SgColumns.Col, 0], sDelimiter, SgColumns.Cells[SgColumns.Col, 7]]);
        if ExecuteCondition(FTablesList, S, S1, CoOr) then begin
          S := Trim(S);
          SgColumns.Cells[SgColumns.Col, SgColumns.Row] := S;
          GenerateSQL(FrmMain.ReSQL);
        end;
      end else
        MessageDlg(SWhereCondition, mtInformation, [mbOK], 0);
    end;
    6: begin
      for Index := 0 to 5 do begin
        Application.ProcessMessages;
        NewItem := TMenuItem.Create(PmGrid);
        PmGrid.Items.Add(NewItem);
        NewItem.Caption := SFAgregate[Index];
        NewItem.Tag     := Index;
        NewItem.OnClick := AgregateClick;
          NewItem.GroupIndex := 1;
          NewItem.RadioItem  := True;
        case Index of
          0: begin
            NewItem.Checked := SgColumns.Cells[SgColumns.Col, 6] = ''
          end;
          1: begin
            NewItem.Checked := Copy(SgColumns.Cells[SgColumns.Col, 6], 2, 1) = 'V'
          end;
          2: begin
            NewItem.Checked := Copy(SgColumns.Cells[SgColumns.Col, 6], 2, 1) = 'O'
          end;
          3: begin
            NewItem.Checked := Copy(SgColumns.Cells[SgColumns.Col, 6], 2, 1) = 'A'
          end;
          4: begin
            NewItem.Checked := Copy(SgColumns.Cells[SgColumns.Col, 6], 2, 1) = 'I'
          end;
          5: begin
            NewItem.Checked := Copy(SgColumns.Cells[SgColumns.Col, 6], 2, 1) = 'U'
          end;
        end;
      end;
      Tr  := SgColumns.Cellrect(SgColumns.Col, SgColumns.Row);
      P.X := Tr.Left;
      P.Y := Tr.Top;
      P   := SgColumns.ClientToScreen(P);
      PmGrid.Popup(P.X, P.Y);
    end;
    7: begin
      SelectColumn(ACol);
      if (SgColumns.Cells[SgColumns.Col, 7] = SCalcField) then begin
        Ti := TTableImage(longint(SgColumns.Objects[SgColumns.Col, 1]));
        I  := longint(SgColumns.Objects[SgColumns.Col, 7]);
        S := Format(SDefCalcField, [SgColumns.Cells[SgColumns.Col, 0],
          Trim((Ti.Columns.Objects[I] as TColumnsParams).FFormula)]);
        MessageDlg(S, mtInformation, [mbOK], 0);
      end;
    end;
    8: begin
      for Index := 0 to 2 do begin
        Application.ProcessMessages;
        NewItem := TMenuItem.Create(PmGrid);
        PmGrid.Items.Add(NewItem);
        NewItem.Caption := SFShow[Index];
        NewItem.Tag     := Index;
        NewItem.OnClick := VisibleClick;
          NewItem.GroupIndex := 1;
          NewItem.RadioItem  := True;
        case Index of
          0: begin
            NewItem.Checked := Copy(SgColumns.Cells[SgColumns.Col, 8], 1, 1) = 'S'
          end;
          1: begin
            NewItem.Checked := Copy(SgColumns.Cells[SgColumns.Col, 8], 1, 1) = 'H'
          end;
          2: begin
            NewItem.Checked := Copy(SgColumns.Cells[SgColumns.Col, 8], 1, 1) = 'O'
          end;
        end;
      end;
      Tr  := SgColumns.Cellrect(SgColumns.Col, SgColumns.Row);
      P.X := Tr.Left;
      P.Y := Tr.Top;
      P   := SgColumns.ClientToScreen(P);
      PmGrid.Popup(P.X, P.Y);
    end;
    9: begin
      if longint(SgColumns.Objects[SgColumns.Col, 9]) > 0 then begin
        ResetGrouped(SgColumns.Col);
        GenerateSQL(FrmMain.ReSQL);
      end
      else begin
        SgColumns.Cells[SgColumns.Col, 8] := SFShow[0];
        SetGrouped;
      end;
    end;
    10: begin
      if (SgColumns.Cells[SgColumns.Col, 6] <> '') or (SgColumns.Cells[SgColumns.Col, 9] <> '') then begin
        S := SgColumns.Cells[SgColumns.Col, SgColumns.Row];
        S1 := Format('"%s" %s %s', [SgColumns.Cells[SgColumns.Col, 0], sDelimiter, SgColumns.Cells[SgColumns.Col, 7]]);
        if ExecuteCondition(FTablesList, S, S1, CoHaving) then begin
          S := Trim(S);
          SgColumns.Cells[SgColumns.Col, SgColumns.Row] := S;
          GenerateSQL(FrmMain.ReSQL);
        end;
      end else
        MessageDlg(SHavingCondition, mtInformation, [mbOK], 0);
    end;
  end;
  SelectColumn(ACol);
end;

procedure TfrmQueryMaker.RemoveAllColsClick(Sender: TObject);
var
  I: integer;
begin
  for I := SgColumns.ColCount - 1 downto 1 do begin
    Application.ProcessMessages;
    DeleteColumn(I);
  end;
end;

procedure TfrmQueryMaker.SgColumnsContextPopup(Sender: TObject; MousePos: TPoint; var Handled: boolean);
var
  ACol, ARow: longint;
  Index:      integer;
  NewItem:    TMenuItem;
begin
  SgColumns.MouseToCell(MousePos.X, MousePos.Y, ACol, ARow);
  for Index := PmGrid.Items.Count - 1 downto 0 do begin
    PmGrid.Items[Index].Free
  end;
  if (Acol > 0) and (Length(SgColumns.Cells[1, 0]) <> 0) then begin
    SelectColumn(ACol);
    NewItem := TMenuItem.Create(PmGrid);
    PmGrid.Items.Add(NewItem);
    NewItem.Caption := Format(sfRemove, [SgColumns.Cells[ACol, 0]]);
    NewItem.OnClick := RemoveSelColClick;
    NewItem := TMenuItem.Create(PmGrid);
    PmGrid.Items.Add(NewItem);
    NewItem.Caption := sRemoveAllCols;
    NewItem.OnClick := RemoveAllColsClick;
  end;
end;

procedure TJoinImage.ReadFromFile(Fs: TFileStream);
begin
  Fs.ReadComponent(Self);
end;

procedure TJoinImage.WriteToFile(Fs: TFileStream);
begin
  Fs.WriteComponent(Self);
end;

constructor TCols.Create(AOwner: TComponent);
begin
  inherited;
  FSel := TStringList.Create;
  FObj := TStringList.Create;
end;

destructor TCols.Destroy;
begin
  FSel.Free;
  FObj.Free;
  inherited;
end;

procedure TfrmQueryMaker.SgColumnsMouseWheelDown(Sender: TObject; Shift: TShiftState; MousePos: TPoint; var Handled: boolean);
var
  ACol, ARow: longint;
  ClientPoint, ScreenPoint: TPoint;
begin
  GetCursorPos(ScreenPoint);
  ClientPoint := SgColumns.ScreenToClient(ScreenPoint);
  SgColumns.MouseToCell(ClientPoint.X, ClientPoint.Y, ACol, ARow);
  if ((ACol > 0) and (ARow > 0)) then begin
    IsWheel := True
  end
  else begin
    IsWheel := False
  end;
end;

procedure TfrmQueryMaker.VisibleClick(Sender: TObject);
begin
  SgColumns.Objects[SgColumns.Col, 8] := TObject((Sender as TmenuItem).Tag);
  case (Sender as TmenuItem).Tag of
    0: begin
      SgColumns.Cells[SgColumns.Col, 8]  := SFShow[0];
    end;
    1: begin
      SgColumns.Cells[SgColumns.Col, 8] := SFShow[1];
      if (SgColumns.Cells[SgColumns.Col, 7] = SCalcField) and (SgColumns.Cells[SgColumns.Col, 8] = SFShow[1]) then begin
        SgColumns.Cells[SgColumns.Col, 10] := '';
        ResetSort(SgColumns.Col);
        ResetGrouped(SgColumns.Col);
      end;
    end;
    2: begin
      SgColumns.Cells[SgColumns.Col, 8] := SFShow[2];
    end;
  end;
  GenerateSQL(FrmMain.ReSQL);
end;

procedure TfrmQueryMaker.RemoveSelColClick(Sender: TObject);
begin
  DeleteColumn(SgColumns.Col);
  GenerateSQL(FrmMain.ReSQL);
end;

procedure TfrmQueryMaker.Label1Click(Sender: TObject);
var
  S: string;
begin
  S := ExtractFilePath(Application.ExeName) + ChmHelpFileName;
  if FileExists(S) then
    ShellExecute(Handle, 'open', PChar(S), nil, nil, SW_SHOWNORMAL)
  else
    MessageDlg(Format(sFileNotFound, [ChmHelpFileName]), mtError, [mbOK], 0)
end;

procedure TfrmQueryMaker.FormResize(Sender: TObject);
begin
  Label1.Left := (FTablesBoard.ClientWidth - Label1.Width) div 2;
  Label1.Top := (FTablesBoard.ClientHeight - Label1.Height) div 2;
end;

procedure TfrmQueryMaker.Label1MouseEnter(Sender: TObject);
begin
  Label1.Font.Color := clWindowText;
end;

procedure TfrmQueryMaker.Label1MouseLeave(Sender: TObject);
begin
  Label1.Font.Color := clSilver;
end;

procedure TfrmQueryMaker.WheelWndProc(var Msg: TMsg; var Handled: boolean);
var
  HRegion: Hrgn;
  ClientPoint, ScreenPoint: TPoint;
begin
  if frmQueryMaker.Active then
  if FTablesList.Count <> 0 then begin
  GetCursorPos(ScreenPoint);
  ClientPoint := frmQueryMaker.ScreenToClient(ScreenPoint);
  HRegion := CreateRectRgn(0, 0, ToolBar1.Width, ToolBar1.Top);
  try
  if PtInRegion(HRegion, ClientPoint.X, ClientPoint.Y) then
  if Msg.message = WM_MOUSEWHEEL then begin
    if (Msg.wParam and MK_CONTROL) = 0 then begin
      if Msg.wParam > 0 then
        FTablesBoard.VertScrollBar.Position :=
          FTablesBoard.VertScrollBar.Position - 8
      else
        FTablesBoard.VertScrollBar.Position :=
          FTablesBoard.VertScrollBar.Position + 8
    end else begin
      if Msg.wParam > 0 then
        FTablesBoard.HorzScrollBar.Position :=
          FTablesBoard.HorzScrollBar.Position - 8
      else
        FTablesBoard.HorzScrollBar.Position :=
          FTablesBoard.HorzScrollBar.Position + 8
    end;
  end;
  finally
    DeleteObject(HRegion);
  end;
  end;
end;

procedure TfrmQueryMaker.FormShow(Sender: TObject);
begin
  Caption := FCurCaption;
  Top := Screen.WorkAreaHeight - frmQueryMaker.Height;
  Left := Screen.WorkAreaWidth - frmQueryMaker.Width;
end;

procedure TfrmQueryMaker.FormHide(Sender: TObject);
begin
  FCurCaption := Caption;
  frmMain.ToolButton3.Hint := sShowQueryMaker;
end;

end.

