Интересен ли аналог TRichEdit под FireMonkey? Я - "знаю" как его "быстро" сделать.
P.S. http://18delphi.blogspot.ru/search/label/%D0%BF%D0%B8%D1%81%D0%B0%D1%82%D0%B5%D0%BB%D1%8F%D0%BC%20%D1%80%D0%B5%D0%B4%D0%B0%D0%BA%D1%82%D0%BE%D1%80%D0%BE%D0%B2
"Я бы лично посоветовал бы поглядеть в сторону CoreText и AttributedString от xCode.
Ну и в сторону - GoF."
P.P.S. Говоря о "портянках кода" - наверное теперь понятно, что я имею в виду. Но я (пока) не знаю - как это улучшить.
P.P.P.S. Этот код в таком (или примерно таком) виде "тянется" аж с 1996 (!) года. Я бы хотел его улучшить. Но пока - не понял как. При всей моей любви к "паттернам". Интересно было бы заглянуть ВНУТРЬ CoreText. И его Frame и DrawFrame.
P.P.P.P.S. Многое менялось за эти годы. COM|CORBA пришли и "почти ушли". Интерфейсы. Примеси. Шаблоны. UML. Но этот код как был "странным". Так и остался. Зато - "вполне эффективным". Это ещё вы не видели кода "рисования фрейма". Там - "ещё веселее".
Кстати. Вот и он:
procedure TevTextParaPainterEx.CheckDrawFocused;
//#UC START# *502268560274_49DA30D901B6_var*
//#UC END# *502268560274_49DA30D901B6_var*
begin
//#UC START# *502268560274_49DA30D901B6_impl*
if (Area.rView.As_InevView.Metrics.AACLike = nev_aacLeft) then
Area.rCanvas.NotFocused := False;
//#UC END# *502268560274_49DA30D901B6_impl*
end;//TevTextParaPainterEx.CheckDrawFocused
procedure TevTextParaPainterEx.CheckInvertInLeftAAC;
//#UC START# *5052EF710307_49DA30D901B6_var*
//#UC END# *5052EF710307_49DA30D901B6_var*
begin
//#UC START# *5052EF710307_49DA30D901B6_impl*
if (Area.rView.As_InevView.Metrics.AACLike > nev_aacNone) and Area.rCanvas.Invert then
Area.rCanvas.Font.ForeColor := clBlack
//#UC END# *5052EF710307_49DA30D901B6_impl*
end;//TevTextParaPainterEx.CheckInvertInLeftAAC
procedure TevTextParaPainterEx.DrawArrow(aRectHeight: Integer);
//#UC START# *50A4D6E90261_49DA30D901B6_var*
var
l_Delta : Integer;
l_ParaH : Integer;
l_Width : Integer;
l_Height : Integer;
l_ImageInfo : PnevControlImageInfo;
//#UC END# *50A4D6E90261_49DA30D901B6_var*
begin
//#UC START# *50A4D6E90261_49DA30D901B6_impl*
if (Area.rView.As_InevView.Metrics.AACLike = nev_aacLeft) and Area.rCanvas.Invert and ParentPainter.IsSelectedOneWholePara(ParaX) then
with Area.rCanvas do
begin
l_ImageInfo := FormatInfo.ImageInfo;
if l_ImageInfo <> nil then
begin
l_Height := DP2LP(PointX(l_ImageInfo.rImageList.Height)).X;
l_ParaH := (aRectHeight + Spaces.Top + Spaces.Bottom);
l_Delta := (l_ParaH - l_Height) div 2;
l_Delta := l_Delta + l_Height - l_ParaH + Spaces.Bottom;
l_Width := DP2LP(PointX(l_ImageInfo.rImageList.Width)).X;
MoveWindowOrg(l3Point(l_Width + (l3Inch div 16), l_Delta));
FillRect(l3SRect(0, 0, l_ImageInfo.rImageList.Width, l_ImageInfo.rImageList.Height));
l_ImageInfo.rImageList.Draw(As_Il3Canvas, 0, 0, l_ImageInfo.rFirstIndex);
end; // if l_ImageInfo <> nil then
end; // if Area.rView.As_InevView.Metrics.AACLike then
//#UC END# *50A4D6E90261_49DA30D901B6_impl*
end;//TevTextParaPainterEx.DrawArrow
procedure TevTextParaPainterEx.ValidateSegFont(const aSeg: InevTag);
//#UC START# *4D664903006E_49DA30D901B6_var*
//#UC END# *4D664903006E_49DA30D901B6_var*
begin
//#UC START# *4D664903006E_49DA30D901B6_impl*
if (Area.rView.As_InevView.Metrics.AACLike > nev_aacNone) and Area.rCanvas.Invert then
if aSeg.InheritsFrom(k2_idHyperlink) then
Area.rCanvas.Font.ForeColor := clBlack;
//#UC END# *4D664903006E_49DA30D901B6_impl*
end;//TevTextParaPainterEx.ValidateSegFont
procedure TevTextParaPainterEx.ValidateParaFont;
//#UC START# *4D725A550218_49DA30D901B6_var*
//#UC END# *4D725A550218_49DA30D901B6_var*
begin
//#UC START# *4D725A550218_49DA30D901B6_impl*
// ничего не делаем
//#UC END# *4D725A550218_49DA30D901B6_impl*
end;//TevTextParaPainterEx.ValidateParaFont
procedure TevTextParaPainterEx.Release;
//#UC START# *479F2AFB0397_49DA30D901B6_var*
//#UC END# *479F2AFB0397_49DA30D901B6_var*
begin
//#UC START# *479F2AFB0397_49DA30D901B6_impl*
FreeAndNil(f_LinePainter);
inherited;
//#UC END# *479F2AFB0397_49DA30D901B6_impl*
end;//TevTextParaPainterEx.Release
procedure TevTextParaPainterEx.GetFramePartPrim(const aFrame: InevTag;
anIndex: Tl3FramePartIndex;
var thePart: TnevFramePart);
//#UC START# *4804B9BB0383_49DA30D901B6_var*
var
l_Style : InevTag;
l_N : InevPara;
//#UC END# *4804B9BB0383_49DA30D901B6_var*
begin
//#UC START# *4804B9BB0383_49DA30D901B6_impl*
inherited;
if (anIndex = l3_fpiUp) then
if (Spaces.Top > 0) AND not ParaX.Attr[k2_tiStyle].BoolA[k2_tiHeaderHasOwnSpace] then
begin
thePart.rDrawType := lpdDraw;
thePart.rColor := clBlack;
thePart.rWidth := 0;
thePart.rSpaceBefore := (l3Inch div 8) div 2;
thePart.rSpaceAfter := (l3Inch div 8) div 2;
end;//Spaces.Top > 0..
if (thePart.rDrawType = lpdDraw) AND (anIndex = l3_fpiLeft) then
thePart.rSpaceBefore := Max(Min(f_FirstIndent, Spaces.Left) - l3Inch div 16, 0);
if (thePart.rDrawType <> lpdHidden) then
// - тут прячем лишние рамки (если идёт пачка параграфов с одним стилем)
begin
l_Style := ParaX.Attr[k2_tiStyle];
if l_Style.IsValid then
begin
Case anIndex of
l3_fpiUp:
begin
l_N := evPrevOverallPara(ParaX);
if (l_N <> nil) AND l_N.IsValid AND
(l_Style.IntA[k2_tiHandle] = l_N.IntA[k2_tiStyle]) AND
l_N.Attr[k2_tiFrame].IsValid then
thePart.rDrawType := lpdHidden;
end;//l3_fpiUp
l3_fpiDown:
begin
l_N := evNextOverallPara(ParaX);
if (l_N <> nil) AND l_N.IsValid AND
(l_Style.IntA[k2_tiHandle] = l_N.IntA[k2_tiStyle]) AND
l_N.Attr[k2_tiFrame].IsValid then
thePart.rDrawType := lpdHidden;
end;//l3_fpiDown
end;//Case anIndex
end;//l_Style.IsValid
end;//thePart.rDrawType <> lpdHidden
if (thePart.rDrawType = lpdDraw) then
begin
if (anIndex = l3_fpiDown) then
begin
thePart.rSpaceBefore := Max(thePart.rSpaceBefore, l3Inch div 16);
thePart.rSpaceAfter := Max(thePart.rSpaceAfter, l3Inch div 32);
end;//anIndex = l3_fpiDown
end;//thePart.rDrawType = lpdDraw
//#UC END# *4804B9BB0383_49DA30D901B6_impl*
end;//TevTextParaPainterEx.GetFramePartPrim
procedure TevTextParaPainterEx.InitBottom(var theBottom: InevBasePoint;
var theCellBottom: InevBasePoint);
//#UC START# *4804BC800172_49DA30D901B6_var*
var
l_Pt : TnevPoint;
//#UC END# *4804BC800172_49DA30D901B6_var*
begin
//#UC START# *4804BC800172_49DA30D901B6_impl*
theCellBottom := nil;
if (Map <> nil) then
// - эта проверка НУЖНА, например для вложенных объектов (формул)
with Map.Bounds do
begin
l_Pt := l3Point(Left, Bottom);
if not DrawCompleted then
// ^ - http://mdp.garant.ru/pages/viewpage.action?pageId=132220046
Dec(l_Pt.P.Y, l3Epsilon);
theBottom.AsLeaf.InitPointByPt(Area.rView.As_InevView, l_Pt, Map);
if (Bottom > Top + Spaces.Top) then
// - !!! вместо этой проверки для текстовых параграфов надо использовать
// номер строки, т.к. он всё равно известен.
theBottom := TnevAfterEndPoint.Make(Area.rView.As_InevView, theBottom);
end;//with Map.Bounds
//#UC END# *4804BC800172_49DA30D901B6_impl*
end;//TevTextParaPainterEx.InitBottom
procedure TevTextParaPainterEx.DoDrawFrameText(aTop: Boolean);
//#UC START# *4804C35B00B2_49DA30D901B6_var*
var
l_Space : Integer;
l_Style : InevTag;
l_Text : Tl3PCharLen;
l_Decor : InevTag;
l_DT : TnevDecorType;
//#UC END# *4804C35B00B2_49DA30D901B6_var*
begin
//#UC START# *4804C35B00B2_49DA30D901B6_impl*
f_TopLine := Succ(TopAnchor.VertPosition(Area.rView.As_InevView, FormatInfo));
if aTop then
l_Space := Spaces.Top
else
l_Space := Spaces.Bottom;
if (l_Space > 0) then
begin
if (aTop AND
(f_TopLine = 1)
) OR
(not aTop {AND
(f_TopLine = FormatInfo.Lines.Count) AND}
) then
begin
if aTop then
l_DT := nev_dtHeader
else
l_DT := nev_dtFooter;
l_Decor := FormatInfo.DecorObj(l_DT);
l_Style := ParaX.Attr[k2_tiStyle];
if l_Style.IsValid then
begin
l_Text := l_Style.PCharLenA[k2_tiShortName];
if not l3IsNil(l_Text) then
InternalDrawFrameText(l_Text, aTop, l_Space, l_DT, l_Decor, f_FirstIndent)
else
if l_Decor.IsValid then
InternalDrawFrameText(Tl3PCharLen(cc_EmptyStr), aTop, l_Space, l_DT, l_Decor, f_FirstIndent)
end;//l_Style.IsValid
end;//aTop
end;//l_Space > 0
//#UC END# *4804C35B00B2_49DA30D901B6_impl*
end;//TevTextParaPainterEx.DoDrawFrameText
function TevTextParaPainterEx.DrawLeaf: Boolean;
//#UC START# *4804C81000B9_49DA30D901B6_var*
var
LH : Integer; {- высота i-й строки}
l_Justification : TevIndentType; {- тип выравнивания параграфа}
l_ParaFont : Il3Font;
l_ParaS : Tl3PCharLen;
l_IsHyperlink : Boolean;
procedure DrawIndent(First : Boolean;
DX : Integer;
LL : Boolean;
AddHyphen : Boolean);
var
D : Integer;
HardEnterWidth : Integer;
l_PMStr : Tl3PCharLenPrim;
begin//DrawIndent
with Area.rCanvas do
begin
if not First AND AddHyphen then
ExtTextOut(l3Point0,
l3Rect(0, 0, evHyphenWidth, LH),
cc_HyphenStr);
if ((DX > 0) AND (l_Justification <> ev_itWidth)) then
begin
if ((First AND (l_Justification in [ev_itRight, ev_itCenter])) OR
(not First AND
((l_Justification in [ev_itLeft, ev_itCenter]) OR DrawSpecial))) then
begin
D := 0;
case l_Justification of
ev_itLeft:
if not First then
D := DX;
ev_itRight:
Inc(D, DX);
ev_itCenter:
if First then
Inc(D, DX div 2)
else
Inc(D, DX - (DX div 2));
ev_itWidth: {D уже равен X0};
end;//case l_Justification
if not First AND DrawSpecial AND LL then
begin
if (ParaMarkStyle = ev_pmCell) then
l_PMStr := cc_CurrencyStr
else
l_PMStr := cc_HardEnterStr;
if l3IsNil(l_ParaS) then
begin
Font := l_ParaFont;
ValidateParaFont;
end;//l3IsNil(l_ParaS)
PushFC;
try
with Font do
begin
Name := def_ArialFontName;
Style := [];
end;//with Font
finally
PopFC;
end;//try..finally
HardEnterWidth := TextExtent(l_PMStr).X;
if (HardEnterWidth > D) then
D := HardEnterWidth;
if (SelRange <> nil) AND SelRange.ContainsEnd(Area.rView.As_InevView) then
begin
BeginInvert;
try
ExtTextOut(l3Point0, l3Rect(0, 0, HardEnterWidth, LH), l_PMStr);
finally
EndInvert;
end;//try..finally
end//(SelRange <> nil) AND SelRange.ContainsEnd
else
ExtTextOut(l3Point0, l3Rect(0, 0, HardEnterWidth, LH), l_PMStr);
end;//not First
MoveWindowOrg(l3PointX(-D));
if l_IsHyperlink then DrawArrow(LH);
end;//First...
end;//AddHyphen
end;//with Area.rCanvas
end;//DrawIndent
var
WC : Integer; {- количество слов в i-й строке}
DW : Integer;
ParaCaretPos : Integer;
LineCaretPos : Integer;
ParaCaretLine : Integer;
l_NeedCaret : Boolean;
l_Map : InevLines;
l_SolidBlock : Boolean;
function loc_DrawText(S: Tl3CustomString): Boolean;
function _TextRect(const S: Tl3PCharLen): Boolean;
procedure DrawSoftEnter;
begin//DrawSoftEnter
with Area.rCanvas do
begin
PushFC;
try
with Font do
begin
Name := def_SymbolFontName;
Style := [];
end;//with Font
finally
PopFC;
end;//try..finally
CaretLineOut(cc_SoftEnterStr, LH, not l_NeedCaret, LineCaretPos);
end;//with Area.rCanvas
end;//DrawSoftEnter
var
TLen : Integer;
l_NeedSoftEnter : Boolean;
S2Draw : Tl3PCharLen;
l_Str : Tl3PCharLen;
begin
with Area.rCanvas do
begin
CheckInvertInLeftAAC;
Result := ((ClipRect.Right > 0) OR (Caret <> nil)) AND HasToDraw;
if Result then
begin
S2Draw := S;
TLen := l3RTrim(S2Draw).SLen;
if (TLen > 0) AND (S2Draw.S[Pred(TLen)] = cc_SoftEnter) then
begin
l_NeedSoftEnter := DrawSpecial;
if l_NeedSoftEnter OR (LineCaretPos < 0) OR (LineCaretPos > S2Draw.SLen) then
Dec(TLen)
else
begin
{-эта ветка необходима для правильного позиционирования курсора
на конец строки с SoftEnter'ом }
S2Draw := CheckOutString(S2Draw);
S2Draw.S[Pred(TLen)] := cc_HardSpace;
TLen := S2Draw.SLen;
end;//l_NeedSoftEnter
end//TLen > 0
else
begin
l_NeedSoftEnter := false;
TLen := S2Draw.SLen;
end;//TLen > 0
l_Str := l3PCharLen(S2Draw.S, TLen, S2Draw.SCodePage);
if DrawSpecial then
l_Str := CheckConvertString(l_Str);
CaretLineOut(l_Str, LH, not l_NeedCaret, LineCaretPos);
if l_NeedSoftEnter then
DrawSoftEnter;
end;//Result
end;//with Area.rCanvas
end;//_TextRect
function DrawAllLine: Boolean;
{-нарисовать всю строку, без выделения}
begin//DrawAllLine
Result := _TextRect(S.AsPCharLen);
S.Offset(S.Len);
end;//DrawAllLine
var
D2S : Integer; {-ширина пустого места}
procedure GetD2S;
begin//GetD2S
if (WC > 0) then
begin
{if (DW < 0) then begin
D2S := 0;
DW := 0;
end else }begin
if (WC = 1) then
D2S := DW
else
D2S := DW div WC;
Dec(WC);
with Area.rCanvas do
begin
D2S := LP2DP(l3PointX(D2S)).X;
Dec(DW, DP2LP(PointX(D2S)).X);
end;//with Area.rCanvas
end;//DW < 0
end
else
D2S := 0;
end;//GetD2S
var
l_InDrawBlock : Boolean;
l_NeedCorrectCaret : Boolean;
l_pxLH : Integer;
l_NS : Tl3CustomString;
begin//loc_DrawText
Result := true;
with Area.rCanvas do
begin
if S.Empty then
begin
if (LineCaretPos = 0) then
begin
SetCaret(l3Point0, l3Point(AverageCharWidth, LH), not l_NeedCaret);
ParaCaretPos := -1;
LineCaretPos := -1;
ShowCursor := false;
end;//LineCaretPos = 0
end//S.Empty
else
begin
if (l_Justification <> ev_itWidth) OR
not l3HasWhiteSpace(S.AsPCharLen) then
begin
{-проверяем нужна ли разгонка и есть ли в строке пробелы}
if l_SolidBlock then
Result := DrawAllLine
else
f_LinePainter.DrawF(As_Il3Canvas, evL2DLA(@_TextRect), S);
end//l_Justification <> ev_itWidth
else
begin
{-рисуем разогнанную строку}
l_pxLH := LP2DP(l3PointY(LH)).Y;
l_NS := S.Clone;
try
while Result AND not S.Empty do
begin
S.FindCharEx(cc_HardSpace, l_NS);
l_NeedCorrectCaret := (LineCaretPos = S.Len);
if l_SolidBlock then
begin
Result := DrawAllLine;
l_InDrawBlock := false;
end//l_SolidBlock
else
l_InDrawBlock := Boolean(f_LinePainter.DrawF(As_Il3Canvas, evL2DLA(@_TextRect), S));
if Result AND l3IsWhiteSpaceS(S.AsPCharLen, -1) then
begin
if l_InDrawBlock then
begin
BeginInvert;
CheckInvertInLeftAAC;
end;
try
GetD2S;
FillRect(l3SRect(0, 0, D2S, l_pxLH));
MoveWindowOrg(PointX(-D2S));{-сдвигаем курсор отрисовки на ширину пустого места}
if l_NeedCorrectCaret then
IncCaret(D2S - 1);
Result := (ClipRect.Right > 0) AND HasToDraw;
finally
if l_InDrawBlock then
EndInvert;
end;//try..finally
end;//Result AND evWhiteSpace
S.AssignString(l_NS);
end;//while Result AND not S.Empty
finally
l3Free(l_NS);
end;//try..finally
end;//l_Justification <> ev_itWidth
end;//S.Empty
Result := Result AND (ClipRect.Right > 0) AND HasToDraw;
end;//with Area.rCanvas
end;//loc_DrawText
var
FirstLineWidth : Integer;
OtherLineWidth : Integer;
l_LineOffsetX : Integer;
l_ExtentX : Integer;
l_LineCount : Integer;
l_ActiveElement : InevActiveElement;
procedure DrawPara;
function DrawLine(ppLI: PPevLineInfo; LID: Integer): Boolean;
var
l_SegmentsList : Ik2TagList;
function DrawSegment(const aSeg: InevTag; Index: Integer): Boolean;
var
l_OM : TOutlineTextMetric;
l_Pnt : IevPainter;
l_DrawingObject : InevTag;
function DrawObject(const S: Tl3PCharLen): Boolean;
var
l_Ex : Tl3Point;
l_Obj : InevObject;
l_SegInf : TnevShapeInfo;
l_Points : TnevShapePoints;
l_Point : InevPoint;
l_BL : Integer;
begin//DrawObject
Result := true;
with Area.rCanvas do
begin
l_Ex := TextExtent(S);
PushWO;
try
l_BL := aSeg.IntA[k2_tiBaseLine];
if not l_DrawingObject.InheritsFrom(k2_idBitmapPara) then
// http://mdp.garant.ru/pages/viewpage.action?pageId=174295160&focusedCommentId=344137422#comment-344137422
// - пока так
MoveWindowOrg(l3PointY(-((LH - l_Ex.Y) div 2)))
else
begin
{ if (l_OM.otmDescent <> 0) then
if (l_OM.otmDescent <> 1) then
l_BL := l_BL + Area.rCanvas.DP2LP(PointY(l_OM.otmDescent - 1)).Y;}
if (l_OM.otmDescent <> 0) then
l_BL := l_BL + Area.rCanvas.DP2LP(PointY(l_OM.otmDescent)).Y;
MoveWindowOrg(l3PointY(-((LH - l_Ex.Y))));
end;//not l_DrawingObject.InheritsFrom(k2_idBitmapPara)
if (l_BL <> 0) then
MoveWindowOrg(l3PointY(-l_BL));
l3FillChar(l_Points, SizeOf(l_Points));
l_Points.rTop := nil;
l_Points.rCaret := nil;
l_Points.rSelection := nil;
l_Points.rPrevHeight := 0;
l_Points.rRealTop := nil;
l_Points.rFake := False;
l_Points.rCheckCaret := nil;
l_Points.rCheckSelection := nil;
if (LineCaretPos > 0) then
Dec(LineCaretPos, S.SLen);
if (SelRange <> nil) then
begin
l_Point := ParaX.MakePoint;
l_Point.PositionW := aSeg.IntA[k2_tiStart];
if SelRange.Contains(Area.rView.As_InevView, l_Point) then
if l_DrawingObject.QT(InevObject, l_Obj) then
l_Points.rSelection := l_Obj.Range;
end;//SelRange <> nil
(* if (LineCaretPos = 0) AND QT(InevObject, l_Obj) then
l_Pnt.Draw(nil, Area.rCanvas, nil, l_Obj.Point, nil, l_SegMap, High(Integer))
else*)
l_Pnt.Draw(Area^, l_Points, l_SegInf);
finally
PopWO;
end;//try..finally
MoveWindowOrg(l3PointX(-l_Ex.X));
MoveWindowOrg(PointX(-1));
end;//with Area.rCanvas
end;//DrawObject
var
l_SegFont : Il3Font;
l_BC : Tl3Color;
l_ActiveHyperlinkStyle : InevTag;
l_AH : InevActiveElement;
l_CheckedS1 : Tl3CustomString;
l_CheckedS2 : Tl3CustomString;
l_CheckedS3 : Tl3CustomString;
begin//DrawSegment
l_CheckedS1 := CheckS(1);
l_CheckedS2 := CheckS(2);
l_CheckedS3 := CheckS(3);
evTextParaGetLineSegment(l_Map, l_ParaS, Succ(LID), Index, l_SegmentsList,
l_CheckedS1, l_CheckedS2, l_CheckedS3
{CheckS(1), CheckS(2), CheckS(3)});
Result := loc_DrawText(l_CheckedS1{S[1]});
if Result then
begin
with Area.rCanvas do
begin
Push;
try
l_SegFont := FormatInfo.InfoForChild(TnevSegmentObject.Make(aSeg)).Font[false];
//l_SegFont := ETAOIN_SHRDLU_GetObjIFont(Area.rView.Metrics, aSeg, true, FormatInfo^, false);
if l_SegFont.IsAtomic then
begin
l3FillChar(l_OM, SizeOf(l_OM));
if (GetOutlineTextMetrics(Area.rCanvas.DC, SizeOf(l_OM), @l_OM) = 0) then
Assert(false, 'Не удалось получить метрики текущего шрифта канвы');
end;//l_SegFont.IsAtomic
l_BC := BackColor;
Font := l_SegFont;
// - устанавливаем шрифт сегмента
if (l_ActiveElement <> nil) then
begin
l_AH := TnevActiveHyperlink.Make(ParaX, aSeg);
if ParaX.IsDecorationElement then
l_AH := TnevDecorActiveHyperlink.Make(ParentPainter.Obj, l_AH);
//if aSeg.InheritsFrom(k2_idHyperlink) then // - это убрано, чтобы корректно продолжения ссылок подчёркивались
if l_ActiveElement.IsSame(l_AH) then
begin
l_ActiveHyperlinkStyle := k2.TypeTable.ObjToTag(k2.TypeTable[k2_idTextStyle].ValueTable.DRByID[evd_saActiveHyperLink]);
if l_ActiveHyperlinkStyle.IsValid then
begin
if l_ActiveHyperlinkStyle.HasSubAtom(k2_tiFont) then
begin
with l_ActiveHyperlinkStyle.Attr[k2_tiFont] do
begin
with Font do
begin
if HasSubAtom(k2_tiUnderline) then
Underline := BoolA[k2_tiUnderline];
if HasSubAtom(k2_tiBold) then
Bold := BoolA[k2_tiBold];
if HasSubAtom(k2_tiItalic) then
Italic := BoolA[k2_tiItalic];
if HasSubAtom(k2_tiStrikeout) then
Strikeout := BoolA[k2_tiStrikeout];
if HasSubAtom(k2_tiForeColor) then
ForeColor := IntA[k2_tiForeColor];
if HasSubAtom(k2_tiBackColor) then
BackColor := IntA[k2_tiBackColor];
end;//Font
end;//with l_ActiveHyperlinkStyle.Attr[k2_tiFont]
end;//l_ActiveHyperlinkStyle.HasSybAtom(k2_iFont)
end;//l_ActiveHyperlinkStyle.IsValid
end;//l_ActiveElement.IsSame(TnevActiveHyperlink.Make(ParaX, aSeg))
end;//l_ActiveElement <> nil
if (l_SegFont.BackColor = nevDefaultColor) AND
aSeg.HasSubAtom(k2_tiVisible) then
if aSeg.BoolA[k2_tiVisible] then
if not ParaX.BoolA[k2_tiVisible] then
// - принудительно выставляем цвет фона для видимых сегментов
BackColor := l_BC;
ValidateSegFont(aSeg);
if l_SegFont.IsAtomic then
begin
l_DrawingObject := aSeg.Child[0];
if l_DrawingObject.QT(IevPainter, l_Pnt) then
try
l_Pnt.ParentPainter := Self;
f_LinePainter.DrawF(As_Il3Canvas, evL2DLA(@DrawObject), l_CheckedS2{S[2]});
Result := true;
finally
l_Pnt := nil;
end//try..finally
else
Result := loc_DrawText(l_CheckedS2{S[2]});
end//l_SegFont.IsAtomic
else
Result := loc_DrawText(l_CheckedS2{S[2]});
finally
Pop;
end;//try..finally
if Result then
begin
Font := l_ParaFont;
// - возвращаем шрифт параграфа
ValidateParaFont;
Result := loc_DrawText(l_CheckedS3{S[3]});
end;//Result
end;//with Area.rCanvas
end;//Result
end;//DrawSegment
procedure DrawBullet;
const
cc_BulletStr : Tl3PCharLenConst = (S : #$B7;
SLen : 1;
SCodePage : CP_ANSI);
var
l_BulletWidth : Integer;
l_Bullet : Integer;
begin//DrawBullet
l_Bullet := ParaX.IntA[k2_tiBullet];
if (l_Bullet > 0) then
begin
// - bullet присутствует
with Area.rCanvas do
begin
Font.Name := def_SymbolFontName;
l_BulletWidth := TextExtent(cc_BulletStr).X;
MoveWindowOrg(l3PointX(2 * l_BulletWidth));
ExtTextOut(l3Point0, l3Rect(0, 0, l_BulletWidth, LH), cc_BulletStr);
MoveWindowOrg(l3PointX(-2 * l_BulletWidth));
Font := l_ParaFont;
//- восстанавливаем шрифт параграфа
ValidateParaFont;
end;//with Area.rCanvas
end;//l_Bullet > 0
end;//DrawBullet
var
LeftIndent : Integer;
MaxLineWidth : Integer;
IsLastLine : Boolean;
l_LineLength : Integer;
l_TabInfo : Il3TabInfo;
l_SaveOrg : Tl3Point;
l_TabIndent : Integer;
l_CheckedS1 : Tl3CustomString;
begin//DrawLine
Result := true;
l_TabIndent := 0;
with Area.rCanvas do
begin
StartTabs(l_TabInfo, ParaX.TabStops);
try
try
if (l_LineOffsetX >= 0) then
begin
l_SaveOrg := WindowOrg;
try
LH := ppLI^^.LE.Y;
Dec(l_SaveOrg.P.Y, LH);
{-сдвигаем курсор отрисовки на высоту строки}
l_LineLength := ppLI^^.B - l_LineOffsetX;
{-вычисляем длину строки}
if ShowCursor AND (ParaCaretPos >= l_LineOffsetX) AND
(LID = ParaCaretLine) then
LineCaretPos := ParaCaretPos - l_LineOffsetX
else
LineCaretPos := -1;
if (ClipRect.Top >= LH) and ((Caret = nil) or not ShowCursor) then
Exit;
if not DrawRgnOrBlock AND
((LineCaretPos < 0) OR (LineCaretPos > l_LineLength)) then
Exit;
f_LinePainter.StartLine(l_LineOffsetX, l_LineLength);
Font := l_ParaFont;
if (LID > 0) then
begin
MaxLineWidth := OtherLineWidth;
LeftIndent := Spaces.Left;
end//LID > 0
else
begin
MaxLineWidth := FirstLineWidth;
LeftIndent := f_FirstIndent;
if (ParentPainter <> nil) and
(ParentPainter.ParentPainter <> nil) and
(ParentPainter.ParentPainter.TableRowPainter <> nil) then
begin
l_TabIndent := evCalcDecimalTabIndent(ParaX, l_ParaS, l_Justification, l_LineCount, As_Il3Canvas);
Inc(LeftIndent, l_TabIndent);
end; // if (ParaMarkStyle = ev_pmCell) and (ParaX.TabStops <> nil) then
end;//(LID > 0)
IsLastLine := (LID = l_LineCount);
if IsLastLine AND (l_Justification = ev_itWidth) then
l_Justification := ev_itLeft;
FontIndexSet := ppLI^^.FI;
WC := ppLI^^.WC;
if Printing then
begin
if (ClipRect.Bottom - LH) <= 5 * l3Epsilon then
// - не нужно ли начать новую страницу
begin
Result := false;
// - не нужно оставшиеся строки печатать
DrawLeaf := false;
// -недопечатали...
Inc(l_SaveOrg.P.Y, LH);
// - учитываем недопечатанную строчку
Exit;
// -выходим
end;//ClipRect.Bottom < LH
end;//Printing
DW := MaxLineWidth - ppLI^^.LE.X - l_TabIndent;
Assert((l_Justification = ev_itPreformatted) OR (DW < High(Integer) div 4), 'Значение ширины похоже на мусор');
l_SegmentsList := evTextParaGetLineSegments(FormatInfo, l_ParaS, ParaX, Succ(LID));
MoveWindowOrg(l3PointX(-LeftIndent));
// - сдвигаем курсор отрисовки на ширину отступа
DrawIndent(true, DW, IsLastLine, false);
// - рисуем левый отступ
//Font := l_ParaFont;
// - устанавливаем шрифт параграфа
ValidateParaFont;
if (LID = 0) then
DrawBullet;
// - рисуем маркер списка
if l_SegmentsList.Empty then
begin
l_CheckedS1 := CheckS(1);
with l_CheckedS1 do
if (l_ParaS.S = nil) then
Clear
else
AsPCharLen := l3PCharLenPart(l_ParaS.S,
l_LineOffsetX, l_LineOffsetX + l_LineLength,
l_ParaS.SCodePage);
loc_DrawText(l_CheckedS1{S[1]});
end//l_SegmentsList.Empty
else
begin
CheckDrawFocused;
l_SegmentsList.ForEachF(k2L2TIA(@DrawSegment));
end;
DrawIndent(false, DW, IsLastLine, ppLI^^.AddHyphen);
{-рисуем правый отступ и символ конца параграфа}
finally
WindowOrg := l_SaveOrg;
if (ClipRect.Bottom <= 0) then
Result := false;
// -если уперлись вниз экрана то ВЫХОД
Result := Result AND HasToDraw;
// - еще осталось что рисовать
end;//try..finally
end;//l_LineOffsetX >= 0
finally
l_LineOffsetX := ppLI^^.B;
// - запоминаем конец текущей строки
end;//try..finally
finally
FinishTabs(l_TabInfo);
end;//try..finally
end;//with Area.rCanvas
end;//DrawLine
var
IA : Tl3IteratorAction;
begin//DrawPara
l_Justification := TevIndentType(ParaX.IntA[k2_tiJustification]);
with Area.rCanvas do
begin
if not ParaX.TreatCollapsedAsHidden OR not Area.rView.IsObjectCollapsed[ParaX] then
begin
if (l_Justification = ev_itWidth) AND IsVirtual then
l_Justification := ev_itLeft;
f_FirstIndent := FormatInfo.FirstIndent;
l_LineCount := Pred(evTextParaLineCount(FormatInfo));
FirstLineWidth := l_ExtentX - f_FirstIndent;
OtherLineWidth := l_ExtentX - Spaces.Left;
Push;
try
l_ParaFont := FormatInfo.Font[false];
//l_ParaFont := ETAOIN_SHRDLU_GetObjIFont(Area.rView.Metrics, ParaX, false, FormatInfo^, false);
// -вычисляем шрифт параграфа
try
IA := l3L2IA(@DrawLine);
try
if (l_LineOffsetX >= 0) then
evTextParaIterateLines(FormatInfo, l_ParaS, ParaX, IA, Max(0, Pred(f_TopLine)))
else
evTextParaIterateLines(FormatInfo, l_ParaS, ParaX, IA, Max(0, f_TopLine - 2));
finally
l3FreeIA(IA);
end;//try..finally
finally
l_ParaFont := nil;
end;//try..finally
finally
Pop;
end;//try..finally
end//not ParaX.TreatCollapsedAsHidden OR not Area.rView.IsObjectCollapsed[ParaX]
else
Result := true;
if Result then
MoveWindowOrg(l3PointY(-Spaces.Bottom));
// - учитываем отступ после абзаца
end;//with Area.rCanvas
end;//DrawPara
//#UC END# *4804C81000B9_49DA30D901B6_var*
begin
//#UC START# *4804C81000B9_49DA30D901B6_impl*
StartDrawingInitFields;
f_HeaderHeight := 0;
l_IsHyperlink := EvIsParaHyperlink(ParaX);
Result := true;
l_ActiveElement := Area.rView.ActiveElement;
l_Map := FormatInfo.Lines;
l_ExtentX := Max(FormatInfo.rLimitWidth - Spaces.Right, FormatInfo.Width);
f_TopLine := Succ(TopAnchor.VertPosition(Area.rView.As_InevView, FormatInfo));
if (Caret <> nil) AND Area.rView.ForceDrawFocusRect then
f_ForceFocusRect := true;
with Area.rCanvas do
begin
PushWO;
try
if Result AND (f_TopLine = 1) then
MoveWindowOrg(l3PointY(-Spaces.Top));
// - учитываем отступ перед абзацем
if not Printing OR (ClipRect.Bottom > 0) then
begin
l_ParaS := ParaX.Text;
if ShowCursor AND (Caret <> nil) then
begin
l_NeedCaret := Caret.NeedWindowsCaret;
l_LineOffsetX := evTextParaGetPosByLine(l_Map, l_ParaS, f_TopLine);
ParaCaretPos := Caret.Position;
ParaCaretLine := Caret.VertPosition(Area.rView.As_InevView, FormatInfo);
end//ShowCursor
else
begin
l_NeedCaret := false;
if (f_TopLine = 1) then
l_LineOffsetX := 0
else
l_LineOffsetX := -1;
ParaCaretPos := -1;
end;//ShowCursor
if (f_LinePainter = nil) then
f_LinePainter := TevLinePainter.Create;
f_LinePainter.StartPara(Area.rView.As_InevView, SelRange);
try
l_SolidBlock := (SelRange = nil);
if l_SolidBlock then
DrawPara
else
begin
l_SolidBlock := SelRange.Solid(Area.rView.As_InevView);
if l_SolidBlock then
begin
if (l_ParaS.S = nil) then
DrawPara
else
begin
BeginInvert;
try
DrawPara;
finally
EndInvert;
end;{try..finally}
end;//l_ParaS.S = nil
end//l_SolidBlock
else
DrawPara;
end;//l_SolidBlock
finally
f_LinePainter.FinishPara;
end;//try..finally
end;//not Printing OR (ClipRect.Bottom >= 0
finally
PopWO;
end;//try..finally
end;//with Area.rCanvas
//#UC END# *4804C81000B9_49DA30D901B6_impl*
end;//TevTextParaPainterEx.DrawLeaf
procedure TevTextParaPainterEx.FillUnfilled(const aRect: Tl3Rect);
//#UC START# *4E2702FE01EA_49DA30D901B6_var*
const
cRad = 12;
cArrow = 10;
cFW = 1;
var
l_Rgn : Tl3Region;
l_Rgn1 : Tl3Region;
l_Rgn2 : Il3Region;
l_R : Tl3Rect;
l_SR : Tl3SRect;
l_PointArray : array of TPoint;
l_Med : Integer;
//l_BC : TColor;
l_NeedTriangle : Boolean;
//#UC END# *4E2702FE01EA_49DA30D901B6_var*
begin
//#UC START# *4E2702FE01EA_49DA30D901B6_impl*
if ParaX.Attr[k2_tiStyle].BoolA[k2_tiCollapsable] AND
(
(ParaX.BackColor <> nevDefaultColor)
(* (ParaX.IntA[k2_tiBackColor] <> nevDefaultColor) OR
(ParaX.Attr[k2_tiStyle].Attr[k2_tiFont].IntA[k2_tiBackColor] <> nevDefaultColor)*)
) AND
not Area.rView.IsObjectCollapsed[ParaX] AND
Area.rCanvas.DrawEnabled and not Area.rCanvas.IsVirtual then
begin
(* l_BC := ParaX.IntA[k2_tiBackColor];
if (l_BC = nevDefaultColor) then
begin
l_BC := ParaX.Attr[k2_tiStyle].Attr[k2_tiFont].IntA[k2_tiBackColor];
if (l_BC = nevDefaultColor) then
begin
inherited;
Exit;
end;//l_BC = nevDefaultColor
end;//l_BC = nevDefaultColor*)
with Area.rCanvas do
begin
l_Rgn := Tl3Region.Create;
try
PushClipRect;
try
l_R := aRect;
if (f_TopLine = 1) then
l_R.Top := l_R.Top + f_HeaderHeight
else
l_R.Top := l_R.Top - f_HeaderHeight;
l_SR := LR2DR(l_R);
with l_SR do
l_Rgn.Rgn := CreateRoundRectRgn(Left, Top, Right, Bottom, cRad, cRad);
ClipRegion := l_Rgn;
inherited;
finally
PopClipRect;
end;//try..finally
PushClipRect;
try
l_Rgn1 := Tl3Region.Create;
try
l_NeedTriangle := (f_TopLine = 1) AND (f_HeaderHeight <> 0);
if l_NeedTriangle then
begin
SetLength(l_PointArray, 3);
l_Med := l_SR.Left + (l_SR.Right - l_SR.Left) div 2;
l_PointArray[0] := Point(l_Med - cArrow, l_SR.Top);
l_PointArray[1] := Point(l_Med + cArrow, l_SR.Top);
l_PointArray[2] := Point(l_Med, l_SR.Top - cArrow);
l_Rgn1.Rgn := Windows.CreatePolygonRgn(l_PointArray[0],
Length(l_PointArray),
ALTERNATE{WINDING});
ClipRegion := l_Rgn1;
FillRgn(l_Rgn1);
end;//f_TopLine = 1
l_Rgn2 := l_Rgn.Clone;
if l_NeedTriangle then
l_Rgn2.Combine(l_Rgn1, RGN_OR);
ClipRegion := l_Rgn2;
Push;
try
Canvas.Brush.Color := clSilver;
FrameRgn(DC, l_Rgn2.Rgn, Canvas.Brush.Handle, cFW, cFW);
finally
Pop;
end;//try..finally
finally
FreeAndNil(l_Rgn1);
end;//try..finally
finally
PopClipRect;
end;//try..finally
PushClipRect;
try
BackColor := TopBC;
//BackColor := l_BC;
l_Rgn1 := Tl3Region.Create;
try
l_Rgn1.Rect := LR2DR(l_R);
l_Rgn1.Combine(l_Rgn, RGN_DIFF);
ClipRegion := l_Rgn1;
FillRect(l_R);
finally
FreeAndNil(l_Rgn1);
end;//try..finally
finally
PopClipRect;
end;//try..finally
finally
FreeAndNil(l_Rgn);
end;//try..finally
end;//with Area.rCanvas
end//ParaX.Attr[k2_tiStyle].BoolA[k2_tiCollapsable]
else
if not SpecialFill(aRect, False) then
inherited;
//#UC END# *4E2702FE01EA_49DA30D901B6_impl*
end;//TevTextParaPainterEx.FillUnfilled
P.S. http://18delphi.blogspot.ru/search/label/%D0%BF%D0%B8%D1%81%D0%B0%D1%82%D0%B5%D0%BB%D1%8F%D0%BC%20%D1%80%D0%B5%D0%B4%D0%B0%D0%BA%D1%82%D0%BE%D1%80%D0%BE%D0%B2
"Я бы лично посоветовал бы поглядеть в сторону CoreText и AttributedString от xCode.
Ну и в сторону - GoF."
"Не смотрите в сторону RTF как "модели документа в памяти". Смотрите на него не более чем на ВНЕШНИЙ формат. Внутри раскладывайте в СВОИ структуры. Лучше в древесные. И описываемые DTD. Смотрите в сторону XML. Это не значит, что "надо сделать кальку с XML". Конечно же - НЕТ. СМОТРИТЕ на него. Анализируйте и рождайте собственные БИНАРНЫЕ структуры "по образу и подобию"."
В общем - что надо сделать? Написать рендеринг ОТДЕЛЬНО взятого текстового параграфа. С текстом и шрифтовым оформлением. Всё остальное - из него выводится. А писать там в общем - несложно. Прямо как "кальку" с CoreText. Параграф с оформлением -> Frame -> DrawFrame.
Как писать? Взять GoF и "концепцию Glyph'ов".
Мой привет. Далеко не идеальный:
type
TnevTextParaRenderInfo = class(TnevLeafRenderInfo)
private
// private fields
f_L : InevLines;
f_VS : InevTag;
f_Children : TnevFormatInfoList;
protected
// overridden property methods
function pm_GetViewSegmentsPlace: InevTag; override;
function pm_GetLinesPlace: InevLines; override;
procedure pm_SetLinesPlace(const aValue: InevLines); override;
function pm_GetLocSpacing: TnevRect; override;
protected
// overridden protected methods
procedure Cleanup; override;
{* Функция очистки полей объекта. }
function GetMaxLinesCount: Integer; override;
procedure DoRecalc(const aView: InevViewMetrics); override;
public
// overridden public methods
function GetInfoForChild(const aChild: InevObjectPrim): TnevFormatInfo; override;
procedure DoInvalidateShape(const aShape: InevObject;
aParts: TnevShapeParts); override;
procedure WForce(aParts: TnevRenderingInfoParts); override;
end;//TnevTextParaRenderInfo
...
procedure TnevTextParaRenderInfo.Cleanup;
//#UC START# *479731C50290_481F174D01BF_var*
//#UC END# *479731C50290_481F174D01BF_var*
begin
//#UC START# *479731C50290_481F174D01BF_impl*
if (f_Children <> nil) then
f_Children.ClearReferencesToParentFormatInfo;
FreeAndNil(f_Children);
f_L := nil;
f_VS := nil;
inherited;
//#UC END# *479731C50290_481F174D01BF_impl*
end;//TnevTextParaRenderInfo.Cleanup
function TnevTextParaRenderInfo.GetInfoForChild(const aChild: InevObjectPrim): TnevFormatInfo;
//#UC START# *4815C94A027A_481F174D01BF_var*
var
l_Index : Integer;
l_Item : TnevFormatInfo;
//#UC END# *4815C94A027A_481F174D01BF_var*
begin
//#UC START# *4815C94A027A_481F174D01BF_impl*
Assert(aChild.InheritsFrom(k2_idLeafPara) OR
aChild.InheritsFrom(k2_idTextSegment));
if (f_Children = nil) then
f_Children := TnevFormatInfoList.Create;
with f_Children do
begin
for l_Index := 0 to Pred(Count) do
begin
l_Item := Items[l_Index];
if l_Item.Obj.IsSame(aChild) then
begin
Result := l_Item;
Exit;
end;//l_Item.f_Obj.IsSame(aShape)
end;//for l_Index
l_Item := TnevFormatInfoFactory.CreateFormatInfo(aChild, Self, Metrics);
try
l_Item.LimitWidth := LimitWidth;
// ^ - может быть здесь надо отступы учесть
Add(l_Item);
Result := l_Item;
finally
FreeAndNil(l_Item);
end;//try..finally
end;//with f_Children
//#UC END# *4815C94A027A_481F174D01BF_impl*
end;//TnevTextParaRenderInfo.GetInfoForChild
procedure TnevTextParaRenderInfo.DoInvalidateShape(const aShape: InevObject;
aParts: TnevShapeParts);
//#UC START# *48172A690313_481F174D01BF_var*
//#UC END# *48172A690313_481F174D01BF_var*
begin
//#UC START# *48172A690313_481F174D01BF_impl*
if (nev_spSegments in aParts) then
begin
if (f_VS <> nil) then
begin
f_VS := nil;
Include(aParts, nev_spExtent);
end;//f_VS <> nil
end;//nev_spSegments in aParts
inherited;
//#UC END# *48172A690313_481F174D01BF_impl*
end;//TnevTextParaRenderInfo.DoInvalidateShape
procedure TnevTextParaRenderInfo.WForce(aParts: TnevRenderingInfoParts);
//#UC START# *48175C1302A3_481F174D01BF_var*
//#UC END# *48175C1302A3_481F174D01BF_var*
begin
//#UC START# *48175C1302A3_481F174D01BF_impl*
if (([nev_ripWidth, nev_ripLines, nev_ripHeight] * aParts) <> []) then
FreeAndNil(f_Children);
inherited;
//#UC END# *48175C1302A3_481F174D01BF_impl*
end;//TnevTextParaRenderInfo.WForce
function TnevTextParaRenderInfo.pm_GetViewSegmentsPlace: InevTag;
//#UC START# *4821DB2500CB_481F174D01BFget_var*
var
l_Super : InevTag absolute Result;
l_SuperHandle : Integer;
l_Exclude : TevNormalSegLayerHandleSet;
l_TextPara : InevTextPara;
function _AddLayer(const SegLst: InevTag; Index: Integer): Boolean;
function _AddSegment(const Seg: InevTag; Index: Integer): Boolean;
var
S1 : InevTag;
begin
S1 := Seg.CloneTag;
try
evSegmentsLayer_AddSegment(l_TextPara, l_Super, S1);
finally
S1 := nil;
end;{try..finally}
Result := true;
end;{_AddSegment}
var
l_Handle : Integer;
begin
l_Handle := SegLst.IntA[k2_tiHandle];
if (l_Handle > ev_slSuperposition) AND not (l_Handle in l_Exclude) then
SegLst.IterateChildrenF(k2L2TIA(@_AddSegment));
Result := true;
end;{_AddLayer}
var
l_Segments : InevTag;
l_CC : Integer;
//#UC END# *4821DB2500CB_481F174D01BFget_var*
begin
//#UC START# *4821DB2500CB_481F174D01BFget_impl*
if (f_VS = nil) then
begin
if not Obj.QT(InevTextPara, l_TextPara) then
Assert(false);
l_Segments := l_TextPara.Attr[k2_tiSegments];
if l_Segments.IsValid then
begin
if not l3IsNil(l_TextPara.Text) then
begin
l_CC := l_Segments.ChildrenCount;
if (l_CC <= 0) then
begin
// - нет никакого оформления, значит и суперпозиция не нужна
Result := k2NullTag;
l_TextPara.Attr[k2_tiSegments] := nil;
// - зачищаем странные сегменты
end//l_CC <= 0
else
begin
l_SuperHandle := 0;
if Self.Parent <> nil then
l_Exclude := Metrics.ExcludeSuper
else
l_Exclude := [];
if (l_Exclude = []) AND (l_CC = 1) then
begin
// - не нужно копировать сегменты в суперпозицию
Result := l_Segments.Child[0];
end
else
begin
Result := l_Segments.rAtomEx([k2_tiChildren, k2_tiHandle, l_SuperHandle]);
if (Result = nil) OR not Result.IsValid then
begin
Result := l_Segments.cAtomEx([k2_tiChildren, k2_tiHandle, l_SuperHandle], nil);
l_Segments.IterateChildrenF(k2L2TIA(@_AddLayer));
l_Segments.DeleteChild(Result);
end//not Result.IsValid
else
Assert(false);
end;//l_Exclude = []..
end;//l_CC <= 0
end//not l3IsNil(Text)
else
Result := k2NullTag;
end//l_Segments.IsValid
else
Result := k2NullTag;
f_VS := Result;
end//f_VS = nil
else
Result := f_VS;
//#UC END# *4821DB2500CB_481F174D01BFget_impl*
end;//TnevTextParaRenderInfo.pm_GetViewSegmentsPlace
function TnevTextParaRenderInfo.pm_GetLinesPlace: InevLines;
//#UC START# *4821DE24003D_481F174D01BFget_var*
//#UC END# *4821DE24003D_481F174D01BFget_var*
begin
//#UC START# *4821DE24003D_481F174D01BFget_impl*
Result := f_L;
//#UC END# *4821DE24003D_481F174D01BFget_impl*
end;//TnevTextParaRenderInfo.pm_GetLinesPlace
procedure TnevTextParaRenderInfo.pm_SetLinesPlace(const aValue: InevLines);
//#UC START# *4821DE24003D_481F174D01BFset_var*
//#UC END# *4821DE24003D_481F174D01BFset_var*
begin
//#UC START# *4821DE24003D_481F174D01BFset_impl*
f_L := aValue;
//#UC END# *4821DE24003D_481F174D01BFset_impl*
end;//TnevTextParaRenderInfo.pm_SetLinesPlace
function TnevTextParaRenderInfo.GetMaxLinesCount: Integer;
//#UC START# *4BC45843011E_481F174D01BF_var*
var
l_Lines: InevLines;
//#UC END# *4BC45843011E_481F174D01BF_var*
begin
//#UC START# *4BC45843011E_481F174D01BF_impl*
l_Lines := Lines;
if l_Lines = nil then
Result := 1
else
begin
Result := l_Lines.Count;
Assert(Result > 0);
end;//l_Lines = nil
//#UC END# *4BC45843011E_481F174D01BF_impl*
end;//TnevTextParaRenderInfo.GetMaxLinesCount
function TnevTextParaRenderInfo.pm_GetLocSpacing: TnevRect;
//#UC START# *4E5F3D1102B8_481F174D01BFget_var*
const
cnTopSpace = nevInch div 8;
//#UC END# *4E5F3D1102B8_481F174D01BFget_var*
begin
//#UC START# *4E5F3D1102B8_481F174D01BFget_impl*
Result := inherited pm_GetLocSpacing;
if (Metrics.AACLike = nev_aacLeft) and EvIsParaHyperlink(Obj) then
begin
Result.Top := Result.Top + cnTopSpace;
Result.Bottom := Result.Bottom + cnTopSpace;
end;// if (Metrics.AACLike = nev_aacLeft) and EvIsParaHyperlink(Obj) then
//#UC END# *4E5F3D1102B8_481F174D01BFget_impl*
end;//TnevTextParaRenderInfo.pm_GetLocSpacing
procedure TnevTextParaRenderInfo.DoRecalc(const aView: InevViewMetrics);
//#UC START# *4E7094780214_481F174D01BF_var*
var
l_Height : Integer;
l_InfoCanvas : InevInfoCanvas;
l_Map : TevLineArray;
l_InLimitWidth : Integer;
procedure DoFormat{(const ETAOIN_SHRDLU_aSegmentAndFontTool: InevFontTool)};
var
l_SoftEnterCount : Integer;
l_Str : Tl3PCharLen; // - Строка параграфа.
l_PrevWrap : PAnsiChar;
l_FontIndexes : Tl3FontIndexes;
l_AllowHyphen : Boolean;
l_ITabInfo : Il3TabInfo;
l_MTabInfo : Il3TabInfo;
l_MeasureCanvas : InevInfoCanvas;
l_CharSize : Integer;
l_Spaces : TnevRect;
procedure FormatStr(var theLimitWidth : Integer;
const aCurFont : InevFont;
IsSeg : Boolean;
var aStr : Tl3PCharLen;
var theStrExtent : TnevPoint);
var
l_AddHyphen : Boolean;
l_StrExtent : TnevPoint;
procedure AddLine;
begin{AddLine}
l_PrevWrap := aStr.S;
if (theStrExtent.Y = 0) then
theStrExtent.Y := l_MeasureCanvas.AverageCharHeight;
l_Map.Add((aStr.S - l_Str.S) div l_CharSize,
theStrExtent, l_FontIndexes, l_AddHyphen);
l_InfoCanvas.FinishTabs(l_ITabInfo);
l_InfoCanvas.StartTabs(l_ITabInfo, Obj.TabStops);
// - Переоткрываем новую строку
if (l_InfoCanvas <> l_MeasureCanvas) then
begin
l_MeasureCanvas.FinishTabs(l_MTabInfo);
l_MeasureCanvas.StartTabs(l_MTabInfo, Obj.TabStops);
// - Переоткрываем новую строку
end;//l_InfoCanvas <> l_MeasureCanvas
Inc(l_Height, theStrExtent.Y);
theStrExtent := nevPt0;
l_StrExtent := nevPt0;
l_FontIndexes := [l_InfoCanvas.Font.Index];
theLimitWidth := l_InLimitWidth - l_Spaces.Left;
end;//AddLine
var
l_WrappedStr : Tl3PCharLen;
l_OTabInfo : Il3TabInfo;
procedure MeasureStr(aNeedTrim: Boolean);
var
l_Str : Tl3WString;
l_TabInfo : Il3TabInfo;
l_Len : Integer;
begin//MeasureStr
if aNeedTrim then
l_Str := l3RTrim(l_WrappedStr)
else
l_Str := l_WrappedStr;
if (l_InfoCanvas = l_MeasureCanvas) then
begin
l_MeasureCanvas.StartTabs(l_TabInfo, l_OTabInfo);
with l_MeasureCanvas.TextExtent(l_Str).P do
begin
Inc(theStrExtent.P.X, X);
Inc(l_StrExtent.P.X, X);
if l_AddHyphen then
begin
Inc(theStrExtent.P.X, evHyphenWidth);
Inc(l_StrExtent.P.X, evHyphenWidth);
end;//l_AddHyphen
if (Y > theStrExtent.Y) then
begin
theStrExtent.Y := Y;
l_StrExtent.Y := Y;
end;//Y > theStrExtent.Y
end;//with l_MeasureCanvas.TextExtent(l_Str).P
l_MeasureCanvas.FinishTabs(l_TabInfo);
end//l_InfoCanvas = l_MeasureCanvas
else
begin
l_MeasureCanvas.Font := l_InfoCanvas.Font;
l_MeasureCanvas.StartTabs(l_TabInfo, l_OTabInfo);
with l_MeasureCanvas.TextExtent(l_Str).P do
begin
Inc(theStrExtent.P.X, X);
if l_AddHyphen then
Inc(theStrExtent.P.X, evHyphenWidth);
if (Y > theStrExtent.Y) then
theStrExtent.Y := Y;
end;//with l_MeasureCanvas.TextExtent(l_Str).P
l_MeasureCanvas.FinishTabs(l_TabInfo);
l_InfoCanvas.StartTabs(l_TabInfo, l_OTabInfo);
with l_InfoCanvas.TextExtent(l_Str).P do
begin
Inc(l_StrExtent.P.X, X);
if l_AddHyphen then
Inc(l_StrExtent.P.X, evHyphenWidth);
if (Y > l_StrExtent.Y) then
l_StrExtent.Y := Y;
end;//with l_MeasureCanvas.TextExtent(l_Str).P
l_InfoCanvas.FinishTabs(l_TabInfo);
end;//l_InfoCanvas = l_MeasureCanvas
end;//MeasureStr
var
l_WrapPos : Integer; { - Позиция соответствующая максимальной ширине. }
l_SoftEnterStr : PAnsiChar; { - Позиция SoftEnter'а. }
l_NoTabs : Boolean;
l_InTable : Boolean;
begin//FormatStr
l_StrExtent := theStrExtent;
with l_InfoCanvas do
begin
Font := aCurFont;
Include(l_FontIndexes, Font.Index);
end;//with l_InfoCanvas
l_NoTabs := False;
while True do
begin
l_AddHyphen := False;
l_OTabInfo := l_InfoCanvas.TabInfo;
l_WrapPos := l_InfoCanvas.Pos2IndexQ(theLimitWidth - l_StrExtent.X, aStr, l_NoTabs);
Assert(l_WrapPos >= 0);
//l_WrapPos := l_InfoCanvas.Pos2Index(theLimitWidth - l_StrExtent.X, aStr);
if (l_WrapPos <= 0) then
begin
if (l_StrExtent.X <= 0) then
l_WrapPos := 1
else
if l3Same(aStr, l_Str) then
//if (aStr.S = l_Str.S) then
// - если често - я не до конца понял, что это за проверка и какая из двух
// проверок правильнее.
Break
{ else
if (aStr.SLen > 0) then
Assert(false)};
end;//l_WrapPos <= 0
l_WrappedStr := aStr;
if (l_WrapPos >= aStr.SLen) then
l_WrappedStr.SLen := l_WrapPos
else
l_WrappedStr.SLen := l3Utils.l3FindNextLine(aStr, l_WrapPos);
// - Нашли где должна кончаться строка без учета SoftEnter.
if (l_SoftEnterCount > 0) then
begin
// - Еще есть SoftEnter'ы - надо их обработать.
l_SoftEnterStr := ev_lpScan(cc_SoftEnter, l_WrappedStr.S, l_WrappedStr.SLen);
if (l_SoftEnterStr <> nil) then
begin
Dec(l_SoftEnterCount);
l_WrappedStr.SLen := l_SoftEnterStr - l_WrappedStr.S;
aStr.Init(l_SoftEnterStr + 1, aStr.SLen - (l_WrappedStr.SLen + 1), aStr.SCodePage);
MeasureStr(False); // - Измеряем размеры полученной строки.
AddLine; // - Добавляем строку с SoftEnter'ом.
if (aStr.SLen = 0) then
Break // - Строка закончилась
else
Continue; // - Продолжаем форматировать остаток строки.
end;//l_SoftEnterStr <> nil
end;//l_SoftEnterCount > 0
l_InTable := Obj.Owner.IsValid and Obj.Owner.InheritsFrom(k2_idTableCell);
if (l_WrapPos < aStr.SLen) then
begin
// - Обрабатываем переносы по слогам
if l_AllowHyphen then
begin
case l3SplitBySlog(aStr, l_WrapPos, l_InTable) of
l3_ssHyphen : l_WrappedStr.SLen := l_WrapPos;
l3_ssYes :
begin
l_WrappedStr.SLen := l_WrapPos;
l_AddHyphen := True;
end;//l3_ssYes
end;//case l3SplitBySlog(aStr, l_WrapPos)
end//l_AllowHyphen
else
(*with l_WrappedStr do
if not evWhiteSpace(S[Pred(SLen)]) then*)
if (l_StrExtent.X <= 0) then
l_AddHyphen := l3SplitBySlog(aStr, l_WrappedStr.SLen, l_InTable) = l3_ssYes;
end;//l_AllowHyphen
aStr.Shift(l_WrappedStr.SLen);
if (aStr.SLen = 0) then
begin
// - Строка вся кончилась.
MeasureStr(False); // - Измеряем размеры полученной строки.
Break; // - Выходим, т.к. добавлять нецелую строку не надо.
end;//aStr.SLen
if not l_AddHyphen AND
// не было переноса
(l_WrappedStr.S > l_PrevWrap) AND
(l_WrappedStr.SLen > 0) then
// - предыдущие две проверки защищают от зацикливания и выхода за границы
begin
if not l3IsWhiteSpace((aStr.S-1)^) then
// - разрезали посередине слова
if l3IsWhiteSpace((l_WrappedStr.S-1)^)
// - пробел был где-то раньше
OR IsSeg
// - это сегмент, который не надо резать пополам
then
begin
// - Учитываем ситуацию когда сегмент режется посередине слова
aStr.Init(l_WrappedStr.S, aStr.SLen + l_WrappedStr.SLen, aStr.SCodePage);
l_WrappedStr.SLen := 0;
end;//evWhiteSpace(l_WrappedStr.S-1)^)
end;//not l_AddHyphen..
MeasureStr(True); // - Измеряем размеры полученной строки.
AddLine; // - Добавляем полученную строку.
end;//while True
end;//FormatStr
var
l_CurStr : Tl3PCharLen;
l_StrExtent : TnevPoint;
l_LimitWidth : Integer;
l_ParaFont : InevFont;
function FormatSegment(const aSegment: InevTag; Index: Integer): Boolean;
var
l_SegFont : InevFont;
l_SegStart : Integer;
l_SegFinish : Integer;
begin//FormatSegment
Result := True;
with aSegment do
begin
l_SegStart := Pred(IntA[k2_tiStart]);
l_SegFinish := IntA[k2_tiFinish];
end;//with aSegment
{ Обрабатываем часть строки до выделения: }
l_CurStr.SLen := Min(l_SegStart, l_Str.SLen) - (l_CurStr.S - l_Str.S);
if (l_CurStr.SLen > 0) then
FormatStr(l_LimitWidth, l_ParaFont, false, l_CurStr, l_StrExtent);
{ Обрабатываем отрезок выделения. }
l_CurStr.SLen := Min(l_SegFinish - l_SegStart,
l_Str.SLen - (l_CurStr.S - l_Str.S));
if (l_CurStr.SLen > 0) then
begin
//evCheckRenderedObject(aView, aSegment, Obj, aSegmentAndFontTool, Self);
l_SegFont := Self.InfoForChild(TnevSegmentObject.Make(aSegment)).Font[true];
//l_SegFont := aSegmentAndFontTool.ETAOIN_SHRDLU_GetObjIFont(aView, aSegment, true, Self);
try
FormatStr(l_LimitWidth, l_SegFont, true, l_CurStr, l_StrExtent);
finally
l_SegFont := nil;
end;//try..finally
end;//l_CurStr.SLen > 0
end;//FormatSegment
function CheckSegmentHeight(const aSegment: InevTag; Index: Integer): Boolean;
var
l_SegFont : InevFont;
l_SegStart : Integer;
l_SegFinish : Integer;
begin//
Result := True;
with aSegment do
begin
l_SegStart := Pred(IntA[k2_tiStart]);
l_SegFinish := IntA[k2_tiFinish];
end;//with aSegment
l_CurStr.S := l_Str.S + l_SegStart;
l_CurStr.SLen := l_SegFinish - l_SegStart;
if (l_CurStr.SLen > 0) then
begin
//evCheckRenderedObject(aView, aSegment, Obj, aSegmentAndFontTool, Self);
l_SegFont := Self.InfoForChild(TnevSegmentObject.Make(aSegment)).Font[true];
//l_SegFont := aSegmentAndFontTool.ETAOIN_SHRDLU_GetObjIFont(aView, aSegment, true, Self);
try
l_MeasureCanvas.Font := l_SegFont;
l_Height := Max(l_Height, l_MeasureCanvas.TextExtent(l_Str).Y);
finally
l_SegFont := nil;
end;//try..finally
end;//l_CurStr.SLen > 0
end;//CheckSegmentHeight
var
l_FirstIndent : Integer;
l_SLI : Tl3SingleLineInfo;
begin//DoFormat
(*if (RecalcThread <> nil) AND not RecalcThread.ChangeCanvas then
l_MeasureCanvas := l_InfoCanvas
else *)begin
l_MeasureCanvas := aView.InfoCanvas;
//l_MeasureCanvas := l_InfoCanvas;
// http://mdp.garant.ru/pages/viewpage.action?pageId=88639598
// - может статься, что что-то не доделал
// Точно не доделал:
// http://mdp.garant.ru/pages/viewpage.action?pageId=195758154
//l_MeasureCanvas := ETAOIN_SHRDLU_l3CrtIC;
if l_InfoCanvas.EQ(l_MeasureCanvas) then
l_MeasureCanvas := l_InfoCanvas;
end;//RecalcThread <> nil
l_Spaces := Self.Get_Spacing;
l_Map := TevLineArray.Create;
try
Self.wMap(l_Map);
finally
l_Map := l_Map.Free;
end;//try..finally
try
if not Obj.TreatCollapsedAsHidden OR not aView.IsTagCollapsed(Obj) then
begin
l_AllowHyphen := Obj.BoolA[k2_tiAllowHyphen];
l_Str := Obj.Text;
if (l_Str.SCodePage = CP_Unicode) then
l_CharSize := SizeOf(WideChar)
else
l_CharSize := SizeOf(ANSIChar);
l_ParaFont := Self.Get_Font(true);
//l_ParaFont := aSegmentAndFontTool.ETAOIN_SHRDLU_GetObjIFont(aView, Obj, False, Self);
try
l_InfoCanvas.Font := l_ParaFont;
if (l_InfoCanvas <> l_MeasureCanvas) then
l_MeasureCanvas.Font := l_ParaFont;
l_InfoCanvas.StartTabs(l_ITabInfo, Obj.TabStops);
if (l_InfoCanvas <> l_MeasureCanvas) then
l_MeasureCanvas.StartTabs(l_MTabInfo, Obj.TabStops);
try
Dec(l_InLimitWidth, l_Spaces.Right);
if l3IsNil(l_Str) then
begin
l_Height := l_MeasureCanvas.AverageCharHeight;
l_Map.Add(0, Tl3_Point_C(0, l_Height));
if (Obj.IntA[k2_tiJustification] = Ord(ev_itPreformatted)) then
Self.wWidth(0)
else
Self.wWidth(l_InLimitWidth);
end//l3IsNil(l_Str)
else
if (Obj.IntA[k2_tiJustification] = Ord(ev_itPreformatted)) and Obj.IsValid then
begin
l_StrExtent := l_MeasureCanvas.TextExtent(l_Str);
l_Height := l_StrExtent.Y;
with Self.ViewSegments do
if IsValid then
begin
IterateChildrenF(k2L2TIA(@CheckSegmentHeight));
l_StrExtent.Y := l_Height;
end;//IsValid
// Не уверен, что такая проверка нужна...
l_Map.Add(l_Str.SLen, l_StrExtent);
Self.wWidth(l_StrExtent.X + l_Spaces.Left);
end//IntA[k2_tiJustification] = Ord(ev_itPreformatted)
else
begin
Self.wWidth(l_InLimitWidth);
l_FirstIndent := Self.Get_FirstIndent;
if (l_Spaces.Left >= l_InLimitWidth - 100) OR
(l_FirstIndent >= l_InLimitWidth - 100) then
begin
l_Height := l_MeasureCanvas.AverageCharHeight;
l_Map.Add(0, Tl3_Point_C(0, l_Height));
end//l_Spaces.Left >= l_InLimitWidth - 100
else
begin
l_Height := 0;
l_StrExtent := nevPt0;
l_LimitWidth := l_InLimitWidth - l_FirstIndent;
l_FontIndexes := [];
l_SoftEnterCount := l3CountOfChar(cc_SoftEnter, l_Str);
l_CurStr := l_Str;
l_PrevWrap := l_Str.S;
with Self.ViewSegments do
if IsValid then
begin
IterateChildrenF(k2L2TIA(@FormatSegment));
l_CurStr.SLen := l_Str.SLen - (l_CurStr.S - l_Str.S);
FormatStr(l_LimitWidth, l_ParaFont, false, l_CurStr, l_StrExtent);
// - доформатируем хвост после последнего сегмента
end//IsValid
else
FormatStr(l_LimitWidth, l_ParaFont, false, l_CurStr, l_StrExtent);
if (l_StrExtent.Y <= 0) then
begin
l_MeasureCanvas.Font := l_ParaFont;
l_StrExtent.Y := l_MeasureCanvas.AverageCharHeight;
end;//l_StrExtent.Y <= 0
l_Map.Add(l_Str.SLen, l_StrExtent, l_FontIndexes, false);
Inc(l_Height, l_StrExtent.Y);
end;//l_Spaces.Left >=..
end;//..ev_itPreformatted..
finally
if (l_InfoCanvas <> l_MeasureCanvas) then
l_MeasureCanvas.FinishTabs(l_MTabInfo);
l_InfoCanvas.FinishTabs(l_ITabInfo);
end;//try..finally
finally
l_ParaFont := nil;
end;//try..finally
(* with Obj.rAtomEx([k2_tiFrame, k2_tiFrameUp]) do
if IsValid then
Inc(l_Height, IntA[k2_tiWidth]);*)
end//not Obj.TreatCollapsedAsHidden OR not aView.IsTagCollapsed(Obj)
else
begin
Self.wWidth(0);
l_Height := 0;
end//not Obj.TreatCollapsedAsHidden OR not aView.IsTagCollapsed(Obj)
;
Inc(l_Height, l_Spaces.Top);
Inc(l_Height, l_Spaces.Bottom);
finally
if (l_Map <> nil) then
begin
if (l_Map.Count < 1) then
begin
// - удаляем ненужное в данном случае форматирование
l_Map := nil;
Self.wMap(nil);
end//l_Map.Count < 1
else
if (l_Map.Count = 1) then
begin
if (Obj.IntA[k2_tiJustification] = Ord(ev_itPreformatted)) then
begin
// - удаляем ненужное в данном случае форматирование
l_Map := nil;
Self.wMap(nil);
end//IntA[k2_tiJustification] = Ord(ev_itPreformatted)
else
begin
with PevLineInfo(l_Map.Items[0])^ do
l_SLI := Tl3SingleLineInfo.Make(LE.X, LE.Y, FI);
try
l_Map := nil;
Self.wMap(l_SLI);
finally
FreeAndNil(l_SLI);
end;//try..finally
end;//IntA[k2_tiJustification] = Ord(ev_itPreformatted)
end;//l_Map.Count <= 1
end;//l_Map <> nil
end;//try..finally
end;//DoFormat
{var
l_SegmentAndFontTool : InevFontTool;}
//#UC END# *4E7094780214_481F174D01BF_var*
begin
//#UC START# *4E7094780214_481F174D01BF_impl*
l_InLimitWidth := Self.rLimitWidth;
l_Height := 0;
Obj.DoLoad;
l_InfoCanvas := aView.FormatCanvas;
// l_InfoCanvas := aView.InfoCanvas;
try
if Self.IsHidden(false,
(l_InfoCanvas <> nil) AND
l_InfoCanvas.Printing) then
begin
Self.wWidth(0);
Self.wMap(nil);
end//Obj.IsHiddenPrim(Self, aView.HiddenStyles)
else
begin
(* l_SegmentAndFontTool := aView.FontTool{ As InevFontTool};
try*)
(* l_SegmentAndFontTool.ForPrinting := (l_InfoCanvas.Printing
{$IFNDef Nemesis}OR not aView.IsWebStyle{$EndIf});*)
l_InfoCanvas.Lock;
try
l_InfoCanvas.PushBC;
l_InfoCanvas.PushFC;
// - Запоминаем - чтобы не испортить фон для отрисовки
try
DoFormat{(l_SegmentAndFontTool)};
finally
l_InfoCanvas.PopFC;
l_InfoCanvas.PopBC;
end;//try..finally
finally
l_InfoCanvas.Unlock;
end;//try..finally
(* finally
l_SegmentAndFontTool := nil;
end;//try..finally*)
end;//not Obj.IsHiddenPrim(Self, aView.HiddenStyles)
finally
l_InfoCanvas := nil;
end;//try..finally
Self.wHeight(l_Height);
//#UC END# *4E7094780214_481F174D01BF_impl*
end;//TnevTextParaRenderInfo.DoRecalc
P.P.S. Говоря о "портянках кода" - наверное теперь понятно, что я имею в виду. Но я (пока) не знаю - как это улучшить.
P.P.P.S. Этот код в таком (или примерно таком) виде "тянется" аж с 1996 (!) года. Я бы хотел его улучшить. Но пока - не понял как. При всей моей любви к "паттернам". Интересно было бы заглянуть ВНУТРЬ CoreText. И его Frame и DrawFrame.
P.P.P.P.S. Многое менялось за эти годы. COM|CORBA пришли и "почти ушли". Интерфейсы. Примеси. Шаблоны. UML. Но этот код как был "странным". Так и остался. Зато - "вполне эффективным". Это ещё вы не видели кода "рисования фрейма". Там - "ещё веселее".
Кстати. Вот и он:
procedure TevTextParaPainterEx.CheckDrawFocused;
//#UC START# *502268560274_49DA30D901B6_var*
//#UC END# *502268560274_49DA30D901B6_var*
begin
//#UC START# *502268560274_49DA30D901B6_impl*
if (Area.rView.As_InevView.Metrics.AACLike = nev_aacLeft) then
Area.rCanvas.NotFocused := False;
//#UC END# *502268560274_49DA30D901B6_impl*
end;//TevTextParaPainterEx.CheckDrawFocused
procedure TevTextParaPainterEx.CheckInvertInLeftAAC;
//#UC START# *5052EF710307_49DA30D901B6_var*
//#UC END# *5052EF710307_49DA30D901B6_var*
begin
//#UC START# *5052EF710307_49DA30D901B6_impl*
if (Area.rView.As_InevView.Metrics.AACLike > nev_aacNone) and Area.rCanvas.Invert then
Area.rCanvas.Font.ForeColor := clBlack
//#UC END# *5052EF710307_49DA30D901B6_impl*
end;//TevTextParaPainterEx.CheckInvertInLeftAAC
procedure TevTextParaPainterEx.DrawArrow(aRectHeight: Integer);
//#UC START# *50A4D6E90261_49DA30D901B6_var*
var
l_Delta : Integer;
l_ParaH : Integer;
l_Width : Integer;
l_Height : Integer;
l_ImageInfo : PnevControlImageInfo;
//#UC END# *50A4D6E90261_49DA30D901B6_var*
begin
//#UC START# *50A4D6E90261_49DA30D901B6_impl*
if (Area.rView.As_InevView.Metrics.AACLike = nev_aacLeft) and Area.rCanvas.Invert and ParentPainter.IsSelectedOneWholePara(ParaX) then
with Area.rCanvas do
begin
l_ImageInfo := FormatInfo.ImageInfo;
if l_ImageInfo <> nil then
begin
l_Height := DP2LP(PointX(l_ImageInfo.rImageList.Height)).X;
l_ParaH := (aRectHeight + Spaces.Top + Spaces.Bottom);
l_Delta := (l_ParaH - l_Height) div 2;
l_Delta := l_Delta + l_Height - l_ParaH + Spaces.Bottom;
l_Width := DP2LP(PointX(l_ImageInfo.rImageList.Width)).X;
MoveWindowOrg(l3Point(l_Width + (l3Inch div 16), l_Delta));
FillRect(l3SRect(0, 0, l_ImageInfo.rImageList.Width, l_ImageInfo.rImageList.Height));
l_ImageInfo.rImageList.Draw(As_Il3Canvas, 0, 0, l_ImageInfo.rFirstIndex);
end; // if l_ImageInfo <> nil then
end; // if Area.rView.As_InevView.Metrics.AACLike then
//#UC END# *50A4D6E90261_49DA30D901B6_impl*
end;//TevTextParaPainterEx.DrawArrow
procedure TevTextParaPainterEx.ValidateSegFont(const aSeg: InevTag);
//#UC START# *4D664903006E_49DA30D901B6_var*
//#UC END# *4D664903006E_49DA30D901B6_var*
begin
//#UC START# *4D664903006E_49DA30D901B6_impl*
if (Area.rView.As_InevView.Metrics.AACLike > nev_aacNone) and Area.rCanvas.Invert then
if aSeg.InheritsFrom(k2_idHyperlink) then
Area.rCanvas.Font.ForeColor := clBlack;
//#UC END# *4D664903006E_49DA30D901B6_impl*
end;//TevTextParaPainterEx.ValidateSegFont
procedure TevTextParaPainterEx.ValidateParaFont;
//#UC START# *4D725A550218_49DA30D901B6_var*
//#UC END# *4D725A550218_49DA30D901B6_var*
begin
//#UC START# *4D725A550218_49DA30D901B6_impl*
// ничего не делаем
//#UC END# *4D725A550218_49DA30D901B6_impl*
end;//TevTextParaPainterEx.ValidateParaFont
procedure TevTextParaPainterEx.Release;
//#UC START# *479F2AFB0397_49DA30D901B6_var*
//#UC END# *479F2AFB0397_49DA30D901B6_var*
begin
//#UC START# *479F2AFB0397_49DA30D901B6_impl*
FreeAndNil(f_LinePainter);
inherited;
//#UC END# *479F2AFB0397_49DA30D901B6_impl*
end;//TevTextParaPainterEx.Release
procedure TevTextParaPainterEx.GetFramePartPrim(const aFrame: InevTag;
anIndex: Tl3FramePartIndex;
var thePart: TnevFramePart);
//#UC START# *4804B9BB0383_49DA30D901B6_var*
var
l_Style : InevTag;
l_N : InevPara;
//#UC END# *4804B9BB0383_49DA30D901B6_var*
begin
//#UC START# *4804B9BB0383_49DA30D901B6_impl*
inherited;
if (anIndex = l3_fpiUp) then
if (Spaces.Top > 0) AND not ParaX.Attr[k2_tiStyle].BoolA[k2_tiHeaderHasOwnSpace] then
begin
thePart.rDrawType := lpdDraw;
thePart.rColor := clBlack;
thePart.rWidth := 0;
thePart.rSpaceBefore := (l3Inch div 8) div 2;
thePart.rSpaceAfter := (l3Inch div 8) div 2;
end;//Spaces.Top > 0..
if (thePart.rDrawType = lpdDraw) AND (anIndex = l3_fpiLeft) then
thePart.rSpaceBefore := Max(Min(f_FirstIndent, Spaces.Left) - l3Inch div 16, 0);
if (thePart.rDrawType <> lpdHidden) then
// - тут прячем лишние рамки (если идёт пачка параграфов с одним стилем)
begin
l_Style := ParaX.Attr[k2_tiStyle];
if l_Style.IsValid then
begin
Case anIndex of
l3_fpiUp:
begin
l_N := evPrevOverallPara(ParaX);
if (l_N <> nil) AND l_N.IsValid AND
(l_Style.IntA[k2_tiHandle] = l_N.IntA[k2_tiStyle]) AND
l_N.Attr[k2_tiFrame].IsValid then
thePart.rDrawType := lpdHidden;
end;//l3_fpiUp
l3_fpiDown:
begin
l_N := evNextOverallPara(ParaX);
if (l_N <> nil) AND l_N.IsValid AND
(l_Style.IntA[k2_tiHandle] = l_N.IntA[k2_tiStyle]) AND
l_N.Attr[k2_tiFrame].IsValid then
thePart.rDrawType := lpdHidden;
end;//l3_fpiDown
end;//Case anIndex
end;//l_Style.IsValid
end;//thePart.rDrawType <> lpdHidden
if (thePart.rDrawType = lpdDraw) then
begin
if (anIndex = l3_fpiDown) then
begin
thePart.rSpaceBefore := Max(thePart.rSpaceBefore, l3Inch div 16);
thePart.rSpaceAfter := Max(thePart.rSpaceAfter, l3Inch div 32);
end;//anIndex = l3_fpiDown
end;//thePart.rDrawType = lpdDraw
//#UC END# *4804B9BB0383_49DA30D901B6_impl*
end;//TevTextParaPainterEx.GetFramePartPrim
procedure TevTextParaPainterEx.InitBottom(var theBottom: InevBasePoint;
var theCellBottom: InevBasePoint);
//#UC START# *4804BC800172_49DA30D901B6_var*
var
l_Pt : TnevPoint;
//#UC END# *4804BC800172_49DA30D901B6_var*
begin
//#UC START# *4804BC800172_49DA30D901B6_impl*
theCellBottom := nil;
if (Map <> nil) then
// - эта проверка НУЖНА, например для вложенных объектов (формул)
with Map.Bounds do
begin
l_Pt := l3Point(Left, Bottom);
if not DrawCompleted then
// ^ - http://mdp.garant.ru/pages/viewpage.action?pageId=132220046
Dec(l_Pt.P.Y, l3Epsilon);
theBottom.AsLeaf.InitPointByPt(Area.rView.As_InevView, l_Pt, Map);
if (Bottom > Top + Spaces.Top) then
// - !!! вместо этой проверки для текстовых параграфов надо использовать
// номер строки, т.к. он всё равно известен.
theBottom := TnevAfterEndPoint.Make(Area.rView.As_InevView, theBottom);
end;//with Map.Bounds
//#UC END# *4804BC800172_49DA30D901B6_impl*
end;//TevTextParaPainterEx.InitBottom
procedure TevTextParaPainterEx.DoDrawFrameText(aTop: Boolean);
//#UC START# *4804C35B00B2_49DA30D901B6_var*
var
l_Space : Integer;
l_Style : InevTag;
l_Text : Tl3PCharLen;
l_Decor : InevTag;
l_DT : TnevDecorType;
//#UC END# *4804C35B00B2_49DA30D901B6_var*
begin
//#UC START# *4804C35B00B2_49DA30D901B6_impl*
f_TopLine := Succ(TopAnchor.VertPosition(Area.rView.As_InevView, FormatInfo));
if aTop then
l_Space := Spaces.Top
else
l_Space := Spaces.Bottom;
if (l_Space > 0) then
begin
if (aTop AND
(f_TopLine = 1)
) OR
(not aTop {AND
(f_TopLine = FormatInfo.Lines.Count) AND}
) then
begin
if aTop then
l_DT := nev_dtHeader
else
l_DT := nev_dtFooter;
l_Decor := FormatInfo.DecorObj(l_DT);
l_Style := ParaX.Attr[k2_tiStyle];
if l_Style.IsValid then
begin
l_Text := l_Style.PCharLenA[k2_tiShortName];
if not l3IsNil(l_Text) then
InternalDrawFrameText(l_Text, aTop, l_Space, l_DT, l_Decor, f_FirstIndent)
else
if l_Decor.IsValid then
InternalDrawFrameText(Tl3PCharLen(cc_EmptyStr), aTop, l_Space, l_DT, l_Decor, f_FirstIndent)
end;//l_Style.IsValid
end;//aTop
end;//l_Space > 0
//#UC END# *4804C35B00B2_49DA30D901B6_impl*
end;//TevTextParaPainterEx.DoDrawFrameText
function TevTextParaPainterEx.DrawLeaf: Boolean;
//#UC START# *4804C81000B9_49DA30D901B6_var*
var
LH : Integer; {- высота i-й строки}
l_Justification : TevIndentType; {- тип выравнивания параграфа}
l_ParaFont : Il3Font;
l_ParaS : Tl3PCharLen;
l_IsHyperlink : Boolean;
procedure DrawIndent(First : Boolean;
DX : Integer;
LL : Boolean;
AddHyphen : Boolean);
var
D : Integer;
HardEnterWidth : Integer;
l_PMStr : Tl3PCharLenPrim;
begin//DrawIndent
with Area.rCanvas do
begin
if not First AND AddHyphen then
ExtTextOut(l3Point0,
l3Rect(0, 0, evHyphenWidth, LH),
cc_HyphenStr);
if ((DX > 0) AND (l_Justification <> ev_itWidth)) then
begin
if ((First AND (l_Justification in [ev_itRight, ev_itCenter])) OR
(not First AND
((l_Justification in [ev_itLeft, ev_itCenter]) OR DrawSpecial))) then
begin
D := 0;
case l_Justification of
ev_itLeft:
if not First then
D := DX;
ev_itRight:
Inc(D, DX);
ev_itCenter:
if First then
Inc(D, DX div 2)
else
Inc(D, DX - (DX div 2));
ev_itWidth: {D уже равен X0};
end;//case l_Justification
if not First AND DrawSpecial AND LL then
begin
if (ParaMarkStyle = ev_pmCell) then
l_PMStr := cc_CurrencyStr
else
l_PMStr := cc_HardEnterStr;
if l3IsNil(l_ParaS) then
begin
Font := l_ParaFont;
ValidateParaFont;
end;//l3IsNil(l_ParaS)
PushFC;
try
with Font do
begin
Name := def_ArialFontName;
Style := [];
end;//with Font
finally
PopFC;
end;//try..finally
HardEnterWidth := TextExtent(l_PMStr).X;
if (HardEnterWidth > D) then
D := HardEnterWidth;
if (SelRange <> nil) AND SelRange.ContainsEnd(Area.rView.As_InevView) then
begin
BeginInvert;
try
ExtTextOut(l3Point0, l3Rect(0, 0, HardEnterWidth, LH), l_PMStr);
finally
EndInvert;
end;//try..finally
end//(SelRange <> nil) AND SelRange.ContainsEnd
else
ExtTextOut(l3Point0, l3Rect(0, 0, HardEnterWidth, LH), l_PMStr);
end;//not First
MoveWindowOrg(l3PointX(-D));
if l_IsHyperlink then DrawArrow(LH);
end;//First...
end;//AddHyphen
end;//with Area.rCanvas
end;//DrawIndent
var
WC : Integer; {- количество слов в i-й строке}
DW : Integer;
ParaCaretPos : Integer;
LineCaretPos : Integer;
ParaCaretLine : Integer;
l_NeedCaret : Boolean;
l_Map : InevLines;
l_SolidBlock : Boolean;
function loc_DrawText(S: Tl3CustomString): Boolean;
function _TextRect(const S: Tl3PCharLen): Boolean;
procedure DrawSoftEnter;
begin//DrawSoftEnter
with Area.rCanvas do
begin
PushFC;
try
with Font do
begin
Name := def_SymbolFontName;
Style := [];
end;//with Font
finally
PopFC;
end;//try..finally
CaretLineOut(cc_SoftEnterStr, LH, not l_NeedCaret, LineCaretPos);
end;//with Area.rCanvas
end;//DrawSoftEnter
var
TLen : Integer;
l_NeedSoftEnter : Boolean;
S2Draw : Tl3PCharLen;
l_Str : Tl3PCharLen;
begin
with Area.rCanvas do
begin
CheckInvertInLeftAAC;
Result := ((ClipRect.Right > 0) OR (Caret <> nil)) AND HasToDraw;
if Result then
begin
S2Draw := S;
TLen := l3RTrim(S2Draw).SLen;
if (TLen > 0) AND (S2Draw.S[Pred(TLen)] = cc_SoftEnter) then
begin
l_NeedSoftEnter := DrawSpecial;
if l_NeedSoftEnter OR (LineCaretPos < 0) OR (LineCaretPos > S2Draw.SLen) then
Dec(TLen)
else
begin
{-эта ветка необходима для правильного позиционирования курсора
на конец строки с SoftEnter'ом }
S2Draw := CheckOutString(S2Draw);
S2Draw.S[Pred(TLen)] := cc_HardSpace;
TLen := S2Draw.SLen;
end;//l_NeedSoftEnter
end//TLen > 0
else
begin
l_NeedSoftEnter := false;
TLen := S2Draw.SLen;
end;//TLen > 0
l_Str := l3PCharLen(S2Draw.S, TLen, S2Draw.SCodePage);
if DrawSpecial then
l_Str := CheckConvertString(l_Str);
CaretLineOut(l_Str, LH, not l_NeedCaret, LineCaretPos);
if l_NeedSoftEnter then
DrawSoftEnter;
end;//Result
end;//with Area.rCanvas
end;//_TextRect
function DrawAllLine: Boolean;
{-нарисовать всю строку, без выделения}
begin//DrawAllLine
Result := _TextRect(S.AsPCharLen);
S.Offset(S.Len);
end;//DrawAllLine
var
D2S : Integer; {-ширина пустого места}
procedure GetD2S;
begin//GetD2S
if (WC > 0) then
begin
{if (DW < 0) then begin
D2S := 0;
DW := 0;
end else }begin
if (WC = 1) then
D2S := DW
else
D2S := DW div WC;
Dec(WC);
with Area.rCanvas do
begin
D2S := LP2DP(l3PointX(D2S)).X;
Dec(DW, DP2LP(PointX(D2S)).X);
end;//with Area.rCanvas
end;//DW < 0
end
else
D2S := 0;
end;//GetD2S
var
l_InDrawBlock : Boolean;
l_NeedCorrectCaret : Boolean;
l_pxLH : Integer;
l_NS : Tl3CustomString;
begin//loc_DrawText
Result := true;
with Area.rCanvas do
begin
if S.Empty then
begin
if (LineCaretPos = 0) then
begin
SetCaret(l3Point0, l3Point(AverageCharWidth, LH), not l_NeedCaret);
ParaCaretPos := -1;
LineCaretPos := -1;
ShowCursor := false;
end;//LineCaretPos = 0
end//S.Empty
else
begin
if (l_Justification <> ev_itWidth) OR
not l3HasWhiteSpace(S.AsPCharLen) then
begin
{-проверяем нужна ли разгонка и есть ли в строке пробелы}
if l_SolidBlock then
Result := DrawAllLine
else
f_LinePainter.DrawF(As_Il3Canvas, evL2DLA(@_TextRect), S);
end//l_Justification <> ev_itWidth
else
begin
{-рисуем разогнанную строку}
l_pxLH := LP2DP(l3PointY(LH)).Y;
l_NS := S.Clone;
try
while Result AND not S.Empty do
begin
S.FindCharEx(cc_HardSpace, l_NS);
l_NeedCorrectCaret := (LineCaretPos = S.Len);
if l_SolidBlock then
begin
Result := DrawAllLine;
l_InDrawBlock := false;
end//l_SolidBlock
else
l_InDrawBlock := Boolean(f_LinePainter.DrawF(As_Il3Canvas, evL2DLA(@_TextRect), S));
if Result AND l3IsWhiteSpaceS(S.AsPCharLen, -1) then
begin
if l_InDrawBlock then
begin
BeginInvert;
CheckInvertInLeftAAC;
end;
try
GetD2S;
FillRect(l3SRect(0, 0, D2S, l_pxLH));
MoveWindowOrg(PointX(-D2S));{-сдвигаем курсор отрисовки на ширину пустого места}
if l_NeedCorrectCaret then
IncCaret(D2S - 1);
Result := (ClipRect.Right > 0) AND HasToDraw;
finally
if l_InDrawBlock then
EndInvert;
end;//try..finally
end;//Result AND evWhiteSpace
S.AssignString(l_NS);
end;//while Result AND not S.Empty
finally
l3Free(l_NS);
end;//try..finally
end;//l_Justification <> ev_itWidth
end;//S.Empty
Result := Result AND (ClipRect.Right > 0) AND HasToDraw;
end;//with Area.rCanvas
end;//loc_DrawText
var
FirstLineWidth : Integer;
OtherLineWidth : Integer;
l_LineOffsetX : Integer;
l_ExtentX : Integer;
l_LineCount : Integer;
l_ActiveElement : InevActiveElement;
procedure DrawPara;
function DrawLine(ppLI: PPevLineInfo; LID: Integer): Boolean;
var
l_SegmentsList : Ik2TagList;
function DrawSegment(const aSeg: InevTag; Index: Integer): Boolean;
var
l_OM : TOutlineTextMetric;
l_Pnt : IevPainter;
l_DrawingObject : InevTag;
function DrawObject(const S: Tl3PCharLen): Boolean;
var
l_Ex : Tl3Point;
l_Obj : InevObject;
l_SegInf : TnevShapeInfo;
l_Points : TnevShapePoints;
l_Point : InevPoint;
l_BL : Integer;
begin//DrawObject
Result := true;
with Area.rCanvas do
begin
l_Ex := TextExtent(S);
PushWO;
try
l_BL := aSeg.IntA[k2_tiBaseLine];
if not l_DrawingObject.InheritsFrom(k2_idBitmapPara) then
// http://mdp.garant.ru/pages/viewpage.action?pageId=174295160&focusedCommentId=344137422#comment-344137422
// - пока так
MoveWindowOrg(l3PointY(-((LH - l_Ex.Y) div 2)))
else
begin
{ if (l_OM.otmDescent <> 0) then
if (l_OM.otmDescent <> 1) then
l_BL := l_BL + Area.rCanvas.DP2LP(PointY(l_OM.otmDescent - 1)).Y;}
if (l_OM.otmDescent <> 0) then
l_BL := l_BL + Area.rCanvas.DP2LP(PointY(l_OM.otmDescent)).Y;
MoveWindowOrg(l3PointY(-((LH - l_Ex.Y))));
end;//not l_DrawingObject.InheritsFrom(k2_idBitmapPara)
if (l_BL <> 0) then
MoveWindowOrg(l3PointY(-l_BL));
l3FillChar(l_Points, SizeOf(l_Points));
l_Points.rTop := nil;
l_Points.rCaret := nil;
l_Points.rSelection := nil;
l_Points.rPrevHeight := 0;
l_Points.rRealTop := nil;
l_Points.rFake := False;
l_Points.rCheckCaret := nil;
l_Points.rCheckSelection := nil;
if (LineCaretPos > 0) then
Dec(LineCaretPos, S.SLen);
if (SelRange <> nil) then
begin
l_Point := ParaX.MakePoint;
l_Point.PositionW := aSeg.IntA[k2_tiStart];
if SelRange.Contains(Area.rView.As_InevView, l_Point) then
if l_DrawingObject.QT(InevObject, l_Obj) then
l_Points.rSelection := l_Obj.Range;
end;//SelRange <> nil
(* if (LineCaretPos = 0) AND QT(InevObject, l_Obj) then
l_Pnt.Draw(nil, Area.rCanvas, nil, l_Obj.Point, nil, l_SegMap, High(Integer))
else*)
l_Pnt.Draw(Area^, l_Points, l_SegInf);
finally
PopWO;
end;//try..finally
MoveWindowOrg(l3PointX(-l_Ex.X));
MoveWindowOrg(PointX(-1));
end;//with Area.rCanvas
end;//DrawObject
var
l_SegFont : Il3Font;
l_BC : Tl3Color;
l_ActiveHyperlinkStyle : InevTag;
l_AH : InevActiveElement;
l_CheckedS1 : Tl3CustomString;
l_CheckedS2 : Tl3CustomString;
l_CheckedS3 : Tl3CustomString;
begin//DrawSegment
l_CheckedS1 := CheckS(1);
l_CheckedS2 := CheckS(2);
l_CheckedS3 := CheckS(3);
evTextParaGetLineSegment(l_Map, l_ParaS, Succ(LID), Index, l_SegmentsList,
l_CheckedS1, l_CheckedS2, l_CheckedS3
{CheckS(1), CheckS(2), CheckS(3)});
Result := loc_DrawText(l_CheckedS1{S[1]});
if Result then
begin
with Area.rCanvas do
begin
Push;
try
l_SegFont := FormatInfo.InfoForChild(TnevSegmentObject.Make(aSeg)).Font[false];
//l_SegFont := ETAOIN_SHRDLU_GetObjIFont(Area.rView.Metrics, aSeg, true, FormatInfo^, false);
if l_SegFont.IsAtomic then
begin
l3FillChar(l_OM, SizeOf(l_OM));
if (GetOutlineTextMetrics(Area.rCanvas.DC, SizeOf(l_OM), @l_OM) = 0) then
Assert(false, 'Не удалось получить метрики текущего шрифта канвы');
end;//l_SegFont.IsAtomic
l_BC := BackColor;
Font := l_SegFont;
// - устанавливаем шрифт сегмента
if (l_ActiveElement <> nil) then
begin
l_AH := TnevActiveHyperlink.Make(ParaX, aSeg);
if ParaX.IsDecorationElement then
l_AH := TnevDecorActiveHyperlink.Make(ParentPainter.Obj, l_AH);
//if aSeg.InheritsFrom(k2_idHyperlink) then // - это убрано, чтобы корректно продолжения ссылок подчёркивались
if l_ActiveElement.IsSame(l_AH) then
begin
l_ActiveHyperlinkStyle := k2.TypeTable.ObjToTag(k2.TypeTable[k2_idTextStyle].ValueTable.DRByID[evd_saActiveHyperLink]);
if l_ActiveHyperlinkStyle.IsValid then
begin
if l_ActiveHyperlinkStyle.HasSubAtom(k2_tiFont) then
begin
with l_ActiveHyperlinkStyle.Attr[k2_tiFont] do
begin
with Font do
begin
if HasSubAtom(k2_tiUnderline) then
Underline := BoolA[k2_tiUnderline];
if HasSubAtom(k2_tiBold) then
Bold := BoolA[k2_tiBold];
if HasSubAtom(k2_tiItalic) then
Italic := BoolA[k2_tiItalic];
if HasSubAtom(k2_tiStrikeout) then
Strikeout := BoolA[k2_tiStrikeout];
if HasSubAtom(k2_tiForeColor) then
ForeColor := IntA[k2_tiForeColor];
if HasSubAtom(k2_tiBackColor) then
BackColor := IntA[k2_tiBackColor];
end;//Font
end;//with l_ActiveHyperlinkStyle.Attr[k2_tiFont]
end;//l_ActiveHyperlinkStyle.HasSybAtom(k2_iFont)
end;//l_ActiveHyperlinkStyle.IsValid
end;//l_ActiveElement.IsSame(TnevActiveHyperlink.Make(ParaX, aSeg))
end;//l_ActiveElement <> nil
if (l_SegFont.BackColor = nevDefaultColor) AND
aSeg.HasSubAtom(k2_tiVisible) then
if aSeg.BoolA[k2_tiVisible] then
if not ParaX.BoolA[k2_tiVisible] then
// - принудительно выставляем цвет фона для видимых сегментов
BackColor := l_BC;
ValidateSegFont(aSeg);
if l_SegFont.IsAtomic then
begin
l_DrawingObject := aSeg.Child[0];
if l_DrawingObject.QT(IevPainter, l_Pnt) then
try
l_Pnt.ParentPainter := Self;
f_LinePainter.DrawF(As_Il3Canvas, evL2DLA(@DrawObject), l_CheckedS2{S[2]});
Result := true;
finally
l_Pnt := nil;
end//try..finally
else
Result := loc_DrawText(l_CheckedS2{S[2]});
end//l_SegFont.IsAtomic
else
Result := loc_DrawText(l_CheckedS2{S[2]});
finally
Pop;
end;//try..finally
if Result then
begin
Font := l_ParaFont;
// - возвращаем шрифт параграфа
ValidateParaFont;
Result := loc_DrawText(l_CheckedS3{S[3]});
end;//Result
end;//with Area.rCanvas
end;//Result
end;//DrawSegment
procedure DrawBullet;
const
cc_BulletStr : Tl3PCharLenConst = (S : #$B7;
SLen : 1;
SCodePage : CP_ANSI);
var
l_BulletWidth : Integer;
l_Bullet : Integer;
begin//DrawBullet
l_Bullet := ParaX.IntA[k2_tiBullet];
if (l_Bullet > 0) then
begin
// - bullet присутствует
with Area.rCanvas do
begin
Font.Name := def_SymbolFontName;
l_BulletWidth := TextExtent(cc_BulletStr).X;
MoveWindowOrg(l3PointX(2 * l_BulletWidth));
ExtTextOut(l3Point0, l3Rect(0, 0, l_BulletWidth, LH), cc_BulletStr);
MoveWindowOrg(l3PointX(-2 * l_BulletWidth));
Font := l_ParaFont;
//- восстанавливаем шрифт параграфа
ValidateParaFont;
end;//with Area.rCanvas
end;//l_Bullet > 0
end;//DrawBullet
var
LeftIndent : Integer;
MaxLineWidth : Integer;
IsLastLine : Boolean;
l_LineLength : Integer;
l_TabInfo : Il3TabInfo;
l_SaveOrg : Tl3Point;
l_TabIndent : Integer;
l_CheckedS1 : Tl3CustomString;
begin//DrawLine
Result := true;
l_TabIndent := 0;
with Area.rCanvas do
begin
StartTabs(l_TabInfo, ParaX.TabStops);
try
try
if (l_LineOffsetX >= 0) then
begin
l_SaveOrg := WindowOrg;
try
LH := ppLI^^.LE.Y;
Dec(l_SaveOrg.P.Y, LH);
{-сдвигаем курсор отрисовки на высоту строки}
l_LineLength := ppLI^^.B - l_LineOffsetX;
{-вычисляем длину строки}
if ShowCursor AND (ParaCaretPos >= l_LineOffsetX) AND
(LID = ParaCaretLine) then
LineCaretPos := ParaCaretPos - l_LineOffsetX
else
LineCaretPos := -1;
if (ClipRect.Top >= LH) and ((Caret = nil) or not ShowCursor) then
Exit;
if not DrawRgnOrBlock AND
((LineCaretPos < 0) OR (LineCaretPos > l_LineLength)) then
Exit;
f_LinePainter.StartLine(l_LineOffsetX, l_LineLength);
Font := l_ParaFont;
if (LID > 0) then
begin
MaxLineWidth := OtherLineWidth;
LeftIndent := Spaces.Left;
end//LID > 0
else
begin
MaxLineWidth := FirstLineWidth;
LeftIndent := f_FirstIndent;
if (ParentPainter <> nil) and
(ParentPainter.ParentPainter <> nil) and
(ParentPainter.ParentPainter.TableRowPainter <> nil) then
begin
l_TabIndent := evCalcDecimalTabIndent(ParaX, l_ParaS, l_Justification, l_LineCount, As_Il3Canvas);
Inc(LeftIndent, l_TabIndent);
end; // if (ParaMarkStyle = ev_pmCell) and (ParaX.TabStops <> nil) then
end;//(LID > 0)
IsLastLine := (LID = l_LineCount);
if IsLastLine AND (l_Justification = ev_itWidth) then
l_Justification := ev_itLeft;
FontIndexSet := ppLI^^.FI;
WC := ppLI^^.WC;
if Printing then
begin
if (ClipRect.Bottom - LH) <= 5 * l3Epsilon then
// - не нужно ли начать новую страницу
begin
Result := false;
// - не нужно оставшиеся строки печатать
DrawLeaf := false;
// -недопечатали...
Inc(l_SaveOrg.P.Y, LH);
// - учитываем недопечатанную строчку
Exit;
// -выходим
end;//ClipRect.Bottom < LH
end;//Printing
DW := MaxLineWidth - ppLI^^.LE.X - l_TabIndent;
Assert((l_Justification = ev_itPreformatted) OR (DW < High(Integer) div 4), 'Значение ширины похоже на мусор');
l_SegmentsList := evTextParaGetLineSegments(FormatInfo, l_ParaS, ParaX, Succ(LID));
MoveWindowOrg(l3PointX(-LeftIndent));
// - сдвигаем курсор отрисовки на ширину отступа
DrawIndent(true, DW, IsLastLine, false);
// - рисуем левый отступ
//Font := l_ParaFont;
// - устанавливаем шрифт параграфа
ValidateParaFont;
if (LID = 0) then
DrawBullet;
// - рисуем маркер списка
if l_SegmentsList.Empty then
begin
l_CheckedS1 := CheckS(1);
with l_CheckedS1 do
if (l_ParaS.S = nil) then
Clear
else
AsPCharLen := l3PCharLenPart(l_ParaS.S,
l_LineOffsetX, l_LineOffsetX + l_LineLength,
l_ParaS.SCodePage);
loc_DrawText(l_CheckedS1{S[1]});
end//l_SegmentsList.Empty
else
begin
CheckDrawFocused;
l_SegmentsList.ForEachF(k2L2TIA(@DrawSegment));
end;
DrawIndent(false, DW, IsLastLine, ppLI^^.AddHyphen);
{-рисуем правый отступ и символ конца параграфа}
finally
WindowOrg := l_SaveOrg;
if (ClipRect.Bottom <= 0) then
Result := false;
// -если уперлись вниз экрана то ВЫХОД
Result := Result AND HasToDraw;
// - еще осталось что рисовать
end;//try..finally
end;//l_LineOffsetX >= 0
finally
l_LineOffsetX := ppLI^^.B;
// - запоминаем конец текущей строки
end;//try..finally
finally
FinishTabs(l_TabInfo);
end;//try..finally
end;//with Area.rCanvas
end;//DrawLine
var
IA : Tl3IteratorAction;
begin//DrawPara
l_Justification := TevIndentType(ParaX.IntA[k2_tiJustification]);
with Area.rCanvas do
begin
if not ParaX.TreatCollapsedAsHidden OR not Area.rView.IsObjectCollapsed[ParaX] then
begin
if (l_Justification = ev_itWidth) AND IsVirtual then
l_Justification := ev_itLeft;
f_FirstIndent := FormatInfo.FirstIndent;
l_LineCount := Pred(evTextParaLineCount(FormatInfo));
FirstLineWidth := l_ExtentX - f_FirstIndent;
OtherLineWidth := l_ExtentX - Spaces.Left;
Push;
try
l_ParaFont := FormatInfo.Font[false];
//l_ParaFont := ETAOIN_SHRDLU_GetObjIFont(Area.rView.Metrics, ParaX, false, FormatInfo^, false);
// -вычисляем шрифт параграфа
try
IA := l3L2IA(@DrawLine);
try
if (l_LineOffsetX >= 0) then
evTextParaIterateLines(FormatInfo, l_ParaS, ParaX, IA, Max(0, Pred(f_TopLine)))
else
evTextParaIterateLines(FormatInfo, l_ParaS, ParaX, IA, Max(0, f_TopLine - 2));
finally
l3FreeIA(IA);
end;//try..finally
finally
l_ParaFont := nil;
end;//try..finally
finally
Pop;
end;//try..finally
end//not ParaX.TreatCollapsedAsHidden OR not Area.rView.IsObjectCollapsed[ParaX]
else
Result := true;
if Result then
MoveWindowOrg(l3PointY(-Spaces.Bottom));
// - учитываем отступ после абзаца
end;//with Area.rCanvas
end;//DrawPara
//#UC END# *4804C81000B9_49DA30D901B6_var*
begin
//#UC START# *4804C81000B9_49DA30D901B6_impl*
StartDrawingInitFields;
f_HeaderHeight := 0;
l_IsHyperlink := EvIsParaHyperlink(ParaX);
Result := true;
l_ActiveElement := Area.rView.ActiveElement;
l_Map := FormatInfo.Lines;
l_ExtentX := Max(FormatInfo.rLimitWidth - Spaces.Right, FormatInfo.Width);
f_TopLine := Succ(TopAnchor.VertPosition(Area.rView.As_InevView, FormatInfo));
if (Caret <> nil) AND Area.rView.ForceDrawFocusRect then
f_ForceFocusRect := true;
with Area.rCanvas do
begin
PushWO;
try
if Result AND (f_TopLine = 1) then
MoveWindowOrg(l3PointY(-Spaces.Top));
// - учитываем отступ перед абзацем
if not Printing OR (ClipRect.Bottom > 0) then
begin
l_ParaS := ParaX.Text;
if ShowCursor AND (Caret <> nil) then
begin
l_NeedCaret := Caret.NeedWindowsCaret;
l_LineOffsetX := evTextParaGetPosByLine(l_Map, l_ParaS, f_TopLine);
ParaCaretPos := Caret.Position;
ParaCaretLine := Caret.VertPosition(Area.rView.As_InevView, FormatInfo);
end//ShowCursor
else
begin
l_NeedCaret := false;
if (f_TopLine = 1) then
l_LineOffsetX := 0
else
l_LineOffsetX := -1;
ParaCaretPos := -1;
end;//ShowCursor
if (f_LinePainter = nil) then
f_LinePainter := TevLinePainter.Create;
f_LinePainter.StartPara(Area.rView.As_InevView, SelRange);
try
l_SolidBlock := (SelRange = nil);
if l_SolidBlock then
DrawPara
else
begin
l_SolidBlock := SelRange.Solid(Area.rView.As_InevView);
if l_SolidBlock then
begin
if (l_ParaS.S = nil) then
DrawPara
else
begin
BeginInvert;
try
DrawPara;
finally
EndInvert;
end;{try..finally}
end;//l_ParaS.S = nil
end//l_SolidBlock
else
DrawPara;
end;//l_SolidBlock
finally
f_LinePainter.FinishPara;
end;//try..finally
end;//not Printing OR (ClipRect.Bottom >= 0
finally
PopWO;
end;//try..finally
end;//with Area.rCanvas
//#UC END# *4804C81000B9_49DA30D901B6_impl*
end;//TevTextParaPainterEx.DrawLeaf
procedure TevTextParaPainterEx.FillUnfilled(const aRect: Tl3Rect);
//#UC START# *4E2702FE01EA_49DA30D901B6_var*
const
cRad = 12;
cArrow = 10;
cFW = 1;
var
l_Rgn : Tl3Region;
l_Rgn1 : Tl3Region;
l_Rgn2 : Il3Region;
l_R : Tl3Rect;
l_SR : Tl3SRect;
l_PointArray : array of TPoint;
l_Med : Integer;
//l_BC : TColor;
l_NeedTriangle : Boolean;
//#UC END# *4E2702FE01EA_49DA30D901B6_var*
begin
//#UC START# *4E2702FE01EA_49DA30D901B6_impl*
if ParaX.Attr[k2_tiStyle].BoolA[k2_tiCollapsable] AND
(
(ParaX.BackColor <> nevDefaultColor)
(* (ParaX.IntA[k2_tiBackColor] <> nevDefaultColor) OR
(ParaX.Attr[k2_tiStyle].Attr[k2_tiFont].IntA[k2_tiBackColor] <> nevDefaultColor)*)
) AND
not Area.rView.IsObjectCollapsed[ParaX] AND
Area.rCanvas.DrawEnabled and not Area.rCanvas.IsVirtual then
begin
(* l_BC := ParaX.IntA[k2_tiBackColor];
if (l_BC = nevDefaultColor) then
begin
l_BC := ParaX.Attr[k2_tiStyle].Attr[k2_tiFont].IntA[k2_tiBackColor];
if (l_BC = nevDefaultColor) then
begin
inherited;
Exit;
end;//l_BC = nevDefaultColor
end;//l_BC = nevDefaultColor*)
with Area.rCanvas do
begin
l_Rgn := Tl3Region.Create;
try
PushClipRect;
try
l_R := aRect;
if (f_TopLine = 1) then
l_R.Top := l_R.Top + f_HeaderHeight
else
l_R.Top := l_R.Top - f_HeaderHeight;
l_SR := LR2DR(l_R);
with l_SR do
l_Rgn.Rgn := CreateRoundRectRgn(Left, Top, Right, Bottom, cRad, cRad);
ClipRegion := l_Rgn;
inherited;
finally
PopClipRect;
end;//try..finally
PushClipRect;
try
l_Rgn1 := Tl3Region.Create;
try
l_NeedTriangle := (f_TopLine = 1) AND (f_HeaderHeight <> 0);
if l_NeedTriangle then
begin
SetLength(l_PointArray, 3);
l_Med := l_SR.Left + (l_SR.Right - l_SR.Left) div 2;
l_PointArray[0] := Point(l_Med - cArrow, l_SR.Top);
l_PointArray[1] := Point(l_Med + cArrow, l_SR.Top);
l_PointArray[2] := Point(l_Med, l_SR.Top - cArrow);
l_Rgn1.Rgn := Windows.CreatePolygonRgn(l_PointArray[0],
Length(l_PointArray),
ALTERNATE{WINDING});
ClipRegion := l_Rgn1;
FillRgn(l_Rgn1);
end;//f_TopLine = 1
l_Rgn2 := l_Rgn.Clone;
if l_NeedTriangle then
l_Rgn2.Combine(l_Rgn1, RGN_OR);
ClipRegion := l_Rgn2;
Push;
try
Canvas.Brush.Color := clSilver;
FrameRgn(DC, l_Rgn2.Rgn, Canvas.Brush.Handle, cFW, cFW);
finally
Pop;
end;//try..finally
finally
FreeAndNil(l_Rgn1);
end;//try..finally
finally
PopClipRect;
end;//try..finally
PushClipRect;
try
BackColor := TopBC;
//BackColor := l_BC;
l_Rgn1 := Tl3Region.Create;
try
l_Rgn1.Rect := LR2DR(l_R);
l_Rgn1.Combine(l_Rgn, RGN_DIFF);
ClipRegion := l_Rgn1;
FillRect(l_R);
finally
FreeAndNil(l_Rgn1);
end;//try..finally
finally
PopClipRect;
end;//try..finally
finally
FreeAndNil(l_Rgn);
end;//try..finally
end;//with Area.rCanvas
end//ParaX.Attr[k2_tiStyle].BoolA[k2_tiCollapsable]
else
if not SpecialFill(aRect, False) then
inherited;
//#UC END# *4E2702FE01EA_49DA30D901B6_impl*
end;//TevTextParaPainterEx.FillUnfilled
Да, интересен, конечно.
ОтветитьУдалитьПару лет назад мне почему-то думалось, что разработка компонентов для FM - хорошая идея. В том числе с коммерческой точки зрения.
Тоже хотел сделать аналог TRichEdit (да, тоже есть наработки в данной области).
Чтобы с этим делом не попасть, даже спросил у Леонова в блоге, не собирается ли EMBT сама такое делать (было бы логично).
Разумеется, внятного ответа не получил. И забил. В общем-то сейчас даже благодарен ему за это.:)
Внятный ответ:
Удалить"Да, братуха! ты поднимешь миллион!"
Оно?
>>что разработка компонентов для FM - хорошая идея
Это кто умеет разрабатывать компоненты.
TMS Software, например.
- Является программирование хорошей идеей?
- Да, для тех, кто умеет программировать. Для остальных - плохая идея.
Всеволод, утешили.. :-)
Удалить> "Да, братуха! ты поднимешь миллион!"
Удалить> Оно?
Ага, братуха, оно!11
Самый что ни на есть внятный ответ на вопрос "не собирается ли EMBT сама такое делать?"!11
> Это кто умеет разрабатывать компоненты.
Ну вот, а нельзя на сайте прямо написать, что компоненты для FMX можно делать только тем, кто умеет?
А то я чуть было не полез со своим свиным рылом, да в калашный ряд.
Да, интересен.
ОтветитьУдалить" В общем-то сейчас даже благодарен ему за это.:)"
ОтветитьУдалить-- я вот пока - так же думаю.
Кода дофига, наверное даже важного и нужного. Уж извините за следующее..
ОтветитьУдалитьНо такое уродское именование всего и вся просто убивает желание его читать/понимать.
Поподробнее можно? Я ведь тоже могу сказать - "код - говно"... А детали?
УдалитьХм... А где тут уродское именование? GetViewSegmentsPlace? DrawAllLine? l_LineOffsetX?
УдалитьХотите примеров уродского именования - пожалуйста:
select ecfil139.DATA as myDate, (0 - ecfil139.SUMMA_ZAPROSHENAYA_CHEM) as summa
from ecfil139 where ID_KLIENTA = 314 and ID_KOSH_ZA_CHTO = 6
UNION all
select ecfil096.DATA as myDate, ecfil096.SUMMA as summa from ecfil096 where ID_VLAD = 314
order by myDate ASC