unit Form_StringGridEditDialog;

{$I _crw_sysdef}

{$IFDEF FPC}{$mode Delphi}{$ENDIF}{$H+}

interface

uses
 _crw_sharemem, // NB: THIS UNIT MUST BE FIRST !!!
 {$IFDEF UNIX} cthreads, dl, {$ENDIF}
 {$IFDEF WINDOWS} windows, {$ENDIF}
 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  ApplyParams(const Params:LongString):Integer;
  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}
const                 // ApplyParams flags:
 apFormL = $00000001; // Applied Form.Left
 apFormT = $00000002; // Applied Form.Top
 apFormW = $00000004; // Applied Form.Width
 apFormH = $00000008; // Applied Form.Height
 apFonts = $00000010; // Applied Panel.Font

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,apFormL+apFormT) 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.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.FixedRows:=0;
   StringGrid.FixedCols:=0;
   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,apFormW) 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,apFormH) 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 WindowName  ComponentName
 // @set Form.Top     400  relative WindowName  ComponentName
 // @set Form.Width   400
 // @set Form.Height  300
 // @set Form.Width   80  relative Screen
 // @set Form.Height  50  relative Desktop
function TFormStringGridEditDialog.ApplyParams(const Params:LongString):Integer;
var pars:TText; i,w,h,l,t:Integer; line:LongString; FontPars:TFontParams; p:TPoint;
begin
 Result:=0;
 if (Self<>nil) then
 if UseEditSettings then
 if (SysUtils.Trim(Params)<>'') then
 try
  pars:=NewText;
  try
   SafeFillChar(FontPars,SizeOf(FontPars),0);
   pars.Text:=SysUtils.Trim(Params);
   for i:=0 to pars.Count-1 do begin
    line:=Trim(pars[i]);
    if (StrFetch(line,1)='@') then begin
     if SameText(ExtractWord(1,line,ScanSpaces),'@set') then begin
      if SameText(ExtractWord(2,line,ScanSpaces),'Panel.Font') then begin
       if ReadBufferedFont(FontPars,ExtractWord(3,line,ScanSpaces),true,StandardFont)
       then RestoreFont(PanelStringGrid.Font,FontPars);
       Result:=Result or apFonts;
      end;
      if SameText(ExtractWord(2,line,ScanSpaces),'StringGrid.Font') then begin
       if ReadBufferedFont(FontPars,ExtractWord(3,line,ScanSpaces),true,StandardFont)
       then RestoreFont(StringGrid.Font,FontPars);
       Result:=Result or apFonts;
      end;
      if SameText(ExtractWord(2,line,ScanSpaces),'Form.Width') then begin
       if Str2Int(ExtractWord(3,line,ScanSpaces),w) then begin
        if SameText(ExtractWord(4,line,ScanSpaces),'relative')
        then w:=FindRelativeSize(Point(w,0),ExtractWord(5,line,ScanSpaces),ExtractWord(6,line,ScanSpaces)).x;
        Width:=w;
        Result:=Result or apFormW;
       end;
      end;
      if SameText(ExtractWord(2,line,ScanSpaces),'Form.Height') then begin
       if Str2Int(ExtractWord(3,line,ScanSpaces),h) then begin
        if SameText(ExtractWord(4,line,ScanSpaces),'relative')
        then h:=FindRelativeSize(Point(0,h),ExtractWord(5,line,ScanSpaces),ExtractWord(6,line,ScanSpaces)).y;
        Height:=h;
        Result:=Result or apFormH;
       end;
      end;
      if SameText(ExtractWord(2,line,ScanSpaces),'Form.Left') then begin
       if Str2Int(ExtractWord(3,line,ScanSpaces),l) then begin
        p:=Point(l,0);
        if SameText(ExtractWord(4,line,ScanSpaces),'relative')
        then p:=FindRelativePos(p,ExtractWord(5,line,ScanSpaces),ExtractWord(6,line,ScanSpaces));
        Left:=p.x;
        Result:=Result or apFormL;
       end;
      end;
      if SameText(ExtractWord(2,line,ScanSpaces),'Form.Top') then begin
       if Str2Int(ExtractWord(3,line,ScanSpaces),t) then begin
        p:=Point(0,t);
        if SameText(ExtractWord(4,line,ScanSpaces),'relative')
        then p:=FindRelativePos(p,ExtractWord(5,line,ScanSpaces),ExtractWord(6,line,ScanSpaces));
        Top:=p.y;
        Result:=Result or apFormT;
       end;
      end;
     end;
    end;
   end;
  finally
   Kill(pars);
  end;
 except
  on E:Exception do BugReport(E,Self,'ApplyParams');
 end;
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;

end.
