lazarus-ccr/components/richview/richview.pas
2014-07-19 03:16:50 +00:00

2149 lines
70 KiB
ObjectPascal

unit RichView;
{$mode Delphi}
interface
{$I RV_Defs.inc}
uses
{$IFDEF FPC}
RVLazIntf, LCLType, LCLIntf,
{$ELSE}
Windows, Messages,
{$ENDIF}
SysUtils, Classes, Graphics, Controls, Forms,
RVStyle, RVScroll, ClipBrd,
{$IFDEF RICHVIEWDEF4}
ImgList,
{$ENDIF}
ExtCtrls;
{------------------------------------------------------------------}
const
rvsBreak = -1;
rvsCheckPoint = -2;
rvsPicture = -3;
rvsHotSpot = -4;
rvsComponent = -5;
rvsBullet = -6;
type
TCustomRichView = class;
TRVSaveFormat = (rvsfText,
rvsfHTML,
rvsfRTF, //<---not yet implemented
rvsfRVF //<---not yet implemented
);
TRVSaveOption = (rvsoOverrideImages);
TRVSaveOptions = set of TRVSaveOption;
{------------------------------------------------------------------}
TDrawLineInfo = class
Left, Top, Width, Height: Integer;
LineNo, Offs: Integer;
FromNewLine: Boolean;
end;
{------------------------------------------------------------------}
TLineInfo = class
StyleNo: Integer;
SameAsPrev: Boolean;
Center: Boolean;
imgNo: Integer; { for rvsJump# used as jump id }
gr: TPersistent;
DataPtr: Pointer;
end;
{------------------------------------------------------------------}
TCPInfo = class
public
Y, LineNo: Integer;
end;
{------------------------------------------------------------------}
TJumpInfo = class
public
l,t,w,h: Integer;
id, idx: Integer;
end;
{------------------------------------------------------------------}
TJumpEvent = procedure (Sender: TObject; id: Integer) of object;
TRVMouseMoveEvent = procedure (Sender: TObject; id: Integer) of object;
TRVSaveComponentToFileEvent = procedure (Sender: TCustomRichView; Path: String; SaveMe: TPersistent; SaveFormat: TRVSaveFormat; var OutStr:String) of object;
TRVURLNeededEvent = procedure (Sender: TCustomRichView; id: Integer; var url:String) of object;
TRVDblClickEvent = procedure (Sender: TCustomRichView; ClickedWord: String; Style: Integer) of object;
TRVRightClickEvent = procedure (Sender: TCustomRichView; ClickedWord: String; Style, X, Y: Integer) of object;
{------------------------------------------------------------------}
TBackgroundStyle = (bsNoBitmap, bsStretched, bsTiled, bsTiledAndScrolled);
{------------------------------------------------------------------}
TRVDisplayOption = (rvdoImages, rvdoComponents, rvdoBullets);
TRVDisplayOptions = set of TRVDisplayOption;
{------------------------------------------------------------------}
TScreenAndDevice = record
ppixScreen, ppiyScreen, ppixDevice, ppiyDevice: Integer;
LeftMargin: Integer;
end;
{------------------------------------------------------------------}
TRVInteger2 = class
public
val: Integer;
end;
{------------------------------------------------------------------}
{ TCustomRichView }
TCustomRichView = class(TRVScroller)
private
{ Private declarations }
ScrollDelta: Integer;
ScrollTimer: TTimer;
FAllowSelection, FSingleClick: Boolean;
FDelimiters: String;
DrawHover, Selection: Boolean;
FOnJump: TJumpEvent;
FOnRVMouseMove: TRVMouseMoveEvent;
FOnSaveComponentToFile: TRVSaveComponentToFileEvent;
FOnURLNeeded: TRVURLNeededEvent;
FOnRVDblClick: TRVDblClickEvent;
FOnRVRightClick: TRVRightClickEvent;
FOnSelect, FOnResized: TNotifyEvent;
FFirstJumpNo, FMaxTextWidth, FMinTextWidth, FLeftMargin, FRightMargin: Integer;
FBackBitmap: TBitmap;
FBackgroundStyle: TBackgroundStyle;
OldWidth, OldHeight: Integer;
FSelStartNo, FSelEndNo, FSelStartOffs, FSelEndOffs: Integer;
procedure InvalidateJumpRect(no: Integer);
procedure WMSize(var Message: TWMSize); message WM_SIZE;
procedure WMEraseBkgnd(var Message: TWMEraseBkgnd); message WM_ERASEBKGND;
function FindItemAtPos(X,Y: Integer): Integer;
procedure FindItemForSel(X,Y: Integer; var No, Offs: Integer);
function GetLineCount: Integer;
procedure CMMouseLeave(var Message: TMessage); message CM_MOUSELEAVE;
procedure GetSelBounds(var StartNo, EndNo, StartOffs, EndOffs: Integer);
procedure StoreSelBounds(var StartNo, EndNo, StartOffs, EndOffs: Integer);
procedure RestoreSelBounds(StartNo, EndNo, StartOffs, EndOffs: Integer);
protected
{ Protected declarations }
drawlines:TStringList;
checkpoints: TStringList;
jumps: TStringList;
FStyle: TRVStyle;
nJmps: Integer;
skipformatting: Boolean;
TextWidth, TextHeight: Integer;
LastJumpMovedAbove, LastLineFormatted: Integer;
LastJumpDowned, XClicked, YClicked, XMouse, YMouse: Integer;
imgSavePrefix: String;
imgSaveNo: Integer;
SaveOptions: TRVSaveOptions;
ShareContents: Boolean;
procedure Notification(AComponent: TComponent; Operation: TOperation);override;
procedure Click; override;
procedure MouseMove(Shift: TShiftState; X, Y: Integer); override;
procedure MouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Integer);override;
procedure MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Integer);override;
procedure DblClick; override;
procedure KeyDown(var Key: Word; Shift: TShiftState); override;
procedure FormatLine(no: Integer; var x,baseline,prevdesc,prevabove:Integer; Canvas: TCanvas;
var sad: TScreenAndDevice);
procedure AdjustJumpsCoords;
procedure AdjustChildrenCoords;
procedure ClearTemporal;
function GetFirstVisible(TopLine: Integer): Integer;
function GetFirstLineVisible: Integer;
function GetLastLineVisible: Integer;
function GetDrawLineNo(BoundLine: Integer; Option: Integer): Integer;
procedure Format_(OnlyResized:Boolean; depth: Integer; Canvas: TCanvas; OnlyTail: Boolean);
procedure SetBackBitmap(Value: TBitmap);
procedure DrawBack(DC: HDC; Rect: TRect; Width,Height:Integer);
procedure SetBackgroundStyle(Value: TBackgroundStyle);
procedure SetVSmallStep(Value: Integer);
function GetNextFileName(Path: String): String; virtual;
procedure ShareLinesFrom(Source: TCustomRichView);
function FindClickedWord(var clickedword: String; var StyleNo: Integer): Boolean;
procedure OnScrollTimer(Sender: TObject);
procedure Loaded; override;
protected // to be published properties
function GetCredits: string; virtual;
{ Published declarations }
//property PopupMenu;
//property OnClick;
//property OnKeyDown;
//property OnKeyUp;
//property OnKeyPress;
property FirstJumpNo: Integer read FFirstJumpNo write FFirstJumpNo;
property OnJump: TJumpEvent read FOnJump write FOnJump;
property OnRVMouseMove: TRVMouseMoveEvent read FOnRVMouseMove write FOnRVMouseMove;
property OnSaveComponentToFile: TRVSaveComponentToFileEvent read FOnSaveComponentToFile write FOnSaveComponentToFile;
property OnURLNeeded: TRVURLNeededEvent read FOnURLNeeded write FOnURLNeeded;
property OnRVDblClick: TRVDblClickEvent read FOnRVDblClick write FOnRVDblClick;
property OnRVRightClick: TRVRightClickEvent read FOnRVRightClick write FOnRVRightClick;
property OnSelect: TNotifyEvent read FOnSelect write FOnSelect;
property OnResized: TNotifyEvent read FOnResized write FOnResized;
property Style: TRVStyle read FStyle write FStyle;
property MaxTextWidth:Integer read FMaxTextWidth write FMaxTextWidth;
property MinTextWidth:Integer read FMinTextWidth write FMinTextWidth;
property LeftMargin: Integer read FLeftMargin write FLeftMargin;
property RightMargin: Integer read FRightMargin write FRightMargin;
property BackgroundBitmap: TBitmap read FBackBitmap write SetBackBitmap;
property BackgroundStyle: TBackgroundStyle read FBackgroundStyle write SetBackgroundStyle;
property Delimiters: String read FDelimiters write FDelimiters;
property AllowSelection: Boolean read FAllowSelection write FAllowSelection;
property SingleClick: Boolean read FSingleClick write FSingleClick;
public
{ Public declarations }
lines:TStringList;
DisplayOptions: TRVDisplayOptions;
FClientTextWidth: Boolean;
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
procedure Paint; override;
procedure AddFromNewLine(s: String;StyleNo:Integer);
procedure Add(s: String;StyleNo:Integer);
procedure AddCenterLine(s: String;StyleNo:Integer);
procedure AddText(s: String;StyleNo:Integer);
procedure AddTextFromNewLine(s: String;StyleNo:Integer);
procedure AddBreak;
function AddCheckPoint: Integer; { returns cp # }
function AddNamedCheckPoint(CpName: String): Integer; { returns cp # }
function GetCheckPointY(no: Integer): Integer;
function GetJumpPointY(no: Integer): Integer;
procedure AddPicture(gr: TGraphic);
procedure AddHotSpot(imgNo: Integer; lst: TImageList; fromnewline: Boolean);
procedure AddBullet (imgNo: Integer; lst: TImageList; fromnewline: Boolean);
procedure AddControl(ctrl: TControl; center: Boolean);
function GetMaxPictureWidth: Integer;
procedure Clear;
procedure Format;
procedure FormatTail;
procedure AppendFrom(Source: TCustomRichView);
function GetLastCP: Integer;
property VSmallStep: Integer read SmallStep write SetVSmallStep;
function SaveHTML(FileName, Title, ImagesPrefix: String; Options: TRVSaveOptions):Boolean;
function SaveText(FileName: String; LineWidth: Integer):Boolean;
procedure DeleteSection(CpName: String);
procedure DeleteLines(FirstLine, Count: Integer);
//use this only inside OnSaveComponentToFile event handler:
function SavePicture(DocumentSaveFormat: TRVSaveFormat; Path: String; gr: TGraphic): String; virtual;
procedure CopyText;
function GetSelText: String;
function SelectionExists: Boolean;
procedure Deselect;
procedure SelectAll;
property LineCount: Integer read GetLineCount;
property FirstLineVisible: Integer read GetFirstLineVisible;
property LastLineVisible: Integer read GetLastLineVisible;
end;
TRichView=class(TCustomRichView)
published
// published from TRVScroller
property Visible;
property TabStop;
property TabOrder;
property Align;
property HelpContext;
property Tracking;
property VScrollVisible;
property OnVScrolled;
// published from TCustomRichView
property PopupMenu;
property OnClick;
property OnKeyDown;
property OnKeyUp;
property OnKeyPress;
property FirstJumpNo;
property OnJump;
property OnRVMouseMove;
property OnSaveComponentToFile;
property OnURLNeeded;
property OnRVDblClick;
property OnRVRightClick;
property OnSelect;
property OnResized;
property Style;
property MaxTextWidth;
property MinTextWidth;
property LeftMargin;
property RightMargin;
property BackgroundBitmap;
property BackgroundStyle;
property Delimiters;
property AllowSelection;
property SingleClick;
end;
procedure InfoAboutSaD(var sad:TScreenAndDevice; Canvas: TCanvas);
implementation
{$IFDEF FPC}
uses Printers;
{-------------------------------------}
procedure InfoAboutSaD(var sad:TScreenAndDevice; Canvas: TCanvas);
var screenDC: HDC;
begin
if Canvas is TPrinterCanvas then begin
sad.ppixDevice := Printer.XDPI;
sad.ppiyDevice := Printer.YDPI;
end else begin
sad.ppixDevice := GetDeviceCaps(Canvas.Handle, LOGPIXELSX);
sad.ppiyDevice := GetDeviceCaps(Canvas.Handle, LOGPIXELSY);
end;
screenDc := CreateCompatibleDC(0);
sad.ppixScreen := GetDeviceCaps(screenDC, LOGPIXELSY);
sad.ppiyScreen := GetDeviceCaps(screenDC, LOGPIXELSY);
DeleteDC(screenDC);
end;
{$ELSE}
{-------------------------------------}
procedure InfoAboutSaD(var sad:TScreenAndDevice; Canvas: TCanvas);
var screenDC: HDC;
begin
sad.ppixDevice := GetDeviceCaps(Canvas.Handle, LOGPIXELSX);
sad.ppiyDevice := GetDeviceCaps(Canvas.Handle, LOGPIXELSY);
screenDc := CreateCompatibleDC(0);
sad.ppixScreen := GetDeviceCaps(screenDC, LOGPIXELSX);
sad.ppiyScreen := GetDeviceCaps(screenDC, LOGPIXELSY);
DeleteDC(screenDC);
end;
{$ENDIF}
{==================================================================}
constructor TCustomRichView.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
FClientTextWidth := False;
FLeftMargin := 5;
FRightMargin := 5;
FMaxTextWidth := 0;
FMinTextWidth := 0;
TextWidth := -1;
TextHeight := 0;
LastJumpMovedAbove := -1;
FStyle := nil;
LastJumpDowned := -1;
drawlines := TStringList.Create;
lines := TStringList.Create;
checkpoints := TStringList.Create;
jumps := TStringList.Create;
FBackBitmap := TBitmap.Create;
FBackGroundStyle := bsNoBitmap;
nJmps :=0;
FirstJumpNo :=0;
skipformatting := False;
OldWidth := 0;
OldHeight := 0;
Width := 100;
Height := 40;
DisplayOptions := [rvdoImages, rvdoComponents, rvdoBullets];
ShareContents := False;
FDelimiters := ' .;,:(){}"';
DrawHover := False;
FSelStartNo := -1;
FSelEndNo := -1;
FSelStartOffs := 0;
FSelEndOffs := 0;
Selection := False;
FAllowSelection:= True;
LastLineFormatted := -1;
ScrollTimer := nil;
//Format_(False,0, Canvas, False);
end;
{-------------------------------------}
destructor TCustomRichView.Destroy;
begin
FBackBitmap.Free;
Clear;
drawlines.Free;
checkpoints.Free;
jumps.Free;
if not ShareContents then lines.Free;
inherited Destroy;
end;
{-------------------------------------}
procedure TCustomRichView.WMSize(var Message: TWMSize);
begin
Format_(True, 0, Canvas, False);
if Assigned(FOnResized) then FOnResized(Self);
end;
{-------------------------------------}
procedure TCustomRichView.Format;
begin
Format_(False, 0, Canvas, False);
end;
{-------------------------------------}
procedure TCustomRichView.FormatTail;
begin
Format_(False, 0, Canvas, True);
end;
{-------------------------------------}
procedure TCustomRichView.ClearTemporal;
var i: Integer;
begin
if ScrollTimer<>nil then begin
ScrollTimer.Free;
ScrollTimer := nil;
end;
drawlines.BeginUpdate;
for i:=0 to drawlines.Count-1 do begin
TDrawLineInfo(drawlines.objects[i]).Free;
drawlines.objects[i] := nil;
end;
drawlines.Clear;
drawlines.EndUpdate;
checkpoints.BeginUpdate;
for i:=0 to checkpoints.Count-1 do begin
TCPInfo(checkpoints.objects[i]).Free;
checkpoints.objects[i] := nil;
end;
checkpoints.Clear;
checkpoints.EndUpdate;
jumps.BeginUpdate;
for i:=0 to jumps.Count-1 do begin
TJumpInfo(jumps.objects[i]).Free;
jumps.objects[i] := nil;
end;
jumps.Clear;
jumps.EndUpdate;
nJmps :=0;
end;
{-------------------------------------}
procedure TCustomRichView.Deselect;
begin
Selection := False;
FSelStartNo := -1;
FSelEndNo := -1;
FSelStartOffs := 0;
FSelEndOffs := 0;
if Assigned(FOnSelect) then OnSelect(Self);
end;
{-------------------------------------}
procedure TCustomRichView.SelectAll;
begin
FSelStartNo := 0;
FSelEndNo := DrawLines.Count-1;
FSelStartOffs := 0;
FSelEndOffs := 0;
if TLineInfo(Lines.Objects[TDrawLineInfo(DrawLines.Objects[FSelEndNo]).LineNo]).StyleNo>=0 then
FSelEndOffs := Length(DrawLines[FSelEndNo])+1;
if Assigned(FOnSelect) then OnSelect(Self);
end;
{-------------------------------------}
procedure TCustomRichView.Clear;
var i: Integer;
begin
Deselect;
if not ShareContents then begin
lines.BeginUpdate;
for i:=0 to lines.Count-1 do begin
if TLineInfo(lines.objects[i]).StyleNo = -3 then { image}
begin
TLineInfo(lines.objects[i]).gr.Free;
TLineInfo(lines.objects[i]).gr := nil;
end;
if TLineInfo(lines.objects[i]).StyleNo = -5 then {control}
begin
RemoveControl(TControl(TLineInfo(lines.objects[i]).gr));
TLineInfo(lines.objects[i]).gr.Free;
TLineInfo(lines.objects[i]).gr := nil;
end;
TLineInfo(lines.objects[i]).Free;
lines.objects[i] := nil;
end;
lines.Clear;
lines.EndUpdate;
end;
ClearTemporal;
end;
{-------------------------------------}
procedure TCustomRichView.AddFromNewLine(s: String; StyleNo:Integer);
var info: TLineInfo;
begin
info := TLineInfo.Create;
info.StyleNo := StyleNo;
info.SameAsPrev := False;
info.Center := False;
lines.AddObject(s, info);
end;
{-------------------------------------}
procedure TCustomRichView.Add(s: String; StyleNo:Integer);
var info: TLineInfo;
begin
info := TLineInfo.Create;
info.StyleNo := StyleNo;
info.SameAsPrev := (lines.Count<>0);
info.Center := False;
lines.AddObject(s, info);
end;
{-------------------------------------}
procedure TCustomRichView.AddText(s: String;StyleNo:Integer);
var p: Integer;
begin
{$IFDEF FPC}
s:=AdjustLineBreaks(s, tlbsCRLF);
{$ELSE}
s:=AdjustLineBreaks(s);
{$ENDIF}
p := Pos(chr(13)+chr(10),s);
if p=0 then begin
if s<>'' then Add(s,StyleNo);
exit;
end;
Add(Copy(s,1,p-1), StyleNo);
Delete(s,1, p+1);
while s<>'' do begin
p := Pos(chr(13)+chr(10),s);
if p=0 then begin
AddFromNewLine(s,StyleNo);
break;
end;
AddFromNewLine(Copy(s,1,p-1), StyleNo);
Delete(s,1, p+1);
end;
end;
{-------------------------------------}
procedure TCustomRichView.AddTextFromNewLine(s: String;StyleNo:Integer);
var p: Integer;
begin
{$IFDEF FPC}
s:=AdjustLineBreaks(s, tlbsCRLF);
{$ELSE}
s:=AdjustLineBreaks(s);
{$ENDIF}
p := Pos(chr(13)+chr(10),s);
if p=0 then begin
AddFromNewLine(s,StyleNo);
exit;
end;
while s<>'' do begin
p := Pos(chr(13)+chr(10),s);
if p=0 then begin
AddFromNewLine(s,StyleNo);
break;
end;
AddFromNewLine(Copy(s,1,p-1), StyleNo);
Delete(s,1, p+1);
end;
end;
{-------------------------------------}
procedure TCustomRichView.AddCenterLine(s: String;StyleNo:Integer);
var info: TLineInfo;
begin
info := TLineInfo.Create;
info.StyleNo := StyleNo;
info.SameAsPrev := False;
info.Center := True;
lines.AddObject(s, info);
end;
{-------------------------------------}
procedure TCustomRichView.AddBreak;
var info: TLineInfo;
begin
info := TLineInfo.Create;
info.StyleNo := -1;
lines.AddObject('', info);
end;
{-------------------------------------}
function TCustomRichView.AddNamedCheckPoint(CpName: String): Integer;
var info: TLineInfo;
cpinfo: TCPInfo;
begin
info := TLineInfo.Create;
info.StyleNo := -2;
lines.AddObject(CpName, info);
cpInfo := TCPInfo.Create;
cpInfo.Y := 0;
checkpoints.AddObject(CpName,cpinfo);
AddNamedCheckPoint := checkpoints.Count-1;
end;
{-------------------------------------}
function TCustomRichView.AddCheckPoint: Integer;
begin
AddCheckPoint := AddNamedCheckPoint('');
end;
{-------------------------------------}
function TCustomRichView.GetCheckPointY(no: Integer): Integer;
begin
GetCheckPointY := TCPInfo(checkpoints.Objects[no]).Y;
end;
{-------------------------------------}
function TCustomRichView.GetJumpPointY(no: Integer): Integer;
var i: Integer;
begin
GetJumpPointY := 0;
for i:=0 to Jumps.Count-1 do
if TJumpInfo(jumps.objects[i]).id = no-FirstJumpNo then begin
GetJumpPointY := TJumpInfo(jumps.objects[i]).t;
exit;
end;
end;
{-------------------------------------}
procedure TCustomRichView.AddPicture(gr: TGraphic); { gr not copied, do not free it!}
var info: TLineInfo;
begin
info := TLineInfo.Create;
info.StyleNo := -3;
info.gr := gr;
info.SameAsPrev := False;
info.Center := True;
lines.AddObject('', info);
end;
{-------------------------------------}
procedure TCustomRichView.AddHotSpot(imgNo: Integer; lst: TImageList; fromnewline: Boolean);
var info: TLineInfo;
begin
info := TLineInfo.Create;
info.StyleNo := -4;
info.gr := lst;
info.imgNo := imgNo;
info.SameAsPrev := not FromNewLine;
lines.AddObject('', info);
end;
{-------------------------------------}
procedure TCustomRichView.AddBullet(imgNo: Integer; lst: TImageList; fromnewline: Boolean);
var info: TLineInfo;
begin
info := TLineInfo.Create;
info.StyleNo := -6;
info.gr := lst;
info.imgNo := imgNo;
info.SameAsPrev := not FromNewLine;
lines.AddObject('', info);
end;
{-------------------------------------}
procedure TCustomRichView.AddControl(ctrl: TControl; center: Boolean); { do not free ctrl! }
var info: TLineInfo;
begin
info := TLineInfo.Create;
info.StyleNo := -5;
info.gr := ctrl;
info.SameAsPrev := False;
info.Center := center;
lines.AddObject('', info);
InsertControl(ctrl);
end;
{-------------------------------------}
function TCustomRichView.GetMaxPictureWidth: Integer;
var i,m: Integer;
begin
m := 0;
for i:=0 to lines.Count-1 do begin
if TLineInfo(lines.objects[i]).StyleNo = -3 then
if m<TGraphic(TLineInfo(lines.objects[i]).gr).Width then
m := TGraphic(TLineInfo(lines.objects[i]).gr).Width;
if TLineInfo(lines.objects[i]).StyleNo = -5 then
if m<TControl(TLineInfo(lines.objects[i]).gr).Width then
m := TControl(TLineInfo(lines.objects[i]).gr).Width;
end;
GetMaxPictureWidth := m;
end;
{-------------------------------------}
function max(a,b: Integer): Integer;
begin
if a>b then
max := a
else
max := b;
end;
{-------------------------------------}
procedure TCustomRichView.Format_(OnlyResized:Boolean; depth: Integer; Canvas: TCanvas;
OnlyTail: Boolean);
var i: Integer;
x,b,d,a: Integer;
mx : Integer;
oldy, oldtextwidth, cw, ch: Integer;
sad: TScreenAndDevice;
StyleNo: Integer;
StartLine: Integer;
StartNo, EndNo, StartOffs, EndOffs: Integer;
begin
if smallstep = 0 then exit;
if (csDesigning in ComponentState) or
not Assigned(FStyle) or
skipformatting or
(depth>1)
then exit;
skipformatting := True;
if depth=0 then StoreSelBounds(StartNo, EndNo, StartOffs, EndOffs);
OldY := VPos*SmallStep;
oldtextwidth := TextWidth;
mx := max(ClientWidth-(FLeftMargin+FRightMargin), GetMaxPictureWidth);
if mx<FMinTextWidth then mx := FMinTextWidth;
if FClientTextWidth then begin { widths of pictures and maxtextwidth are ignored }
TextWidth := ClientWidth-(FLeftMargin+FRightMargin);
if TextWidth<FMinTextWidth then TextWidth := FMinTextWidth;
end
else begin
if (mx > FMaxTextWidth) and (FMaxTextWidth>0) then
TextWidth := FMaxTextWidth
else
TextWidth := mx;
end;
if not (OnlyResized and (TextWidth=OldTextWidth)) then begin
if OnlyTail then begin
StartLine := LastLineFormatted+1;
b:= TextHeight;
end
else begin
StartLine := 0;
b := 0;
ClearTemporal;
end;
x:=0;
d:=0;
InfoAboutSaD(sad, Canvas);
sad.LeftMargin := MulDiv(FLeftMargin, sad.ppixDevice, sad.ppixScreen);
for i:=StartLine to lines.Count-1 do begin
StyleNo := TLineInfo(Lines.Objects[i]).StyleNo;
if not (((StyleNo = rvsPicture) and (not (rvdoImages in DisplayOptions))) or
((StyleNo = rvsComponent)and(not (rvdoComponents in DisplayOptions))) or
(((StyleNo = rvsBullet) or (StyleNo = rvsHotspot))and(not (rvdoBullets in DisplayOptions)))) then
FormatLine(i,x,b, d,a, Canvas, sad);
end;
TextHeight := b+d+1;
if TextHeight div SmallStep > 30000 then
SmallStep := TextHeight div 30000;
AdjustJumpsCoords;
end
else
AdjustChildrenCoords;
HPos := 0;
VPos := 0;
cw := ClientWidth;
ch := ClientHeight;
UpdateScrollBars(mx+FLeftMargin+FRightMargin, TextHeight div SmallStep);
if (cw<>ClientWidth) or (ch<>ClientHeight) then begin
skipformatting := False;
ScrollTo(OldY);
Format_(OnlyResized, depth+1, Canvas, False);
end;
if OnlyResized then ScrollTo(OldY);
if OnlyTail then ScrollTo(TextHeight);
if depth=0 then RestoreSelBounds(StartNo, EndNo, StartOffs, EndOffs);
skipformatting := False;
LastLineFormatted := Lines.Count-1;
end;
{-------------------------------------}
procedure TCustomRichView.AdjustChildrenCoords;
var i: Integer;
dli: TDrawLineInfo;
li : TLineInfo;
begin
for i:=0 to drawlines.Count-1 do begin
dli := TDrawLineInfo(drawlines.Objects[i]);
li := TLineInfo(lines.Objects[dli.LineNo]);
if li.StyleNo = -5 then {control}
begin
TControl(li.gr).Left := dli.Left;
TControl(li.gr).Tag := dli.Top;
Tag2Y(TControl(li.gr));
end;
end;
end;
{-------------------------------------}
procedure TCustomRichView.FormatLine(no: Integer; var x,baseline,prevdesc,prevabove:Integer; Canvas: TCanvas;
var sad: TScreenAndDevice);
var sourceStrPtr, strForAdd, strSpacePos: PChar;
sourceStrPtrLen: Integer;
sz: TSIZE;
max,j, y, ctrlw, ctrlh : Integer;
{$IFNDEF RICHVIEWDEF4}
arr: array[0..1000] of integer;
{$ENDIF}
str: array[0..1000] of char;
info: TDrawLineInfo;
metr: TTextMetric;
StyleNo: Integer;
newline, center:Boolean;
cpinfo: TCPInfo;
jmpinfo: TJumpInfo;
width, y5, Offs : Integer;
begin
width := TextWidth;
case TLineInfo(lines.Objects[no]).StyleNo of
-5: { Control }
begin
ctrlw := TControl(TLineInfo(lines.Objects[no]).gr).Width;
ctrlh := TControl(TLineInfo(lines.Objects[no]).gr).Height;
ctrlw := MulDiv(ctrlw, sad.ppixDevice, sad.ppixScreen);
ctrlh := MulDiv(ctrlh, sad.ppiyDevice, sad.ppiyScreen);
info := TDrawLineInfo.Create;
info.LineNo := no;
info.Top := baseline + prevdesc + 1;
info.Width := ctrlw;
info.Height := ctrlh+1;
if TLineInfo(lines.Objects[no]).Center then begin
info.Left := (width-ctrlw) div 2;
if info.Left<0 then info.Left := 0;
inc(info.Left,sad.LeftMargin);
end
else
info.Left := sad.LeftMargin;
drawlines.AddObject('',info);
TControl(TLineInfo(lines.Objects[no]).gr).Left := info.Left;
TControl(TLineInfo(lines.Objects[no]).gr).Tag := info.Top;
Tag2Y(TControl(TLineInfo(lines.Objects[no]).gr));
inc (baseline,prevdesc+ctrlh+1);
prevdesc :=1;
prevabove := ctrlh+1;
end;
-4, -6: { hotSpot or Bullet }
begin
ctrlw := TImageList(TLineInfo(lines.Objects[no]).gr).Width;
ctrlh := TImageList(TLineInfo(lines.Objects[no]).gr).Height;
ctrlw := MulDiv(ctrlw, sad.ppixDevice, sad.ppixScreen);
ctrlh := MulDiv(ctrlh, sad.ppiyDevice, sad.ppiyScreen);
info := TDrawLineInfo.Create;
info.Width := ctrlw+1;
info.Height := ctrlh+1;
if not TLineInfo(lines.Objects[no]).SameAsPrev or (x+ctrlw+2 > width) then begin
x:=0;
y:=baseline + prevdesc;
inc (baseline,prevdesc+ctrlh+1);
prevdesc :=1;
prevabove := ctrlh+1;
end
else begin
if prevabove < ctrlh+1 then begin
j := drawlines.Count-1;
if j>=0 then
repeat
inc(TDrawLineInfo(drawlines.Objects[j]).Top,ctrlh+1-prevabove);
dec(j);
until TDrawLineInfo(drawlines.Objects[j+1]).FromNewLine;
inc(baseline,ctrlh+1-prevabove);
prevabove := ctrlh+1;
end;
y := baseline - (ctrlh+1);
end;
if TLineInfo(lines.Objects[no]).StyleNo = -4 then begin{HotSpot}
jmpinfo := TJumpInfo.Create;
jmpinfo.l := x+1+sad.LeftMargin;;
jmpinfo.t := y+1;
jmpinfo.w := ctrlw;
jmpinfo.h := ctrlh;
jmpinfo.id := nJmps;
jmpinfo.idx := drawlines.Count;
jumps.AddObject('',jmpinfo);
inc(nJmps);
end;
info.Left := x+1+sad.LeftMargin;;
inc(x, ctrlw+2);
info.Top := y+1;
info.LineNo := no;
info.FromNewLine := not TLineInfo(lines.Objects[no]).SameAsPrev;
drawlines.AddObject('',info);
end;
-3: { graphics}
begin
ctrlw := TGraphic(TLineInfo(lines.Objects[no]).gr).Width;
ctrlh := TGraphic(TLineInfo(lines.Objects[no]).gr).Height;
ctrlw := MulDiv(ctrlw, sad.ppixDevice, sad.ppixScreen);
ctrlh := MulDiv(ctrlh, sad.ppiyDevice, sad.ppiyScreen);
info := TDrawLineInfo.Create;
info.Width := ctrlw;
info.Height := ctrlh+1;
info.Left := (width-ctrlw) div 2;
if info.Left<0 then info.Left := 0;
inc(info.Left,sad.LeftMargin);
info.Top := baseline + prevdesc + 1;
info.LineNo := no;
drawlines.AddObject('',info);
inc (baseline,prevdesc+ctrlh+1);
prevdesc :=1;
prevabove := ctrlh+1;
end;
-2: { check point}
begin
cpinfo := TCPInfo.Create;
cpinfo.Y := baseline + prevDesc;
cpinfo.LineNo := no;
checkpoints.AddObject(lines[no], cpinfo);
end;
-1: { break line}
begin
y5 := MulDiv(5, sad.ppiyDevice, sad.ppiyScreen);
info := TDrawLineInfo.Create;
info.Left := sad.LeftMargin;
info.Top := baseline + prevdesc;
info.LineNo := no;
info.Width := Width;
info.Height := y5+y5+1;
drawlines.AddObject(Lines[no],info);
inc (baseline,prevdesc+y5+y5+1);
prevdesc := y5;
prevabove := y5;
end;
else begin
sourceStrPtr := PChar(lines.Strings[no]);
strForAdd := str;
sourceStrPtrLen := StrLen(sourceStrPtr);
StyleNo := TLineInfo(lines.Objects[no]).StyleNo;
with FStyle.TextStyles[StyleNo] do begin
Canvas.Font.Style := Style;
Canvas.Font.Size := Size;
Canvas.Font.Name := FontName;
{$IFDEF RICHVIEWDEF3}
Canvas.Font.CharSet := CharSet;
{$ENDIF}
end;
GetTextMetrics(Canvas.Handle,metr);
newline := not TLineInfo(lines.Objects[no]).SameAsPrev;
Center := TLineInfo(lines.Objects[no]).Center;
while sourceStrPtrLen>0 do begin
if newline then x:=0;
{$IFDEF FPC}
MyGetTextExtentExPoint(Canvas.Handle, sourceStrPtr, sourceStrPtrLen, Width-x,
{$ELSE}
GetTextExtentExPoint(Canvas.Handle, sourceStrPtr, sourceStrPtrLen, Width-x,
{$ENDIF}
{$IFDEF RICHVIEWDEF4}
@max, nil,
{$ELSE}
max, arr[0],
{$ENDIF}
sz);
if max=0 then max := 1;
StrLCopy(strForAdd, sourceStrPtr,max);
if max<sourceStrPtrLen then
{if sourceStrPtr[max]<>' ' then } begin
StrLCopy(strForAdd, sourceStrPtr,max);
strSpacePos := StrRScan(strForAdd,' ');
if strSpacePos<>nil then begin
max := strSpacePos-strForAdd;
StrLCopy(strForAdd, sourceStrPtr,max);
inc(max);
end
else
if not newline then begin
x:=0;
newline := true;
continue;
end;
end;
Offs := sourceStrPtr - PChar(Lines.Strings[no])+1;
sourceStrPtr := @(sourceStrPtr[max]);
info := TDrawLineInfo.Create;
info.LineNo := no;
info.Offs := Offs;
{$IFDEF FPC}
MyGetTextExtentExPoint(Canvas.Handle, strForAdd, StrLen(strForAdd), Width-x,
{$ELSE}
GetTextExtentExPoint(Canvas.Handle, strForAdd, StrLen(strForAdd), Width-x,
{$ENDIF}
{$IFDEF RICHVIEWDEF4}
@max, nil,
{$ELSE}
max,arr[0],
{$ENDIF}
sz);
if not newline then begin {continue line}
if prevabove < metr.tmExternalLeading+metr.tmAscent then begin
j := drawlines.Count-1;
if j>=0 then
repeat
inc(TDrawLineInfo(drawlines.Objects[j]).Top,metr.tmExternalLeading+metr.tmAscent-prevabove);
dec(j);
until TDrawLineInfo(drawlines.Objects[j+1]).FromNewLine;
inc(baseline,metr.tmExternalLeading+metr.tmAscent-prevabove);
prevabove := metr.tmExternalLeading+metr.tmAscent;
end;
y := baseline - metr.tmAscent;
info.FromNewLine := False;
end
else begin { new line }
info.FromNewLine := True;
if Center then
x := (Width - sz.cx) div 2
else
x :=0;
y := baseline+prevDesc+metr.tmExternalLeading;
inc(baseline, prevDesc+metr.tmExternalLeading+metr.tmAscent);
prevabove := metr.tmExternalLeading+metr.tmAscent;
end;
info.Left :=x+sad.LeftMargin;;
info.Top := y;
info.Width := sz.cx;
info.Height := sz.cy;
drawlines.AddObject(strForAdd,info);
if (StyleNo=rvsJump1) or (StyleNo=rvsJump2) then begin
jmpinfo := TJumpInfo.Create;
jmpinfo.l := x+sad.LeftMargin;
jmpinfo.t := y;
jmpinfo.w := sz.cx;
jmpinfo.h := sz.cy;
jmpinfo.id := nJmps;
jmpinfo.idx := drawlines.Count-1;
TLineInfo(lines.Objects[no]).imgNo := nJmps;
jumps.AddObject('',jmpinfo);
end;
sourceStrPtrLen := StrLen(sourceStrPtr);
if newline or (prevDesc < metr.tmDescent) then prevDesc := metr.tmDescent;
inc(x,sz.cx);
newline := True;
end;
if (StyleNo=rvsJump1) or (StyleNo=rvsJump2) then inc(nJmps);
end;
end;
end;
{-------------------------------------}
procedure TCustomRichView.AdjustJumpsCoords;
var i: Integer;
begin
for i:=0 to jumps.Count-1 do begin
TJumpInfo(jumps.Objects[i]).l :=
TDrawLineInfo(drawlines.Objects[TJumpInfo(jumps.Objects[i]).idx]).left;
TJumpInfo(jumps.Objects[i]).t :=
TDrawLineInfo(drawlines.Objects[TJumpInfo(jumps.Objects[i]).idx]).top;
end;
end;
{-------------------------------------}
const gdlnFirstVisible =1;
const gdlnLastCompleteVisible =2;
const gdlnLastVisible =3;
{-------------------------------------}
function TCustomRichView.GetFirstVisible(TopLine: Integer): Integer;
begin
Result := GetDrawLineNo(TopLine,gdlnFirstVisible);
end;
{-------------------------------------}
function TCustomRichView.GetFirstLineVisible: Integer;
var v: Integer;
begin
v := GetDrawLineNo(VPos*SmallStep, gdlnFirstVisible);
if v>=DrawLines.Count then v := DrawLines.Count-1;
if v<0 then
Result := -1
else
Result := TDrawLineInfo(DrawLines.Objects[v]).LineNo;
end;
{-------------------------------------}
function TCustomRichView.GetLastLineVisible: Integer;
var v: Integer;
begin
v := GetDrawLineNo(VPos*SmallStep+ClientHeight, gdlnLastVisible);
if v>=DrawLines.Count then v := DrawLines.Count-1;
if v<0 then
Result := -1
else
Result := TDrawLineInfo(DrawLines.Objects[v]).LineNo;
end;
{-------------------------------------}
function TCustomRichView.GetDrawLineNo(BoundLine: Integer; Option: Integer): Integer;
var
a,b,mid: Integer;
begin
if DrawLines.Count = 0 then begin
GetDrawLineNo := 0;
exit;
end;
if TDrawLineInfo(drawlines.Objects[0]).Top>=BoundLine then begin
GetDrawLineNo := 0;
exit;
end;
if (Option=gdlnLastVisible) and (TDrawLineInfo(drawlines.Objects[DrawLines.Count-1]).Top<BoundLine) then begin
GetDrawLineNo := DrawLines.Count-1;
exit;
end;
a := 1;
b := DrawLines.Count-1;
mid := a;
if Option = gdlnLastCompleteVisible then begin
{
while (b-a)>1 do begin
mid := (a+b) div 2;
if (TDrawLineInfo(drawlines.Objects[mid]).Top+TDrawLineInfo(drawlines.Objects[mid]).Height>BoundLine) then
b := mid
else
a := mid;
end;
if mid>= DrawLines.Count then mid := DrawLines.Count-1;
while (mid>0) and (TDrawLineInfo(drawlines.Objects[mid]).Top+TDrawLineInfo(drawlines.Objects[mid]).Height>BoundLine) do dec(mid);
if (mid>0) then dec(mid);
while (mid>0) and not TDrawLineInfo(drawlines.Objects[mid]).FromNewLine do dec(mid);
if (mid>0) then dec(mid);
end
}
end
else begin
while (b-a)>1 do begin
mid := (a+b) div 2;
if (TDrawLineInfo(drawlines.Objects[mid]).Top>=BoundLine) then begin
if (TDrawLineInfo(drawlines.Objects[mid-1]).Top<BoundLine) then break;
b := mid;
end
else
a := mid;
end;
if mid>= DrawLines.Count then mid := DrawLines.Count-1;
if Option = gdlnFirstVisible then begin
while (mid>0) and not TDrawLineInfo(drawlines.Objects[mid]).FromNewLine do dec(mid);
if (mid>0) then dec(mid);
while (mid>0) and not TDrawLineInfo(drawlines.Objects[mid]).FromNewLine do dec(mid);
if (mid>0) then dec(mid);
end
else
while TDrawLineInfo(drawlines.Objects[mid]).Top<BoundLine do inc(mid);
end;
GetDrawLineNo := mid;
end;
{
function TCustomRichView.GetFirstVisible(TopLine: Integer): Integer;
var
a,b,mid: Integer;
begin
if DrawLines.Count = 0 then begin
GetFirstVisible := 0;
exit;
end;
if TDrawLineInfo(drawlines.Objects[0]).Top>=TopLine then begin
GetFirstVisible := 0;
exit;
end;
a := 1;
b := DrawLines.Count-1;
mid := a;
while (b-a)>1 do begin
mid := (a+b) div 2;
if (TDrawLineInfo(drawlines.Objects[mid]).Top>=TopLine) then begin
if (TDrawLineInfo(drawlines.Objects[mid-1]).Top<TopLine) then break;
b := mid;
end
else
a := mid;
end;
dec(mid);
while (mid>=2) and
(TDrawLineInfo(drawlines.Objects[mid]).Left>
TDrawLineInfo(drawlines.Objects[mid-1]).Left) do dec(mid);
if mid=0 then begin
GetFirstVisible := mid;
exit;
end;
dec(mid);
while (mid>=1) and
(TDrawLineInfo(drawlines.Objects[mid]).Left>
TDrawLineInfo(drawlines.Objects[mid-1]).Left) do dec(mid);
GetFirstVisible := mid;
end;
}
{$IFDEF FPC}
procedure TxtOut(Canvas: Tcanvas; X,Y: Integer; Text: String);
var
Sz: TSize;
R: TRect;
ts: TTextStyle;
begin
Sz := Canvas.TextExtent(Text);
R := Bounds(X,Y,Sz.cx, Sz.cy);
ts := Canvas.TextStyle;
ts.Opaque := Canvas.Brush.Style <> bsClear;
Canvas.TextRect(R, R.Left, R.Top, Text, ts);
end;
{$ENDIF}
{-------------------------------------}
procedure TCustomRichView.Paint;
var i,no, yshift, xshift: Integer;
cl, textcolor: TColor;
dli:TDrawLineInfo;
li: TLineInfo;
lastline, hovernow: Boolean;
r :TRect;
buffer: TBitmap;
canv: TCanvas;
s, s1: String;
StartNo, EndNo, StartOffs, EndOffs: Integer;
{$IFDEF FPC}
St: string;
{$ENDIF}
begin
if (csDesigning in ComponentState) or
not Assigned(FStyle)
then begin
cl := Canvas.Brush.Color;
if Assigned(FStyle) then
Canvas.Brush.Color := FStyle.Color
else
Canvas.Brush.Color := clWindow;
Canvas.Brush.Style := bsSolid;
Canvas.Pen.Color := clWindowText;
Canvas.Font.Color := clWindowText;
Canvas.Font.Name := 'MS Sans Serif';
Canvas.Font.Size := 8;
Canvas.Font.Style := [];
Canvas.FillRect(Canvas.ClipRect);
if (csDesigning in ComponentState) then
Canvas.TextOut(ClientRect.Left+1, ClientRect.Top+1, GetCredits)
else
Canvas.TextOut(ClientRect.Left+1, ClientRect.Top+1, 'Error: style is not assigned');
Canvas.Brush.Color := clWindowText;
Canvas.FrameRect(ClientRect);
Canvas.Brush.Color := cl;
exit;
end;
GetSelBounds(StartNo, EndNo, StartOffs, EndOffs);
lastline := False;
r := Canvas.ClipRect;
buffer := TBitmap.Create;
buffer.Width := r.Right-r.Left+1;
buffer.Height := r.Bottom-r.Top+1;
canv := buffer.Canvas;
DrawBack(canv.Handle, Canvas.ClipRect, ClientWidth, ClientHeight);
yshift := VPos*SmallStep;
inc(r.Top, yshift);
inc(r.Bottom, yshift);
inc(yshift, Canvas.ClipRect.Top);
xshift := HPos + Canvas.ClipRect.Left;
canv.Brush.Style := bsClear;
for i:= GetFirstVisible(r.Top) to drawlines.Count-1 do begin
dli := TDrawLineInfo(drawlines.Objects[i]);
if lastline and (dli.Left<=TDrawLineInfo(drawlines.Objects[i-1]).left) then break;
if dli.Top>r.Bottom then lastline := True;
li := TLineInfo(lines.Objects[dli.LineNo]);
no := li.StyleNo;
if no>=0 then begin { text }
canv.Font.Style := FStyle.TextStyles[no].Style;
canv.Font.Size := FStyle.TextStyles[no].Size;
canv.Font.Name := FStyle.TextStyles[no].FontName;
{$IFDEF RICHVIEWDEF3}
canv.Font.CharSet := FStyle.TextStyles[no].CharSet;
{$ENDIF}
if not ((no in [rvsJump1, rvsJump2]) and DrawHover and
(LastJumpMovedAbove<>-1) and
(li.ImgNo = LastJumpMovedAbove)) then begin
textcolor := FStyle.TextStyles[no].Color;
hovernow := False;
end
else begin
textcolor := FStyle.HoverColor;
hovernow := True;
canv.Font.Color := textcolor;
end;
if (StartNo>i) or (EndNo<i) then begin
canv.Font.Color := textcolor;
canv.TextOut(dli.Left-xshift, dli.Top-yshift, drawlines.Strings[i])
end
else if ((StartNo<i) and (EndNo>i)) or
((StartNo=i) and (EndNo<>i) and (StartOffs<=1)) or
((StartNo<>i) and (EndNo=i) and (EndOffs>Length(drawlines.Strings[i])))
then begin
canv.Brush.Style := bsSolid;
canv.Brush.Color := FStyle.SelColor;
if not hovernow then canv.Font.Color := FStyle.SelTextColor;
{$IFDEF FPC}
TxtOut(canv, dli.Left-xshift, dli.Top-yshift, drawlines.Strings[i]);
{$ELSE}
canv.TextOut(dli.Left-xshift, dli.Top-yshift, drawlines.Strings[i]);
{$ENDIF}
canv.Brush.Style := bsClear;
end
else if (StartNo=i) then begin
canv.Font.Color := textcolor;
s := Copy(drawlines.Strings[i], 1, StartOffs-1);
canv.TextOut(dli.Left-xshift, dli.Top-yshift, s);
canv.Brush.Style := bsSolid;
canv.Brush.Color := FStyle.SelColor;
if not hovernow then canv.Font.Color := FStyle.SelTextColor;
if (i<>EndNo) or (EndOffs>Length(DrawLines[i])) then begin
{$IFDEF FPC}
St := Copy(drawlines.Strings[i], StartOffs, Length(drawlines.Strings[i]));
TxtOut(canv, dli.Left-xshift+canv.TextWidth(s), dli.Top-yshift,st);
{$ELSE}
canv.TextOut(dli.Left-xshift+canv.TextWidth(s), dli.Top-yshift,
Copy(drawlines.Strings[i], StartOffs, Length(drawlines.Strings[i])));
{$ENDIF}
canv.Brush.Style := bsClear;
end
else begin
s1 := Copy(drawlines.Strings[i], StartOffs, EndOffs-StartOffs);
{$IFDEF FPC}
TxtOut(canv, dli.Left-xshift+canv.TextWidth(s), dli.Top-yshift, s1);
{$ELSE}
canv.TextOut(dli.Left-xshift+canv.TextWidth(s), dli.Top-yshift, s1);
{$ENDIF}
canv.Font.Color := textcolor;
canv.Brush.Style := bsClear;
canv.TextOut(dli.Left-xshift+canv.TextWidth(s+s1), dli.Top-yshift,
Copy(drawlines.Strings[i], EndOffs, Length(DrawLines[i])));
end;
end else
if (EndNo=i) then begin
s := Copy(drawlines.Strings[i], 1, EndOffs-1);
canv.Brush.Style := bsSolid;
canv.Brush.Color := FStyle.SelColor;
if not hovernow then canv.Font.Color := FStyle.SelTextColor;
{$IFDEF FPC}
TxtOut(canv, dli.Left-xshift, dli.Top-yshift, s);
{$ELSE}
canv.TextOut(dli.Left-xshift, dli.Top-yshift, s);
{$ENDIF}
canv.Brush.Style := bsClear;
canv.Font.Color := textcolor;
canv.TextOut(dli.Left-xshift+canv.TextWidth(s), dli.Top-yshift,
Copy(drawlines.Strings[i], EndOffs, Length(drawlines.Strings[i])));
end;
continue;
end;
if (no = -3) then begin { graphics }
canv.Draw(dli.Left-xshift, dli.Top-yshift, TGraphic(li.gr));
continue;
end;
if (no = -4) or (no = -6) then begin { hotspots and bullets }
if (StartNo<=i) and (EndNo>=i) and
not ((EndNo=i) and (EndOffs=0)) and
not ((StartNo=i) and (StartOffs=2))
then begin
TImageList(li.gr).BlendColor := FStyle.SelColor;
TImageList(li.gr).DrawingStyle := dsSelected;
end;
TImageList(li.gr).Draw(canv, dli.Left-xshift, dli.Top-yshift, li.imgNo);
TImageList(li.gr).DrawingStyle := ImgList.dsNormal;
continue;
end;
if no = -2 then continue; { check point }
if no = -1 then begin {break line}
canv.Pen.Color := FStyle.TextStyles[0].Color;
canv.MoveTo(dli.Left+5-xshift, dli.Top+5-yshift);
canv.LineTo(XSize-5-xshift-FRightMargin, dli.Top+5-yshift);
end;
{ controls ignored }
end;
Canvas.Draw(Canvas.ClipRect.Left, Canvas.ClipRect.Top, buffer);
buffer.Free;
end;
{------------------------------------------------------------------}
procedure TCustomRichView.InvalidateJumpRect(no: Integer);
var rec: TRect;
i, id : Integer;
begin
if Style.FullRedraw then
Invalidate
else begin
id := no;
for i:=0 to Jumps.Count -1 do
if id = TJumpInfo(jumps.objects[i]).id then
with TJumpInfo(jumps.objects[i]) do begin
rec.Left := l-Hpos-5;
rec.Top := t-VPos*SmallStep-5;
rec.Right := l+w-Hpos+5;
rec.Bottom := t+h-VPos*SmallStep+5;
InvalidateRect(Handle, @rec, False);
end;
end;
Update;
end;
{------------------------------------------------------------------}
procedure TCustomRichView.CMMouseLeave(var Message: TMessage);
begin
if DrawHover and (LastJumpMovedAbove<>-1) then begin
DrawHover := False;
InvalidateJumpRect(LastJumpMovedAbove);
end;
if Assigned(FOnRVMouseMove) and
(LastJumpMovedAbove<>-1) then begin
LastJumpMovedAbove := -1;
OnRVMouseMove(Self,-1);
end;
end;
{------------------------------------------------------------------}
procedure TCustomRichView.MouseMove(Shift: TShiftState; X, Y: Integer);
var i, no, offs,ys: Integer;
begin
ScrollDelta := 0;
if Y<0 then ScrollDelta := -1;
if Y<-20 then ScrollDelta := -10;
if Y>ClientHeight then ScrollDelta := 1;
if Y>ClientHeight+20 then ScrollDelta := 10;
inherited MouseMove(Shift, X, Y);
if Selection then begin
XMouse := x;
YMouse := y;
ys := y;
if ys<0 then y:=0;
if ys>ClientHeight then ys:=ClientHeight;
FindItemForSel(X+HPos, ys+VPos*SmallStep, no, offs);
FSelEndNo := no;
FselEndOffs := offs;
Invalidate;
end;
for i:=0 to jumps.Count-1 do
if (X>=TJumpInfo(jumps.objects[i]).l-HPos) and
(X<=TJumpInfo(jumps.objects[i]).l+TJumpInfo(jumps.objects[i]).w-HPos) and
(Y>=TJumpInfo(jumps.objects[i]).t-VPos*SmallStep) and
(Y<=TJumpInfo(jumps.objects[i]).t+TJumpInfo(jumps.objects[i]).h-VPos*SmallStep) then
begin
Cursor := FStyle.JumpCursor;
if Assigned(FOnRVMouseMove) and
(LastJumpMovedAbove<>TJumpInfo(jumps.objects[i]).id) then begin
OnRVMouseMove(Self,TJumpInfo(jumps.objects[i]).id+FirstJumpNo);
end;
if DrawHover and (LastJumpMovedAbove<>-1) and
(LastJumpMovedAbove<>TJumpInfo(jumps.objects[i]).id) then begin
DrawHover := False;
InvalidateJumpRect(LastJumpMovedAbove);
end;
LastJumpMovedAbove := TJumpInfo(jumps.objects[i]).id;
if (Style<>nil) and (Style.HoverColor<>clNone) and not DrawHover then begin
DrawHover := True;
InvalidateJumpRect(LastJumpMovedAbove);
end;
exit;
end;
Cursor := crDefault;
if DrawHover and (LastJumpMovedAbove<>-1) then begin
DrawHover := False;
InvalidateJumpRect(LastJumpMovedAbove);
end;
if Assigned(FOnRVMouseMove) and
(LastJumpMovedAbove<>-1) then begin
LastJumpMovedAbove := -1;
OnRVMouseMove(Self,-1);
end;
if Selection then Invalidate;
end;
{-------------------------------------}
procedure TCustomRichView.MouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
var i, StyleNo, no, offs, ys: Integer;
clickedword: String;
p: TPoint;
begin
if ScrollTimer<> nil then begin
ScrollTimer.Free;
ScrollTimer := nil;
end;
XClicked := X;
YClicked := Y;
if Selection and (Button = mbLeft) then begin
ys := y;
if ys<0 then y:=0;
if ys>ClientHeight then ys:=ClientHeight;
FindItemForSel(XClicked+HPos, ys+VPos*SmallStep, no, offs);
FSelEndNo := no;
FselEndOffs := offs;
Selection := False;
Invalidate;
if Assigned(FOnSelect) then FOnSelect(Self);
end;
if Button = mbRight then begin
inherited MouseUp(Button, Shift, X, Y);
if not Assigned(FOnRVRightClick) then exit;
p := ClientToScreen(Point(X,Y));
if FindClickedWord(clickedword, StyleNo) then
FOnRVRightClick(Self, clickedword, StyleNo,p.X,p.Y);
exit;
end;
if Button <> mbLeft then exit;
if (LastJumpDowned=-1) or not Assigned(FOnJump) then begin
exit;
end;
for i:=0 to jumps.Count-1 do
with jumps.objects[i] as TJumpInfo do
if (LastJumpDowned=id) and
(X>=l-HPos) and
(X<=l+w-HPos) and
(Y>=t-VPos*SmallStep) and
(Y<=t+h-VPos*SmallStep) then
begin
OnJump(Self,id+FirstJumpNo);
break;
end;
LastJumpDowned:=-1;
inherited MouseUp(Button, Shift, X, Y);
end;
{-------------------------------------}
procedure TCustomRichView.MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
var i,no, StyleNo: Integer;
clickedword: String;
begin
if Button <> mbLeft then exit;
XClicked := X;
YClicked := Y;
//if Assigned(FOnJump) then begin
LastJumpDowned := -1;
for i:=0 to jumps.Count-1 do
with jumps.objects[i] as TJumpInfo do
if (X>=l-HPos) and
(X<=l+w-HPos) and
(Y>=t-VPos*SmallStep) and
(Y<=t+h-VPos*SmallStep) then
begin
LastJumpDowned := id;
break;
end;
if {LastJumpDowned=-1} AllowSelection then begin
FindItemForSel(XClicked+HPos, YClicked+VPos*SmallStep, no, FSelStartOffs);
FSelStartNo := no;
FSelEndNo := no;
Selection := (no<>-1);
FSelEndOffs := FSelStartOffs;
Invalidate;
if ScrollTimer = nil then begin
ScrollTimer := TTimer.Create(nil);
ScrollTimer.OnTimer := OnScrollTimer;
ScrollTimer.Interval := 100;
end;
end;
if SingleClick and Assigned(FOnRVDblClick) and FindClickedWord(clickedword, StyleNo) then
FOnRVDblClick(Self, clickedword, StyleNo);
inherited MouseDown(Button, Shift, X, Y);
end;
{-------------------------------------}
procedure TCustomRichView.AppendFrom(Source: TCustomRichView);
var i: Integer;
gr: TGraphic;
grclass: TGraphicClass;
li: TLineInfo;
begin
ClearTemporal;
for i:=0 to Source.Lines.Count-1 do begin
li := TLineInfo(Source.Lines.Objects[i]);
case li.StyleNo of
-1: AddBreak;
-2: AddCheckPoint;
-3: begin
grclass := TGraphicClass(li.gr.ClassType);
gr := grclass.Create;
gr.Assign(li.gr);
AddPicture(gr);
end;
-4: AddHotSpot(li.imgNo, TImageList(li.gr), not li.SameAsPrev);
-5: ;
{
begin
if li.gr is
ctrlclass := TControlClass(li.gr.ClassType);
ctrl := ctrlclass.Create(Self);
ctrl.Assign(li.gr);
AddControl(ctrl, li.Center);
end;
}
-6: AddBullet(li.imgNo, TImageList(li.gr), not li.SameAsPrev);
else
begin
if li.Center then
AddCenterLine(Source.Lines[i], li.StyleNo)
else
if li.SameAsPrev then
Add(Source.Lines[i], li.StyleNo)
else
AddFromNewLine(Source.Lines[i], li.StyleNo)
end;
end;
end;
end;
{-------------------------------------}
function TCustomRichView.GetLastCP: Integer;
begin
GetLastCP := CheckPoints.Count-1;
end;
{-------------------------------------}
procedure TCustomRichView.SetBackBitmap(Value: TBitmap);
begin
FBackBitmap.Assign(Value);
if (Value=nil) or (Value.Empty) then
FullRedraw := False
else
case FBackgroundStyle of
bsNoBitmap, bsTiledAndScrolled:
FullRedraw := False;
bsStretched, bsTiled:
FullRedraw := True;
end;
end;
{-------------------------------------}
procedure TCustomRichView.SetBackgroundStyle(Value: TBackgroundStyle);
begin
FBackgroundStyle := Value;
if FBackBitmap.Empty then
FullRedraw := False
else
case FBackgroundStyle of
bsNoBitmap, bsTiledAndScrolled:
FullRedraw := False;
bsStretched, bsTiled:
FullRedraw := True;
end;
end;
{-------------------------------------}
procedure TCustomRichView.DrawBack(DC: HDC; Rect: TRect; Width,Height:Integer);
var i, j: Integer;
hbr: HBRUSH;
begin
if FStyle = nil then exit;
if FBackBitmap.Empty or (FBackgroundStyle=bsNoBitmap) then begin
hbr := CreateSolidBrush(ColorToRGB(FStyle.Color));
dec(Rect.Bottom, Rect.Top);
dec(Rect.Right, Rect.Left);
Rect.Left := 0;
Rect.Top := 0;
FillRect(DC, Rect, hbr);
DeleteObject(hbr);
end
else
case FBackgroundStyle of
bsTiled:
for i:= Rect.Top div FBackBitmap.Height to Rect.Bottom div FBackBitmap.Height do
for j:= Rect.Left div FBackBitmap.Width to Rect.Right div FBackBitmap.Width do
BitBlt(DC, j*FBackBitmap.Width-Rect.Left,i*FBackBitmap.Height-Rect.Top, FBackBitmap.Width,
FBackBitmap.Height, FBackBitmap.Canvas.Handle, 0, 0, SRCCOPY);
bsStretched:
StretchBlt(DC, -Rect.Left, -Rect.Top, Width, Height,
FBackBitmap.Canvas.Handle, 0, 0, FBackBitmap.Width, FBackBitmap.Height,
SRCCOPY);
bsTiledAndScrolled:
for i:= (Rect.Top+VPos*SmallStep) div FBackBitmap.Height to
(Rect.Bottom+VPos*SmallStep) div FBackBitmap.Height do
for j:= (Rect.Left+HPos) div FBackBitmap.Width to
(Rect.Right+HPos) div FBackBitmap.Width do
BitBlt(DC, j*FBackBitmap.Width-HPos-Rect.Left,i*FBackBitmap.Height-VPos*SmallStep-Rect.Top, FBackBitmap.Width,
FBackBitmap.Height, FBackBitmap.Canvas.Handle, 0, 0, SRCCOPY);
end
end;
{-------------------------------------}
procedure TCustomRichView.WMEraseBkgnd(var Message: TWMEraseBkgnd);
var r1: TRect;
begin
if (csDesigning in ComponentState) then exit;
Message.Result := 1;
if (OldWidth<ClientWidth) or (OldHeight<ClientHeight) then begin
{$IFDEF FPC}
GetClipBox(Message.DC, @r1);
{$ELSE}
GetClipBox(Message.DC, r1);
{$ENDIF}
DrawBack(Message.DC, r1, ClientWidth, ClientHeight);
end;
OldWidth := ClientWidth;
OldHeight := ClientHeight;
end;
{-------------------------------------}
procedure TCustomRichView.SetVSmallStep(Value: Integer);
begin
if (Value<=0) or (TextHeight div Value > 30000) then exit;
SmallStep := Value;
end;
{-------------------------------------}
procedure TCustomRichView.ShareLinesFrom(Source: TCustomRichView);
begin
if ShareContents then begin
Clear;
lines := Source.Lines;
end;
end;
{-------------------------------------}
function TCustomRichView.FindItemAtPos(X,Y: Integer): Integer;
var
i, a,b,mid, midtop: Integer;
dli: TDrawLineInfo;
begin
if DrawLines.Count = 0 then begin
FindItemAtPos := -1;
exit;
end;
dli := TDrawLineInfo(drawlines.Objects[0]);
if (dli.Top<=Y) and (dli.Top+dli.Height>Y) and
(dli.Left<=X) and (dli.Left+dli.Width>X) then begin
FindItemAtPos := 0;
exit;
end;
a := 1;
b := DrawLines.Count-1;
while (b-a)>1 do begin
mid := (a+b) div 2;
if (TDrawLineInfo(drawlines.Objects[mid]).Top<=Y) then
a := mid
else
b := mid;
end;
mid := a;
midtop := TDrawLineInfo(drawlines.Objects[mid]).Top;
while (mid>=1) and
(TDrawLineInfo(drawlines.Objects[mid-1]).Top+
TDrawLineInfo(drawlines.Objects[mid-1]).Height>midtop) do dec(mid);
for i:=1 to 2 do begin
if mid = DrawLines.Count then break;
midtop := TDrawLineInfo(drawlines.Objects[mid]).Top+
TDrawLineInfo(drawlines.Objects[mid]).Height-1;
while (mid<drawlines.Count) do begin
dli := TDrawLineInfo(drawlines.Objects[mid]);
if (dli.Top>midtop) then break;
if (dli.Top<=Y) and (dli.Top+dli.Height>Y) and
(dli.Left<=X) and (dli.Left+dli.Width>X) then begin
FindItemAtPos := mid;
exit;
end;
inc(mid);
end;
end;
FindItemAtPos := -1;
end;
{------------------------------------------------------------------}
procedure TCustomRichView.FindItemForSel(X,Y: Integer; var No, Offs: Integer);
var
styleno,i, a,b,mid, midtop, midbottom, midleft, midright, beginline, endline: Integer;
dli: TDrawLineInfo;
{$IFNDEF RICHVIEWDEF4}
arr: array[0..1000] of integer;
{$ENDIF}
sz: TSIZE;
begin
if DrawLines.Count = 0 then begin
No := -1;
exit;
end;
dli := TDrawLineInfo(drawlines.Objects[0]);
if {(dli.Top<=Y) and }(dli.Top+dli.Height>Y) {and
(dli.Left<=X) and (dli.Left+dli.Width>X)} then
mid := 0
else begin
a := 1;
b := DrawLines.Count-1;
while (b-a)>1 do begin
mid := (a+b) div 2;
if (TDrawLineInfo(drawlines.Objects[mid]).Top<=Y) then
a := mid
else
b := mid;
end;
mid := a;
if TDrawLineInfo(drawlines.Objects[b]).Top<=Y then mid := b;
end;
midtop := TDrawLineInfo(drawlines.Objects[mid]).Top;
midbottom := midtop + TDrawLineInfo(drawlines.Objects[mid]).Height;
// searching beginning of line "mid" belong to
beginline := mid;
while (beginline>=1) and
(TDrawLineInfo(drawlines.Objects[beginline-1]).Top+
TDrawLineInfo(drawlines.Objects[beginline-1]).Height>midtop) do dec(beginline);
// searching end of line "mid" belong to
endline := mid;
while (endline<DrawLines.Count-1) and
(TDrawLineInfo(drawlines.Objects[endline+1]).Top<midbottom) do inc(endline);
// calculating line bounds
midleft := TDrawLineInfo(drawlines.Objects[mid]).Left;
midright := midleft+TDrawLineInfo(drawlines.Objects[mid]).Width;
for i:= beginline to endline do begin
dli := TDrawLineInfo(drawlines.Objects[i]);
if dli.Top < midtop then midtop := dli.Top;
if dli.Top + dli.Height > midbottom then midbottom := dli.Top + dli.Height;
if dli.Left < midleft then midleft := dli.Left;
if dli.Left + dli.Width > midright then midright := dli.Left + dli.Width;
end;
if (Y<midtop) or (X<midleft) then begin
{
No := beginline-1;
if No<0 then begin
No := 0;
Offs := 1;
end
else begin
if TLineInfo(Lines.Objects[TDrawLineInfo(DrawLines.Objects[No]).LineNo]).StyleNo<0 then
Offs := 2
else
Offs := Length(DrawLines[No])+1;
end;
exit;
}
No := beginline;
if TLineInfo(Lines.Objects[TDrawLineInfo(DrawLines.Objects[No]).LineNo]).StyleNo<0 then
Offs := 0
else
Offs := 1;
exit;
end;
if (Y>midbottom) or (X>midright) then begin
No := endline+1;
Offs := 1;
if No>=DrawLines.Count then begin
No := DrawLines.Count-1;
Offs := Length(DrawLines[No])+1;
end
else begin
if TLineInfo(Lines.Objects[TDrawLineInfo(DrawLines.Objects[No]).LineNo]).StyleNo<0 then
Offs := 0;
end;
exit;
end;
for i:= beginline to endline do begin
dli := TDrawLineInfo(drawlines.Objects[i]);
if (dli.Left<=X) and (dli.Left+dli.Width>=X) then begin
styleno := TLineInfo(lines.Objects[dli.LineNo]).StyleNo;
No := i;
Offs := 0;
if styleno>=0 then begin
with FStyle.TextStyles[StyleNo] do begin
Canvas.Font.Style := Style;
Canvas.Font.Size := Size;
Canvas.Font.Name := FontName;
{$IFDEF RICHVIEWDEF3}
Canvas.Font.CharSet := CharSet;
{$ENDIF}
end;
{$IFDEF FPC}
MyGetTextExtentExPoint(Canvas.Handle, PChar(DrawLines[i]), Length(DrawLines[i]),
{$ELSE}
GetTextExtentExPoint(Canvas.Handle, PChar(DrawLines[i]), Length(DrawLines[i]),
{$ENDIF}
X-dli.Left,
{$IFDEF RICHVIEWDEF4}
@Offs, nil,
{$ELSE}
Offs, arr[0],
{$ENDIF}
sz);
inc(Offs);
if Offs>Length(DrawLines[i]) then Offs := Length(DrawLines[i]);
if (Offs < 1) and (Length(DrawLines[i])>0) then Offs := 1;
end
else
Offs := 1;
end;
end;
end;
{------------------------------------------------------------------}
function TCustomRichView.FindClickedWord(var clickedword: String; var StyleNo: Integer): Boolean;
var no, lno: Integer;
{$IFNDEF RICHVIEWDEF4}
arr: array[0..1000] of integer;
{$ENDIF}
sz: TSIZE;
max,first,len: Integer;
begin
FindClickedWord := False;
no := FindItemAtPos(XClicked+HPos, YClicked+VPos*SmallStep);
if no<>-1 then begin
lno := TDrawLineInfo(drawlines.Objects[no]).LineNo;
clickedword := drawlines[no];
styleno := TLineInfo(lines.Objects[lno]).StyleNo;
if styleno>=0 then begin
with FStyle.TextStyles[StyleNo] do begin
Canvas.Font.Style := Style;
Canvas.Font.Size := Size;
Canvas.Font.Name := FontName;
{$IFDEF RICHVIEWDEF3}
Canvas.Font.CharSet := CharSet;
{$ENDIF}
end;
{$IFDEF FPC}
MyGetTextExtentExPoint(Canvas.Handle,PChar(clickedword),Length(clickedword),
{$ELSE}
GetTextExtentExPoint(Canvas.Handle, PChar(clickedword), Length(clickedword),
{$ENDIF}
XClicked+HPos-TDrawLineInfo(drawlines.Objects[no]).Left,
{$IFDEF RICHVIEWDEF4}
@max, nil,
{$ELSE}
max, arr[0],
{$ENDIF}
sz);
inc(max);
if max>Length(clickedword) then max := Length(clickedword);
first := max;
if (Pos(clickedword[first], Delimiters)<>0) then begin
ClickedWord := '';
FindClickedWord := True;
exit;
end;
while (first>1) and (Pos(clickedword[first-1], Delimiters)=0) do
dec(first);
len := max-first+1;
while (first+len-1<Length(clickedword)) and (Pos(clickedword[first+len], Delimiters)=0) do
inc(len);
clickedword := copy(clickedword, first, len);
end;
FindClickedWord := True;
end;
end;
{------------------------------------------------------------------}
procedure TCustomRichView.DblClick;
var
StyleNo: Integer;
clickedword: String;
begin
inherited DblClick;
if SingleClick or (not Assigned(FOnRVDblClick)) then exit;
if FindClickedWord(clickedword, StyleNo) then
FOnRVDblClick(Self, clickedword, StyleNo);
end;
{------------------------------------------------------------------}
procedure TCustomRichView.DeleteSection(CpName: String);
var i,j, startno, endno: Integer;
begin
if ShareContents then exit;
for i:=0 to checkpoints.Count-1 do
if checkpoints[i]=CpName then begin
startno := TCPInfo(checkpoints.Objects[i]).LineNo;
endno := Lines.Count-1;
for j := i+1 to checkpoints.Count-1 do
if checkpoints[j]<>'' then
begin
endno := TCPInfo(checkpoints.Objects[j]).LineNo-1;
break;
end;
DeleteLines(startno, endno-startno+1);
exit;
end;
end;
{------------------------------------------------------------------}
procedure TCustomRichView.DeleteLines(FirstLine, Count: Integer);
var i: Integer;
begin
if ShareContents then exit;
if FirstLine>=lines.Count then exit;
Deselect;
if FirstLine+Count>lines.Count then Count := lines.Count-firstline;
lines.BeginUpdate;
for i:=FirstLine to FirstLine+Count-1 do begin
if TLineInfo(lines.objects[i]).StyleNo = -3 then { image}
begin
TLineInfo(lines.objects[i]).gr.Free;
TLineInfo(lines.objects[i]).gr := nil;
end;
if TLineInfo(lines.objects[i]).StyleNo = -5 then {control}
begin
RemoveControl(TControl(TLineInfo(lines.objects[i]).gr));
TLineInfo(lines.objects[i]).gr.Free;
TLineInfo(lines.objects[i]).gr := nil;
end;
TLineInfo(lines.objects[i]).Free;
lines.objects[i] := nil;
end;
for i:=1 to Count do lines.Delete(FirstLine);
lines.EndUpdate;
end;
{------------------------------------------------------------------}
procedure TCustomRichView.GetSelBounds(var StartNo, EndNo, StartOffs, EndOffs: Integer);
begin
if FSelStartNo <= FSelEndNo then begin
StartNo := FSelStartNo;
EndNo := FSelEndNo;
if not ((StartNo=EndNo) and (FSelStartOffs>FSelEndOffs)) then begin
StartOffs := FSelStartOffs;
EndOffs := FSelEndOffs;
end
else begin
StartOffs := FSelEndOffs;
EndOffs := FSelStartOffs;
end;
end
else begin
StartNo := FSelEndNo;
EndNo := FSelStartNo;
StartOffs := FSelEndOffs;
EndOffs := FSelStartOffs;
end;
end;
{------------------------------------------------------------------}
procedure TCustomRichView.StoreSelBounds(var StartNo, EndNo, StartOffs, EndOffs: Integer);
var dli: TDrawLineInfo;
begin
GetSelBounds(StartNo, EndNo, StartOffs, EndOffs);
if StartNo<>-1 then begin
dli := TDrawLineInfo(DrawLines.Objects[StartNo]);
if TLineInfo(Lines.Objects[dli.LineNo]).StyleNo>=0 then
inc(StartOffs, dli.Offs-1);
StartNo := dli.LineNo;
dli := TDrawLineInfo(DrawLines.Objects[EndNo]);
if TLineInfo(Lines.Objects[dli.LineNo]).StyleNo>=0 then
inc(EndOffs, dli.Offs-1);
EndNo := dli.LineNo;
end;
end;
{------------------------------------------------------------------}
procedure TCustomRichView.RestoreSelBounds(StartNo, EndNo, StartOffs, EndOffs: Integer);
var i: Integer;
dli, dli2, dli3: TDrawLineInfo;
begin
if StartNo = -1 then exit;
for i :=0 to DrawLines.Count-1 do begin
dli := TDrawLineInfo(DrawLines.Objects[i]);
if dli.LineNo = StartNo then
if TLineInfo(Lines.Objects[dli.LineNo]).StyleNo<0 then begin
FSelStartNo := i;
FSelStartOffs := StartOffs;
end
else begin
if i<>DrawLines.Count-1 then
dli2 := TDrawLineInfo(DrawLines.Objects[i+1])
else
dli2 := nil;
if i<>0 then
dli3 := TDrawLineInfo(DrawLines.Objects[i-1])
else
dli3 := nil;
if
((dli.Offs<=StartOffs) and (Length(DrawLines[i])+dli.Offs>StartOffs)) or
((StartOffs>Length(Lines[dli.LineNo])) and ((dli2=nil)or(dli2.LineNo<>dli.LineNo))) or
((dli.Offs>StartOffs) and ((dli3=nil)or(dli3.LineNo<>dli.LineNo)))
then begin
FSelStartNo := i;
FSelStartOffs := StartOffs-dli.Offs+1;
if FSelStartOffs<0 then FSelStartOffs := 0;
if FSelStartOffs>dli.Offs+Length(DrawLines[i]) then FSelStartOffs := dli.Offs+Length(DrawLines[i]);
end;
end;
if dli.LineNo = EndNo then
if TLineInfo(Lines.Objects[dli.LineNo]).StyleNo<0 then begin
FSelEndNo := i;
FSelEndOffs := EndOffs;
end
else begin
if i<>DrawLines.Count-1 then
dli2 := TDrawLineInfo(DrawLines.Objects[i+1])
else
dli2 := nil;
if i<>0 then
dli3 := TDrawLineInfo(DrawLines.Objects[i-1])
else
dli3 := nil;
if
((dli.Offs<=EndOffs) and (Length(DrawLines[i])+dli.Offs>EndOffs)) or
((EndOffs>Length(Lines[dli.LineNo])) and ((dli2=nil)or(dli2.LineNo<>dli.LineNo))) or
((dli.Offs>EndOffs) and ((dli3=nil)or(dli3.LineNo<>dli.LineNo)))
then begin
FSelEndNo := i;
FSelEndOffs := EndOffs-dli.Offs+1;
if FSelEndOffs<0 then FSelEndOffs := 0;
if FSelEndOffs>dli.Offs+Length(DrawLines[i]) then FSelEndOffs := dli.Offs+Length(DrawLines[i]);
end;
end;
end;
end;
{------------------------------------------------------------------}
function TCustomRichView.GetLineCount: Integer;
begin
GetLineCount := lines.Count;
end;
{------------------------------------------------------------------}
function TCustomRichView.SelectionExists: Boolean;
var StartNo, EndNo, StartOffs, EndOffs: Integer;
begin
GetSelBounds(StartNo, EndNo, StartOffs, EndOffs);
if (StartNo = -1) or (EndNo = -1) or ((StartNo=EndNo) and (StartOffs=EndOffs)) then
Result := False
else
Result := True;
end;
{------------------------------------------------------------------}
function TCustomRichView.GetSelText: String;
var StartNo, EndNo, StartOffs, EndOffs, i: Integer;
s : String;
li : TLineInfo;
begin
Result := '';
if not SelectionExists then exit;
{ getting selection as Lines indices }
StoreSelBounds(StartNo, EndNo, StartOffs, EndOffs);
if StartNo = EndNo then begin
li := TLineInfo(Lines.Objects[StartNo]);
if li.StyleNo < 0 then exit;
Result := Copy(Lines[StartNo], StartOffs, EndOffs-StartOffs);
exit;
end
else begin
li := TLineInfo(Lines.Objects[StartNo]);
if li.StyleNo < 0 then
s := ''
else
s := Copy(Lines[StartNo], StartOffs, Length(Lines[StartNo]));
for i := StartNo+1 to EndNo do begin
li := TLineInfo(Lines.Objects[i]);
if (li.StyleNo<>rvsCheckpoint) and not li.SameAsPrev then
s := s+chr(13);
if li.StyleNo >= 0 then
if i<>EndNo then
s := s + Lines[i]
else
s := s + Copy(Lines[i], 1, EndOffs-1);
end;
{$IFDEF FPC}
Result := AdjustLineBreaks(s, tlbsCRLF);
{$ELSE}
Result := AdjustLineBreaks(s);
{$ENDIF}
exit;
end;
end;
{------------------------------------------------------------------}
procedure TCustomRichView.CopyText;
begin
if SelectionExists then begin
ClipBoard.Clear;
Clipboard.SetTextBuf(PChar(GetSelText));
end;
end;
{------------------------------------------------------------------}
procedure TCustomRichView.KeyDown(var Key: Word; Shift: TShiftState);
begin
if SelectionExists and (ssCtrl in Shift) then begin
if (Key = ord('C')) or (Key = VK_INSERT) then CopyText;
end
else
inherited KeyDown(Key,Shift)
end;
{------------------------------------------------------------------}
procedure TCustomRichView.OnScrollTimer(Sender: TObject);
begin
if ScrollDelta<>0 then begin
VScrollPos := VScrollPos+ScrollDelta;
MouseMove([], XMouse, YMouse);
end;
end;
{------------------------------------------------------------------}
procedure TCustomRichView.Notification(AComponent: TComponent; Operation: TOperation);
begin
inherited Notification(AComponent, Operation);
if (Operation=opRemove) and (AComponent=FStyle) then begin
Style := nil;
end;
end;
{------------------------------------------------------------------}
procedure TCustomRichView.Click;
begin
SetFocus;
inherited;
end;
{------------------------------------------------------------------}
procedure TCustomRichView.Loaded;
begin
inherited Loaded;
Format;
end;
function TCustomRichView.GetCredits: string;
begin
result := 'Lazarus TRichView based on RichView v0.5.1 (www.TCustomRichView.com)'
end;
{------------------------------------------------------------------}
{$I RV_Save.inc}
{------------------------------------------------------------------}
end.