unit qt5richmemo; interface {$mode delphi} {$define RMQT5_TEXTFORMATS} // the feature is available in Qt5 Trunk // it allows to query Qt5TextEdit for character formats array {$ifdef RMQT5_NOTEXTFORMATS}{$undef RMQT5_TEXTFORMATS}{$endif} // // Following class methods are need for the implementation // QTextCharFormatH // QTextBlockFormatH uses LCLType, Controls, StdCtrls, Graphics, qt5, qtobjects, qtwidgets, qtprivate, WSProc, RichMemo, WSRichMemo; type { TQtWSCustomRichMemo } TQtWSCustomRichMemo = class(TWSCustomRichMemo) published class function CreateHandle(const AWinControl: TWinControl; const AParams: TCreateParams): HWND; override; class function GetParaAlignment(const AWinControl: TWinControl; TextStart: Integer; var AAlign: TIntParaAlignment): Boolean; override; class procedure SetParaAlignment(const AWinControl: TWinControl; TextStart, TextLen: Integer; const AAlign: TIntParaAlignment); override; class function GetTextAttributes(const AWinControl: TWinControl; TextStart: Integer; var Params: TIntFontParams): Boolean; override; class procedure SetTextAttributes(const AWinControl: TWinControl; TextStart, TextLen: Integer; const Params: TIntFontParams); override; class function Search(const AWinControl: TWinControl; const ANiddle: string; const SearchOpts: TIntSearchOpt): Integer; override; class function isInternalChange(const AWinControl: TWinControl; Params: TTextModifyMask ): Boolean; override; class procedure SetTextAttributesInternal(const AWinControl: TWinControl; TextStart, TextLen: Integer; const AModifyMask: TTextModifyMask; const Params: TIntFontParams); override; class function GetStyleRange(const AWinControl: TWinControl; TextStart: Integer; var RangeStart, RangeLen: Integer): Boolean; override; end; type TEditorState = record selst : integer; // selection start sellen : integer; // selection length end; // no sanity check is done in these functions procedure MakeBackup(te: TQtTextEdit; out backup: TEditorState); procedure ApplyBackup(te: TQtTextEdit; const backup: TEditorState); implementation const WordWrapMap: array[Boolean] of QTextEditLineWrapMode = ( QTextEditNoWrap, QTextEditWidgetWidth ); AlignmentMap: array[TIntParaAlignment] of QtAlignment = ( QtAlignLeft, QtAlignRight, QtAlignHCenter, QtAlignJustify ); { TQtWSCustomRichMemo } class function TQtWSCustomRichMemo.CreateHandle(const AWinControl: TWinControl; const AParams: TCreateParams): HWND; var QtTextEdit: TQtTextEdit; begin QtTextEdit := TQtTextEdit.Create(AWinControl, AParams); QtTextEdit.AcceptRichText := True; QtTextEdit.ClearText; QtTextEdit.setBorder(TCustomMemo(AWinControl).BorderStyle = bsSingle); QtTextEdit.setReadOnly(TCustomMemo(AWinControl).ReadOnly); QtTextEdit.setLineWrapMode(WordWrapMap[TCustomMemo(AWinControl).WordWrap]); // create our FList helper QtTextEdit.FList := TQtMemoStrings.Create(TCustomMemo(AWinControl)); QtTextEdit.setScrollStyle(TCustomMemo(AWinControl).ScrollBars); QtTextEdit.setTabChangesFocus(not TCustomMemo(AWinControl).WantTabs); QtTextEdit.AttachEvents; Result := TLCLIntfHandle(QtTextEdit); end; class procedure TQtWSCustomRichMemo.SetParaAlignment( const AWinControl: TWinControl; TextStart, TextLen: Integer; const AAlign: TIntParaAlignment); var w : QTextEditH; te : TQtTextEdit; ss, sl : Integer; begin if not WSCheckHandleAllocated(AWinControl, 'SetParaAlignment') then Exit; te:=TQtTextEdit(AWinControl.Handle); w:=QTextEditH(te.Widget); ss:=te.getSelectionStart; sl:=te.getSelectionLength; te.setSelection(TextStart, TextLen); // alignment QTextEdit_setAlignment(w, AlignmentMap[AAlign]); te.setSelection(ss, sl); end; const QNormal = 50; QBold = 75; class function TQtWSCustomRichMemo.GetTextAttributes( const AWinControl: TWinControl; TextStart: Integer; var Params: TIntFontParams ): Boolean; var w : QTextEditH; te : TQtTextEdit; ws : WideString; clr: TQColor; bck : TEditorState; begin InitFontParams(Params); if not WSCheckHandleAllocated(AWinControl, 'GetTextAttributes') then begin Result:=false; Exit; end; te:=TQtTextEdit(AWinControl.Handle); w:=QTextEditH(te.Widget); MakeBackup(te, bck); te.setSelection(TextStart, 1); //todo! ws:=''; QTextEdit_fontFamily(w, @ws); if ws<>'' then Params.Name:=UTF8Encode(ws); Params.Size:=round(QTextEdit_fontPointSize(w)); if QTextEdit_fontWeight(w)>=QBold then Include(Params.Style, fsBold); if QTextEdit_fontItalic(w) then Include(Params.Style, fsItalic); if QTextEdit_fontUnderline(w) then Include(Params.Style, fsUnderline); FillChar(clr, sizeof(clr), 0); QTextEdit_textColor(w, @clr); TQColorToColorRef(clr, TColorRef(params.Color)); FillChar(clr, sizeof(clr), 0); QTextEdit_textBackgroundColor(w, @clr); TQColorToColorRef(clr, TColorRef(params.BkColor)); //todo! params.HasBkClr:=false; ApplyBackup(te, bck); Result:=true; end; class procedure TQtWSCustomRichMemo.SetTextAttributes( const AWinControl: TWinControl; TextStart, TextLen: Integer; const Params: TIntFontParams); var w : QTextEditH; te : TQtTextEdit; ss, sl : Integer; ws : WideString; clr: TQColor; const QNormal = 50; QBold = 75; const QIsBold: array [Boolean] of integer = (QNormal, QBold); begin if not WSCheckHandleAllocated(AWinControl, 'SetTextAttributes') then Exit; te:=TQtTextEdit(AWinControl.Handle); w:=QTextEditH(te.Widget); ss:=te.getSelectionStart; sl:=te.getSelectionLength; te.setSelection(TextStart, TextLen); ws:=UTF8Decode(Params.Name); if ws<>'' then QTextEdit_setFontFamily(w, @ws); if Params.Size>0 then QTextEdit_setFontPointSize(w, Params.Size); QTextEdit_setFontUnderline(w, fsUnderline in Params.Style); QTextEdit_setFontWeight(w, QisBold[fsBold in Params.Style]); QTextEdit_setFontItalic(w, fsItalic in Params.Style); ColorRefToTQColor(Params.Color, clr); QTextEdit_setTextColor(w, @clr); //todo: { if not Params.HasBkClr then begin ColorRefToTQColor(Params.BkColor, clr); clr.Alpha:=0; end else ColorRefToTQColor(Params.BkColor, clr); QTextEdit_setTextBackgroundColor(w, @clr); } te.setSelection(ss, sl); end; class function TQtWSCustomRichMemo.Search(const AWinControl: TWinControl; const ANiddle: string; const SearchOpts: TIntSearchOpt): Integer; var w : QTextEditH; te : TQtTextEdit; ws : Widestring; fl : QTextDocumentFindFlags; begin if not WSCheckHandleAllocated(AWinControl, 'SetParaAlignment') then Exit; te:=TQtTextEdit(AWinControl.Handle); w:=QTextEditH(te.Widget); fl:=0; if soMatchCase in SearchOpts.Options then fl:=fl or QTextDocumentFindCaseSensitively; if soWholeWord in SearchOpts.Options then fl:=fl or QTextDocumentFindWholeWords; if soBackward in SearchOpts.Options then fl:=fl or QTextDocumentFindBackward; //todo: range filtering in Serach Opts ws:=UTF8Decode(ANiddle); if QTextEdit_find(w, @ws, fl) then Result:=te.getSelectionStart else Result:=-1; end; class function TQtWSCustomRichMemo.isInternalChange( const AWinControl: TWinControl; Params: TTextModifyMask): Boolean; begin Result := false; //Result:=inherited isInternalChange(AWinControl, Params); end; class procedure TQtWSCustomRichMemo.SetTextAttributesInternal( const AWinControl: TWinControl; TextStart, TextLen: Integer; const AModifyMask: TTextModifyMask; const Params: TIntFontParams); begin SetTextAttributes(AWinControl, TextStart, TextLen, Params); end; class function TQtWSCustomRichMemo.GetStyleRange( const AWinControl: TWinControl; TextStart: Integer; var RangeStart, RangeLen: Integer): Boolean; var te : TQtTextEdit; bck : TEditorState; al : QtAlignment; qcur : QTextCursorH; qblck : QTextBlockH; qbfmt : QTextBlockFormatH; i : integer; cnt : integer; rng : array of TTextRange; blckofs: integer; begin if not WSCheckHandleAllocated(AWinControl, 'GetStyleRange') then begin Result:=false; Exit; end; RangeStart:=TextStart; RangeLen:=1; Result:=true; {$ifndef RMQT5_TEXTFORMATS} Exit; {$else} te:=TQtTextEdit(AWinControl.Handle); MakeBackup(te, bck); qcur := QTextCursor_Create(); qblck := QTextBlock_Create(); try te.setSelection(TextStart, 0); QTextEdit_textCursor(QTextEditH(te.Widget), qcur); QTextCursor_block(qcur, qblck); cnt := QTextBlock_textFormatsCount(qblck); SetLength(rng, cnt); if cnt>0 then begin blckofs := QTextBlock_position(qblck); textStart := textStart - blckofs; for i:=0 to cnt-1 do begin if (textStart >= rng[i].start) and (textStart 0 then AAlign:=paLeft else if QtAlignTrailing and al > 0 then AAlign:=paRight else if QtAlignCenter and al > 0 then AAlign:=paCenter else if QtAlignJustify and al > 0 then AAlign:=paJustify else AAlign:=paLeft; Result:=true; end; procedure MakeBackup(te: TQtTextEdit; out backup: TEditorState); begin backup.selst:=te.getSelectionStart; backup.sellen:=te.getSelectionLength; end; procedure ApplyBackup(te: TQtTextEdit; const backup: TEditorState); begin te.setSelection(backup.selst, backup.sellen); end; end.