//---------------------------------------------------------------------------
/*
  Working with RVF and RTF files containing shared images.
  This demo stores them in the special subdirectory, but you can store them
  in a database, etc.
  How to save pictures as references in RVF:
    rvfoSavePicturesBody is EXCLUDED from RichViewEdit1.RVFOptions.
  How to save pictures as references in RTF:
    see RichViewEdit1SaveItemToFile.
  How to load pictures when reading such RVF files:
    see RichViewEdit1RVFPictureNeeded.
  How to load pictures when reading such RTF files:
    see RichViewEdit1ImportPicture.

  Paths to image file names are stored in rvespImageFileName properties.
  Normally, full paths to images are stored there.
  In this demo, path is stored relative to the application directory.
  Saved RTF and HTML files will be opened normally in other applications
  only if they are stored in the application directory.
*/
//---------------------------------------------------------------------------
/*
  This is the second version of this demo.
  Changes:
  - RTF support;
  - HTML saving;
  - storing file names in rvespImageFileName property instead of item names;
  - storing paths relative to the application path instead of storing just
    file names (allows external applications to open RTF and HTML files
    correctly, if they are saved in the application path)
*/
//---------------------------------------------------------------------------
#include <vcl.h>
#include <stdlib.h>
#pragma hdrstop

#include "Unit1.h"
#include "CreateGraphics.hpp"
//---------------------------------------------------------------------------
#pragma link "RVEdit"
#pragma link "RichView"
#pragma link "RVScroll"
#pragma link "RVStyle"
#pragma resource "*.dfm"
TForm1 *Form1;
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
	: TForm(Owner)
{
}
//---------------------------------------------------------------------------
void __fastcall TForm1::FormCreate(TObject *Sender)
{
  BasePath = ExtractFilePath(Application->ExeName);
  // Removing "Debug\\" from BasePath
  BasePath = BasePath.SubString(1, BasePath.Length()-1);
  wchar_t* start = BasePath.w_str();
  wchar_t* pos = StrRScan(start, '\\');
  BasePath = BasePath.SubString(1, pos-start+1);


  Randomize();
  OpenDialog2->InitialDir = BasePath;
  SaveDialog1->InitialDir = BasePath;
  RichViewEdit1->LoadRVF(BasePath+"demo.rvf");
  RichViewEdit1->Format();
}
//---------------------------------------------------------------------------
// RichViewEdit1->OnRVFPictureNeeded
// This event occurs when reading RVF files.
// Image file name is stored in the Name parameter.
// This event load this image from the Images subdirectory.
void __fastcall TForm1::RichViewEdit1RVFPictureNeeded(TCustomRichView *Sender,
	UnicodeString Name, int Tag, TGraphic *&gr)
{
  // First time, this event is called with item name (empty)
  // Second time, this event is called with rvespImageFileName property
  if (Name == "")
    return;
  Name = BasePath+Name; // path is relative to the application path
  TPicture* pic = new TPicture;
  try
  {
    try
    {
      pic->LoadFromFile(Name);
    }
    catch(...)
    {
      pic->Assign(RVStyle1->InvalidPicture);
    }
    gr = GetGraphicCopy(pic->Graphic); // from CreateGraphics unit
    gr->Assign(pic->Graphic);
  }
  catch(...)
  {
  }
  delete pic;
}
//---------------------------------------------------------------------------
// RichViewEdit1.OnImportPicture
// This event occurs when loading RTF files with external pictures
void __fastcall TForm1::RichViewEdit1ImportPicture(TCustomRichView *Sender,
      const UnicodeString Location, int Width, int Height, TGraphic *&Graphic)
{
  UnicodeString FileName = ExtractRelativePath(BasePath, Location);
  RichViewEdit1RVFPictureNeeded(Sender, FileName, 0, Graphic);
}
//---------------------------------------------------------------------------
// RichViewEdit1.OnSaveItemToFile
// Modifying how pictures are saved in RTF: saving as external pictures
void __fastcall TForm1::RichViewEdit1SaveItemToFile(
      TCustomRichView *Sender, const UnicodeString Path,
      TCustomRVData *RVData, int ItemNo, TRVSaveFormat SaveFormat,
      bool Unicode, TRVRawByteString &OutStr, bool &DoDefault)
{
 if (SaveFormat==rvsfRTF &&
    (RVData->GetItemStyle(ItemNo)==rvsPicture ||
     RVData->GetItemStyle(ItemNo)==rvsHotPicture))
 {
    UnicodeString FileName;
    RVData->GetItemExtraStrProperty(ItemNo, rvespImageFileName, FileName);
    OutStr = Ansistrings::Format("{\\field{\\*\\fldinst INCLUDEPICTURE \"%s\" \\\\d }}",
      ARRAYOFCONST((
        RVMakeRTFFileNameStr(FileName,
          RichViewEdit1->Style->DefCodePage,
          RichViewEdit1->RTFOptions.Contains(rvrtfDuplicateUnicode)).c_str())));
    DoDefault = false;
 }
}
//---------------------------------------------------------------------------
// Inserting image.
// If this image is not from the Images subdirectory, copying it there
// (under the unique file name)
// Image file name is written in rvespImageFileName
//  (relative to the application path)
void __fastcall TForm1::Button1Click(TObject *Sender)
{
  if (OpenDialog1->Execute())
    try
    {
      TPicture* pic = new TPicture;
      try
      {
        pic->LoadFromFile(OpenDialog1->FileName);
        TGraphic* gr = GetGraphicCopy(pic->Graphic); // from CreateGraphics unit
        gr->Assign(pic->Graphic);
        UnicodeString ImageName = ExtractRelativePath(BasePath,
          CopyImageToTheImagesDir(OpenDialog1->FileName, NULL));
        RichViewEdit1->TopLevelEditor->BeginUndoGroup(rvutInsert);
        RichViewEdit1->TopLevelEditor->SetUndoGroupMode(true);
        try
        {
          if (RichViewEdit1->InsertPicture("", gr, rvvaBaseline))
            RichViewEdit1->SetCurrentItemExtraStrProperty(rvespImageFileName, ImageName, true);
        }
        __finally
        {
          RichViewEdit1->TopLevelEditor->SetUndoGroupMode(false);
        }
      }
      catch(...)
      {
      }
      delete pic;
    }
    catch(...)
    {
      Application->MessageBox(L"Image loading error", L"Error", 0);
    }
}
//---------------------------------------------------------------------------
// Copying the file ImageFileName to the images subdirectory (if gr==NULL)
// or saving gr in the images subdirectory.
// Assigning an unique file name.
// Both ImageFileName and returned value are fully qualified paths.
UnicodeString TForm1::CopyImageToTheImagesDir(UnicodeString ImageFileName, TGraphic* gr)
{
  UnicodeString ImagesDir, NewImageFileName, ImageExt;
  int RandomValue;

  ImageFileName = AnsiLowerCase(ImageFileName);
  ImagesDir = AnsiLowerCase(BasePath+"Images\\");
  if (ImageFileName.Pos(ImagesDir)!=1)
  {
    NewImageFileName = ImagesDir+ExtractFileName(ImageFileName);
    if (FileExists(NewImageFileName))
    {
      ImageExt = ExtractFileExt(NewImageFileName);
      NewImageFileName = NewImageFileName.SubString(1, NewImageFileName.Length()-ImageExt.Length());
      RandomValue = random(MaxInt);
      while (FileExists(NewImageFileName+IntToStr(RandomValue)+ImageExt))
        RandomValue++;
      NewImageFileName = NewImageFileName+IntToStr(RandomValue)+ImageExt;
    }
    if (!gr)
      CopyFile(ImageFileName.w_str(), NewImageFileName.w_str(), false);
    else
      gr->SaveToFile(NewImageFileName);
    return NewImageFileName;
  }
  else
    return ImageFileName;
}
//---------------------------------------------------------------------------
// Saving all images that not in the images directory
// Such images can appear when loading or pasting files with images
void TForm1::SaveAllUnknownImages(TCustomRVData* RVData)
{
  for (int i=0; i<RVData->ItemCount; i++)
    switch (RVData->GetItemStyle(i))
    {
      case rvsPicture:
      case rvsHotPicture:
      {
        UnicodeString ImageFileName;
        RVData->GetItemExtraStrProperty(i, rvespImageFileName, ImageFileName);
        ImageFileName = BasePath+ImageFileName;
        if (!FileExists(ImageFileName))
        {
          TRVVAlign VAlign;
          TGraphic* gr;
          UnicodeString Ext;
          TRVAnsiString s;
          RVData->GetPictureInfo(i, s, gr, VAlign, Tag);
          Ext = GetGraphicExtension(gr); // from CreateGraphics unit
          // using relative path
          ImageFileName = ExtractRelativePath(BasePath,
            CopyImageToTheImagesDir("Image."+Ext, gr));
          RVData->SetItemExtraStrProperty(i, rvespImageFileName, ImageFileName);
        }
        break;
      }
      case rvsTable:
      {
        TRVTableItemInfo* table = (TRVTableItemInfo*)(RVData->GetItem(i));
        for (int r=0; r<table->Rows->Count; r++)
          for (int c=0; c<table->Rows->Items[r]->Count; c++)
            if (table->Cells[r][c])
              SaveAllUnknownImages(table->Cells[r][c]->GetRVData());
        break;
      }
    }
}
//---------------------------------------------------------------------------
// After loading from RTF, full file names are assigned to rvespImageFileName
// properties. This procedure is called to convert them to relative paths.
void TForm1::ConvertAllPathsToRelativePaths(TCustomRVData* RVData)
{
  for (int i=0; i<RVData->ItemCount; i++)
    switch (RVData->GetItemStyle(i))
    {
      case rvsPicture:
      case rvsHotPicture:
      {
        UnicodeString ImageFileName;
        RVData->GetItemExtraStrProperty(i, rvespImageFileName, ImageFileName);
        ImageFileName = ExtractRelativePath(BasePath, ImageFileName);
        RVData->SetItemExtraStrProperty(i, rvespImageFileName, ImageFileName);
        break;
      }
      case rvsTable:
      {
        TRVTableItemInfo* table = (TRVTableItemInfo*)(RVData->GetItem(i));
        for (int r=0; r<table->Rows->Count; r++)
          for (int c=0; c<table->Rows->Items[r]->Count; c++)
            if (table->Cells[r][c])
              ConvertAllPathsToRelativePaths(table->Cells[r][c]->GetRVData());
        break;
      }
    }
}
//---------------------------------------------------------------------------
// Before copying to the clipboard
void __fastcall TForm1::RichViewEdit1Copy(TObject *Sender)
{
  SaveAllUnknownImages(RichViewEdit1->RVData);
}
//---------------------------------------------------------------------------
// Loading doc
void __fastcall TForm1::Button3Click(TObject *Sender)
{
  if (OpenDialog2->Execute())
  {
    RichViewEdit1->Clear();
    bool r = false;
    switch (OpenDialog2->FilterIndex)
    {
      case 1:
        r = RichViewEdit1->LoadRVF(OpenDialog2->FileName);
        break;
      case 2:
        r = RichViewEdit1->LoadRTF(OpenDialog2->FileName);
        ConvertAllPathsToRelativePaths(RichViewEdit1->RVData);
        break;
    }
    RichViewEdit1->Format();
    if (!r)
      Application->MessageBox(L"Document loading error", L"Error", 0);
  }
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Button2Click(TObject *Sender)
{
  if (SaveDialog1->Execute())
  {
    SaveAllUnknownImages(RichViewEdit1->RVData);
    bool r = false;
    switch (SaveDialog1->FilterIndex)
    {
      case 1:
        r = RichViewEdit1->SaveRVF(SaveDialog1->FileName, false);
        break;
      case 2:
        r = RichViewEdit1->SaveRTF(SaveDialog1->FileName, false);
        break;
      case 3:
        TRVSaveOptions SaveOptions = TRVSaveOptions();
        SaveOptions << rvsoUseCheckpointsNames;
        SaveOptions << rvsoUseItemImageFileNames;
        r = RichViewEdit1->SaveHTMLEx(SaveDialog1->FileName, "Shared Image Demo",
        "img", "", "", "", SaveOptions);
    }
    if (!r)
      Application->MessageBox(L"Document saving error", L"Error", 0);
  }
}
//---------------------------------------------------------------------------

