{ 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+} 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); 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; function Print(const params: TPrintParams): Integer; function CharAtPos(x, y: Integer): Integer; 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; 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; 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; { 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; 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; begin if not HandleAllocated then HandleNeeded; if (ModifyMask = []) or (TextLength = 0) then Exit; if TWSCustomRichMemoClass(WidgetSetClass).isInternalChange(Self, ModifyMask) then begin // more effecient from OS view TWSCustomRichMemoClass(WidgetSetClass).SetTextAttributesInternal(Self, TextStart, TextLength, ModifyMask, fnt); Exit; end; // 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; 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 so : TIntSearchOpt; begin if not HandleAllocated then HandleNeeded; if HandleAllocated then begin so.len:=Len; so.start:=Start; so.options:=SearchOpt; Result:=TWSCustomRichMemoClass(WidgetSetClass).Search(Self, ANiddle, so); end else Result:=-1; 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.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; end.