////////////////////////////////////////////////////////////////////////////////
// Copyright (c) 2001-2026 Alexey Kuryakin daqgroup@mail.ru under MIT license //
////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////
// This file is part of the CRW-DAQ project by DaqGroup - component CRWLIB.   //
////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////
// Purpose:                                                                   //
// String grid table dialog.                                                  //
////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////
// History:                                                                   //
// 2001xxxx - Created by A.K.                                                 //
// 2023xxxx - Modified for FPC - A.K.                                         //
// 20240626 - ApplyParams                                                     //
////////////////////////////////////////////////////////////////////////////////

unit form_stringgrideditdialog;

{$I _crw_sysdef.inc}

{$I _crw_sysmode.inc}

interface

uses
 //////////////////////////////////////////////////////
 {$I _crw_uses_first.inc} // NB: MUST BE FIRST USES !!!
 //////////////////////////////////////////////////////
 sysutils, classes, graphics, controls, forms, dialogs, stdctrls, buttons,
 extctrls, grids, math, types, lcltype, lclproc, lclintf,
 _crw_alloc, _crw_str, _crw_ef, _crw_plut, _crw_eldraw,
 _crw_appforms, _crw_apptools;

type
  TFormStringGridEditDialog = class(TMasterForm)
    PanelStringGrid: TPanel;
    StringGrid: TStringGrid;
    PanelButtons: TPanel;
    BitBtnOk: TBitBtn;
    BitBtnCancel: TBitBtn;
    procedure FormCreate(Sender: TObject);
    procedure FormActivate(Sender: TObject);
    procedure StringGridKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
    procedure StringGridSetEditText(Sender: TObject; ACol, ARow: Integer; const Value: String);
  private
    { Private declarations }
    apFlags   : Integer;
    Separator : Char;
    procedure SetArguments(const Arg:LongString);
    function  GetArguments:LongString;
  public
    { Public declarations }
    property Arguments:LongString read GetArguments write SetArguments;
    procedure ResetParams;
    function  DoApplyParams(const Params:LongString):Integer; override;
  end;

 {
 Dialog to edit table, given by plain text with Separator to separate items...
 }
function ExecuteFormStringGridEditDialog(const TheCaption:LongString; TheTable:TText; TheSeparator:Char='|';
                                         FixRows:Integer=0; FixCols:Integer=1; ToMouse:Boolean=false;
                                         const Params:LongString=''):TModalResult; overload;

function ExecuteFormStringGridEditDialog(const TheCaption:LongString; var TheTable:LongString; TheSeparator:Char='|';
                                         FixRows:Integer=0; FixCols:Integer=1; ToMouse:Boolean=false;
                                         const Params:LongString=''):TModalResult; overload;

implementation

{$R *.lfm}

function ExecuteFormStringGridEditDialog(const TheCaption:LongString; TheTable:TText; TheSeparator:Char='|';
                                         FixRows:Integer=0; FixCols:Integer=1; ToMouse:Boolean=false;
                                         const Params:LongString=''):TModalResult;
const
  TheForm:TFormStringGridEditDialog = nil;
begin
 Result:=mrCancel;
 if TheTable.MaxLength>0 then
 if CanShowModal(TheForm) then
 try
  if not Assigned(TheForm) then begin
   Application.CreateForm(TFormStringGridEditDialog, TheForm);
   TheForm.Master:=@TheForm;
   TheForm.Show;
   TheForm.Hide;
   TheForm.StringGrid.ScrollBars:=ssNone; // First time appearence
  end;
  if TheForm.Ok then
  with TheForm do
  try
   ResetParams;
   apFlags:=ApplyParams(Params);
   Separator:=TheSeparator;
   Caption:=TheCaption;
   Arguments:=TheTable.Text;
   StringGrid.FixedRows:=max(0,min(FixRows,StringGrid.RowCount-1));
   StringGrid.FixedCols:=max(0,min(FixCols,StringGrid.ColCount-1));
   if not HasFlags(apFlags,apf_FormLeft+apf_FormTop) then
   if ToMouse
   then LocateFormToCenterOfMouse(TheForm)
   else LocateFormToCenterOfScreen(TheForm);
   ValidateFormPosition(TheForm);
   Result:=mrVoice(ShowModal);
   if Result = mrOk then TheTable.Text:=Arguments;
   TheForm.StringGrid.ScrollBars:=ssAutoBoth;
  finally
   ResetParams;
  end;
 except
  on E:Exception do BugReport(E,nil,'ExecuteFormStringGridEditDialog');
 end;
