2 * Copyright 2013-2015, Stephan Aßmus <superstippi@gmx.de>.
3 * All rights reserved. Distributed under the terms of the MIT License.
6 #include "TextDocumentLayout.h"
14 class LayoutTextListener
: public TextListener
{
16 LayoutTextListener(TextDocumentLayout
* layout
)
22 virtual void TextChanging(TextChangingEvent
& event
)
26 virtual void TextChanged(const TextChangedEvent
& event
)
28 printf("TextChanged(%" B_PRIi32
", %" B_PRIi32
")\n",
29 event
.FirstChangedParagraph(),
30 event
.ChangedParagraphCount());
31 // TODO: The event does not contain useful data. Make the event
32 // system work so only the affected paragraphs are updated.
33 // I think we need "first affected", "last affected" (both relative
34 // to the original paragraph count), and than how many new paragraphs
35 // are between these. From the difference in old number of paragraphs
36 // inbetween and the new count, we know how many new paragraphs are
37 // missing, and the rest in the range needs to be updated.
38 // fLayout->InvalidateParagraphs(event.FirstChangedParagraph(),
39 // event.ChangedParagraphCount());
40 fLayout
->Invalidate();
44 TextDocumentLayout
* fLayout
;
49 TextDocumentLayout::TextDocumentLayout()
55 fTextListener(new(std::nothrow
) LayoutTextListener(this), true),
61 TextDocumentLayout::TextDocumentLayout(const TextDocumentRef
& document
)
67 fTextListener(new(std::nothrow
) LayoutTextListener(this), true),
70 SetTextDocument(document
);
74 TextDocumentLayout::TextDocumentLayout(const TextDocumentLayout
& other
)
77 fLayoutValid(other
.fLayoutValid
),
79 fDocument(other
.fDocument
),
80 fTextListener(new(std::nothrow
) LayoutTextListener(this), true),
81 fParagraphLayouts(other
.fParagraphLayouts
)
83 if (fDocument
.Get() != NULL
)
84 fDocument
->AddListener(fTextListener
);
88 TextDocumentLayout::~TextDocumentLayout()
90 SetTextDocument(NULL
);
95 TextDocumentLayout::SetTextDocument(const TextDocumentRef
& document
)
97 if (fDocument
== document
)
100 if (fDocument
.Get() != NULL
)
101 fDocument
->RemoveListener(fTextListener
);
103 fDocument
= document
;
105 fLayoutValid
= false;
107 if (fDocument
.Get() != NULL
)
108 fDocument
->AddListener(fTextListener
);
113 TextDocumentLayout::Invalidate()
115 if (fDocument
.Get() != NULL
)
116 InvalidateParagraphs(0, fDocument
->Paragraphs().CountItems());
121 TextDocumentLayout::InvalidateParagraphs(int32 start
, int32 count
)
123 if (start
< 0 || count
== 0 || fDocument
.Get() == NULL
)
126 fLayoutValid
= false;
128 const ParagraphList
& paragraphs
= fDocument
->Paragraphs();
131 if (start
>= paragraphs
.CountItems())
133 const Paragraph
& paragraph
= paragraphs
.ItemAtFast(start
);
134 if (start
>= fParagraphLayouts
.CountItems()) {
135 ParagraphLayoutRef
layout(new(std::nothrow
) ParagraphLayout(
137 if (layout
.Get() == NULL
138 || !fParagraphLayouts
.Add(ParagraphLayoutInfo(0.0f
, layout
))) {
139 fprintf(stderr
, "TextDocumentLayout::InvalidateParagraphs() - "
144 const ParagraphLayoutInfo
& info
= fParagraphLayouts
.ItemAtFast(
146 info
.layout
->SetParagraph(paragraph
);
153 // Remove any extra paragraph layouts
154 while (paragraphs
.CountItems() < fParagraphLayouts
.CountItems())
155 fParagraphLayouts
.Remove(fParagraphLayouts
.CountItems() - 1);
160 TextDocumentLayout::SetWidth(float width
)
162 if (fWidth
!= width
) {
164 fLayoutValid
= false;
170 TextDocumentLayout::Height()
176 if (fParagraphLayouts
.CountItems() > 0) {
177 const ParagraphLayoutInfo
& lastLayout
= fParagraphLayouts
.LastItem();
178 height
= lastLayout
.y
+ lastLayout
.layout
->Height();
186 TextDocumentLayout::Draw(BView
* view
, const BPoint
& offset
,
187 const BRect
& updateRect
)
191 int layoutCount
= fParagraphLayouts
.CountItems();
192 for (int i
= 0; i
< layoutCount
; i
++) {
193 const ParagraphLayoutInfo
& layout
= fParagraphLayouts
.ItemAtFast(i
);
194 BPoint
location(offset
.x
, offset
.y
+ layout
.y
);
195 if (location
.y
> updateRect
.bottom
)
197 if (location
.y
+ layout
.layout
->Height() > updateRect
.top
)
198 layout
.layout
->Draw(view
, location
);
204 TextDocumentLayout::LineIndexForOffset(int32 textOffset
)
206 int32 index
= _ParagraphLayoutIndexForOffset(textOffset
);
209 for (int32 i
= 0; i
< index
; i
++) {
210 lineIndex
+= fParagraphLayouts
.ItemAtFast(i
).layout
->CountLines();
213 const ParagraphLayoutInfo
& info
= fParagraphLayouts
.ItemAtFast(index
);
214 return lineIndex
+ info
.layout
->LineIndexForOffset(textOffset
);
222 TextDocumentLayout::FirstOffsetOnLine(int32 lineIndex
)
224 int32 paragraphOffset
;
225 int32 index
= _ParagraphLayoutIndexForLineIndex(lineIndex
, paragraphOffset
);
227 const ParagraphLayoutInfo
& info
= fParagraphLayouts
.ItemAtFast(index
);
228 return info
.layout
->FirstOffsetOnLine(lineIndex
) + paragraphOffset
;
236 TextDocumentLayout::LastOffsetOnLine(int32 lineIndex
)
238 int32 paragraphOffset
;
239 int32 index
= _ParagraphLayoutIndexForLineIndex(lineIndex
, paragraphOffset
);
241 const ParagraphLayoutInfo
& info
= fParagraphLayouts
.ItemAtFast(index
);
242 return info
.layout
->LastOffsetOnLine(lineIndex
) + paragraphOffset
;
250 TextDocumentLayout::CountLines()
256 int32 count
= fParagraphLayouts
.CountItems();
257 for (int32 i
= 0; i
< count
; i
++) {
258 const ParagraphLayoutInfo
& info
= fParagraphLayouts
.ItemAtFast(i
);
259 lineCount
+= info
.layout
->CountLines();
267 TextDocumentLayout::GetLineBounds(int32 lineIndex
, float& x1
, float& y1
,
268 float& x2
, float& y2
)
270 int32 paragraphOffset
;
271 int32 index
= _ParagraphLayoutIndexForLineIndex(lineIndex
, paragraphOffset
);
273 const ParagraphLayoutInfo
& info
= fParagraphLayouts
.ItemAtFast(index
);
274 info
.layout
->GetLineBounds(lineIndex
, x1
, y1
, x2
, y2
);
288 TextDocumentLayout::GetTextBounds(int32 textOffset
, float& x1
, float& y1
,
289 float& x2
, float& y2
)
291 int32 index
= _ParagraphLayoutIndexForOffset(textOffset
);
293 const ParagraphLayoutInfo
& info
= fParagraphLayouts
.ItemAtFast(index
);
294 info
.layout
->GetTextBounds(textOffset
, x1
, y1
, x2
, y2
);
308 TextDocumentLayout::TextOffsetAt(float x
, float y
, bool& rightOfCenter
)
312 int32 textOffset
= 0;
313 rightOfCenter
= false;
315 int32 paragraphs
= fParagraphLayouts
.CountItems();
316 for (int32 i
= 0; i
< paragraphs
; i
++) {
317 const ParagraphLayoutInfo
& info
= fParagraphLayouts
.ItemAtFast(i
);
318 if (y
> info
.y
+ info
.layout
->Height()) {
319 textOffset
+= info
.layout
->CountGlyphs();
323 textOffset
+= info
.layout
->TextOffsetAt(x
, y
- info
.y
, rightOfCenter
);
330 // #pragma mark - private
334 TextDocumentLayout::_ValidateLayout()
344 TextDocumentLayout::_Init()
346 fParagraphLayouts
.Clear();
348 if (fDocument
.Get() == NULL
)
351 const ParagraphList
& paragraphs
= fDocument
->Paragraphs();
353 int paragraphCount
= paragraphs
.CountItems();
354 for (int i
= 0; i
< paragraphCount
; i
++) {
355 const Paragraph
& paragraph
= paragraphs
.ItemAtFast(i
);
356 ParagraphLayoutRef
layout(new(std::nothrow
) ParagraphLayout(paragraph
),
358 if (layout
.Get() == NULL
359 || !fParagraphLayouts
.Add(ParagraphLayoutInfo(0.0f
, layout
))) {
360 fprintf(stderr
, "TextDocumentLayout::_Layout() - out of memory\n");
368 TextDocumentLayout::_Layout()
372 int layoutCount
= fParagraphLayouts
.CountItems();
373 for (int i
= 0; i
< layoutCount
; i
++) {
374 ParagraphLayoutInfo info
= fParagraphLayouts
.ItemAtFast(i
);
375 const ParagraphStyle
& style
= info
.layout
->Style();
378 y
+= style
.SpacingTop();
380 fParagraphLayouts
.Replace(i
, ParagraphLayoutInfo(y
, info
.layout
));
382 info
.layout
->SetWidth(fWidth
);
383 y
+= info
.layout
->Height() + style
.SpacingBottom();
389 TextDocumentLayout::_ParagraphLayoutIndexForOffset(int32
& textOffset
)
393 int32 paragraphs
= fParagraphLayouts
.CountItems();
394 for (int32 i
= 0; i
< paragraphs
- 1; i
++) {
395 const ParagraphLayoutInfo
& info
= fParagraphLayouts
.ItemAtFast(i
);
397 int32 length
= info
.layout
->CountGlyphs();
398 if (textOffset
>= length
) {
399 textOffset
-= length
;
406 if (paragraphs
> 0) {
407 const ParagraphLayoutInfo
& info
= fParagraphLayouts
.LastItem();
409 // Return last paragraph if the textOffset is still within or
410 // exactly behind the last valid offset in that paragraph.
411 int32 length
= info
.layout
->CountGlyphs();
412 if (textOffset
<= length
)
413 return paragraphs
- 1;
420 TextDocumentLayout::_ParagraphLayoutIndexForLineIndex(int32
& lineIndex
,
421 int32
& paragraphOffset
)
426 int32 paragraphs
= fParagraphLayouts
.CountItems();
427 for (int32 i
= 0; i
< paragraphs
; i
++) {
428 const ParagraphLayoutInfo
& info
= fParagraphLayouts
.ItemAtFast(i
);
430 int32 lineCount
= info
.layout
->CountLines();
431 if (lineIndex
>= lineCount
) {
432 lineIndex
-= lineCount
;
433 paragraphOffset
+= info
.layout
->CountGlyphs();