{
  This demo draws rectangles around the item below the mouse pointer.
  There are two options:
  - drawing rectangle around the item part (when a text item occupies
    several lines, each line is a part of this item)
  - drawing rectangle around the whole item
}


unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls, ExtCtrls, SclRView, CRVFData;

type
  TForm1 = class(TForm)
    SRichViewEdit1: TSRichViewEdit;
    Panel1: TPanel;
    RadioButton1: TRadioButton;
    RadioButton2: TRadioButton;
    procedure FormCreate(Sender: TObject);
    procedure PaintPage1(Sender: TObject; PageNo: Integer;
      PageRect, R: TRect; Canvas: TCanvas; Prepaint, Printing: Boolean);
    procedure PaintPage2(Sender: TObject; PageNo: Integer;
      PageRect, R: TRect; Canvas: TCanvas; Prepaint, Printing: Boolean);
    procedure MouseMove1(Sender: TObject; Shift: TShiftState;
      X, Y: Integer);
    procedure MouseMove2(Sender: TObject; Shift: TShiftState;
      X, Y: Integer);
    procedure RadioButton1Click(Sender: TObject);
    procedure RadioButton2Click(Sender: TObject);
  private
    function CalcRect1(PageNo, X, Y: Integer; ClientCoords: Boolean;
      var R: TRect): Boolean;
    function CalcRect2(PageNo, X, Y: Integer; ClientCoords: Boolean;
      var R: TRect): Boolean;
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;
  OldRepaintRect : TRect;

implementation

{$R *.DFM}

procedure TForm1.FormCreate(Sender: TObject);
begin
  OldRepaintRect := Bounds(0, 0, 0, 0);
end;

{------------------------------------------------------------------------------}
// Calculating rectangle around the item at (X,Y), option 1:
//   rectangle around the item part.
// If ClientCoords=False:
//  the rectangle is calculated in 100% zooming, relative to the PageNo-th page
// If ClientCoords=True:
//  the rectangle is calculated in client coordinates, PageNo is ignored; rectangle
//  is expanded by 5 pixels
function TForm1.CalcRect1(PageNo, X, Y: Integer; ClientCoords: Boolean; var R: TRect): Boolean;
var
  ItemPart, FirstPageNo, LastPageNo: Integer;
  ItemPartRect, PageRect: TRect;
  RVData: TCustomRVFormattedData;
  ItemNo, OffsetInItem: Integer;
begin
  Result := False;
  // finding the item at X,Y
  If not SRichViewEdit1.GetItemAt(X, Y, RVData, ItemNo, OffsetInItem, True) then begin
    // no item there
    R := Rect(0,0,0,0);
    Result := True;
    exit;
  end;
  ItemPart := RVData.GetItemPart(ItemNo, OffsetInItem);
  // receiving the position of the item part containing caret
  SRichViewEdit1.GetItemBounds100(RVData, ItemNo, ItemPartRect,
    FirstPageNo, LastPageNo, ItemPart);
  if ClientCoords then begin
    // expanding rectangle by 5 pixels
    InflateRect(ItemPartRect, 5, 5);
    // converting from { zoom=100%, relative to page } to { current zoom, client coordinates }
    PageRect := SRichViewEdit1.GetPageClientRect(FirstPageNo);
    ItemPartRect := SRichViewEdit1.ZoomRectIn(ItemPartRect, SRichViewEdit1.ViewProperty.ZoomPercent * 0.01);
    OffsetRect(ItemPartRect, PageRect.Left, PageRect.Top);
    end
  else begin
    // if not on this page, exiting
    if FirstPageNo<>PageNo then
      exit;
  end;
  R := ItemPartRect;
  Result := True;
end;
{------------------------------------------------------------------------------}
// Calculating rectangle around the item at (X,Y), option 2:
//   rectangle around the item
// If ClientCoords=False:
//  the rectangle is calculated in 100% zooming, relative to the PageNo-th page
// If ClientCoords=True:
//  the rectangle is calculated in client coordinates, PageNo is ignored; rectangle
//  is expanded by 5 pixels
function TForm1.CalcRect2(PageNo, X, Y: Integer; ClientCoords: Boolean; var R: TRect): Boolean;
var
  ItemPart : Integer;
  FirstPageNo, LastPageNo : Integer;
  ItemPartRect, PageRect : TRect;
  RVData: TCustomRVFormattedData;
  ItemNo, OffsetInItem: Integer;