end;

function ExecuteFormStringGridEditDialog(const TheCaption:LongString; var TheTable:LongString; TheSeparator:Char='|';
                                         FixRows:Integer=0; FixCols:Integer=1; ToMouse:Boolean=false;
                                         const Params:LongString=''):TModalResult;
var p:TText;
begin
 Result:=mrCancel;
 if (TheTable<>'') then
 try
  p:=NewText;
  try
   p.Text:=AdjustLineBreaks(TheTable);
   Result:=ExecuteFormStringGridEditDialog(TheCaption,p,TheSeparator,FixRows,FixCols,ToMouse,Params);
   if (Result=mrOk) then TheTable:=p.Text;
  finally
   Kill(p);
  end;
 except
   on E:Exception do BugReport(E,nil,'ExecuteFormStringGridEditDialog');
 end;
end;

procedure TFormStringGridEditDialog.SetArguments(const Arg:LongString);
var P:TText; r,c,w,h,rh,dh:Integer;
const GrigSpaceX=10;
const GrigSpaceY=15;
 function WordCount(const s:LongString; c:Char):Integer;
 var
  i : Integer;
 begin
  Result:=1;
  for i:=1 to Length(s) do if (s[i]=c) then inc(Result);
 end;
 function ExtractWord(n:Integer; const s:LongString; c:Char):LongString;
 var i,k:Integer;
 begin
  Result:=s;
  k:=0;
  for i:=1 to Length(Result) do begin
   if (Result[i]=c) then inc(k);
   if (k=n) then begin
    Result:=Copy(Result,1,i-1);
    Break;
   end;
  end;
  for i:=Length(Result) downto 1 do begin
   if (Result[i]=c) then begin
    System.Delete(Result,1,i);
    Break;
   end;
  end;
 end;
begin
 if Ok then
 try
  P:=NewText;
  try
   P.Text:=Arg;
   StringGrid.FixedRows:=0;
   StringGrid.FixedCols:=0;
   StringGrid.Options:=StringGrid.Options-[goAlwaysShowEditor];
   StringGrid.RowCount:=max(1,P.Count);
   StringGrid.ColCount:=1;
   for r:=0 to P.Count-1 do StringGrid.ColCount:=max(StringGrid.ColCount,WordCount(P[r],Separator));
   StringGrid.DefaultColWidth:=128;
   for r:=0 to StringGrid.RowCount-1 do
   for c:=0 to StringGrid.ColCount-1 do begin
    StringGrid.Cells[c,r]:=ExtractWord(c+1,P[r],Separator);
    StringGrid.ColWidths[c]:=max(StringGrid.ColWidths[c],StringGrid.Canvas.TextWidth(StringGrid.Cells[c,r])+GrigSpaceX);
   end;
   w:=0;
   for c:=0 to StringGrid.ColCount-1 do w:=w+StringGrid.ColWidths[c];
   w:=max(w+20,StringGrid.Constraints.MinWidth);
   w:=w+Width-PanelStringGrid.ClientWidth;
   w:=min(w,round(Screen.Width*0.75));
   if not HasFlags(apFlags,apf_FormWidth) then Width:=w;
   rh:=0; // sum of all row heights
   for r:=0 to StringGrid.RowCount-1 do begin
    inc(rh,StringGrid.RowHeights[r]);
    inc(rh,StringGrid.GridLineWidth);
   end;
   dh:=Height-PanelStringGrid.ClientHeight;
   h:=rh+GrigSpaceY; // Some space around Grid
   h:=max(h,StringGrid.Constraints.MinHeight);
   h:=h+dh; // Add non-greed elements height
   h:=min(h,round(Screen.Height*0.75));
   if not HasFlags(apFlags,apf_FormHeight) then Height:=h;
   StringGrid.Row:=max(0,min(StringGrid.FixedRows,StringGrid.RowCount-1));
   StringGrid.Col:=max(0,min(StringGrid.FixedCols,StringGrid.ColCount-1));
  finally
   Kill(P);
  end;
 except
  on E:Exception do BugReport(E,Self,'SetArguments');
 end;
 Repaint;
