Problem with TRichEdit in XE5: EM_STREAMIN adds a new line! |
|
Hi there,
During the migration of an D7 application to Delphi XE5 I encountered a problem with the TRichEdit component.
My application allows the user to write formatted text and to insert formatted text templates/modules into the text.
All the necessary moving of formatted text between the diffrent RichEdit components is managed by a set of functions using the EM_STREAMIN and EM_STREAMOUT messages.
These functions worked properly in D7 and I've included them at the end of this post so you can have a look at them.
When these functions are used in XE5 this is happening:
GetRTFSelection seems to work correctly and gets the selected part of the richedit code. But when this data is streamed into another richEdit using EM_STREAMIN
there is always an additional line inserted! And it's not just an additonal CR+LF it's an extra paragraph in the richedit code as well (\pard\par) !
I've done a simple test: typing two rows of text in one richedit and stream them into another one. See what's happened to the rtf-code:
RTF-Code in the source richEdit:
{\rtf1\ansi\ansicpg1252\deff0\deflang1031{\fonttbl{\f0\fnil\fcharset0 Tahoma;}}
\viewkind4\uc1\pard\f0\fs16 FirstRow\par
SecondRow\par
}
RTF-Code of the target RichEdit:
{\rtf1\ansi\ansicpg1252\deff0\deflang1031{\fonttbl{\f0\fnil\fcharset0 Tahoma;}}
\viewkind4\uc1\pard\f0\fs16 FirstRow\par
SecondRow\par
*\pard\par*
}
I dont't think it's a D7-migration related problem because I wrote a small test application in XE5 and it behaves just the same.
It could be something with the interpretation of the CRLF because in one function the text of the RichEdit is moved character by character to
another RichEdit (kind of parsing) and the CRLF isn't recognized properly: Calling GetTextLen shows a CRLF is counted as two but during the parsing only CR is found no LF.
Does XE5 use a different version of Win32 RichEdit control than D7?
The error doesn't occur when EM_STREAMOUT is called with the replacement option (SF_RTF) instead of (SF_RTF or SFF_Selection), but I need to keep existing text in the target so that's not an option.
Thanks in advance for any help,
regards
Regine
//EditStreamCallback callback functions
function GetRTFSelCB(dwCookie: DWORD_PTR; pbBuff: PByte; cb: Longint; var pcb: Longint): Longint; stdcall;
begin
pcb := cb;
if cb > 0 then begin
TStream(dwCookie).WriteBuffer(pbBuff^, cb);
Result := 0;
end else
Result := 1;
end;
function SetRTFSelCB(dwCookie: DWORD_PTR; pbBuff: PByte; cb: Longint; var pcb: Longint): Longint; stdcall;
begin
pcb := TStream(dwCookie).Read(pbBuff^, cb);
if pcb > 0 then
Result := 0
else
Result := 1;
end;
//Gets the selected contents of a richedit
function GetRTFSelection(RichEdit: TRichEdit): AnsiString;
var
Str: TStringStream;
ES: TEditStream;
begin
Str := TStringStream.Create('',TEncoding.ANSI);
try
ES.dwCookie := DWORD_PTR(Str);
ES.dwError := 0;
ES.pfnCallback:=GetRTFSelCB;
{Causes a rich edit control to pass its contents to an
application–defined EditStreamCallback callback function.}
RichEdit.Perform(EM_STREAMOUT, SF_RTF or SFF_SELECTION, Integer(@ES));
if ES.dwError <> 0 then
raise EOutOfResources.Create(sRichEditSaveFail);
// Result := Str.DataString; //nullterminated
Result := pAnsiChar(Str.Memory); //not nullterminated
Str.SaveToFile('result.rtf');
finally
Str.Free
end
end;
//Sets the rtf-code in RichEdit at cursorpos
procedure SetRTFSelection(RichEdit:TRichEdit; const Source: AnsiString);
var
Str: TStringStream;
ES: TEditStream;
begin
RichEdit.Lines.BeginUpdate;
Str := TStringStream.Create(Source);
try
ES.dwCookie := DWORD_PTR(Str);
ES.dwError := 0;
ES.pfnCallback := SetRTFSelCB;
RichEdit.Perform(EM_STREAMIN, SF_RTF or SFF_SELECTION, Integer(@ES));
if ES.dwError <> 0 then
raise EOutOfResources.Create(sRichEditSaveFail);
finally
Str.Free;
RichEdit.Lines.EndUpdate;
end;
end;
//copies selected part of source to target richedit
procedure RTFToRTF(Source, Target: TRichEdit; OverwriteTarget, ClearSource: Boolean);
var
S: AnsiString;
begin
If OverwriteTarget then
Target.Clear
else
Target.SelStart:=Length(Target.Text);
Source.SelStart:=0;
Source.SelLength:=Length(Source.Text);
S:=GetRTFSelection(Source);
SetRTFSelection(Target, S);
If ClearSource then Source.Clear;
end;
|