begin
  // finding the item at X,Y  
  If not SRichViewEdit1.GetItemAt(X, Y, RVData, ItemNo, OffsetInItem, True) then begin
    // no item there
    R := Rect(0,0,0,0);
    Result := True;
    exit;
  end;
  // Calculating the rectangle around this item
  ItemPart := 0;
  R := Bounds(0, 0, 0, 0);
  while SRichViewEdit1.GetItemBounds100(RVData, ItemNo,
    ItemPartRect, FirstPageNo, LastPageNo, ItemPart) do begin
    if ClientCoords then begin
      // expanding rectangle by 5 pixels
      InflateRect(ItemPartRect, 5, 5);
      // converting from { zoom=100%, relative to page } to { current zoom, client coordinates }
      PageRect := SRichViewEdit1.GetPageClientRect(FirstPageNo);
      ItemPartRect := SRichViewEdit1.ZoomRectIn(ItemPartRect, SRichViewEdit1.ViewProperty.ZoomPercent * 0.01);
      OffsetRect(ItemPartRect, PageRect.Left, PageRect.Top);
      // combining
      UnionRect(R, R, ItemPartRect);
      end
    else if (FirstPageNo = PageNo) then
      // combining, if on this page
      UnionRect(R, R, ItemPartRect);
    inc(ItemPart);
  end;
  Result := True;  
end;
{------------------------------------------------------------------------------}
// OnPaintPage, option 1: rectangle around the item part
procedure TForm1.PaintPage1(Sender: TObject; PageNo: Integer;
  PageRect, R: TRect; Canvas: TCanvas; Prepaint, Printing: Boolean);
var ItemRect: TRect;
    p : TPoint;
begin
  // receiving coordinates in zoom=100%, relative to page
  p := SRichViewEdit1.ScreenToClient(Mouse.CursorPos);
  if not CalcRect1(PageNo, p.X, p.Y, False, ItemRect) then
    exit;
  // converting to "client" coordinates in zoom=100%
  OffsetRect(ItemRect, PageRect.Left, PageRect.Top);
  // drawing
  Canvas.Pen.Color := clRed;
  Canvas.Brush.Style := bsClear;
  Canvas.Rectangle(ItemRect);
end;
{------------------------------------------------------------------------------}
// OnPaintPage, option 1: rectangle around the item
procedure TForm1.PaintPage2(Sender: TObject; PageNo: Integer;
  PageRect, R: TRect; Canvas: TCanvas; Prepaint, Printing: Boolean);
var ItemRect: TRect;
    p : TPoint;
begin
  // receiving coordinates in zoom=100%, relative to page
  p := SRichViewEdit1.ScreenToClient(Mouse.CursorPos);
  if not CalcRect2(PageNo, p.X, p.Y, False, ItemRect) then
    exit;
  // converting to "client" coordinates in zoom=100%
  OffsetRect(ItemRect, PageRect.Left, PageRect.Top);
  // drawing
  Canvas.Pen.Color := clRed;
  Canvas.Brush.Style := bsClear;
  Canvas.Rectangle(ItemRect);
end;
{------------------------------------------------------------------------------}
// OnMouseMove for option 1
procedure TForm1.MouseMove1(Sender: TObject;
  Shift: TShiftState; X, Y: Integer);
var ARect, ItemRect : TRect;
begin
  // receiving client coordinates of the rectangle, expanded by 5 pixels
  if not CalcRect1(0, X, Y, True, ItemRect) then
    exit;
  // redrawing, if necessary
  if not EqualRect(ItemRect, OldRepaintRect) then begin
    UnionRect(ARect, ItemRect, OldRepaintRect);
    SRichViewEdit1.UpdateBufferAt(ARect);
    OldRepaintRect := ItemRect;
  end;
end;
{------------------------------------------------------------------------------}
// OnMouseMove for option 2
procedure TForm1.MouseMove2(Sender: TObject;
  Shift: TShiftState; X, Y: Integer);
var ARect, ItemRect : TRect;
begin
  // receiving client coordinates of the rectangle, expanded by 5 pixels
  if not CalcRect2(0, X, Y, True, ItemRect) then
    exit;
  // redrawing, if necessary
  if not EqualRect(ItemRect, OldRepaintRect) then begin
    UnionRect(ARect, ItemRect, OldRepaintRect);
    SRichViewEdit1.UpdateBufferAt(ARect);
    OldRepaintRect := ItemRect;
  end;
end;
{------------------------------------------------------------------------------}
// Option 1
procedure TForm1.RadioButton1Click(Sender: TObject);
begin
  SRichViewEdit1.OnPaintPage := PaintPage1;
  SRichViewEdit1.OnMouseMove := MouseMove1;
  SRichViewEdit1.UpdateBuffer;
end;
{------------------------------------------------------------------------------}
// Option 2
procedure TForm1.RadioButton2Click(Sender: TObject);
begin
  SRichViewEdit1.OnPaintPage := PaintPage2;
  SRichViewEdit1.OnMouseMove := MouseMove2;
  SRichViewEdit1.UpdateBuffer;
end;

end.
