{ richmemoutils.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 RichMemoML; {$mode delphi}{$H+} interface uses Classes, SysUtils, RichMemo, RichMemoUtils; type TMarkupFormatHandler = procedure (Sender: TObject; var tag: string; tagattr: TStrings; var font: TFontParams; var txt: string; var tagCloses: Boolean ) of object; TMarkupEntityReplace = procedure (Sender: TObject; var txt: string; tagsStack: TStrings) of object; { TFormatStack } TFormatStack = class(Tobject) public tagName : string; fmt : TFontParams; constructor Create(const atag: string; afmt: TFontParams); end; { TMarkupHandler } TMarkupHandler = class(TObject) private fOnMarkup : TMarkupFormatHandler; fOnReplace : TMarkupEntityReplace; fStack : TList; fStackNames : TStrings; fRichMemo : TRichMemo; procedure Append(const s: string; const fmt: TFontParams); procedure EntityReplace(var s: string); procedure GetTagFmt(var atag: string; st: TStrings; var fmt: TFontParams; var AText: string; var toBeClosed: Boolean); procedure AddStack(const atag: string; const fmt: TFontParams); procedure PopStack(const atag: string; var fmt: TFontParams); procedure Clear; public DefParams: TFontParams; constructor Create; destructor Destroy; override; procedure Parse(ARichMemo: TRichMemo; const atext: string); property OnFormatSelect: TMarkupFormatHandler read fOnMarkup write fOnMarkup; property OnEntityReplace: TMarkupEntityReplace read fOnReplace write fOnReplace; end; procedure Parse(const atext: string; ADstMemo: TRichMemo; AFormat: TMarkupFormatHandler; AReplace: TMarkupEntityReplace = nil); implementation procedure Parse(const atext: string; ADstMemo: TRichMemo; AFormat: TMarkupFormatHandler; AReplace: TMarkupEntityReplace); var h : TMarkupHandler; begin if not Assigned(ADstMemo) or (atext='') then Exit; h := TMarkupHandler.Create; try h.OnFormatSelect:=AFormat; h.OnEntityReplace:=AReplace; h.DefParams:=GetFontParams(ADstMemo.Font); h.Parse(ADstMemo, atext); finally h.Free; end; end; { TFormatStack } constructor TFormatStack.Create(const atag: string; afmt: TFontParams); begin inherited Create; tagName:=atag; fmt:=afmt; end; { TMarkupHandler } procedure TMarkupHandler.Append(const s: string; const fmt: TFontParams); begin InsertFontText(fRichMemo, s, fmt); end; procedure TMarkupHandler.EntityReplace(var s: string); var i : integer; begin //todo: more effecient? fStackNames.Clear; for i:=0 to fStack.Count-1 do fStackNames.Add( TFormatStack(fStack[i]).tagName ); if Assigned(OnEntityReplace) then OnEntityReplace(Self, s, fStackNames); end; procedure TMarkupHandler.GetTagFmt(var atag: string; st: TStrings; var fmt: TFontParams; var AText: string; var toBeClosed: Boolean); begin if Assigned(OnFormatSelect) then OnFormatSelect(Self, atag, st, fmt, atext, toBeClosed); end; procedure TMarkupHandler.AddStack(const atag: string; const fmt: TFontParams); begin fStack.Add(TFormatStack.Create(atag, fmt)); end; procedure TMarkupHandler.PopStack(const atag: string; var fmt: TFontParams); var i : integer; j : integer; fs : TFormatStack; begin i:=fStack.Count-1; while i>=0 do begin fs:=TFormatStack(fStack[i]); if fs.tagName=atag then begin for j:=fStack.Count-1 downto i do begin TFormatStack(fStack[j]).Free; fStack[j]:=nil; end; fStack.Pack; dec(i); if i>=0 then fmt:=TFormatStack(fStack[i]).fmt else fmt:=DefParams; Exit; end else dec(i); end; // do nothing. unknown closing tag end; procedure TMarkupHandler.Clear; var i : Integer; begin for i:=0 to fStack.Count-1 do TFormatStack(fStack[i]).free; fStack.Clear; end; constructor TMarkupHandler.Create; begin inherited; fStack:=TList.Create; fStackNames:=TStringList.Create; end; destructor TMarkupHandler.Destroy; begin Clear; fStackNames.Free; fStack.Free; inherited Destroy; end; procedure TMarkupHandler.Parse(ARichMemo: TRichMemo; const atext: string); var i : integer; j : integer; sb : string; fmt : TFontParams; newfmt : TFontParams; tag : string; openTag : Boolean; tagAttr: TStringList; tobeClosed : Boolean; begin if not Assigned(ARichMemo) then Exit; Clear; fmt:=DefParams; i:=1; j:=1; fRichMemo:=ARichMemo; fRichMemo.Lines.BeginUpdate; tagAttr:=TStringList.Create; try while i<=length(atext) do begin if atext[i]='<' then begin tagattr.Clear; if j'>') do inc(i); if (i>j+1) and (i<=length(atext)) and (atext[j]='/') then begin openTag:=false; inc(j); end else begin openTag:=true; end; tag:=Copy(atext, j, i-j); tag:=AnsiLowerCase(tag); if openTag then begin tobeClosed:=true; sb:=''; newfmt:=fmt; GetTagFmt(tag, tagattr, newfmt, sb, tobeClosed); if sb<>'' then Append(sb, newfmt); if tobeClosed then fmt:=newfmt; AddStack(tag, fmt); end else begin PopStack(tag, fmt); end; inc(i); j:=i; // parsing tag! end else inc(i); end; i:=length(atext)+1; if j