unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms,
  Dialogs, RVScroll, RichView, RVEdit, RVStyle, ExtCtrls, RVTable, StdCtrls, Clipbrd,
  SclRView;

{------------------------------------------------------------------------------}
{
  Notes:
  1. Making sure that all cells contain one item (and only one)
    - adding rvpaoDoNotWantReturns in Options of all paragraph styles
    - allowing to paste only one line of plain text (see OnPaste)
  2. Protecting autocalculated text
    - the 1st paragraph style ("Read-Only") has rvpaoReadOnly
    - RichView allows to delete read-only paragraphs when they are parts
      of lager selection (for example, multicell selection)
      An event, allowing to avoid this problem, was added in version 1.6.11
      (OnCellEditing)
  3. Table cannot be deleted because it is added in read-only paragraph.
  4. EConvertError exception occurs if Income column contains non numeric data.
    If running in Delphi IDE, Delphi stops on exception. Just click OK and
    press F9 to continue.
  5. AcceptDragDropFormats is set to []
}
{------------------------------------------------------------------------------}

type
  TForm1 = class(TForm)
    RVStyle1: TRVStyle;
    Panel1: TPanel;
    Button1: TButton;
    Button2: TButton;
    srv: TSRichViewEdit;
    procedure FormCreate(Sender: TObject);
    procedure rveChange(Sender: TObject);
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
    procedure rvePaste(Sender: TCustomRichViewEdit;
      var DoDefault: Boolean);
  private
    { Private declarations }
    procedure Calculate;
    procedure OnCellEditing(Sender: TRVTableItemInfo;
                            Row, Col : Integer;
                            Automatic: Boolean;
                            var AllowEdit: Boolean);
  public
    { Public declarations }
    table: TRVTableItemInfo;
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.FormCreate(Sender: TObject);
var r,c: Integer;
begin
  table := TRVTableItemInfo.CreateEx(5,4, srv.RichViewEdit.RVData);
  table.BorderWidth := 1;
  table.CellBorderWidth := 1;
  table.CellBorderStyle := rvtbColor;
  table.CellBorderColor := clBtnFace;
  table.BorderStyle := rvtbColor;

  table.OnCellEditing := OnCellEditing;

  // Each cell initially contains one empty text item. Deleting it
  for r := 0 to table.Rows.Count-1 do
    for c := 0 to table.Rows[r].Count-1 do
      table.Cells[r,c].Clear;

  // First Row
  table.Cells[0,0].AddNL('Name',1,1);
  table.Cells[0,1].AddNL('Income',1,1);
  table.Cells[0,2].AddNL('Tax Rate',1,1);
  table.Cells[0,3].AddNL('Tax',1,1);

  // Last Row
  r := table.Rows.Count-1;
  table.Cells[r,0].AddNL('TOTAL:',1,1);
  table.Cells[r,1].AddNL('',1,1);
  table.Cells[r,2].AddNL('',1,1);
  table.Cells[r,3].AddNL('',1,1);

  // First Column
  table.Cells[1,0].AddNL('John Smith', 0, 0);
  table.Cells[2,0].AddNL('John Brown', 0, 0);
  table.Cells[3,0].AddNL('Phil Forest', 0, 0);

  // Second Column
  table.Cells[1,1].AddNL('2000', 0, 0);
  table.Cells[2,1].AddNL('2500', 0, 0);
  table.Cells[3,1].AddNL('1000', 0, 0);

  for r := 1 to table.Rows.Count-2 do begin
    table.Cells[r,2].Color := clSilver;
    table.Cells[r,3].Color := clSilver;
    table.Cells[r,2].AddNL('0.20',0,1);
    table.Cells[r,3].AddNL('',0,1);
  end;

  for c := 0 to table.Rows[0].Count-1 do begin
    table.Cells[0,c].Color := clSilver;
    table.Cells[table.Rows.Count-1,c].Color := clSilver;
  end;

  DecimalSeparator := '.';

  Calculate;

  srv.RichViewEdit.InsertItem('Spreadsheet', table);
  srv.RichViewEdit.ApplyParaStyle(1); // read-only style;

