repository_infos: Enable automatic updates on the main Haiku repostiory.
[haiku.git] / src / apps / haikudepot / textview / TextDocumentLayout.cpp
blob8afa1142a2731b1f520a90d51ed6506dbedfb62f
1 /*
2 * Copyright 2013-2015, Stephan Aßmus <superstippi@gmx.de>.
3 * All rights reserved. Distributed under the terms of the MIT License.
4 */
6 #include "TextDocumentLayout.h"
8 #include <new>
9 #include <stdio.h>
11 #include <View.h>
14 class LayoutTextListener : public TextListener {
15 public:
16 LayoutTextListener(TextDocumentLayout* layout)
18 fLayout(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();
43 private:
44 TextDocumentLayout* fLayout;
49 TextDocumentLayout::TextDocumentLayout()
51 fWidth(0.0f),
52 fLayoutValid(false),
54 fDocument(),
55 fTextListener(new(std::nothrow) LayoutTextListener(this), true),
56 fParagraphLayouts()
61 TextDocumentLayout::TextDocumentLayout(const TextDocumentRef& document)
63 fWidth(0.0f),
64 fLayoutValid(false),
66 fDocument(),
67 fTextListener(new(std::nothrow) LayoutTextListener(this), true),
68 fParagraphLayouts()
70 SetTextDocument(document);
74 TextDocumentLayout::TextDocumentLayout(const TextDocumentLayout& other)
76 fWidth(other.fWidth),
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);
94 void
95 TextDocumentLayout::SetTextDocument(const TextDocumentRef& document)
97 if (fDocument == document)
98 return;
100 if (fDocument.Get() != NULL)
101 fDocument->RemoveListener(fTextListener);
103 fDocument = document;
104 _Init();
105 fLayoutValid = false;
107 if (fDocument.Get() != NULL)
108 fDocument->AddListener(fTextListener);
112 void
113 TextDocumentLayout::Invalidate()
115 if (fDocument.Get() != NULL)
116 InvalidateParagraphs(0, fDocument->Paragraphs().CountItems());
120 void
121 TextDocumentLayout::InvalidateParagraphs(int32 start, int32 count)
123 if (start < 0 || count == 0 || fDocument.Get() == NULL)
124 return;
126 fLayoutValid = false;
128 const ParagraphList& paragraphs = fDocument->Paragraphs();
130 while (count > 0) {
131 if (start >= paragraphs.CountItems())
132 break;
133 const Paragraph& paragraph = paragraphs.ItemAtFast(start);
134 if (start >= fParagraphLayouts.CountItems()) {
135 ParagraphLayoutRef layout(new(std::nothrow) ParagraphLayout(
136 paragraph), true);
137 if (layout.Get() == NULL
138 || !fParagraphLayouts.Add(ParagraphLayoutInfo(0.0f, layout))) {
139 fprintf(stderr, "TextDocumentLayout::InvalidateParagraphs() - "
140 "out of memory\n");
141 return;
143 } else {
144 const ParagraphLayoutInfo& info = fParagraphLayouts.ItemAtFast(
145 start);
146 info.layout->SetParagraph(paragraph);
149 start++;
150 count--;
153 // Remove any extra paragraph layouts
154 while (paragraphs.CountItems() < fParagraphLayouts.CountItems())
155 fParagraphLayouts.Remove(fParagraphLayouts.CountItems() - 1);
159 void
160 TextDocumentLayout::SetWidth(float width)
162 if (fWidth != width) {
163 fWidth = width;
164 fLayoutValid = false;
169 float
170 TextDocumentLayout::Height()
172 _ValidateLayout();
174 float height = 0.0f;
176 if (fParagraphLayouts.CountItems() > 0) {
177 const ParagraphLayoutInfo& lastLayout = fParagraphLayouts.LastItem();
178 height = lastLayout.y + lastLayout.layout->Height();
181 return height;
185 void
186 TextDocumentLayout::Draw(BView* view, const BPoint& offset,
187 const BRect& updateRect)
189 _ValidateLayout();
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)
196 break;
197 if (location.y + layout.layout->Height() > updateRect.top)
198 layout.layout->Draw(view, location);
203 int32
204 TextDocumentLayout::LineIndexForOffset(int32 textOffset)
206 int32 index = _ParagraphLayoutIndexForOffset(textOffset);
207 if (index >= 0) {
208 int32 lineIndex = 0;
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);
217 return 0;
221 int32
222 TextDocumentLayout::FirstOffsetOnLine(int32 lineIndex)
224 int32 paragraphOffset;
225 int32 index = _ParagraphLayoutIndexForLineIndex(lineIndex, paragraphOffset);
226 if (index >= 0) {
227 const ParagraphLayoutInfo& info = fParagraphLayouts.ItemAtFast(index);
228 return info.layout->FirstOffsetOnLine(lineIndex) + paragraphOffset;
231 return 0;
235 int32
236 TextDocumentLayout::LastOffsetOnLine(int32 lineIndex)
238 int32 paragraphOffset;
239 int32 index = _ParagraphLayoutIndexForLineIndex(lineIndex, paragraphOffset);
240 if (index >= 0) {
241 const ParagraphLayoutInfo& info = fParagraphLayouts.ItemAtFast(index);
242 return info.layout->LastOffsetOnLine(lineIndex) + paragraphOffset;
245 return 0;
249 int32
250 TextDocumentLayout::CountLines()
252 _ValidateLayout();
254 int32 lineCount = 0;
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();
262 return lineCount;
266 void
267 TextDocumentLayout::GetLineBounds(int32 lineIndex, float& x1, float& y1,
268 float& x2, float& y2)
270 int32 paragraphOffset;
271 int32 index = _ParagraphLayoutIndexForLineIndex(lineIndex, paragraphOffset);
272 if (index >= 0) {
273 const ParagraphLayoutInfo& info = fParagraphLayouts.ItemAtFast(index);
274 info.layout->GetLineBounds(lineIndex, x1, y1, x2, y2);
275 y1 += info.y;
276 y2 += info.y;
277 return;
280 x1 = 0.0f;
281 y1 = 0.0f;
282 x2 = 0.0f;
283 y2 = 0.0f;
287 void
288 TextDocumentLayout::GetTextBounds(int32 textOffset, float& x1, float& y1,
289 float& x2, float& y2)
291 int32 index = _ParagraphLayoutIndexForOffset(textOffset);
292 if (index >= 0) {
293 const ParagraphLayoutInfo& info = fParagraphLayouts.ItemAtFast(index);
294 info.layout->GetTextBounds(textOffset, x1, y1, x2, y2);
295 y1 += info.y;
296 y2 += info.y;
297 return;
300 x1 = 0.0f;
301 y1 = 0.0f;
302 x2 = 0.0f;
303 y2 = 0.0f;
307 int32
308 TextDocumentLayout::TextOffsetAt(float x, float y, bool& rightOfCenter)
310 _ValidateLayout();
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();
320 continue;
323 textOffset += info.layout->TextOffsetAt(x, y - info.y, rightOfCenter);
324 break;
327 return textOffset;
330 // #pragma mark - private
333 void
334 TextDocumentLayout::_ValidateLayout()
336 if (!fLayoutValid) {
337 _Layout();
338 fLayoutValid = true;
343 void
344 TextDocumentLayout::_Init()
346 fParagraphLayouts.Clear();
348 if (fDocument.Get() == NULL)
349 return;
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),
357 true);
358 if (layout.Get() == NULL
359 || !fParagraphLayouts.Add(ParagraphLayoutInfo(0.0f, layout))) {
360 fprintf(stderr, "TextDocumentLayout::_Layout() - out of memory\n");
361 return;
367 void
368 TextDocumentLayout::_Layout()
370 float y = 0.0f;
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();
377 if (i > 0)
378 y += style.SpacingTop();
380 fParagraphLayouts.Replace(i, ParagraphLayoutInfo(y, info.layout));
382 info.layout->SetWidth(fWidth);
383 y += info.layout->Height() + style.SpacingBottom();
388 int32
389 TextDocumentLayout::_ParagraphLayoutIndexForOffset(int32& textOffset)
391 _ValidateLayout();
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;
400 continue;
403 return i;
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;
416 return -1;
419 int32
420 TextDocumentLayout::_ParagraphLayoutIndexForLineIndex(int32& lineIndex,
421 int32& paragraphOffset)
423 _ValidateLayout();
425 paragraphOffset = 0;
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();
434 continue;
437 return i;
440 return -1;