lazarus-ccr/components/richmemo/richmemo.pas
2017-04-30 02:22:02 +00:00

1140 lines
34 KiB
ObjectPascal

{
richmemo.pas
Author: Dmitry 'skalogryz' Boyarintsev
*****************************************************************************
* *
* This file is part of the Lazarus Component Library (LCL) *
* *
* See the file COPYING.modifiedLGPL.txt, included in this distribution, *
* for details about the copyright. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. *
* *
*****************************************************************************
}
unit RichMemo;
{$mode objfpc}{$H+}
{$OBJECTCHECKS OFF}
interface
uses
Types, Classes, SysUtils
, LCLType, LCLIntf, Printers
, Graphics, Controls, StdCtrls, LazUTF8;
type
TVScriptPos = (vpNormal, vpSubScript, vpSuperScript);
TFontParams = record
Name : String;
Size : Integer;
Color : TColor;
Style : TFontStyles;
HasBkClr : Boolean;
BkColor : TColor;
VScriptPos : TVScriptPos;
end;
TParaAlignment = (paLeft, paRight, paCenter, paJustify);
TParaMetric = record
FirstLine : Double; // in points
TailIndent : Double; // in points
HeadIndent : Double; // in points
SpaceBefore : Double; // in points
SpaceAfter : Double; // in points
LineSpacing : Double; // multiplier - matching CSS line-height by percentage/em
// note, that normal LineSpacing is 1.2, not 1.0
end;
const
DefLineSpacing = 1.2;
SingleLineSpacing = DefLineSpacing;
OneHalfLineSpacing = DefLineSpacing * 1.5;
DoubleLineSpacing = DefLineSpacing * 2.0;
type
TParaNumStyle = (pnNone, pnBullet, pnNumber, pnLowLetter
, pnLowRoman, pnUpLetter, pnUpRoman, pnCustomChar);
TParaNumbering = record
Style : TParaNumStyle;
Indent : Double;
CustomChar : WideChar;
NumberStart : Integer; // used for pnNumber only
SepChar : WideChar;
ForceNewNum : Boolean; // if true and Style is pnNumber, NumberStart is used for the new numbering
end;
const
SepNone = #0;
SepPar = ')';
SepDot = '.';
type
TTextModifyMask = set of (tmm_Color, tmm_Name, tmm_Size, tmm_Styles, tmm_BackColor);
TParaModifyMask = set of (pmm_FirstLine, pmm_HeadIndent, pmm_TailIndent, pmm_SpaceBefore, pmm_SpaceAfter, pmm_LineSpacing);
TSearchOption = (soMatchCase, soWholeWord, soBackward);
TSearchOptions = set of TSearchOption;
TParaRange = record
start : Integer; // the first character in the paragraph
lengthNoBr : Integer; // the length of the paragraph, excluding the line break character
length : Integer; // the length of the paragrpah, including the line break, if present
// the last line in the control doesn't contain a line break character,
// thus length = lengthNoBr
end;
type
TTabAlignment = (tabLeft, tabCenter, tabRight, tabDecimal, tabWordBar);
TTabStop = record
Offset : Double;
Align : TTabAlignment; // not used
end;
TTabStopList = record
Count : Integer;
Tabs : array of TTabStop;
end;
type
TRectOffsets = record
Left : Double;
Top : Double;
Right : Double;
Bottom : Double;
end;
TPrintParams = record
JobTitle : String; // print job title to be shown in system printing manager
Margins : TRectOffsets; // margins in points
SelStart : Integer;
SelLength : Integer;
end;
TPrintMeasure = record
Pages : Integer;
end;
TPrintAction = (paDocStart, paPageStart, paPageEnd, paDocEnd);
TPrintActionEvent = procedure (Sender: TObject;
APrintAction: TPrintAction;
PrintCanvas: TCanvas;
CurrentPage: Integer; var AbortPrint: Boolean) of object;
type
TLinkAction = (laClick);
TLinkMouseInfo = record
button : TMouseButton;
end;
TLinkActionEvent = procedure (Sender: TObject;
ALinkAction: TLinkAction;
const info: TLinkMouseInfo;
LinkStart, LinkLen: Integer) of object;
TTextUIFeature = (uiLink);
TTextUIFeatures = set of TTextUIFeature;
TTextUIParam = record
features : TTextUIFeatures;
linkref : String;
end;
type
TRichMemoObject = class(TObject);
TCustomRichMemo = class;
TRichMemoInlineWSObject = TObject;
{ TRichMemoInline }
TRichMemoInline = class(TObject)
private
WSObj : TRichMemoInlineWSObject;
fOwner : TCustomRichMemo;
public
procedure Draw(Canvas: TCanvas; const ASize: TSize); virtual;
procedure SetVisible(AVisible: Boolean); virtual;
procedure Invalidate;
property Owner: TCustomRichMemo read fOwner;
end;
{ TCustomRichMemo }
TCustomRichMemo = class(TCustomMemo)
private
fHideSelection : Boolean;
fOnSelectionChange : TNotifyEvent;
fOnPrintAction : TPrintActionEvent;
fOnLinkAction : TLinkActionEvent;
fZoomFactor : Double;
private
procedure InlineInvalidate(handler: TRichMemoInline);
//todo: PrintMeasure doesn't work propertly
function PrintMeasure(const params: TPrintParams; var est: TPrintMeasure): Boolean;
protected
procedure DoSelectionChange;
class procedure WSRegisterClass; override;
procedure CreateWnd; override;
procedure UpdateRichMemo; virtual;
procedure SetHideSelection(AValue: Boolean);
function GetContStyleLength(TextStart: Integer): Integer;
procedure SetSelText(const SelTextUTF8: string); override;
function GetZoomFactor: Double; virtual;
procedure SetZoomFactor(AValue: Double); virtual;
procedure DoPrintAction(PrintJobEvent: TPrintAction;
PrintCanvas: TCanvas;
CurrentPage: Integer; var AbortPrint: Boolean);
procedure DoLinkAction(ALinkAction: TLinkAction; const AMouseInfo: TLinkMouseInfo;
LinkStart, LinkEnd: Integer);
function GetCanRedo: Boolean; virtual;
public
constructor Create(AOwner: TComponent); override;
procedure CopyToClipboard; override;
procedure CutToClipboard; override;
procedure PasteFromClipboard; override;
function CanPaste: Boolean; virtual;
procedure SetTextAttributes(TextStart, TextLen: Integer; const TextParams: TFontParams); virtual;
function GetTextAttributes(TextStart: Integer; var TextParams: TFontParams): Boolean; virtual;
function GetStyleRange(CharOfs: Integer; var RangeStart, RangeLen: Integer): Boolean; virtual;
function GetParaAlignment(TextStart: Integer; var AAlign: TParaAlignment): Boolean; overload; virtual;
function GetParaAlignment(TextStart: Integer): TParaAlignment; overload;
procedure SetParaAlignment(TextStart, TextLen: Integer; AAlign: TParaAlignment); virtual;
function GetParaMetric(TextStart: Integer; var AMetric: TParaMetric): Boolean; virtual;
procedure SetParaMetric(TextStart, TextLen: Integer; const AMetric: TParaMetric); virtual;
function GetParaNumbering(TextStart: Integer; var ANumber: TParaNumbering): Boolean; virtual;
procedure SetParaNumbering(TextStart, TextLen: Integer; const ANumber: TParaNumbering); virtual;
function GetParaRange(CharOfs: Integer; var ParaRange: TParaRange): Boolean; virtual;
function GetParaRange(CharOfs: Integer; var TextStart, TextLength: Integer): Boolean;
procedure SetParaTabs(TextStart, TextLen: Integer; const AStopList: TTabStopList); virtual;
function GetParaTabs(CharOfs: Integer; var AStopList: TTabStopList): Boolean; virtual;
procedure SetTextAttributes(TextStart, TextLen: Integer; AFont: TFont);
procedure SetRangeColor(TextStart, TextLength: Integer; FontColor: TColor);
procedure SetRangeParams(TextStart, TextLength: Integer; ModifyMask: TTextModifyMask;
const FontName: String; FontSize: Integer; FontColor: TColor; AddFontStyle, RemoveFontStyle: TFontStyles); overload;
procedure SetRangeParams(TextStart, TextLength: Integer; ModifyMask: TTextModifyMask;
const fnt: TFontParams; AddFontStyle, RemoveFontStyle: TFontStyles); overload;
procedure SetRangeParaParams(TextStart, TextLength: Integer; ModifyMask: TParaModifyMask;
const ParaMetric: TParaMetric);
procedure SetLink(TextStart, TextLength: Integer; AIsLink: Boolean; const ALinkRef: String = ''); virtual;
function isLink(TextStart: Integer): Boolean; virtual;
function LoadRichText(Source: TStream): Boolean; virtual;
function SaveRichText(Dest: TStream): Boolean; virtual;
function InDelText(const UTF8Text: string; InsStartChar, ReplaceLength: Integer): Integer; virtual;
function InDelInline(inlineobj: TRichMemoInline; InsStartChar, ReplaceLength: Integer; const ASize: TSize): Integer; virtual;
function GetText(TextStart, TextLength: Integer): String;
function GetUText(TextStart, TextLength: Integer): UnicodeString;
procedure SetSelLengthFor(const aselstr: string);
function Search(const ANiddle: string; Start, Len: Integer; const SearchOpt: TSearchOptions): Integer; overload;
function Search(const ANiddle: string; Start, Len: Integer; const SearchOpt: TSearchOptions; var ATextStart, ATextLength: Integer): Boolean; overload;
function Print(const params: TPrintParams): Integer;
function CharAtPos(x, y: Integer): Integer;
procedure Redo; virtual;
property HideSelection : Boolean read fHideSelection write SetHideSelection;
property OnSelectionChange: TNotifyEvent read fOnSelectionChange write fOnSelectionChange;
property ZoomFactor: Double read GetZoomFactor write SetZoomFactor;
property OnPrintAction: TPrintActionEvent read fOnPrintAction write fOnPrintAction;
property OnLinkAction: TLinkActionEvent read fOnLinkAction write fOnLinkAction;
property CanRedo: Boolean read GetCanRedo;
end;
{ TRichMemo }
TRichMemo = class(TCustomRichMemo)
protected
// this is "design-time" property
fRtf: string; // initial RichText
function GetRTF: string; virtual;
procedure SetRTF(const AValue: string); virtual;
procedure UpdateRichMemo; override;
procedure DestroyHandle; override;
published
property Align;
property Alignment;
property Anchors;
property BidiMode;
property BorderSpacing;
property BorderStyle;
property Color;
property Constraints;
property DragCursor;
property DragKind;
property DragMode;
property Enabled;
property Font;
property HideSelection;
property Lines;
property MaxLength;
property OnChange;
property OnClick;
property OnContextPopup;
property OnDblClick;
property OnDragDrop;
property OnDragOver;
property OnEditingDone;
property OnEndDrag;
property OnEnter;
property OnExit;
property OnKeyDown;
property OnKeyPress;
property OnKeyUp;
property OnLinkAction;
property OnMouseDown;
property OnMouseEnter;
property OnMouseLeave;
property OnMouseMove;
property OnMouseUp;
property OnMouseWheel;
property OnMouseWheelDown;
property OnMouseWheelUp;
property OnSelectionChange;
property OnStartDrag;
property OnPrintAction;
property OnUTF8KeyPress;
property ParentBidiMode;
property ParentColor;
property ParentFont;
property PopupMenu;
property ParentShowHint;
property ReadOnly;
property Rtf: string read GetRTF write SetRTF;
property ScrollBars;
property ShowHint;
property TabOrder;
property TabStop;
property Visible;
property WantReturns;
property WantTabs;
property WordWrap;
property ZoomFactor;
end;
procedure InitFontParams(var p: TFontParams);
function GetFontParams(styles: TFontStyles): TFontParams; overload;
function GetFontParams(color: TColor; styles: TFontStyles): TFontParams; overload;
function GetFontParams(const Name: String; color: TColor; styles: TFontStyles): TFontParams; overload;
function GetFontParams(const Name: String; Size: Integer; color: TColor; styles: TFontStyles): TFontParams; overload;
function GetFontParams(AFont: TFont): TFontParams; overload;
procedure InitParaMetric(var m: TParaMetric);
procedure InitParaNumbering(var n: TParaNumbering);
procedure InitParaNumber(var n: TParaNumbering; ASepChar: WideChar = SepPar; StartNum: Integer = 1);
procedure InitParaBullet(var n: TParaNumbering);
procedure InitTabStopList(var tabs: TTabStopList); overload;
procedure InitTabStopList(var tabs: TTabStopList; const TabStopsPt: array of double); overload;
procedure InitPrintParams(var prm: TPrintParams);
procedure InitTextUIParams(var prm: TTextUIParam);
var
RTFLoadStream : function (AMemo: TCustomRichMemo; Source: TStream): Boolean = nil;
RTFSaveStream : function (AMemo: TCustomRichMemo; Dest: TStream): Boolean = nil;
implementation
uses
{%H-}RichMemoFactory, WSRichMemo;
procedure InitFontParams(var p: TFontParams);
begin
FillChar(p, SizeOf(p), 0);
end;
function GetFontParams(styles: TFontStyles): TFontParams; overload;
begin
Result := GetFontParams('', 0, 0, styles);
end;
function GetFontParams(color: TColor; styles: TFontStyles): TFontParams; overload;
begin
Result := GetFontParams('', 0, color, styles);
end;
function GetFontParams(const Name: String; color: TColor; styles: TFontStyles): TFontParams; overload;
begin
Result := GetFontParams(Name, 0, color, styles);
end;
function GetFontParams(const Name: String; Size: Integer; color: TColor; styles: TFontStyles): TFontParams; overload;
begin
InitFontParams(Result);
Result.Name := Name;
Result.Size := Size;
Result.Color := color;
Result.Style := styles;
end;
//todo: get rid of this Graphics.GetFontData dupication
//this is the only function that's using LCLType and LCLIntf
function RMGetFontData(Font: HFont): TFontData;
var
ALogFont: TLogFont;
begin
Result := DefFontData;
if Font <> 0 then
begin
if GetObject(Font, SizeOf(ALogFont), @ALogFont) <> 0 then
with Result, ALogFont do
begin
Height := lfHeight;
if lfWeight >= FW_BOLD then Include(Style, fsBold);
if lfItalic > 0 then Include(Style, fsItalic);
if lfUnderline > 0 then Include(Style, fsUnderline);
if lfStrikeOut > 0 then Include(Style, fsStrikeOut);
Charset := TFontCharset(lfCharSet);
Name := lfFaceName;
case lfPitchAndFamily and $F of
VARIABLE_PITCH: Pitch := fpVariable;
FIXED_PITCH: Pitch := fpFixed;
else
Pitch := fpDefault;
end;
Orientation := lfOrientation;
Handle := Font;
end;
end;
end;
function GetFontParams(AFont: TFont): TFontParams; overload;
var
data : TFontData;
wstest : Boolean;
begin
InitFontParams(Result);
if not Assigned(AFont) then Exit;
if AFont.Reference.Handle <> 0 then begin
// WSGetFontParams is introduced, because default Gtk widgetset returns
// only FontName from the handle.
wstest:= Assigned(WSGetFontParams) and WSGetFontParams(AFont.Reference.Handle, Result);
if not wstest then begin
data:=RMGetFontData(AFont.Reference.Handle);
if data.Height<0
then Result.Size:=round(abs(data.Height)/ScreenInfo.PixelsPerInchY*72)
else Result.Size:=data.Height;
Result.Name:=data.Name;
Result.Style:=data.Style;
end;
// color is not stored with system font information
// it's an additional attribute introduced in TFont class
Result.Color:=AFont.Color;
end else begin
Result.Name := AFont.Name;
Result.Color := AFont.Color;
Result.Size := AFont.Size;
Result.Style := AFont.Style;
end;
end;
procedure InitParaMetric(var m: TParaMetric);
begin
FillChar(m, sizeof(m), 0);
m.LineSpacing:=DefLineSpacing;
end;
procedure InitParaNumbering(var n: TParaNumbering);
begin
FillChar(n, sizeof(n), 0);
end;
procedure InitParaNumber(var n: TParaNumbering; ASepChar: WideChar; StartNum: Integer);
begin
InitParaNumbering(n);
n.Style:=pnNumber;
n.NumberStart:=StartNum;
n.SepChar:=ASepChar;
end;
procedure InitParaBullet(var n: TParaNumbering);
begin
InitParaNumbering(n);
n.Style:=pnBullet;
end;
procedure InitTabStopList(var tabs: TTabStopList);
begin
FillChar(tabs, sizeof(tabs), 0);
end;
procedure InitTabStopList(var tabs: TTabStopList; const TabStopsPt: array of double);
var
i : Integer;
begin
InitTabStopList(tabs);
tabs.count:=length(TabStopsPt);
SetLength(tabs.tabs, tabs.Count);
for i:=0 to tabs.Count-1 do begin
tabs.tabs[i].Offset:=TabStopsPt[i];
end;
end;
procedure InitPrintParams(var prm: TPrintParams);
begin
FillChar(prm, sizeof(prm), 0);
end;
procedure InitTextUIParams(var prm: TTextUIParam);
begin
FillChar(prm, sizeof(prm), 0);
end;
{ TRichMemoInline }
procedure TRichMemoInline.Draw(Canvas: TCanvas; const ASize: TSize);
begin
end;
procedure TRichMemoInline.SetVisible(AVisible: Boolean);
begin
end;
procedure TRichMemoInline.Invalidate;
begin
if not Assigned(fOwner) then Exit;
Owner.InlineInvalidate( Self );
end;
{ TRichMemo }
function TRichMemo.GetRTF: string;
var
st : TStringStream;
begin
if (csDesigning in ComponentState) or not HandleAllocated then
Result:=fRTF
else begin
try
st := TStringStream.Create('');
try
SaveRichText(st);
Result:=st.DataString;
finally
st.Free;
end;
except
Result:='';
end;
end;
end;
procedure TRichMemo.SetRTF(const AValue: string);
var
st : TStringStream;
begin
if (csDesigning in ComponentState) or not HandleAllocated then
fRTF:=AValue;
if HandleAllocated then
try
st := TStringStream.Create(AValue);
try
LoadRichText(st);
finally
st.Free;
end;
except
end;
if ([csDesigning, csLoading] * ComponentState = []) and HandleAllocated then begin
fRTF:=''; // reduce memory usage in run-time
end;
end;
procedure TRichMemo.UpdateRichMemo;
begin
inherited UpdateRichMemo;
// if fRTF is blank, Text property would be used
if fRTF<>'' then SetRTF(fRTF);
end;
procedure TRichMemo.DestroyHandle;
begin
fRTF:=GetRTF;
inherited DestroyHandle;
end;
{ TCustomRichMemo }
procedure TCustomRichMemo.SetHideSelection(AValue: Boolean);
begin
if HandleAllocated then
TWSCustomRichMemoClass(WidgetSetClass).SetHideSelection(Self, AValue);
fHideSelection := AValue;
end;
function TCustomRichMemo.GetZoomFactor: Double;
begin
Result:=fZoomFactor;
if HandleAllocated then begin
if TWSCustomRichMemoClass(WidgetSetClass).GetZoomFactor(Self, Result) then
fZoomFactor:=Result;
end;
end;
procedure TCustomRichMemo.SetZoomFactor(AValue: Double);
begin
if AValue=0 then AValue:=1.0;
fZoomFactor:=AValue;
if HandleAllocated then
TWSCustomRichMemoClass(WidgetSetClass).SetZoomFactor(Self, AValue);
end;
procedure TCustomRichMemo.DoPrintAction(PrintJobEvent: TPrintAction;
PrintCanvas: TCanvas; CurrentPage: Integer; var AbortPrint: Boolean);
begin
if Assigned(OnPrintAction) then
OnPrintAction(Self, PrintJobEvent, PrintCanvas, CurrentPAge, AbortPrint);
end;
procedure TCustomRichMemo.DoLinkAction(ALinkAction: TLinkAction; const AMouseInfo: TLinkMouseInfo; LinkStart,
LinkEnd: Integer);
begin
if Assigned(OnLinkAction) then
OnLinkAction(Self, ALinkAction, AMouseInfo, LinkStart, LinkEnd);
end;
procedure TCustomRichMemo.InlineInvalidate(handler: TRichMemoInline);
begin
if not Assigned(handler) then Exit;
if not HandleAllocated then HandleNeeded;
if HandleAllocated then
TWSCustomRichMemoClass(WidgetSetClass).InlineInvalidate(Self, handler, handler.WSObj);
end;
procedure TCustomRichMemo.DoSelectionChange;
begin
if Assigned(fOnSelectionChange) then fOnSelectionChange(Self);
end;
class procedure TCustomRichMemo.WSRegisterClass;
begin
inherited;
WSRegisterCustomRichMemo;
end;
procedure TCustomRichMemo.CreateWnd;
begin
inherited CreateWnd;
UpdateRichMemo;
end;
procedure TCustomRichMemo.UpdateRichMemo;
begin
if not HandleAllocated then Exit;
TWSCustomRichMemoClass(WidgetSetClass).SetHideSelection(Self, fHideSelection);
TWSCustomRichMemoClass(WidgetSetClass).SetZoomFactor(Self, fZoomFactor);
end;
procedure TCustomRichMemo.SetTextAttributes(TextStart, TextLen: Integer;
AFont: TFont);
begin
if not Assigned(AFont) then Exit;
SetTextAttributes(TextStart, TextLen, GetFontParams(AFont));
end;
procedure TCustomRichMemo.SetTextAttributes(TextStart, TextLen: Integer;
{SetMask: TTextStyleMask;} const TextParams: TFontParams);
begin
if not HandleAllocated then HandleNeeded;
if HandleAllocated then
TWSCustomRichMemoClass(WidgetSetClass).SetTextAttributes(Self, TextStart, TextLen, {SetMask,} TextParams);
end;
function TCustomRichMemo.GetTextAttributes(TextStart: Integer; var TextParams: TFontParams): Boolean;
begin
if not HandleAllocated then HandleNeeded;
if HandleAllocated then
Result := TWSCustomRichMemoClass(WidgetSetClass).GetTextAttributes(Self, TextStart, TextParams)
else
Result := false;
end;
function TCustomRichMemo.GetStyleRange(CharOfs: Integer; var RangeStart,
RangeLen: Integer): Boolean;
begin
if HandleAllocated then begin
Result := TWSCustomRichMemoClass(WidgetSetClass).GetStyleRange(Self, CharOfs, RangeStart, RangeLen);
if Result and (RangeLen = 0) then RangeLen := 1;
end else begin
RangeStart := -1;
RangeLen := -1;
Result := false;
end;
end;
function TCustomRichMemo.GetParaAlignment(TextStart: Integer;
var AAlign: TParaAlignment): Boolean;
begin
Result := HandleAllocated and
TWSCustomRichMemoClass(WidgetSetClass).GetParaAlignment(Self, TextStart, AAlign);
end;
function TCustomRichMemo.GetParaAlignment(TextStart: Integer): TParaAlignment;
begin
GetParaAlignment(TextStart, Result);
end;
procedure TCustomRichMemo.SetParaAlignment(TextStart, TextLen: Integer;
AAlign: TParaAlignment);
begin
if HandleAllocated then
TWSCustomRichMemoClass(WidgetSetClass).SetParaAlignment(Self, TextStart, TextLen, AAlign);
end;
function TCustomRichMemo.GetParaMetric(TextStart: Integer;
var AMetric: TParaMetric): Boolean;
begin
if HandleAllocated then
Result := TWSCustomRichMemoClass(WidgetSetClass).GetParaMetric(Self, TextStart, AMetric)
else
Result := false;
end;
procedure TCustomRichMemo.SetParaMetric(TextStart, TextLen: Integer;
const AMetric: TParaMetric);
begin
if HandleAllocated then
TWSCustomRichMemoClass(WidgetSetClass).SetParaMetric(Self, TextStart, TextLen, AMetric);
end;
function TCustomRichMemo.GetParaNumbering(TextStart: Integer;
var ANumber: TParaNumbering): Boolean;
begin
if HandleAllocated then
Result := TWSCustomRichMemoClass(WidgetSetClass).GetParaNumbering(Self, TextStart, ANumber)
else
Result := false;
end;
procedure TCustomRichMemo.SetParaNumbering(TextStart, TextLen: Integer;
const ANumber: TParaNumbering);
begin
if HandleAllocated then
TWSCustomRichMemoClass(WidgetSetClass).SetParaNumbering(Self, TextStart, TextLen, ANumber);
end;
function TCustomRichMemo.GetParaRange(CharOfs: Integer;
var ParaRange: TParaRange): Boolean;
begin
Result:=false;
if not HandleAllocated then HandleNeeded;
if HandleAllocated then
Result:=TWSCustomRichMemoClass(WidgetSetClass).GetParaRange(Self, CharOfs, ParaRange);
end;
function TCustomRichMemo.GetParaRange(CharOfs: Integer; var TextStart,
TextLength: Integer): Boolean;
var
p : TParaRange;
begin
Result:=GetParaRange(CharOfs, p);
TextStart:=p.start;
TextLength:=p.length;
end;
procedure TCustomRichMemo.SetParaTabs(TextStart, TextLen: Integer;
const AStopList: TTabStopList);
begin
if HandleAllocated then
TWSCustomRichMemoClass(WidgetSetClass).SetParaTabs(Self, TextStart, TextLen, AStopList);
end;
function TCustomRichMemo.GetParaTabs(CharOfs: Integer; var AStopList: TTabStopList): Boolean;
begin
Result:=false;
if not HandleAllocated then HandleNeeded;
if HandleAllocated then
Result:=TWSCustomRichMemoClass(WidgetSetClass).GetParaTabs(Self, CharOfs, AStopList);
end;
function TCustomRichMemo.GetContStyleLength(TextStart: Integer): Integer;
var
ofs, len : Integer;
begin
if GetStyleRange(TextStart, ofs, len)
then Result := len - (TextStart-ofs)
else Result := 0;
end;
procedure TCustomRichMemo.SetSelText(const SelTextUTF8: string);
var
st : Integer;
begin
if not HandleAllocated then HandleNeeded;
Lines.BeginUpdate;
try
st := SelStart;
if HandleAllocated then
TWSCustomRichMemoClass(WidgetSetClass).InDelText(Self, SelTextUTF8, SelStart, SelLength);
SelStart := st;
SelLength := length(UTF8Decode(SelTextUTF8));
finally
Lines.EndUpdate;
end;
end;
constructor TCustomRichMemo.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
fZoomFactor:=1;
end;
procedure TCustomRichMemo.CopyToClipboard;
begin
if HandleAllocated then
TWSCustomRichMemoClass(WidgetSetClass).CopyToClipboard(Self);
end;
procedure TCustomRichMemo.CutToClipboard;
begin
if HandleAllocated then
TWSCustomRichMemoClass(WidgetSetClass).CutToClipboard(Self);
end;
procedure TCustomRichMemo.PasteFromClipboard;
begin
if HandleAllocated then
TWSCustomRichMemoClass(WidgetSetClass).PasteFromClipboard(Self);
end;
function TCustomRichMemo.CanPaste: Boolean;
begin
if not HandleAllocated then HandleNeeded;
if HandleAllocated then
Result:=TWSCustomRichMemoClass(WidgetSetClass).CanPasteFromClipboard(Self);
end;
procedure TCustomRichMemo.SetRangeColor(TextStart, TextLength: Integer; FontColor: TColor);
begin
SetRangeParams(TextStart, TextLength, [tmm_Color], '', 0, FontColor, [], []);
end;
procedure TCustomRichMemo.SetRangeParams(TextStart, TextLength: Integer; ModifyMask: TTextModifyMask;
const FontName: String; FontSize: Integer; FontColor: TColor; AddFontStyle, RemoveFontStyle: TFontStyles);
var
fnt : TFontParams;
begin
InitFontParams(fnt);
fnt.Name:=FontName;
fnt.Size:=FontSize;
fnt.Color:=FontColor;
SetRangeParams(TextStart, TextLength, ModifyMask, fnt, AddFontStyle, RemoveFontStyle);
end;
procedure TCustomRichMemo.SetRangeParams(TextStart, TextLength: Integer;
ModifyMask: TTextModifyMask; const fnt: TFontParams; AddFontStyle,
RemoveFontStyle: TFontStyles);
var
i : Integer;
j : Integer;
l : Integer;
p : TFontParams;
allowInternalChange: Boolean;
fp : TFontParams;
const
AllFontStyles : TFontStyles = [fsBold, fsItalic, fsUnderline, fsStrikeOut];
begin
if not HandleAllocated then HandleNeeded;
if (ModifyMask = []) or (TextLength = 0) then Exit;
allowInternalChange:=(not (tmm_Styles in ModifyMask)) or (AddFontStyle+RemoveFontStyle=AllFontStyles);
if allowInternalChange and (TWSCustomRichMemoClass(WidgetSetClass).isInternalChange(Self, ModifyMask)) then
begin
// more effecient from OS view
fp:=fnt;
if tmm_Styles in ModifyMask then fp.Style:=AddFontStyle;
TWSCustomRichMemoClass(WidgetSetClass).SetTextAttributesInternal(Self,
TextStart, TextLength, ModifyMask, fp);
Exit;
end;
Lines.BeginUpdate;
try
// manually looping from text ranges and re-applying
// all the style. changing only the ones that in the mask
i := TextStart;
j := TextStart + TextLength;
while i < j do begin
GetTextAttributes(i, p);
if tmm_Name in ModifyMask then p.Name := fnt.Name;
if tmm_Color in ModifyMask then p.Color := fnt.Color;
if tmm_Size in ModifyMask then p.Size := fnt.Size;
if tmm_Styles in ModifyMask then p.Style := p.Style + AddFontStyle - RemoveFontStyle;
if tmm_BackColor in ModifyMask then begin
p.HasBkClr:=fnt.HasBkClr;
p.BkColor:=fnt.BkColor;
end;
l := GetContStyleLength(i);
if i + l > j then l := j - i;
if l = 0 then Break;
SetTextAttributes(i, l, p);
inc(i, l);
end;
finally
Lines.EndUpdate
end;
end;
procedure TCustomRichMemo.SetRangeParaParams(TextStart, TextLength: Integer;
ModifyMask: TParaModifyMask; const ParaMetric: TParaMetric);
var
ln: Integer;
m : TParaMetric;
begin
repeat
if not GetParaRange(TextStart, TextStart, ln) then Break;
if ln=0 then Break;
GetParaMetric(TextStart, m);
if pmm_FirstLine in ModifyMask then m.FirstLine:=ParaMetric.FirstLine;
if pmm_HeadIndent in ModifyMask then m.HeadIndent:=ParaMetric.HeadIndent;
if pmm_TailIndent in ModifyMask then m.TailIndent:=ParaMetric.TailIndent;
if pmm_SpaceBefore in ModifyMask then m.SpaceBefore:=ParaMetric.SpaceBefore;
if pmm_SpaceAfter in ModifyMask then m.SpaceAfter:=ParaMetric.SpaceAfter;
if pmm_LineSpacing in ModifyMask then m.LineSpacing:=ParaMetric.LineSpacing;
SetParaMetric(TextStart, 1, m);
inc(TextStart, ln);
dec(TextLength, ln);
until TextLength<=0;
end;
procedure TCustomRichMemo.SetLink(TextStart, TextLength: Integer; AIsLink: Boolean; const ALinkRef: String);
var
ui : TTextUIParam;
begin
if HandleAllocated then begin
TWSCustomRichMemoClass(WidgetSetClass).GetTextUIParams(Self, TextStart, ui);
if AIsLink then begin
Include(ui.features, uiLink);
ui.linkref:=ALinkRef;
end else
Exclude(ui.features, uiLink);
TWSCustomRichMemoClass(WidgetSetClass).SetTextUIParams(Self, TextStart, TextLength, ui);
end;
end;
function TCustomRichMemo.isLink(TextStart: Integer): Boolean;
var
ui : TTextUIParam;
begin
Result:=HandleAllocated and TWSCustomRichMemoClass(WidgetSetClass).GetTextUIParams(Self, TextStart, ui);
if Result then Result:=uiLink in ui.features;
end;
function TCustomRichMemo.LoadRichText(Source: TStream): Boolean;
begin
Result:=false;
if not HandleAllocated then HandleNeeded;
if Assigned(Source) and HandleAllocated then begin
if Assigned(RTFLoadStream) then begin
Self.Lines.BeginUpdate;
try
Self.Lines.Clear;
Result:=RTFLoadStream(Self, Source);
finally
Self.Lines.EndUpdate;
end;
end else begin
Result := TWSCustomRichMemoClass(WidgetSetClass).LoadRichText(Self, Source);
end;
end;
end;
function TCustomRichMemo.SaveRichText(Dest: TStream): Boolean;
begin
if Assigned(Dest) and HandleAllocated then begin
if Assigned(RTFSaveStream) then begin
Result := RTFSaveStream(Self, Dest)
end else
Result := TWSCustomRichMemoClass(WidgetSetClass).SaveRichText(Self, Dest);
end else
Result := false;
end;
function TCustomRichMemo.InDelText(const UTF8Text: string; InsStartChar, ReplaceLength: Integer): Integer;
begin
Result:=0;
if not HandleAllocated then HandleNeeded;
if HandleAllocated then begin
TWSCustomRichMemoClass(WidgetSetClass).InDelText(Self, UTF8Text, InsStartChar, ReplaceLength);
Result:=UTF8length(UTF8Text);
end;
end;
function TCustomRichMemo.InDelInline(inlineobj: TRichMemoInline; InsStartChar,
ReplaceLength: Integer; const ASize: TSize): Integer;
var
obj : TRichMemoInlineWSObject;
begin
Result:=0;
if not Assigned(inlineObj) then Exit;
if Assigned(inlineobj.fOwner) and (inlineobj.fOwner<>Self) then Exit;
if not HandleAllocated then HandleNeeded;
if HandleAllocated then begin
obj:=nil;
if not TWSCustomRichMemoClass(WidgetSetClass).InlineInsert(Self, InsStartChar
, ReplaceLength, ASize, inlineObj, obj) then begin
inlineObj.Free;
Result:=0;
end;
if not Assigned(inlineObj.fOwner) then inlineObj.fOwner:=Self;
inlineObj.WSObj:=obj;
Result:=ReplaceLength;
end else
inlineObj.Free;
end;
function TCustomRichMemo.GetText(TextStart, TextLength: Integer): String;
var
isu : Boolean;
txt : String;
utxt : UnicodeString;
begin
Result:='';
if not HandleAllocated then HandleNeeded;
if HandleAllocated and not TWSCustomRichMemoClass(WidgetSetClass).GetSubText(Self, TextStart, TextLength, false, isu, txt, utxt) then
Exit;
if isu then Result:=UTF8Decode(utxt)
else Result:=txt;
end;
function TCustomRichMemo.GetUText(TextStart, TextLength: Integer): UnicodeString;
var
isu : Boolean;
txt : String;
utxt : UnicodeString;
begin
Result:='';
if not HandleAllocated then HandleNeeded;
if HandleAllocated and not TWSCustomRichMemoClass(WidgetSetClass).GetSubText(Self, TextStart, TextLength, false, isu, txt, utxt) then
Exit;
if isu then Result:=utxt
else Result:=UTF8Encode(txt);
end;
procedure TCustomRichMemo.SetSelLengthFor(const aselstr: string);
begin
SelLength:=UTF8Length(aselstr);
end;
function TCustomRichMemo.Search(const ANiddle: string; Start, Len: Integer;
const SearchOpt: TSearchOptions): Integer;
var
ln : Integer;
begin
ln := 0;
if not Search(ANiddle, Start, Len, SearchOpt, Result, ln) then Result:=-1;
end;
function TCustomRichMemo.Search(const ANiddle: string; Start, Len: Integer; const SearchOpt: TSearchOptions;
var ATextStart, ATextLength: Integer): Boolean; overload;
var
so : TIntSearchOpt;
begin
if not HandleAllocated then HandleNeeded;
if HandleAllocated then begin
so.len:=Len;
so.start:=Start;
so.options:=SearchOpt;
if not TWSCustomRichMemoClass(WidgetSetClass).isSearchEx then begin
ATextStart:=TWSCustomRichMemoClass(WidgetSetClass).Search(Self, ANiddle, so);
// not recommended. The text found coulbe longer than Niddle
// depending on the language and search options (to be done)
// mostly for Arabi and Hebrew languages
ATextLength:=UTF8Length(ANiddle);
end else begin
Result:=TWSCustomRichMemoClass(WidgetSetClass).SearchEx(Self, ANiddle, so, ATextStart, ATextLength);
end;
end else
Result:=false;
end;
function TCustomRichMemo.Print(const params: TPrintParams): Integer;
begin
Result:=0;
if not Assigned(Printer) then Exit;
if not HandleAllocated then HandleNeeded;
if HandleAllocated then
Result:=TWSCustomRichMemoClass(WidgetSetClass).Print(Self, Printer, params, true);
end;
function TCustomRichMemo.CharAtPos(x, y: Integer): Integer;
begin
if HandleAllocated then
Result:=TWSCustomRichMemoClass(WidgetSetClass).CharAtPos(Self, x, y)
else
Result:=-1;
end;
function TCustomRichMemo.GetCanRedo: Boolean;
begin
if HandleAllocated then
Result:=TWSCustomRichMemoClass(WidgetSetClass).GetCanRedo(Self)
else
Result:=false;
end;
function TCustomRichMemo.PrintMeasure(const params: TPrintParams; var est: TPrintMeasure): Boolean;
begin
if not Assigned(Printer) then begin
Result:=False;
Exit;
end;
if not HandleAllocated then HandleNeeded;
if HandleAllocated then begin
est.Pages:=TWSCustomRichMemoClass(WidgetSetClass).Print(Self, Printer, params, false);
end else
Result:=false;
end;
procedure TCustomRichMemo.Redo;
begin
if HandleAllocated then
TWSCustomRichMemoClass(WidgetSetClass).Redo(Self);
end;
end.