end;


procedure TForm1.Calculate;
var r: Integer;
    s: String;
    total, totaltax, val, valtax: Double;
    totalOK: Boolean;
begin
  // Last Column
  totalOK := True;
  total   := 0.0;
  totaltax := 0.0;
  for r := 1 to table.Rows.Count-2 do begin
    try
      // val <- income
      val := StrToFloat(table.Cells[r,1].GetRVData.GetItemText(0));
      // valtax <- income * tax rate
      valtax := val*StrToFloat(table.Cells[r,2].GetRVData.GetItemText(0));
      s := FloatToStr(valtax);
      total := total + val;
      totaltax := totaltax + valtax;
    except
      s := '?';
      totalOK := False;
    end;
    table.Cells[r,3].GetRVData.SetItemText(0,s);
  end;

  if totalOK then begin
    table.Cells[table.Rows.Count-1,3].GetRVData.SetItemText(0, FloatToStr(totaltax));
    table.Cells[table.Rows.Count-1,1].GetRVData.SetItemText(0, FloatToStr(total));
    end
  else begin
    table.Cells[table.Rows.Count-1,3].GetRVData.SetItemText(0, '?');
    table.Cells[table.Rows.Count-1,1].GetRVData.SetItemText(0, '?');
  end;
  table.Changed;
end;

// OnChange: recalculating
procedure TForm1.rveChange(Sender: TObject);
begin
  Calculate;
  srv.RichViewEdit.Reformat;
  if srv.RichViewEdit.InplaceEditor<>nil then
    srv.RichViewEdit.InplaceEditor.Invalidate;
  // Some ideas:
  // - you can use table.GetEditedCell to get a cell which was changed
end;

// Adding a new row
procedure TForm1.Button1Click(Sender: TObject);
var ItemNo, Data, r,c: Integer;
begin
  ItemNo := srv.RichViewEdit.GetItemNo(table);
  srv.RichViewEdit.BeginItemModify(ItemNo, Data);
  r := table.Rows.Count-1;
  table.InsertRows(r, 1, -1);

  for c := 1 to table.Rows[r].Count-1 do
    table.Cells[r,c].Clear;

  table.Cells[r,1].AddNL('0', 0,0);
  table.Cells[r,2].AddNL('0.20', 0,1);
  table.Cells[r,3].AddNL('', 0,1);
  table.Cells[r,2].Color := clSilver;
  table.Cells[r,3].Color := clSilver;

  srv.RichViewEdit.EndItemModify(ItemNo, Data);
  srv.RichViewEdit.Change;
end;

// Deleting a row with caret
procedure TForm1.Button2Click(Sender: TObject);
var ItemNo, Data, r,c: Integer;
begin
  if (table.GetEditedCell(r,c)<>nil) and (r<>0) and (r<>table.Rows.Count-1) then begin
    ItemNo := srv.RichViewEdit.GetItemNo(table);
    srv.RichViewEdit.BeginItemModify(ItemNo, Data);
    table.DeleteRows(r,1,False);
    srv.RichViewEdit.EndItemModify(ItemNo, Data);
    srv.RichViewEdit.Change;
    end
  else
    Beep;
end;

// OnPaste: allowing to paste only one line of text
procedure TForm1.rvePaste(Sender: TCustomRichViewEdit;
  var DoDefault: Boolean);
var s: String;
begin
  s := Clipboard.AsText;
  if (Pos(#13,s)=0) and (Pos(#10,s)=0) then
    srv.RichViewEdit.InsertText(s, False);
  DoDefault := False;
end;

procedure TForm1.OnCellEditing(Sender: TRVTableItemInfo; Row, Col: Integer;
  Automatic: Boolean; var AllowEdit: Boolean);
begin
  if Automatic then
    AllowEdit := (Row<>0) and (Row<>table.Rows.Count-1) and (Col<2);
end;


end.