end;

function  TFormStringGridEditDialog.GetArguments:LongString;
var P:TText; r:Integer; c:Integer; s:LongString;
begin
 Result:='';
 if Ok then
 if StringGrid.RowCount>0 then
 if StringGrid.ColCount>0 then
 try
  P:=NewText;
  try
   for r:=0 to StringGrid.RowCount-1 do begin
    s:='';
    for c:=0 to StringGrid.ColCount-1 do begin
     if (c>0) then s:=s+Separator;
     s:=s+StringGrid.Cells[c,r];
    end;
    P.Addln(s);
   end;
   Result:=P.Text;
  finally
   Kill(P);
  end;
 except
  on E:Exception do BugReport(E,Self,'GetArguments');
 end;
end;

procedure TFormStringGridEditDialog.FormCreate(Sender: TObject);
begin
 inherited;
 SetStandardFont(Self);
 SetAllButtonsCursor(Self,crHandPoint);
 SmartUpdate(BitBtnOk,mrCaption(mrOk));
 SmartUpdate(BitBtnCancel,mrCaption(mrCancel));
 Separator:='|';
end;

procedure TFormStringGridEditDialog.ResetParams;
begin
 SetStandardFont(Self);
 PanelStringGrid.ParentFont:=true;
 StringGrid.ParentFont:=true;
 apFlags:=0;
end;

 // @set Panel.Font      Name:PT_Mono\Size:10\Color:Black\Style:[Regular]
 // @set StringGrid.Font Name:PT_Mono\Size:10\Color:Black\Style:[Regular]
 // @set Form.Left    400  relative "WindowTitle" ComponentName
 // @set Form.Top     400  relative "WindowTitle" ComponentName
 // @set Form.Width   400
 // @set Form.Height  300
 // @set Form.Width   80  relative Screen
 // @set Form.Height  50  relative Desktop
function TFormStringGridEditDialog.DoApplyParams(const Params:LongString):Integer;
begin
 Result:=Form_ApplyParams_PosSize(Self,Params)
      or Form_ApplyParams_Font(PanelStringGrid.Font,Params,'Panel.Font',apf_Fonts1st)
      or Form_ApplyParams_Font(StringGrid.Font,Params,'StringGrid.Font',apf_Fonts2nd);
end;

procedure TFormStringGridEditDialog.FormActivate(Sender: TObject);
begin
 if Ok then
 try
  ActiveControl:=StringGrid;
  StringGrid.Options:=StringGrid.Options+[goAlwaysShowEditor];
 except
  on E:Exception do BugReport(E,Self,'FormActivate');
 end;
end;

procedure TFormStringGridEditDialog.StringGridKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
begin
 if Ok then
 try
  case Key of
   VK_RETURN : if fsModal in FormState then ModalResult:=mrOk;
   VK_ESCAPE : if fsModal in FormState then ModalResult:=mrCancel;
  end;
 except
  on E:Exception do BugReport(E,Self,'StringGridKeyDown');
 end;
end;

procedure TFormStringGridEditDialog.StringGridSetEditText(Sender: TObject; ACol, ARow: Integer; const Value: String);
begin
 if Ok then
 try
  if Pos(Separator,Value)>0
  then StringGrid.Cells[ACol,ARow]:=StringReplace(Value,Separator,'',[rfReplaceAll]);
 except
  on E:Exception do BugReport(E,Self,'StringGridSetEditText');
 end;
end;

///////////////////////////////////////
// Unit initialization and finalization
///////////////////////////////////////

procedure Init_form_stringgrideditdialog;
begin
end;

procedure Free_form_stringgrideditdialog;
begin
end;

initialization

 Init_form_stringgrideditdialog;

finalization

 Free_form_stringgrideditdialog;

end.

//////////////
// END OF FILE
//////////////

