type
TStringList = class(Classes.TStringList)
protected
function GetTextStr: string; override;
end;
function TStringList.GetTextStr: string;
procedure ConvertToUtf8(var S: string);
begin
// Fans of inlining so complicated the AnsiToUtf8, that one this call
// generates a lot of code, so it is made as a separate procedure
S := AnsiToUtf8(S);
end;
var
LLineBreak: string;
CodePage: TSystemCodePage;
// In most cases, the line break is an ASCII string and does not depend
// on the code page, but we must to check
function LineBreakDependsOnCodePage: Boolean;
var
ChangedLineBreak: RawByteString;
begin
ChangedLineBreak := LLineBreak;
SetCodePage(ChangedLineBreak, CodePage, True); // With conversion
Result := (LLineBreak <> ChangedLineBreak);
end;
const
CLineBreaks: array[TTextLineBreakStyle] of string = (
#10, // tlbsLF
#13#10, // tlbsCRLF
#13); // tlbsCR
var
i, LCount: SizeInt;
DifferentCodePages, CodePageValid: Boolean;
CurrentStringLength, ResultLength, LineBreakLength: SizeInt;
CurrentString: string;
InsertPoint: PAnsiChar;
begin
LCount := Self.Count;
if LCount <= 0 then
Exit('');
CheckSpecialChars;
LLineBreak := Self.LineBreak;
// When the LineBreak is default, the TextLineBreakStyle is used
if LLineBreak = sLineBreak then
LLineBreak := CLineBreaks[TextLineBreakStyle];
// Calculate the required place for the result
ResultLength := 0;
LineBreakLength := Length(LLineBreak);
// When scanning, check that the code pages of all strings are the same
// We do not use the default code page to avoid conversions when
// all code pages are the same but do not match the default
DifferentCodePages := False;
CodePageValid := False;
CodePage := 0; // Only to suppress the compiler warning
i := 0;
repeat
CurrentString := Strings[i];
if CurrentString <> '' then
begin
if CodePageValid then
DifferentCodePages := (StringCodePage(CurrentString) <> CodePage)
else
begin
CodePage := StringCodePage(CurrentString);
CodePageValid := True;
DifferentCodePages := LineBreakDependsOnCodePage;
end;
Inc(ResultLength, Length(CurrentString));
end;
Inc(ResultLength, LineBreakLength);
Inc(i);
until (i >= LCount) or DifferentCodePages;
if not CodePageValid then // All strings are empty, so we use the LineBreak code page
begin
if LLineBreak = '' then // Empty strings with an empty line break
Exit('');
CodePage := StringCodePage(LLineBreak);
end;
if DifferentCodePages then // Convert all strings to UTF8
begin
CodePage := CP_UTF8;
ConvertToUtf8(LLineBreak);
LineBreakLength := Length(LLineBreak);
ResultLength := 0;
i := 0;
repeat
CurrentString := Strings[i];
if CurrentString <> '' then
begin
ConvertToUtf8(CurrentString);
Inc(ResultLength, Length(CurrentString));
end;
Inc(ResultLength, LineBreakLength);
Inc(i);
until i >= LCount;
end;
// OK, new length defined, allocate and copy to it
SetLength(Result, ResultLength);
SetCodePage(RawByteString(Result), CodePage, False); // without conversion!
InsertPoint := Pointer(Result);
i := 0;
repeat
CurrentString := Strings[i];
if CurrentString <> '' then
begin
if DifferentCodePages then
ConvertToUtf8(CurrentString);
CurrentStringLength := Length(CurrentString);
System.Move(Pointer(CurrentString)^, InsertPoint^, CurrentStringLength);
Inc(InsertPoint, CurrentStringLength);
end;
System.Move(Pointer(LLineBreak)^, InsertPoint^, LineBreakLength);
Inc(InsertPoint, LineBreakLength);
Inc(i);
until i >= LCount;
if SkipLastLineBreak then
SetLength(Result, Length(Result) - LineBreakLength);
end;