
{*******************************************************}
{                                                       }
{       RichView                                        }
{       function LoadCSV                                }
{       loads/saves CSV and tab-separated files         }
{                                                       }
{       Copyright (c) Sergey Tkachenko                  }
{       svt@trichview.com                               }
{       http://www.trichview.com                        }
{                                                       }
{*******************************************************}


unit rvcsv;

{$I RV_Defs.inc}

interface
uses SysUtils, Classes, RichView, RVEdit,
     {$IFDEF RICHVIEWDEF2009}AnsiStrings,{$ENDIF}
     RVItem, RVTable, RVTypes, RVGetText, RVFuncs;

{
  LOADING CSV FILE

  Parameters:
    FileName - file to load.
    rv - richview or richviewedit where you plan to insert loaded table.
    Separator - character used to separate values.
      Most common possible values:
      ',' (comma)
      ';' (semicolon)
      ' ' (space)
      #9  (tab)
    UseQuotes - if True, double-quotes are used to allow separators inside
      text values (in the input file);
      double double-quotes are treated as double-quote character.
    TextStyleNo, ParaNo - styles of text and paragraph used to add text
      in cells.
  Return value:
    loaded table; nil in case of failure.
}

function LoadCSV(const FileName: String; rv: TCustomRichView;
  Separator: TRVAnsiChar; UseQuotes: Boolean;
  TextStyleNo, ParaNo: Integer): TRVTableItemInfo;

{
  SAVING CSV FILE (ANSI)

  SaveTableToCSVStreamA and SaveCurrentTableToCSVStreamA save to Stream.
  SaveTableToCSVFileA and SaveCurrentTableToCSVFileA save to file FileName.

  SaveTableToCSVStreamA and SaveTableToCSVFileA save the specified table.
  SaveCurrentTableToCSVStreamA and SaveCurrentTableToCSVFileA saves the current
  table in rve (and return False if there is no current table).
  Return value: saving successful?

  Parameters:
  Separator - character to store between columns (comma, semicolon, etc)
  CellLineBreakDelim - character to insert in place of line breaks inside cells
    (if a cell contains multiline text)
  EqualDelimCount specifies how to save Separator for merged cells.
    If it is True, the same number (table.ColCount-1) of semicolons will be inserted for each row.
    If it is False, semicolons will not be used for horizontally merged cells.
    See http://www.trichview.com/forums/viewtopic.php?t=581#8080 (contains
    example for another function having EqualDelimCount parameter)

}


function SaveTableToCSVStreamA(table: TRVTableItemInfo;
  Separator, CellLineBreakDelim: TRVAnsiChar;
  EqualDelimCount: Boolean;
  Stream: TStream): Boolean;
function SaveTableToCSVFileA(table: TRVTableItemInfo;
  Separator, CellLineBreakDelim: TRVAnsiChar;
  EqualDelimCount: Boolean;
  const FileName: String): Boolean;
function  SaveCurrentTableToCSVStreamA(rve: TCustomRichViewEdit;
  Separator, CellLineBreakDelim: TRVAnsiChar;
  EqualDelimCount: Boolean;
  Stream: TStream): Boolean;
function  SaveCurrentTableToCSVFileA(rve: TCustomRichViewEdit;
  Separator, CellLineBreakDelim: TRVAnsiChar;
  EqualDelimCount: Boolean;
  const FileName: String): Boolean;



implementation

function LoadCSV(const FileName: String; rv: TCustomRichView;
  Separator: TRVAnsiChar; UseQuotes: Boolean;
  TextStyleNo, ParaNo: Integer): TRVTableItemInfo;
var F: TextFile;
    s, line: TRVAnsiString;
    i, j, r, c: Integer;
    quote, prevquote,added: Boolean;
    {...............................................}
    procedure AddCell(table: TRVTableItemInfo);
    begin
      if table.Rows[0].Count<=c then
        table.InsertCols(table.Rows[0].Count, 1, -1);
      table.Cells[r,c].Clear;
      table.Cells[r,c].AddNLATag(line, TextStyleNo, ParaNo, 0);
      inc(c);
      line := '';
    end;
    {...............................................}
