{==============================================================================}
{
  Mail merge application: part 2 - displaying processed template.

  How it works:
  - RVStyle2 has two predefined styles (the same as in the template editor;
    1th text style is reserved for fields).
  - RichView1 and RichView2 are linked to the same RVStyle (RVStyle2)
  - Template is loaded in RichView1 (styles can be added dynamically in RVStyle2)
  - RichView1 is scanned for fields. When field is found, it is deleted,
    and its value is inserted in its place:
    * field value is loaded in invisible RichView2 (styles can be added in RVStyle2)
    * contents of RichView2 is inserted in the proper place of RichView1.
}
{==============================================================================}

unit MainAppFrm;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms,
  Dialogs, RVScroll, RichView, RVStyle, DB, DBTables, CRVData, CRVFData,
  RVItem, RVTable;

type
  TForm1 = class(TForm)
    RichView1: TRichView;
    RichView2: TRichView;
    Table1: TTable;
    RVStyle2: TRVStyle;
    procedure FormCreate(Sender: TObject);
  private
    procedure ReplaceFields(RVData: TCustomRVFormattedData);
    { Private declarations }
  public
    { Public declarations }
    function LoadData(const Code: String): TMemoryStream;
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.FormCreate(Sender: TObject);
begin
  // Loading template with fields
  RichView1.LoadRVF(ExtractFilePath(Application.ExeName)+'Template.rvf');
  // RVData.InsertFromStream (we will call it later) does not support
  // style merging. So changing mode to style ignoring:
  RichView1.RVFParaStylesReadMode := rvf_sIgnore;
  RichView1.RVFTextStylesReadMode := rvf_sIgnore;
  // Replace styles with values
  ReplaceFields(RichView1.RVData);
  RichView1.Format;
end;

procedure TForm1.ReplaceFields(RVData: TCustomRVFormattedData);
var i,j: Integer;
    Stream: TMemoryStream;
    Dummy1: TColor;
    Dummy2, Dummy3: Pointer;
    ParaNo: Integer;
    BR, ContinuePara: Boolean;
    table: TRVTableItemInfo;
    r,c: Integer;
begin
  Dummy1 := clNone;
  Dummy2 := nil;
  Dummy3 := nil;
  i := RVData.ItemCount-1;
  while i>=0 do begin
    case RVData.GetItemStyle(i) of
      1: // the first text style is used for fields
        begin
          // storing parameters of deleted items
          ParaNo := RVData.GetItemPara(i);
          BR     := RVData.GetItem(i).BR;
          ContinuePara := RVData.GetItem(i).SameAsPrev;
          // loading field value in the stream
          Stream := LoadData(RVData.GetItemText(i));
          if Stream<>nil then begin
            // deleting the field code
            RVData.DeleteItems(i,1);
            // inserting the field value
            RVData.InsertRVFFromStream(Stream, i, Dummy1, Dummy2, Dummy3, False);
            // applying stored parameters to the inserted items
            if (i>0) and (RVData.GetItemStyle(i-1)=rvsListMarker) and
              (RVData.GetItemStyle(i)=rvsListMarker) then
              RVData.DeleteItems(i, 1);
            for j := i to RVData.ItemCount-1 do begin
              if (i=j) then begin
                if RVData.GetItem(j).GetBoolValue(rvbpFullWidth) then begin
                  if (i>0) and (RVData.GetItemStyle(i-1)=rvsListMarker) then begin
                    dec(i);
                    RVData.DeleteItems(i, 1);
                  end;
                  break;
                end;
                RVData.GetItem(j).SameAsPrev := ContinuePara;
                if BR then
                  RVData.GetItem(j).BR := True;
              end;
              if (j>i) and RVData.IsParaStart(j) then
                break;
              RVData.GetItem(j).ParaNo := ParaNo;
            end;
            Stream.Free;
          end;
        end;
      rvsTable:
        begin
          // recursive call for table cells
          table := TRVTableItemInfo(RVData.GetItem(i));
          for r := 0 to table.Rows.Count-1 do
            for c := 0 to table.Rows[r].Count-1 do
              if table.Cells[r,c]<>nil then
                ReplaceFields(table.Cells[r,c]);
        end;
    end;
    dec(i);
  end;
  RVData.Normalize;
end;

// Loading field code
function TForm1.LoadData(const Code: String): TMemoryStream;
begin
  Result := nil;
  if not Table1.Locate('Code', Code, []) then
    exit;
  Result := TMemoryStream.Create;
  TBlobField(Table1.FieldByName('Data')).SaveToStream(Result);
  Result.Position := 0;
  RichView2.Clear;
  RichView2.InsertRVFFromStream(Result, 0); // inserting will merge styles;
    // RichView1 and RichView2 have the same collections of styles
  Result.Clear;
  RichView2.SaveRVFToStream(Result, False);
  Result.Position := 0;
end;

end.
