unit data_sorting;

{$mode delphi}{$H+}

interface

uses
  SysUtils, Classes, Controls, Forms, StdCtrls, ExtCtrls, ComCtrls, Buttons,
  Graphics, apphelpers, extra_controls, dbconnection;


type
  TfrmDataSorting = class(TExtForm)
    pnlBevel: TPanel;
    btnOK: TButton;
    btnCancel: TButton;
    btnAddCol: TButton;
    btnReset: TSpeedButton;
    procedure btnAddColClick(Sender: TObject);
    procedure btnCancelClick(Sender: TObject);
    procedure btnOKClick(Sender: TObject);
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
    procedure FormDeactivate(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure DisplaySortingControls(Sender: TObject);
  private
    { Private declarations }
    FColumnNames: TStringList;
    FSortItems: TSortItems;
    FOldOrderClause: String;
    FDeleteTimer: TTimer;
    FDeleteButtonPressed: TSpeedButton;
    procedure DeleteTimerTimer(Sender: TObject);
    procedure comboColumnsChange(Sender: TObject);
    procedure btnOrderClick(Sender: TObject);
    procedure btnDeleteClick(Sender: TObject);
    procedure Modified;
  public
    { Public declarations }
  end;



implementation

uses main;

{$R *.lfm}


procedure TfrmDataSorting.FormCreate(Sender: TObject);
var
  i: Integer;
begin
  FColumnNames := TStringList.Create;
  // Take column names from listColumns and add here
  for i:=0 to Mainform.SelectedTableColumns.Count-1 do begin
    FColumnNames.Add(Mainform.SelectedTableColumns[i].Name);
  end;

  FSortItems := TSortItems.Create(True);
  FSortItems.Assign(MainForm.DataGridSortItems);
  FOldOrderClause := FSortItems.ComposeOrderClause(MainForm.ActiveConnection);

  FDeleteTimer := TTimer.Create(Self);
  FDeleteTimer.Interval := 100;
  FDeleteTimer.Enabled := False;
  FDeleteTimer.OnTimer := DeleteTimerTimer;

  // First creation of controls
  DisplaySortingControls(Sender);
end;


{**
  Create controls for order columns
}
procedure TfrmDataSorting.DisplaySortingControls(Sender: TObject);
var
  SortItem: TSortItem;
  lblNumber: TLabel;
  btnDelete: TSpeedButton;
  comboColumns: TComboBox;
  btnOrder: TSpeedButton;
  i, TopPos,
  Width1, Width2, Width3, Width4,   // Width of controls per row
  Margin,                           // Space between controls
  MarginBig: Integer;               // Space above the very first and last controls, used to separate stuff
begin
  // Remove previously created components, which all have a tag > 0
  for i := ComponentCount - 1 downto 0 do begin
    if Components[i].Tag > 0 then
      Components[i].Free;
  end;

  Margin := ScaleSize(3);
  MarginBig := ScaleSize(Margin * 2);
  Width1 := ScaleSize(15);
  Width2 := ScaleSize(160);
  Width3 := ScaleSize(23);
  Width4 := ScaleSize(23);

  // Set initial width to avoid resizing form to 0
  TopPos := pnlBevel.BorderWidth + MarginBig;

  // Create line with controls for each order column
  // TODO: disable repaint on every created control. Sending WM_SETREDRAW=0 message creates artefacts.
  //LockWindowUpdate(pnlBevel.Handle);
  for i:=0 to FSortItems.Count-1 do begin
    SortItem := FSortItems[i];
    // 1. Label with number
    lblNumber := TLabel.Create(self);
    lblNumber.Parent := pnlBevel;
    lblNumber.AutoSize := False; // Avoids automatic changes to width + height
    lblNumber.Left := pnlBevel.BorderWidth + MarginBig;
    lblNumber.Top := TopPos;
    lblNumber.Width := Width1;
    lblNumber.Alignment := taRightJustify;
    lblNumber.Layout := tlCenter;
    lblNumber.Caption := IntToStr(i+1) + '.';
    lblNumber.Tag := i+1;

    // 2. Dropdown with column names
    comboColumns := TComboBox.Create(self);
    comboColumns.Parent := pnlBevel;
    comboColumns.Width := Width2;
    comboColumns.Left := lblNumber.Left + lblNumber.Width + Margin;
    comboColumns.Top := TopPos;
    comboColumns.Items.Text := FColumnNames.Text;
    comboColumns.Style := csDropDownList; // Not editable
    comboColumns.ItemIndex := FColumnNames.IndexOf(SortItem.Column);
    comboColumns.Tag := i+1;
    comboColumns.OnChange := comboColumnsChange;
    lblNumber.Height := comboColumns.Height;

    // 3. A button for selecting ASC/DESC
    btnOrder := TSpeedButton.Create(self);
    btnOrder.Parent := pnlBevel;
    btnOrder.Width := Width3;
    btnOrder.Height := comboColumns.Height;
    btnOrder.Left := comboColumns.Left + comboColumns.Width + Margin;
    btnOrder.Top := TopPos;
    btnOrder.AllowAllUp := True; // Enables Down = False
    btnOrder.GroupIndex := i+1; // if > 0 enables Down = True
    btnOrder.Glyph.Transparent := True;
    //btnOrder.Glyph.AlphaFormat := afDefined;
    if SortItem.Order = sioDescending then begin
      MainForm.ImageListMain.GetBitmap(110, btnOrder.Glyph);
      btnOrder.Down := True;
    end else begin
      MainForm.ImageListMain.GetBitmap(109, btnOrder.Glyph);
    end;
    btnOrder.Hint := _('Toggle the sort direction for this column.');
    btnOrder.Tag := i+1;
    btnOrder.OnClick := btnOrderClick;

    // 4. Button for deleting
    btnDelete := TSpeedButton.Create(self);
    btnDelete.Parent := pnlBevel;
    btnDelete.Width := Width4;
    btnDelete.Height := comboColumns.Height;
    btnDelete.Left := btnOrder.Left + btnOrder.Width + Margin;
    btnDelete.Top := TopPos;
    btnDelete.Images := MainForm.ImageListMain;
    btnDelete.ImageIndex := 26;
    btnDelete.Hint := _('Drops sorting by this column.');
    btnDelete.Tag := i+1;
    btnDelete.OnClick := btnDeleteClick;

    TopPos := comboColumns.Top + comboColumns.Height + Margin;
  end;
  //LockWindowUpdate(0);

  Inc(TopPos, MarginBig);

  // Auto-adjust size of form
  Height := TopPos +
    btnReset.Height + Margin +
    btnOK.Height + MarginBig +
    pnlBevel.BorderWidth;
  Width := pnlBevel.BorderWidth +
    MarginBig + Width1 +
    Margin + Width2 +
    Margin + Width3 +
    Margin + Width4 +
    MarginBig + pnlBevel.BorderWidth;

  // Auto-adjust width and position of main buttons at bottom
  btnReset.Left := pnlBevel.BorderWidth + MarginBig;
  btnReset.Top := TopPos;
  btnReset.Width := Width - 2 * pnlBevel.BorderWidth - 2 * MarginBig;
  btnReset.Enabled := Mainform.actDataResetSorting.Enabled;

  btnOK.Left := pnlBevel.BorderWidth + MarginBig;
  btnOK.Top := btnReset.Top + btnReset.Height + Margin;
  btnOK.Width := Round(btnReset.Width / 3) - Margin;

  btnCancel.Left := btnOK.Left + btnOK.Width + Margin;
  btnCancel.Top := btnReset.Top + btnReset.Height + Margin;
  btnCancel.Width := btnOK.Width;

  btnAddCol.Left := btnCancel.Left + btnCancel.Width + Margin;
  btnAddCol.Top := btnReset.Top + btnReset.Height + Margin;
  btnAddCol.Width := btnOK.Width;
end;


{**
  Dropdown for column selection was changed
}
procedure TfrmDataSorting.comboColumnsChange( Sender: TObject );
var
  combo : TComboBox;
begin
  combo := Sender as TComboBox;
  FSortItems[combo.Tag-1].Column := combo.Text;

  // Enables OK button
  Modified;
end;


{**
  Button for selecting sort-direction was clicked
}
procedure TfrmDataSorting.btnOrderClick( Sender: TObject );
var
  btn: TSpeedButton;
begin
  btn := Sender as TSpeedButton;
  btn.Glyph := nil;
  if FSortItems[btn.Tag-1].Order = sioAscending then begin
    MainForm.ImageListMain.GetBitmap(110, btn.Glyph);
    FSortItems[btn.Tag-1].Order := sioDescending;
  end else begin
    MainForm.ImageListMain.GetBitmap(109, btn.Glyph);
    FSortItems[btn.Tag-1].Order := sioAscending;
  end;

  // Enables OK button
  Modified;
end;


{**
  Delete order column
}
procedure TfrmDataSorting.btnDeleteClick(Sender: TObject);
begin
  FDeleteButtonPressed := Sender as TSpeedButton;
  FDeleteTimer.Enabled := True;
end;


procedure TfrmDataSorting.DeleteTimerTimer(Sender: TObject);
begin
  FDeleteTimer.Enabled := False;
  FSortItems.Delete(FDeleteButtonPressed.Tag-1);
  // Refresh controls
  DisplaySortingControls(Self);
  // Enables OK button
  Modified;
end;


{**
  Add a new order column
}
procedure TfrmDataSorting.btnAddColClick(Sender: TObject);
var
  UnusedColumns: TStringList;
  NewSortItem, SortItem: TSortItem;
begin
  NewSortItem := FSortItems.AddNew;

  // Take first unused column as default for new sort item
  UnusedColumns := TStringList.Create;
  UnusedColumns.AddStrings(FColumnNames);
  for SortItem in FSortItems do begin
    if UnusedColumns.IndexOf(SortItem.Column) > -1 then
      UnusedColumns.Delete(UnusedColumns.IndexOf(SortItem.Column));
  end;
  if UnusedColumns.Count > 0 then
    NewSortItem.Column := UnusedColumns[0]
  else
    NewSortItem.Column := FColumnNames[0];
  MainForm.LogSQL('Created sorting for column '+NewSortItem.Column+'/'+Integer(NewSortItem.Order).ToString+' in TfrmDataSorting.btnAddColClick', lcDebug);

  // Refresh controls
  DisplaySortingControls(Sender);

  // Enables OK button
  Modified;
end;


{**
  Gets called when any option has changed.
  Enables the OK button if ORDER options have changed
}
procedure TfrmDataSorting.Modified;
begin
  btnOk.Enabled := FSortItems.ComposeOrderClause(MainForm.ActiveConnection) <> FOldOrderClause;
end;


{**
  OK clicked: Write ORDER clause to registry
}
procedure TfrmDataSorting.btnOKClick(Sender: TObject);
begin
  // TODO: apply ordering
  MainForm.DataGridSortItems.Assign(FSortItems);
  InvalidateVT(Mainform.DataGrid, VTREE_NOTLOADED_PURGECACHE, False);
  btnCancel.OnClick(Sender);
end;


procedure TfrmDataSorting.btnCancelClick(Sender: TObject);
begin
  Mainform.tbtnDataSorting.Down := False;
  Close;
end;


{**
  Be sure the form is destroyed after closing.
}
procedure TfrmDataSorting.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  Action := caFree;
end;


{**
  Cancel this dialog if the user clicks elsewhere on mainform
}
procedure TfrmDataSorting.FormDeactivate(Sender: TObject);
begin
  btnCancel.OnClick(Sender);
end;


end.