begin
  Result := nil;
  try
    AssignFile(F, FileName);
    Reset(F);
    try
      Result := TRVTableItemInfo.CreateEx(0,0, rv.RVData);
      r := 0;
      while not EOF(F) do begin
        Readln(F, s);
        if s='' then
          break;
        Result.InsertRows(Result.Rows.Count, 1, -1);
        c := 0;
        line := '';
        j := 1;
        added := False;
        prevquote := False;
        quote := False;
        for i := 1 to Length(s) do begin
          added := False;
          case s[i] of
            '"':
              begin
                if not UseQuotes then
                  continue;
                line := line + Copy(s, j, i-j);
                j := i+1;
                if prevquote then
                  line := line+'"';
                prevquote := not prevquote;
              end;
            else
              begin
                if prevquote then begin
                  prevquote := False;
                  quote := not quote;
                end;
                if not quote and (Separator=s[i]) then begin
                  line := line + Copy(s, j, i-j);
                  j := i+1;
                  AddCell(Result);
                end;
              end;
          end;
        end;
        if not added then begin
          line := line + Copy(s, j, Length(s)+1-j);
          AddCell(Result);
        end;
        inc(r);
      end;
    finally
      if (Result.Rows.Count=0) or (Result.Rows[0].Count=0) then begin
        Result.Free;
        Result := nil;
      end;
      CloseFile(F);
    end;
  except
    Result.Free;
    Result := nil;
  end;
end;
{------------------------------------------------------------------------------}
function SaveTableToCSVStreamA(table: TRVTableItemInfo;
  Separator, CellLineBreakDelim: TRVAnsiChar;
  EqualDelimCount: Boolean;
  Stream: TStream): Boolean;
var
    r,c, mr, mc: Integer;
    s: TRVAnsiString;
    {................................................}
    procedure WriteString(const s: TRVAnsiString);
    begin
      Stream.WriteBuffer(PRVAnsiChar(s)^, Length(s));
    end;
    {................................................}
begin
  Result := False;
  try
    for r := 0 to table.RowCount-1 do begin
      for c := 0 to table.ColCount-1 do begin
        if (c<>0) then begin
          if EqualDelimCount then
            WriteString(Separator)
          else begin
            table.Rows.GetMainCell(r, c, mr, mc);
            if mc=c then
              WriteString(Separator);
          end;
        end;
        if table.Cells[r,c]<>nil then begin
          s := RVGetText.GetRVDataText(table.Cells[r,c].GetRVData);
          RV_ReplaceStrA(s, #13#10, CellLineBreakDelim);
          s := {$IFDEF RICHVIEWDEF2009}AnsiStrings.{$ENDIF}AnsiQuotedStr(s, '"');
          WriteString(s);
        end;
      end;
      if r<table.RowCount-1 then
        WriteString(#13#10);
    end;
    Result := True;
  except
  end;
end;
{------------------------------------------------------------------------------}
function SaveTableToCSVFileA(table: TRVTableItemInfo;
  Separator, CellLineBreakDelim: TRVAnsiChar;
  EqualDelimCount: Boolean;
  const FileName: String): Boolean;
var Stream: TFileStream;
begin
  Result := False;
  try
    Stream := TFileStream.Create(FileName, fmCreate);
    try
      Result := SaveTableToCSVStreamA(table, Separator, CellLineBreakDelim,
        EqualDelimCount, Stream);
    finally
      Stream.Free;
    end;
  except
  end;
end;
{------------------------------------------------------------------------------}
function SaveCurrentTableToCSVStreamA(rve: TCustomRichViewEdit;
  Separator, CellLineBreakDelim: TRVAnsiChar;
  EqualDelimCount: Boolean;
  Stream: TStream): Boolean;
var table: TRVTableItemInfo;
begin
  Result := False;
  if not rve.GetCurrentItemEx(TRVTableItemInfo, rve,
    TCustomRVItemInfo(table)) then
    exit;
  Result := SaveTableToCSVStreamA(table, Separator, CellLineBreakDelim,
    EqualDelimCount, Stream);
end;
{------------------------------------------------------------------------------}
function SaveCurrentTableToCSVFileA(rve: TCustomRichViewEdit;
  Separator, CellLineBreakDelim: TRVAnsiChar;
  EqualDelimCount: Boolean;
  const FileName: String): Boolean;
var table: TRVTableItemInfo;
begin
  Result := False;
  if not rve.GetCurrentItemEx(TRVTableItemInfo, rve,
    TCustomRVItemInfo(table)) then
    exit;
  Result := SaveTableToCSVFileA(table, Separator, CellLineBreakDelim,
    EqualDelimCount, FileName);
end;


end.
