1d7f7bf2dSAxel Dörfler /*
2d7f7bf2dSAxel Dörfler * Copyright 2013-2014, Stephan Aßmus <superstippi@gmx.de>.
3*c4e439b5SAndrew Lindesay * Copyright 2021-2024, Andrew Lindesay <apl@lindesay.co.nz>.
4d7f7bf2dSAxel Dörfler * All rights reserved. Distributed under the terms of the MIT License.
5d7f7bf2dSAxel Dörfler */
6d7f7bf2dSAxel Dörfler
7d7f7bf2dSAxel Dörfler #include "TextDocument.h"
8d7f7bf2dSAxel Dörfler
9d7f7bf2dSAxel Dörfler #include <algorithm>
10d7f7bf2dSAxel Dörfler #include <stdio.h>
113d2fd2acSAndrew Lindesay #include <vector>
12d7f7bf2dSAxel Dörfler
13d7f7bf2dSAxel Dörfler
TextDocument()14d7f7bf2dSAxel Dörfler TextDocument::TextDocument()
15d7f7bf2dSAxel Dörfler :
16d7f7bf2dSAxel Dörfler fParagraphs(),
17d7f7bf2dSAxel Dörfler fEmptyLastParagraph(),
18d7f7bf2dSAxel Dörfler fDefaultCharacterStyle()
19d7f7bf2dSAxel Dörfler {
20d7f7bf2dSAxel Dörfler }
21d7f7bf2dSAxel Dörfler
22d7f7bf2dSAxel Dörfler
TextDocument(CharacterStyle characterStyle,ParagraphStyle paragraphStyle)23f890fab6SStephan Aßmus TextDocument::TextDocument(CharacterStyle characterStyle,
24f890fab6SStephan Aßmus ParagraphStyle paragraphStyle)
25d7f7bf2dSAxel Dörfler :
26d7f7bf2dSAxel Dörfler fParagraphs(),
27d7f7bf2dSAxel Dörfler fEmptyLastParagraph(paragraphStyle),
28d7f7bf2dSAxel Dörfler fDefaultCharacterStyle(characterStyle)
29d7f7bf2dSAxel Dörfler {
30d7f7bf2dSAxel Dörfler }
31d7f7bf2dSAxel Dörfler
32d7f7bf2dSAxel Dörfler
TextDocument(const TextDocument & other)33d7f7bf2dSAxel Dörfler TextDocument::TextDocument(const TextDocument& other)
34d7f7bf2dSAxel Dörfler :
35d7f7bf2dSAxel Dörfler fParagraphs(other.fParagraphs),
36d7f7bf2dSAxel Dörfler fEmptyLastParagraph(other.fEmptyLastParagraph),
37d7f7bf2dSAxel Dörfler fDefaultCharacterStyle(other.fDefaultCharacterStyle)
38d7f7bf2dSAxel Dörfler {
39d7f7bf2dSAxel Dörfler }
40d7f7bf2dSAxel Dörfler
41d7f7bf2dSAxel Dörfler
42d7f7bf2dSAxel Dörfler TextDocument&
operator =(const TextDocument & other)43d7f7bf2dSAxel Dörfler TextDocument::operator=(const TextDocument& other)
44d7f7bf2dSAxel Dörfler {
45d7f7bf2dSAxel Dörfler fParagraphs = other.fParagraphs;
46d7f7bf2dSAxel Dörfler fEmptyLastParagraph = other.fEmptyLastParagraph;
47d7f7bf2dSAxel Dörfler fDefaultCharacterStyle = other.fDefaultCharacterStyle;
48d7f7bf2dSAxel Dörfler
49d7f7bf2dSAxel Dörfler return *this;
50d7f7bf2dSAxel Dörfler }
51d7f7bf2dSAxel Dörfler
52d7f7bf2dSAxel Dörfler
53d7f7bf2dSAxel Dörfler bool
operator ==(const TextDocument & other) const54d7f7bf2dSAxel Dörfler TextDocument::operator==(const TextDocument& other) const
55d7f7bf2dSAxel Dörfler {
56d7f7bf2dSAxel Dörfler if (this == &other)
57d7f7bf2dSAxel Dörfler return true;
58d7f7bf2dSAxel Dörfler
59d7f7bf2dSAxel Dörfler return fEmptyLastParagraph == other.fEmptyLastParagraph
60d7f7bf2dSAxel Dörfler && fDefaultCharacterStyle == other.fDefaultCharacterStyle
61d7f7bf2dSAxel Dörfler && fParagraphs == other.fParagraphs;
62d7f7bf2dSAxel Dörfler }
63d7f7bf2dSAxel Dörfler
64d7f7bf2dSAxel Dörfler
65d7f7bf2dSAxel Dörfler bool
operator !=(const TextDocument & other) const66d7f7bf2dSAxel Dörfler TextDocument::operator!=(const TextDocument& other) const
67d7f7bf2dSAxel Dörfler {
68d7f7bf2dSAxel Dörfler return !(*this == other);
69d7f7bf2dSAxel Dörfler }
70d7f7bf2dSAxel Dörfler
71d7f7bf2dSAxel Dörfler
72d7f7bf2dSAxel Dörfler // #pragma mark -
73d7f7bf2dSAxel Dörfler
74d7f7bf2dSAxel Dörfler
75d7f7bf2dSAxel Dörfler status_t
Insert(int32 textOffset,const BString & text)76d7f7bf2dSAxel Dörfler TextDocument::Insert(int32 textOffset, const BString& text)
77d7f7bf2dSAxel Dörfler {
784a96bcdaSStephan Aßmus return Replace(textOffset, 0, text);
79d7f7bf2dSAxel Dörfler }
80d7f7bf2dSAxel Dörfler
81d7f7bf2dSAxel Dörfler
82d7f7bf2dSAxel Dörfler status_t
Insert(int32 textOffset,const BString & text,CharacterStyle style)83d7f7bf2dSAxel Dörfler TextDocument::Insert(int32 textOffset, const BString& text,
84f890fab6SStephan Aßmus CharacterStyle style)
85d7f7bf2dSAxel Dörfler {
864a96bcdaSStephan Aßmus return Replace(textOffset, 0, text, style);
87d7f7bf2dSAxel Dörfler }
88d7f7bf2dSAxel Dörfler
89d7f7bf2dSAxel Dörfler
90d7f7bf2dSAxel Dörfler status_t
Insert(int32 textOffset,const BString & text,CharacterStyle characterStyle,ParagraphStyle paragraphStyle)91d7f7bf2dSAxel Dörfler TextDocument::Insert(int32 textOffset, const BString& text,
92f890fab6SStephan Aßmus CharacterStyle characterStyle, ParagraphStyle paragraphStyle)
93d7f7bf2dSAxel Dörfler {
944a96bcdaSStephan Aßmus return Replace(textOffset, 0, text, characterStyle, paragraphStyle);
95d7f7bf2dSAxel Dörfler }
96d7f7bf2dSAxel Dörfler
97d7f7bf2dSAxel Dörfler
98d7f7bf2dSAxel Dörfler // #pragma mark -
99d7f7bf2dSAxel Dörfler
100d7f7bf2dSAxel Dörfler
101d7f7bf2dSAxel Dörfler status_t
Remove(int32 textOffset,int32 length)102d7f7bf2dSAxel Dörfler TextDocument::Remove(int32 textOffset, int32 length)
103d7f7bf2dSAxel Dörfler {
1044a96bcdaSStephan Aßmus return Replace(textOffset, length, BString());
105d7f7bf2dSAxel Dörfler }
106d7f7bf2dSAxel Dörfler
107d7f7bf2dSAxel Dörfler
108d7f7bf2dSAxel Dörfler // #pragma mark -
109d7f7bf2dSAxel Dörfler
110d7f7bf2dSAxel Dörfler
111d7f7bf2dSAxel Dörfler status_t
Replace(int32 textOffset,int32 length,const BString & text)112d7f7bf2dSAxel Dörfler TextDocument::Replace(int32 textOffset, int32 length, const BString& text)
113d7f7bf2dSAxel Dörfler {
114d7f7bf2dSAxel Dörfler return Replace(textOffset, length, text, CharacterStyleAt(textOffset));
115d7f7bf2dSAxel Dörfler }
116d7f7bf2dSAxel Dörfler
117d7f7bf2dSAxel Dörfler
118d7f7bf2dSAxel Dörfler status_t
Replace(int32 textOffset,int32 length,const BString & text,CharacterStyle style)119d7f7bf2dSAxel Dörfler TextDocument::Replace(int32 textOffset, int32 length, const BString& text,
120f890fab6SStephan Aßmus CharacterStyle style)
121d7f7bf2dSAxel Dörfler {
122d7f7bf2dSAxel Dörfler return Replace(textOffset, length, text, style,
123d7f7bf2dSAxel Dörfler ParagraphStyleAt(textOffset));
124d7f7bf2dSAxel Dörfler }
125d7f7bf2dSAxel Dörfler
126d7f7bf2dSAxel Dörfler
127d7f7bf2dSAxel Dörfler status_t
Replace(int32 textOffset,int32 length,const BString & text,CharacterStyle characterStyle,ParagraphStyle paragraphStyle)128d7f7bf2dSAxel Dörfler TextDocument::Replace(int32 textOffset, int32 length, const BString& text,
129f890fab6SStephan Aßmus CharacterStyle characterStyle, ParagraphStyle paragraphStyle)
130f890fab6SStephan Aßmus {
131f890fab6SStephan Aßmus TextDocumentRef document = NormalizeText(text, characterStyle,
132f890fab6SStephan Aßmus paragraphStyle);
133*c4e439b5SAndrew Lindesay
134*c4e439b5SAndrew Lindesay if (!document.IsSet())
135f890fab6SStephan Aßmus return B_NO_MEMORY;
136*c4e439b5SAndrew Lindesay
137*c4e439b5SAndrew Lindesay if (document->Length() != text.CountChars())
138*c4e439b5SAndrew Lindesay return B_NO_MEMORY;
139*c4e439b5SAndrew Lindesay
140f890fab6SStephan Aßmus return Replace(textOffset, length, document);
141f890fab6SStephan Aßmus }
142f890fab6SStephan Aßmus
143f890fab6SStephan Aßmus
144f890fab6SStephan Aßmus status_t
Replace(int32 textOffset,int32 length,TextDocumentRef document)145f890fab6SStephan Aßmus TextDocument::Replace(int32 textOffset, int32 length, TextDocumentRef document)
146d7f7bf2dSAxel Dörfler {
1474a96bcdaSStephan Aßmus int32 firstParagraph = 0;
1484a96bcdaSStephan Aßmus int32 paragraphCount = 0;
1494a96bcdaSStephan Aßmus
1504a96bcdaSStephan Aßmus // TODO: Call _NotifyTextChanging() before any change happened
1514a96bcdaSStephan Aßmus
1524a96bcdaSStephan Aßmus status_t ret = _Remove(textOffset, length, firstParagraph, paragraphCount);
153d7f7bf2dSAxel Dörfler if (ret != B_OK)
154d7f7bf2dSAxel Dörfler return ret;
155d7f7bf2dSAxel Dörfler
156f890fab6SStephan Aßmus ret = _Insert(textOffset, document, firstParagraph, paragraphCount);
1574a96bcdaSStephan Aßmus
1584a96bcdaSStephan Aßmus _NotifyTextChanged(TextChangedEvent(firstParagraph, paragraphCount));
1594a96bcdaSStephan Aßmus
1604a96bcdaSStephan Aßmus return ret;
161d7f7bf2dSAxel Dörfler }
162d7f7bf2dSAxel Dörfler
163d7f7bf2dSAxel Dörfler
164d7f7bf2dSAxel Dörfler // #pragma mark -
165d7f7bf2dSAxel Dörfler
166d7f7bf2dSAxel Dörfler
167d7f7bf2dSAxel Dörfler const CharacterStyle&
CharacterStyleAt(int32 textOffset) const168d7f7bf2dSAxel Dörfler TextDocument::CharacterStyleAt(int32 textOffset) const
169d7f7bf2dSAxel Dörfler {
170d7f7bf2dSAxel Dörfler int32 paragraphOffset;
171d7f7bf2dSAxel Dörfler const Paragraph& paragraph = ParagraphAt(textOffset, paragraphOffset);
172d7f7bf2dSAxel Dörfler
173d7f7bf2dSAxel Dörfler textOffset -= paragraphOffset;
1743d2fd2acSAndrew Lindesay int32 index;
1753d2fd2acSAndrew Lindesay int32 count = paragraph.CountTextSpans();
176d7f7bf2dSAxel Dörfler
1773d2fd2acSAndrew Lindesay for (index = 0; index < count; index++) {
1783d2fd2acSAndrew Lindesay const TextSpan& span = paragraph.TextSpanAtIndex(index);
179d7f7bf2dSAxel Dörfler if (textOffset - span.CountChars() < 0)
180d7f7bf2dSAxel Dörfler return span.Style();
181d7f7bf2dSAxel Dörfler textOffset -= span.CountChars();
182d7f7bf2dSAxel Dörfler }
183d7f7bf2dSAxel Dörfler
184d7f7bf2dSAxel Dörfler return fDefaultCharacterStyle;
185d7f7bf2dSAxel Dörfler }
186d7f7bf2dSAxel Dörfler
187d7f7bf2dSAxel Dörfler
1886af13813SPulkoMandy const BMessage*
ClickMessageAt(int32 textOffset) const1896af13813SPulkoMandy TextDocument::ClickMessageAt(int32 textOffset) const
1906af13813SPulkoMandy {
1916af13813SPulkoMandy int32 paragraphOffset;
1926af13813SPulkoMandy const Paragraph& paragraph = ParagraphAt(textOffset, paragraphOffset);
1936af13813SPulkoMandy
1946af13813SPulkoMandy textOffset -= paragraphOffset;
1956af13813SPulkoMandy int32 index;
1966af13813SPulkoMandy int32 count = paragraph.CountTextSpans();
1976af13813SPulkoMandy
1986af13813SPulkoMandy for (index = 0; index < count; index++) {
1996af13813SPulkoMandy const TextSpan& span = paragraph.TextSpanAtIndex(index);
2006af13813SPulkoMandy if (textOffset - span.CountChars() < 0)
2016af13813SPulkoMandy return span.ClickMessage();
2026af13813SPulkoMandy textOffset -= span.CountChars();
2036af13813SPulkoMandy }
2046af13813SPulkoMandy
2056af13813SPulkoMandy return NULL;
2066af13813SPulkoMandy }
2076af13813SPulkoMandy
2086af13813SPulkoMandy
2096af13813SPulkoMandy BCursor
CursorAt(int32 textOffset) const2106af13813SPulkoMandy TextDocument::CursorAt(int32 textOffset) const
2116af13813SPulkoMandy {
2126af13813SPulkoMandy int32 paragraphOffset;
2136af13813SPulkoMandy const Paragraph& paragraph = ParagraphAt(textOffset, paragraphOffset);
2146af13813SPulkoMandy
2156af13813SPulkoMandy textOffset -= paragraphOffset;
2166af13813SPulkoMandy int32 index;
2176af13813SPulkoMandy int32 count = paragraph.CountTextSpans();
2186af13813SPulkoMandy
2196af13813SPulkoMandy for (index = 0; index < count; index++) {
2206af13813SPulkoMandy const TextSpan& span = paragraph.TextSpanAtIndex(index);
2216af13813SPulkoMandy if (textOffset - span.CountChars() < 0)
2226af13813SPulkoMandy return span.Cursor();
2236af13813SPulkoMandy textOffset -= span.CountChars();
2246af13813SPulkoMandy }
2256af13813SPulkoMandy
2266af13813SPulkoMandy return BCursor((BMessage*)NULL);
2276af13813SPulkoMandy }
2286af13813SPulkoMandy
2296af13813SPulkoMandy
230d7f7bf2dSAxel Dörfler const ParagraphStyle&
ParagraphStyleAt(int32 textOffset) const231d7f7bf2dSAxel Dörfler TextDocument::ParagraphStyleAt(int32 textOffset) const
232d7f7bf2dSAxel Dörfler {
233d7f7bf2dSAxel Dörfler int32 paragraphOffset;
234d7f7bf2dSAxel Dörfler return ParagraphAt(textOffset, paragraphOffset).Style();
235d7f7bf2dSAxel Dörfler }
236d7f7bf2dSAxel Dörfler
237d7f7bf2dSAxel Dörfler
238d7f7bf2dSAxel Dörfler // #pragma mark -
239d7f7bf2dSAxel Dörfler
240d7f7bf2dSAxel Dörfler
241d7f7bf2dSAxel Dörfler int32
CountParagraphs() const242f890fab6SStephan Aßmus TextDocument::CountParagraphs() const
243f890fab6SStephan Aßmus {
244dfbcbde1SAndrew Lindesay return fParagraphs.size();
245dfbcbde1SAndrew Lindesay }
246dfbcbde1SAndrew Lindesay
247dfbcbde1SAndrew Lindesay
248dfbcbde1SAndrew Lindesay const Paragraph&
ParagraphAtIndex(int32 index) const249dfbcbde1SAndrew Lindesay TextDocument::ParagraphAtIndex(int32 index) const
250dfbcbde1SAndrew Lindesay {
251dfbcbde1SAndrew Lindesay return fParagraphs[index];
252f890fab6SStephan Aßmus }
253f890fab6SStephan Aßmus
254f890fab6SStephan Aßmus
255f890fab6SStephan Aßmus int32
ParagraphIndexFor(int32 textOffset,int32 & paragraphOffset) const256d7f7bf2dSAxel Dörfler TextDocument::ParagraphIndexFor(int32 textOffset, int32& paragraphOffset) const
257d7f7bf2dSAxel Dörfler {
258d7f7bf2dSAxel Dörfler // TODO: Could binary search the Paragraphs if they were wrapped in classes
259d7f7bf2dSAxel Dörfler // that knew there text offset in the document.
260d7f7bf2dSAxel Dörfler int32 textLength = 0;
261d7f7bf2dSAxel Dörfler paragraphOffset = 0;
262dfbcbde1SAndrew Lindesay int32 count = fParagraphs.size();
263d7f7bf2dSAxel Dörfler for (int32 i = 0; i < count; i++) {
264dfbcbde1SAndrew Lindesay const Paragraph& paragraph = fParagraphs[i];
265d7f7bf2dSAxel Dörfler int32 paragraphLength = paragraph.Length();
266d7f7bf2dSAxel Dörfler textLength += paragraphLength;
267d7f7bf2dSAxel Dörfler if (textLength > textOffset
268d7f7bf2dSAxel Dörfler || (i == count - 1 && textLength == textOffset)) {
269d7f7bf2dSAxel Dörfler return i;
270d7f7bf2dSAxel Dörfler }
271d7f7bf2dSAxel Dörfler paragraphOffset += paragraphLength;
272d7f7bf2dSAxel Dörfler }
273d7f7bf2dSAxel Dörfler return -1;
274d7f7bf2dSAxel Dörfler }
275d7f7bf2dSAxel Dörfler
276d7f7bf2dSAxel Dörfler
277d7f7bf2dSAxel Dörfler const Paragraph&
ParagraphAt(int32 textOffset,int32 & paragraphOffset) const278d7f7bf2dSAxel Dörfler TextDocument::ParagraphAt(int32 textOffset, int32& paragraphOffset) const
279d7f7bf2dSAxel Dörfler {
280d7f7bf2dSAxel Dörfler int32 index = ParagraphIndexFor(textOffset, paragraphOffset);
281d7f7bf2dSAxel Dörfler if (index >= 0)
282dfbcbde1SAndrew Lindesay return fParagraphs[index];
283d7f7bf2dSAxel Dörfler
284d7f7bf2dSAxel Dörfler return fEmptyLastParagraph;
285d7f7bf2dSAxel Dörfler }
286d7f7bf2dSAxel Dörfler
287d7f7bf2dSAxel Dörfler
288d7f7bf2dSAxel Dörfler const Paragraph&
ParagraphAt(int32 index) const289d7f7bf2dSAxel Dörfler TextDocument::ParagraphAt(int32 index) const
290d7f7bf2dSAxel Dörfler {
291dfbcbde1SAndrew Lindesay if (index >= 0 && index < static_cast<int32>(fParagraphs.size()))
292dfbcbde1SAndrew Lindesay return fParagraphs[index];
293d7f7bf2dSAxel Dörfler return fEmptyLastParagraph;
294d7f7bf2dSAxel Dörfler }
295d7f7bf2dSAxel Dörfler
296d7f7bf2dSAxel Dörfler
297d7f7bf2dSAxel Dörfler bool
Append(const Paragraph & paragraph)298d7f7bf2dSAxel Dörfler TextDocument::Append(const Paragraph& paragraph)
299d7f7bf2dSAxel Dörfler {
300dfbcbde1SAndrew Lindesay try {
301dfbcbde1SAndrew Lindesay fParagraphs.push_back(paragraph);
302dfbcbde1SAndrew Lindesay }
303dfbcbde1SAndrew Lindesay catch (std::bad_alloc& ba) {
304dfbcbde1SAndrew Lindesay fprintf(stderr, "bad_alloc when adding a paragraph to a text "
305dfbcbde1SAndrew Lindesay "document\n");
306dfbcbde1SAndrew Lindesay return false;
307dfbcbde1SAndrew Lindesay }
308dfbcbde1SAndrew Lindesay return true;
309d7f7bf2dSAxel Dörfler }
310d7f7bf2dSAxel Dörfler
311d7f7bf2dSAxel Dörfler
312d7f7bf2dSAxel Dörfler int32
Length() const313d7f7bf2dSAxel Dörfler TextDocument::Length() const
314d7f7bf2dSAxel Dörfler {
315d7f7bf2dSAxel Dörfler // TODO: Could be O(1) if the Paragraphs were wrapped in classes that
3164a96bcdaSStephan Aßmus // knew their text offset in the document.
317d7f7bf2dSAxel Dörfler int32 textLength = 0;
318dfbcbde1SAndrew Lindesay int32 count = fParagraphs.size();
319d7f7bf2dSAxel Dörfler for (int32 i = 0; i < count; i++) {
320dfbcbde1SAndrew Lindesay const Paragraph& paragraph = fParagraphs[i];
321d7f7bf2dSAxel Dörfler textLength += paragraph.Length();
322d7f7bf2dSAxel Dörfler }
323d7f7bf2dSAxel Dörfler return textLength;
324d7f7bf2dSAxel Dörfler }
325d7f7bf2dSAxel Dörfler
326d7f7bf2dSAxel Dörfler
327d7f7bf2dSAxel Dörfler BString
Text() const328d7f7bf2dSAxel Dörfler TextDocument::Text() const
329d7f7bf2dSAxel Dörfler {
330d7f7bf2dSAxel Dörfler return Text(0, Length());
331d7f7bf2dSAxel Dörfler }
332d7f7bf2dSAxel Dörfler
333d7f7bf2dSAxel Dörfler
334d7f7bf2dSAxel Dörfler BString
Text(int32 start,int32 length) const335d7f7bf2dSAxel Dörfler TextDocument::Text(int32 start, int32 length) const
336d7f7bf2dSAxel Dörfler {
337d7f7bf2dSAxel Dörfler if (start < 0)
338d7f7bf2dSAxel Dörfler start = 0;
339d7f7bf2dSAxel Dörfler
340d7f7bf2dSAxel Dörfler BString text;
341d7f7bf2dSAxel Dörfler
342dfbcbde1SAndrew Lindesay int32 count = fParagraphs.size();
343d7f7bf2dSAxel Dörfler for (int32 i = 0; i < count; i++) {
344dfbcbde1SAndrew Lindesay const Paragraph& paragraph = fParagraphs[i];
345d7f7bf2dSAxel Dörfler int32 paragraphLength = paragraph.Length();
346d7f7bf2dSAxel Dörfler if (paragraphLength == 0)
347d7f7bf2dSAxel Dörfler continue;
348d7f7bf2dSAxel Dörfler if (start > paragraphLength) {
349d7f7bf2dSAxel Dörfler // Skip paragraph if its before start
350d7f7bf2dSAxel Dörfler start -= paragraphLength;
351d7f7bf2dSAxel Dörfler continue;
352d7f7bf2dSAxel Dörfler }
353d7f7bf2dSAxel Dörfler
354d7f7bf2dSAxel Dörfler // Remaining paragraph length after start
355d7f7bf2dSAxel Dörfler paragraphLength -= start;
356d7f7bf2dSAxel Dörfler int32 copyLength = std::min(paragraphLength, length);
357d7f7bf2dSAxel Dörfler
358d7f7bf2dSAxel Dörfler text << paragraph.Text(start, copyLength);
359d7f7bf2dSAxel Dörfler
360d7f7bf2dSAxel Dörfler length -= copyLength;
361d7f7bf2dSAxel Dörfler if (length == 0)
362d7f7bf2dSAxel Dörfler break;
363d7f7bf2dSAxel Dörfler
364d7f7bf2dSAxel Dörfler // Next paragraph is copied from its beginning
365d7f7bf2dSAxel Dörfler start = 0;
366d7f7bf2dSAxel Dörfler }
367d7f7bf2dSAxel Dörfler
368d7f7bf2dSAxel Dörfler return text;
369d7f7bf2dSAxel Dörfler }
370d7f7bf2dSAxel Dörfler
371d7f7bf2dSAxel Dörfler
372d7f7bf2dSAxel Dörfler TextDocumentRef
SubDocument(int32 start,int32 length) const373d7f7bf2dSAxel Dörfler TextDocument::SubDocument(int32 start, int32 length) const
374d7f7bf2dSAxel Dörfler {
375d7f7bf2dSAxel Dörfler TextDocumentRef result(new(std::nothrow) TextDocument(
376d7f7bf2dSAxel Dörfler fDefaultCharacterStyle, fEmptyLastParagraph.Style()), true);
377d7f7bf2dSAxel Dörfler
378779ab335SX512 if (!result.IsSet())
379d7f7bf2dSAxel Dörfler return result;
380d7f7bf2dSAxel Dörfler
381d7f7bf2dSAxel Dörfler if (start < 0)
382d7f7bf2dSAxel Dörfler start = 0;
383d7f7bf2dSAxel Dörfler
384dfbcbde1SAndrew Lindesay int32 count = fParagraphs.size();
385d7f7bf2dSAxel Dörfler for (int32 i = 0; i < count; i++) {
386dfbcbde1SAndrew Lindesay const Paragraph& paragraph = fParagraphs[i];
387d7f7bf2dSAxel Dörfler int32 paragraphLength = paragraph.Length();
388d7f7bf2dSAxel Dörfler if (paragraphLength == 0)
389d7f7bf2dSAxel Dörfler continue;
390d7f7bf2dSAxel Dörfler if (start > paragraphLength) {
391d7f7bf2dSAxel Dörfler // Skip paragraph if its before start
392d7f7bf2dSAxel Dörfler start -= paragraphLength;
393d7f7bf2dSAxel Dörfler continue;
394d7f7bf2dSAxel Dörfler }
395d7f7bf2dSAxel Dörfler
396d7f7bf2dSAxel Dörfler // Remaining paragraph length after start
397d7f7bf2dSAxel Dörfler paragraphLength -= start;
398d7f7bf2dSAxel Dörfler int32 copyLength = std::min(paragraphLength, length);
399d7f7bf2dSAxel Dörfler
400d7f7bf2dSAxel Dörfler result->Append(paragraph.SubParagraph(start, copyLength));
401d7f7bf2dSAxel Dörfler
402d7f7bf2dSAxel Dörfler length -= copyLength;
403d7f7bf2dSAxel Dörfler if (length == 0)
404d7f7bf2dSAxel Dörfler break;
405d7f7bf2dSAxel Dörfler
406d7f7bf2dSAxel Dörfler // Next paragraph is copied from its beginning
407d7f7bf2dSAxel Dörfler start = 0;
408d7f7bf2dSAxel Dörfler }
409d7f7bf2dSAxel Dörfler
410d7f7bf2dSAxel Dörfler return result;
411d7f7bf2dSAxel Dörfler }
412d7f7bf2dSAxel Dörfler
413d7f7bf2dSAxel Dörfler
414d7f7bf2dSAxel Dörfler // #pragma mark -
415d7f7bf2dSAxel Dörfler
416d7f7bf2dSAxel Dörfler
417d7f7bf2dSAxel Dörfler void
PrintToStream() const418d7f7bf2dSAxel Dörfler TextDocument::PrintToStream() const
419d7f7bf2dSAxel Dörfler {
420dfbcbde1SAndrew Lindesay int32 paragraphCount = fParagraphs.size();
421d7f7bf2dSAxel Dörfler if (paragraphCount == 0) {
422d7f7bf2dSAxel Dörfler printf("<document/>\n");
423d7f7bf2dSAxel Dörfler return;
424d7f7bf2dSAxel Dörfler }
425d7f7bf2dSAxel Dörfler printf("<document>\n");
426d7f7bf2dSAxel Dörfler for (int32 i = 0; i < paragraphCount; i++) {
427dfbcbde1SAndrew Lindesay fParagraphs[i].PrintToStream();
428d7f7bf2dSAxel Dörfler }
429d7f7bf2dSAxel Dörfler printf("</document>\n");
430d7f7bf2dSAxel Dörfler }
431d7f7bf2dSAxel Dörfler
432d7f7bf2dSAxel Dörfler
433f890fab6SStephan Aßmus /*static*/ TextDocumentRef
NormalizeText(const BString & text,CharacterStyle characterStyle,ParagraphStyle paragraphStyle)434f890fab6SStephan Aßmus TextDocument::NormalizeText(const BString& text,
435f890fab6SStephan Aßmus CharacterStyle characterStyle, ParagraphStyle paragraphStyle)
436d7f7bf2dSAxel Dörfler {
437f890fab6SStephan Aßmus TextDocumentRef document(new(std::nothrow) TextDocument(characterStyle,
438f890fab6SStephan Aßmus paragraphStyle), true);
439779ab335SX512 if (!document.IsSet())
440f890fab6SStephan Aßmus throw B_NO_MEMORY;
441d7f7bf2dSAxel Dörfler
442f890fab6SStephan Aßmus Paragraph paragraph(paragraphStyle);
443d7f7bf2dSAxel Dörfler
444f890fab6SStephan Aßmus // Append TextSpans, splitting 'text' into Paragraphs at line breaks.
4454a96bcdaSStephan Aßmus int32 length = text.CountChars();
4464a96bcdaSStephan Aßmus int32 chunkStart = 0;
4474a96bcdaSStephan Aßmus while (chunkStart < length) {
4484a96bcdaSStephan Aßmus int32 chunkEnd = text.FindFirst('\n', chunkStart);
449*c4e439b5SAndrew Lindesay
450*c4e439b5SAndrew Lindesay if (chunkEnd == B_ERROR)
4514a96bcdaSStephan Aßmus chunkEnd = length;
452*c4e439b5SAndrew Lindesay else
453*c4e439b5SAndrew Lindesay chunkEnd++; // the paragraph includes the `\n`
4544a96bcdaSStephan Aßmus
4554a96bcdaSStephan Aßmus BString chunk;
4564a96bcdaSStephan Aßmus text.CopyCharsInto(chunk, chunkStart, chunkEnd - chunkStart);
4574a96bcdaSStephan Aßmus TextSpan span(chunk, characterStyle);
4584a96bcdaSStephan Aßmus
459f890fab6SStephan Aßmus if (!paragraph.Append(span))
460f890fab6SStephan Aßmus throw B_NO_MEMORY;
461f890fab6SStephan Aßmus if (paragraph.Length() > 0 && !document->Append(paragraph))
462f890fab6SStephan Aßmus throw B_NO_MEMORY;
4634a96bcdaSStephan Aßmus
464f890fab6SStephan Aßmus paragraph = Paragraph(paragraphStyle);
465*c4e439b5SAndrew Lindesay chunkStart = chunkEnd;
4664a96bcdaSStephan Aßmus }
4674a96bcdaSStephan Aßmus
468f890fab6SStephan Aßmus return document;
469f890fab6SStephan Aßmus }
470f890fab6SStephan Aßmus
471f890fab6SStephan Aßmus
472f890fab6SStephan Aßmus // #pragma mark -
473f890fab6SStephan Aßmus
474f890fab6SStephan Aßmus
475f890fab6SStephan Aßmus bool
AddListener(TextListenerRef listener)476f890fab6SStephan Aßmus TextDocument::AddListener(TextListenerRef listener)
477f890fab6SStephan Aßmus {
478dfbcbde1SAndrew Lindesay try {
479dfbcbde1SAndrew Lindesay fTextListeners.push_back(listener);
480dfbcbde1SAndrew Lindesay }
481dfbcbde1SAndrew Lindesay catch (std::bad_alloc& ba) {
482dfbcbde1SAndrew Lindesay fprintf(stderr, "bad_alloc when adding a listener to a text "
483dfbcbde1SAndrew Lindesay "document\n");
484dfbcbde1SAndrew Lindesay return false;
485dfbcbde1SAndrew Lindesay }
486dfbcbde1SAndrew Lindesay return true;
487f890fab6SStephan Aßmus }
488f890fab6SStephan Aßmus
489f890fab6SStephan Aßmus
490f890fab6SStephan Aßmus bool
RemoveListener(TextListenerRef listener)491f890fab6SStephan Aßmus TextDocument::RemoveListener(TextListenerRef listener)
492f890fab6SStephan Aßmus {
49379a174c7SAugustin Cavalier fTextListeners.erase(std::remove(fTextListeners.begin(), fTextListeners.end(),
49479a174c7SAugustin Cavalier listener), fTextListeners.end());
495dfbcbde1SAndrew Lindesay return true;
496f890fab6SStephan Aßmus }
497f890fab6SStephan Aßmus
498f890fab6SStephan Aßmus
499f890fab6SStephan Aßmus bool
AddUndoListener(UndoableEditListenerRef listener)500f890fab6SStephan Aßmus TextDocument::AddUndoListener(UndoableEditListenerRef listener)
501f890fab6SStephan Aßmus {
502dfbcbde1SAndrew Lindesay try {
503dfbcbde1SAndrew Lindesay fUndoListeners.push_back(listener);
504dfbcbde1SAndrew Lindesay }
505dfbcbde1SAndrew Lindesay catch (std::bad_alloc& ba) {
506dfbcbde1SAndrew Lindesay fprintf(stderr, "bad_alloc when adding an undo listener to a text "
507dfbcbde1SAndrew Lindesay "document\n");
508dfbcbde1SAndrew Lindesay return false;
509dfbcbde1SAndrew Lindesay }
510dfbcbde1SAndrew Lindesay return true;
511f890fab6SStephan Aßmus }
512f890fab6SStephan Aßmus
513f890fab6SStephan Aßmus
514f890fab6SStephan Aßmus bool
RemoveUndoListener(UndoableEditListenerRef listener)515f890fab6SStephan Aßmus TextDocument::RemoveUndoListener(UndoableEditListenerRef listener)
516f890fab6SStephan Aßmus {
517f5ea62c4SAugustin Cavalier fUndoListeners.erase(std::remove(fUndoListeners.begin(), fUndoListeners.end(),
518f5ea62c4SAugustin Cavalier listener), fUndoListeners.end());
519dfbcbde1SAndrew Lindesay return true;
520f890fab6SStephan Aßmus }
521f890fab6SStephan Aßmus
522f890fab6SStephan Aßmus
523f890fab6SStephan Aßmus // #pragma mark - private
524f890fab6SStephan Aßmus
525f890fab6SStephan Aßmus
526f890fab6SStephan Aßmus status_t
_Insert(int32 textOffset,TextDocumentRef document,int32 & index,int32 & paragraphCount)527f890fab6SStephan Aßmus TextDocument::_Insert(int32 textOffset, TextDocumentRef document,
528f890fab6SStephan Aßmus int32& index, int32& paragraphCount)
529f890fab6SStephan Aßmus {
530f890fab6SStephan Aßmus int32 paragraphOffset;
531f890fab6SStephan Aßmus index = ParagraphIndexFor(textOffset, paragraphOffset);
532f890fab6SStephan Aßmus if (index < 0)
533f890fab6SStephan Aßmus return B_BAD_VALUE;
534f890fab6SStephan Aßmus
535f890fab6SStephan Aßmus if (document->Length() == 0)
536f890fab6SStephan Aßmus return B_OK;
537f890fab6SStephan Aßmus
538f890fab6SStephan Aßmus textOffset -= paragraphOffset;
539f890fab6SStephan Aßmus
540f890fab6SStephan Aßmus bool hasLineBreaks;
541f890fab6SStephan Aßmus if (document->CountParagraphs() > 1) {
542f890fab6SStephan Aßmus hasLineBreaks = true;
543f890fab6SStephan Aßmus } else {
544f890fab6SStephan Aßmus const Paragraph& paragraph = document->ParagraphAt(0);
545f890fab6SStephan Aßmus hasLineBreaks = paragraph.EndsWith("\n");
546f890fab6SStephan Aßmus }
547f890fab6SStephan Aßmus
548f890fab6SStephan Aßmus if (hasLineBreaks) {
549f890fab6SStephan Aßmus // Split paragraph at textOffset
550f890fab6SStephan Aßmus Paragraph paragraph1(ParagraphAt(index).Style());
551f890fab6SStephan Aßmus Paragraph paragraph2(document->ParagraphAt(
552f890fab6SStephan Aßmus document->CountParagraphs() - 1).Style());
553f890fab6SStephan Aßmus {
5543d2fd2acSAndrew Lindesay const Paragraph& paragraphAtIndex = ParagraphAt(index);
5553d2fd2acSAndrew Lindesay int32 spanCount = paragraphAtIndex.CountTextSpans();
556f890fab6SStephan Aßmus for (int32 i = 0; i < spanCount; i++) {
5573d2fd2acSAndrew Lindesay const TextSpan& span = paragraphAtIndex.TextSpanAtIndex(i);
558f890fab6SStephan Aßmus int32 spanLength = span.CountChars();
559f890fab6SStephan Aßmus if (textOffset >= spanLength) {
560f890fab6SStephan Aßmus if (!paragraph1.Append(span))
561f890fab6SStephan Aßmus return B_NO_MEMORY;
562f890fab6SStephan Aßmus textOffset -= spanLength;
563f890fab6SStephan Aßmus } else if (textOffset > 0) {
564f890fab6SStephan Aßmus if (!paragraph1.Append(
565f890fab6SStephan Aßmus span.SubSpan(0, textOffset))
566f890fab6SStephan Aßmus || !paragraph2.Append(
567f890fab6SStephan Aßmus span.SubSpan(textOffset,
568f890fab6SStephan Aßmus spanLength - textOffset))) {
569f890fab6SStephan Aßmus return B_NO_MEMORY;
570f890fab6SStephan Aßmus }
571f890fab6SStephan Aßmus textOffset = 0;
572f890fab6SStephan Aßmus } else {
573f890fab6SStephan Aßmus if (!paragraph2.Append(span))
574f890fab6SStephan Aßmus return B_NO_MEMORY;
575f890fab6SStephan Aßmus }
576f890fab6SStephan Aßmus }
577f890fab6SStephan Aßmus }
578f890fab6SStephan Aßmus
579dfbcbde1SAndrew Lindesay fParagraphs.erase(fParagraphs.begin() + index);
580f890fab6SStephan Aßmus
581f890fab6SStephan Aßmus // Append first paragraph in other document to first part of
582f890fab6SStephan Aßmus // paragraph at insert position
583f890fab6SStephan Aßmus {
584f890fab6SStephan Aßmus const Paragraph& otherParagraph = document->ParagraphAt(0);
5853d2fd2acSAndrew Lindesay int32 spanCount = otherParagraph.CountTextSpans();
586f890fab6SStephan Aßmus for (int32 i = 0; i < spanCount; i++) {
5873d2fd2acSAndrew Lindesay const TextSpan& span = otherParagraph.TextSpanAtIndex(i);
588f890fab6SStephan Aßmus // TODO: Import/map CharacterStyles
589f890fab6SStephan Aßmus if (!paragraph1.Append(span))
590f890fab6SStephan Aßmus return B_NO_MEMORY;
591f890fab6SStephan Aßmus }
592f890fab6SStephan Aßmus }
593f890fab6SStephan Aßmus
594f890fab6SStephan Aßmus // Insert the first paragraph-part again to the document
595dfbcbde1SAndrew Lindesay try {
596dfbcbde1SAndrew Lindesay fParagraphs.insert(fParagraphs.begin() + index, paragraph1);
597dfbcbde1SAndrew Lindesay }
598dfbcbde1SAndrew Lindesay catch (std::bad_alloc& ba) {
599f890fab6SStephan Aßmus return B_NO_MEMORY;
600dfbcbde1SAndrew Lindesay }
601f890fab6SStephan Aßmus paragraphCount++;
602f890fab6SStephan Aßmus
603f890fab6SStephan Aßmus // Insert the other document's paragraph save for the last one
604f890fab6SStephan Aßmus for (int32 i = 1; i < document->CountParagraphs() - 1; i++) {
605f890fab6SStephan Aßmus const Paragraph& otherParagraph = document->ParagraphAt(i);
606f890fab6SStephan Aßmus // TODO: Import/map CharacterStyles and ParagraphStyle
607dfbcbde1SAndrew Lindesay index++;
608dfbcbde1SAndrew Lindesay try {
609dfbcbde1SAndrew Lindesay fParagraphs.insert(fParagraphs.begin() + index, otherParagraph);
610dfbcbde1SAndrew Lindesay }
611dfbcbde1SAndrew Lindesay catch (std::bad_alloc& ba) {
612f890fab6SStephan Aßmus return B_NO_MEMORY;
613dfbcbde1SAndrew Lindesay }
614f890fab6SStephan Aßmus paragraphCount++;
615f890fab6SStephan Aßmus }
616f890fab6SStephan Aßmus
617f890fab6SStephan Aßmus int32 lastIndex = document->CountParagraphs() - 1;
618f890fab6SStephan Aßmus if (lastIndex > 0) {
619f890fab6SStephan Aßmus const Paragraph& otherParagraph = document->ParagraphAt(lastIndex);
620f890fab6SStephan Aßmus if (otherParagraph.EndsWith("\n")) {
621f890fab6SStephan Aßmus // TODO: Import/map CharacterStyles and ParagraphStyle
622dfbcbde1SAndrew Lindesay index++;
623dfbcbde1SAndrew Lindesay try {
624dfbcbde1SAndrew Lindesay fParagraphs.insert(fParagraphs.begin() + index, otherParagraph);
625dfbcbde1SAndrew Lindesay }
626dfbcbde1SAndrew Lindesay catch (std::bad_alloc& ba) {
627f890fab6SStephan Aßmus return B_NO_MEMORY;
628dfbcbde1SAndrew Lindesay }
629f890fab6SStephan Aßmus } else {
6303d2fd2acSAndrew Lindesay int32 spanCount = otherParagraph.CountTextSpans();
631f890fab6SStephan Aßmus for (int32 i = 0; i < spanCount; i++) {
6323d2fd2acSAndrew Lindesay const TextSpan& span = otherParagraph.TextSpanAtIndex(i);
633f890fab6SStephan Aßmus // TODO: Import/map CharacterStyles
634f890fab6SStephan Aßmus if (!paragraph2.Prepend(span))
635f890fab6SStephan Aßmus return B_NO_MEMORY;
636f890fab6SStephan Aßmus }
637f890fab6SStephan Aßmus }
638f890fab6SStephan Aßmus }
639f890fab6SStephan Aßmus
640f890fab6SStephan Aßmus // Insert back the second paragraph-part
6414a96bcdaSStephan Aßmus if (paragraph2.IsEmpty()) {
6424a96bcdaSStephan Aßmus // Make sure Paragraph has at least one TextSpan, even
6435f80d48aSStephan Aßmus // if its empty. This handles the case of inserting a
6445f80d48aSStephan Aßmus // line-break at the end of the document. It than needs to
6455f80d48aSStephan Aßmus // have a new, empty paragraph at the end.
6463d2fd2acSAndrew Lindesay const int32 indexLastSpan = paragraph1.CountTextSpans() - 1;
6473d2fd2acSAndrew Lindesay const TextSpan& span = paragraph1.TextSpanAtIndex(indexLastSpan);
648f890fab6SStephan Aßmus if (!paragraph2.Append(TextSpan("", span.Style())))
649f890fab6SStephan Aßmus return B_NO_MEMORY;
6504a96bcdaSStephan Aßmus }
6514a96bcdaSStephan Aßmus
652dfbcbde1SAndrew Lindesay index++;
653dfbcbde1SAndrew Lindesay try {
654dfbcbde1SAndrew Lindesay fParagraphs.insert(fParagraphs.begin() + index, paragraph2);
655dfbcbde1SAndrew Lindesay }
656dfbcbde1SAndrew Lindesay catch (std::bad_alloc& ba) {
6574a96bcdaSStephan Aßmus return B_NO_MEMORY;
658dfbcbde1SAndrew Lindesay }
659f890fab6SStephan Aßmus
6604a96bcdaSStephan Aßmus paragraphCount++;
6614a96bcdaSStephan Aßmus } else {
6624a96bcdaSStephan Aßmus Paragraph paragraph(ParagraphAt(index));
663f890fab6SStephan Aßmus const Paragraph& otherParagraph = document->ParagraphAt(0);
664f890fab6SStephan Aßmus
6653d2fd2acSAndrew Lindesay int32 spanCount = otherParagraph.CountTextSpans();
666f890fab6SStephan Aßmus for (int32 i = 0; i < spanCount; i++) {
6673d2fd2acSAndrew Lindesay const TextSpan& span = otherParagraph.TextSpanAtIndex(i);
668f890fab6SStephan Aßmus paragraph.Insert(textOffset, span);
669f890fab6SStephan Aßmus textOffset += span.CountChars();
670f890fab6SStephan Aßmus }
671f890fab6SStephan Aßmus
672dfbcbde1SAndrew Lindesay fParagraphs[index] = paragraph;
6734a96bcdaSStephan Aßmus paragraphCount++;
6744a96bcdaSStephan Aßmus }
6754a96bcdaSStephan Aßmus
6764a96bcdaSStephan Aßmus return B_OK;
6774a96bcdaSStephan Aßmus }
6784a96bcdaSStephan Aßmus
6794a96bcdaSStephan Aßmus
6804a96bcdaSStephan Aßmus status_t
_Remove(int32 textOffset,int32 length,int32 & index,int32 & paragraphCount)6814a96bcdaSStephan Aßmus TextDocument::_Remove(int32 textOffset, int32 length, int32& index,
6824a96bcdaSStephan Aßmus int32& paragraphCount)
6834a96bcdaSStephan Aßmus {
6844a96bcdaSStephan Aßmus if (length == 0)
6854a96bcdaSStephan Aßmus return B_OK;
6864a96bcdaSStephan Aßmus
6874a96bcdaSStephan Aßmus int32 paragraphOffset;
6884a96bcdaSStephan Aßmus index = ParagraphIndexFor(textOffset, paragraphOffset);
6894a96bcdaSStephan Aßmus if (index < 0)
6904a96bcdaSStephan Aßmus return B_BAD_VALUE;
6914a96bcdaSStephan Aßmus
6924a96bcdaSStephan Aßmus textOffset -= paragraphOffset;
6934a96bcdaSStephan Aßmus paragraphCount++;
6944a96bcdaSStephan Aßmus
6954a96bcdaSStephan Aßmus // The paragraph at the text offset remains, even if the offset is at
6964a96bcdaSStephan Aßmus // the beginning of that paragraph. The idea is that the selection start
6974a96bcdaSStephan Aßmus // stays visually in the same place. Therefore, the paragraph at that
6984a96bcdaSStephan Aßmus // offset has to keep the paragraph style from that paragraph.
6994a96bcdaSStephan Aßmus
7004a96bcdaSStephan Aßmus Paragraph resultParagraph(ParagraphAt(index));
7014a96bcdaSStephan Aßmus int32 paragraphLength = resultParagraph.Length();
7024a96bcdaSStephan Aßmus if (textOffset == 0 && length > paragraphLength) {
7034a96bcdaSStephan Aßmus length -= paragraphLength;
7044a96bcdaSStephan Aßmus paragraphLength = 0;
7054a96bcdaSStephan Aßmus resultParagraph.Clear();
7064a96bcdaSStephan Aßmus } else {
7074a96bcdaSStephan Aßmus int32 removeLength = std::min(length, paragraphLength - textOffset);
7084a96bcdaSStephan Aßmus resultParagraph.Remove(textOffset, removeLength);
7094a96bcdaSStephan Aßmus paragraphLength -= removeLength;
7104a96bcdaSStephan Aßmus length -= removeLength;
7114a96bcdaSStephan Aßmus }
7124a96bcdaSStephan Aßmus
7134a96bcdaSStephan Aßmus if (textOffset == paragraphLength && length == 0
714dfbcbde1SAndrew Lindesay && index + 1 < static_cast<int32>(fParagraphs.size())) {
7154a96bcdaSStephan Aßmus // Line break between paragraphs got removed. Shift the next
7164a96bcdaSStephan Aßmus // paragraph's text spans into the resulting one.
7174a96bcdaSStephan Aßmus
7183d2fd2acSAndrew Lindesay const Paragraph& paragraph = ParagraphAt(index + 1);
7193d2fd2acSAndrew Lindesay int32 spanCount = paragraph.CountTextSpans();
7204a96bcdaSStephan Aßmus for (int32 i = 0; i < spanCount; i++) {
7213d2fd2acSAndrew Lindesay const TextSpan& span = paragraph.TextSpanAtIndex(i);
7224a96bcdaSStephan Aßmus resultParagraph.Append(span);
7234a96bcdaSStephan Aßmus }
724dfbcbde1SAndrew Lindesay fParagraphs.erase(fParagraphs.begin() + (index + 1));
7254a96bcdaSStephan Aßmus paragraphCount++;
7264a96bcdaSStephan Aßmus }
7274a96bcdaSStephan Aßmus
7284a96bcdaSStephan Aßmus textOffset = 0;
7294a96bcdaSStephan Aßmus
730dfbcbde1SAndrew Lindesay while (length > 0 && index + 1 < static_cast<int32>(fParagraphs.size())) {
7314a96bcdaSStephan Aßmus paragraphCount++;
7324a96bcdaSStephan Aßmus const Paragraph& paragraph = ParagraphAt(index + 1);
7334a96bcdaSStephan Aßmus paragraphLength = paragraph.Length();
7344a96bcdaSStephan Aßmus // Remove paragraph in any case. If some of it remains, the last
7354a96bcdaSStephan Aßmus // paragraph to remove is reached, and the remaining spans are
7364a96bcdaSStephan Aßmus // transfered to the result parahraph.
7374a96bcdaSStephan Aßmus if (length >= paragraphLength) {
7384a96bcdaSStephan Aßmus length -= paragraphLength;
739dfbcbde1SAndrew Lindesay fParagraphs.erase(fParagraphs.begin() + index);
7404a96bcdaSStephan Aßmus } else {
7414a96bcdaSStephan Aßmus // Last paragraph reached
7424a96bcdaSStephan Aßmus int32 removedLength = std::min(length, paragraphLength);
7434a96bcdaSStephan Aßmus Paragraph newParagraph(paragraph);
744dfbcbde1SAndrew Lindesay fParagraphs.erase(fParagraphs.begin() + (index + 1));
7454a96bcdaSStephan Aßmus
7464a96bcdaSStephan Aßmus if (!newParagraph.Remove(0, removedLength))
7474a96bcdaSStephan Aßmus return B_NO_MEMORY;
7484a96bcdaSStephan Aßmus
7494a96bcdaSStephan Aßmus // Transfer remaining spans to resultParagraph
7503d2fd2acSAndrew Lindesay int32 spanCount = newParagraph.CountTextSpans();
7514a96bcdaSStephan Aßmus for (int32 i = 0; i < spanCount; i++) {
7523d2fd2acSAndrew Lindesay const TextSpan& span = newParagraph.TextSpanAtIndex(i);
7534a96bcdaSStephan Aßmus resultParagraph.Append(span);
7544a96bcdaSStephan Aßmus }
7554a96bcdaSStephan Aßmus
7564a96bcdaSStephan Aßmus break;
7574a96bcdaSStephan Aßmus }
7584a96bcdaSStephan Aßmus }
7594a96bcdaSStephan Aßmus
760dfbcbde1SAndrew Lindesay fParagraphs[index] = resultParagraph;
7614a96bcdaSStephan Aßmus
7624a96bcdaSStephan Aßmus return B_OK;
7634a96bcdaSStephan Aßmus }
7644a96bcdaSStephan Aßmus
7654a96bcdaSStephan Aßmus
7664a96bcdaSStephan Aßmus // #pragma mark - notifications
7674a96bcdaSStephan Aßmus
7684a96bcdaSStephan Aßmus
769d7f7bf2dSAxel Dörfler void
_NotifyTextChanging(TextChangingEvent & event) const770d7f7bf2dSAxel Dörfler TextDocument::_NotifyTextChanging(TextChangingEvent& event) const
771d7f7bf2dSAxel Dörfler {
772d7f7bf2dSAxel Dörfler // Copy listener list to have a stable list in case listeners
773d7f7bf2dSAxel Dörfler // are added/removed from within the notification hook.
774dfbcbde1SAndrew Lindesay std::vector<TextListenerRef> listeners(fTextListeners);
775dfbcbde1SAndrew Lindesay
776dfbcbde1SAndrew Lindesay int32 count = listeners.size();
777d7f7bf2dSAxel Dörfler for (int32 i = 0; i < count; i++) {
778dfbcbde1SAndrew Lindesay const TextListenerRef& listener = listeners[i];
779779ab335SX512 if (!listener.IsSet())
780d7f7bf2dSAxel Dörfler continue;
781d7f7bf2dSAxel Dörfler listener->TextChanging(event);
782d7f7bf2dSAxel Dörfler if (event.IsCanceled())
783d7f7bf2dSAxel Dörfler break;
784d7f7bf2dSAxel Dörfler }
785d7f7bf2dSAxel Dörfler }
786d7f7bf2dSAxel Dörfler
787d7f7bf2dSAxel Dörfler
788d7f7bf2dSAxel Dörfler void
_NotifyTextChanged(const TextChangedEvent & event) const789d7f7bf2dSAxel Dörfler TextDocument::_NotifyTextChanged(const TextChangedEvent& event) const
790d7f7bf2dSAxel Dörfler {
791d7f7bf2dSAxel Dörfler // Copy listener list to have a stable list in case listeners
792d7f7bf2dSAxel Dörfler // are added/removed from within the notification hook.
793dfbcbde1SAndrew Lindesay std::vector<TextListenerRef> listeners(fTextListeners);
794dfbcbde1SAndrew Lindesay int32 count = listeners.size();
795d7f7bf2dSAxel Dörfler for (int32 i = 0; i < count; i++) {
796dfbcbde1SAndrew Lindesay const TextListenerRef& listener = listeners[i];
797779ab335SX512 if (!listener.IsSet())
798d7f7bf2dSAxel Dörfler continue;
799d7f7bf2dSAxel Dörfler listener->TextChanged(event);
800d7f7bf2dSAxel Dörfler }
801d7f7bf2dSAxel Dörfler }
802d7f7bf2dSAxel Dörfler
8034bf45bfbSStephan Aßmus
8044bf45bfbSStephan Aßmus void
_NotifyUndoableEditHappened(const UndoableEditRef & edit) const8054bf45bfbSStephan Aßmus TextDocument::_NotifyUndoableEditHappened(const UndoableEditRef& edit) const
8064bf45bfbSStephan Aßmus {
8074bf45bfbSStephan Aßmus // Copy listener list to have a stable list in case listeners
8084bf45bfbSStephan Aßmus // are added/removed from within the notification hook.
809dfbcbde1SAndrew Lindesay std::vector<UndoableEditListenerRef> listeners(fUndoListeners);
810dfbcbde1SAndrew Lindesay int32 count = listeners.size();
8114bf45bfbSStephan Aßmus for (int32 i = 0; i < count; i++) {
812dfbcbde1SAndrew Lindesay const UndoableEditListenerRef& listener = listeners[i];
813779ab335SX512 if (!listener.IsSet())
8144bf45bfbSStephan Aßmus continue;
8154bf45bfbSStephan Aßmus listener->UndoableEditHappened(this, edit);
8164bf45bfbSStephan Aßmus }
8174bf45bfbSStephan Aßmus }
818