2 * Copyright 2013-2014, Stephan Aßmus <superstippi@gmx.de>.
3 * All rights reserved. Distributed under the terms of the MIT License.
12 Paragraph::Paragraph()
19 Paragraph::Paragraph(const ParagraphStyle
& style
)
28 Paragraph::Paragraph(const Paragraph
& other
)
31 fTextSpans(other
.fTextSpans
),
32 fCachedLength(other
.fCachedLength
)
38 Paragraph::operator=(const Paragraph
& other
)
40 fStyle
= other
.fStyle
;
41 fTextSpans
= other
.fTextSpans
;
42 fCachedLength
= other
.fCachedLength
;
49 Paragraph::operator==(const Paragraph
& other
) const
54 return fStyle
== other
.fStyle
55 && fTextSpans
== other
.fTextSpans
;
60 Paragraph::operator!=(const Paragraph
& other
) const
62 return !(*this == other
);
67 Paragraph::SetStyle(const ParagraphStyle
& style
)
74 Paragraph::Prepend(const TextSpan
& span
)
76 _InvalidateCachedLength();
78 // Try to merge with first span if the TextStyles are equal
79 if (fTextSpans
.CountItems() > 0) {
80 const TextSpan
& firstSpan
= fTextSpans
.ItemAtFast(0);
81 if (firstSpan
.Style() == span
.Style()) {
82 BString
text(span
.Text());
83 text
.Append(firstSpan
.Text());
84 return fTextSpans
.Replace(0, TextSpan(text
, span
.Style()));
87 return fTextSpans
.Add(span
, 0);
92 Paragraph::Append(const TextSpan
& span
)
94 _InvalidateCachedLength();
96 // Try to merge with last span if the TextStyles are equal
97 if (fTextSpans
.CountItems() > 0) {
98 const TextSpan
& lastSpan
= fTextSpans
.LastItem();
99 if (lastSpan
.Style() == span
.Style()) {
100 BString
text(lastSpan
.Text());
101 text
.Append(span
.Text());
103 return fTextSpans
.Add(TextSpan(text
, span
.Style()));
106 return fTextSpans
.Add(span
);
111 Paragraph::Insert(int32 offset
, const TextSpan
& newSpan
)
113 _InvalidateCachedLength();
116 while (index
< fTextSpans
.CountItems()) {
117 const TextSpan
& span
= fTextSpans
.ItemAtFast(index
);
118 if (offset
- span
.CountChars() < 0)
120 offset
-= span
.CountChars();
124 if (fTextSpans
.CountItems() == index
)
125 return Append(newSpan
);
127 // Try to merge with span at index if the TextStyles are equal
128 TextSpan span
= fTextSpans
.ItemAtFast(index
);
129 if (span
.Style() == newSpan
.Style()) {
130 span
.Insert(offset
, newSpan
.Text());
131 return fTextSpans
.Replace(index
, span
);
136 // Try to merge with TextSpan before if offset == 0 && index > 0
137 TextSpan span
= fTextSpans
.ItemAtFast(index
- 1);
138 if (span
.Style() == newSpan
.Style()) {
139 span
.Insert(span
.CountChars(), newSpan
.Text());
140 return fTextSpans
.Replace(index
- 1, span
);
143 // Just insert the new span before the one at index
144 return fTextSpans
.Add(newSpan
, index
);
148 TextSpan spanBefore
= span
.SubSpan(0, offset
);
149 TextSpan spanAfter
= span
.SubSpan(offset
, span
.CountChars() - offset
);
151 return fTextSpans
.Replace(index
, spanBefore
)
152 && fTextSpans
.Add(newSpan
, index
+ 1)
153 && fTextSpans
.Add(spanAfter
, index
+ 2);
158 Paragraph::Remove(int32 offset
, int32 length
)
163 _InvalidateCachedLength();
166 while (index
< fTextSpans
.CountItems()) {
167 const TextSpan
& span
= fTextSpans
.ItemAtFast(index
);
168 if (offset
- span
.CountChars() < 0)
170 offset
-= span
.CountChars();
174 if (index
>= fTextSpans
.CountItems())
177 TextSpan
span(fTextSpans
.ItemAtFast(index
));
178 int32 removeLength
= std::min(span
.CountChars() - offset
, length
);
179 span
.Remove(offset
, removeLength
);
180 length
-= removeLength
;
183 // Remove more spans if necessary
184 while (length
> 0 && index
< fTextSpans
.CountItems()) {
185 int32 spanLength
= fTextSpans
.ItemAtFast(index
).CountChars();
186 if (spanLength
<= length
) {
187 fTextSpans
.Remove(index
);
188 length
-= spanLength
;
191 removeLength
= std::min(length
, spanLength
);
192 TextSpan lastSpan
= fTextSpans
.ItemAtFast(index
).SubSpan(
193 removeLength
, spanLength
- removeLength
);
194 // Try to merge with first span, otherwise replace span at index
195 if (lastSpan
.Style() == span
.Style()) {
196 span
.Insert(span
.CountChars(), lastSpan
.Text());
197 fTextSpans
.Remove(index
);
199 fTextSpans
.Replace(index
, lastSpan
);
206 // See if anything from the TextSpan at offset remained, keep it as empty
207 // span if it is the last remaining span.
209 if (span
.CountChars() > 0 || fTextSpans
.CountItems() == 1) {
210 fTextSpans
.Replace(index
, span
);
212 fTextSpans
.Remove(index
);
216 // See if spans can be merged after one has been removed.
217 if (index
>= 0 && index
+ 1 < fTextSpans
.CountItems()) {
218 const TextSpan
& span1
= fTextSpans
.ItemAtFast(index
);
219 const TextSpan
& span2
= fTextSpans
.ItemAtFast(index
+ 1);
220 if (span1
.Style() == span2
.Style()) {
222 span
.Append(span2
.Text());
223 fTextSpans
.Replace(index
, span
);
224 fTextSpans
.Remove(index
+ 1);
240 Paragraph::Length() const
242 if (fCachedLength
>= 0)
243 return fCachedLength
;
246 for (int32 i
= fTextSpans
.CountItems() - 1; i
>= 0; i
--) {
247 const TextSpan
& span
= fTextSpans
.ItemAtFast(i
);
248 length
+= span
.CountChars();
251 fCachedLength
= length
;
257 Paragraph::IsEmpty() const
259 return fTextSpans
.CountItems() == 0;
264 Paragraph::EndsWith(BString string
) const
266 int length
= Length();
267 int endLength
= string
.CountChars();
268 int start
= length
- endLength
;
269 BString end
= Text(start
, endLength
);
270 return end
== string
;
275 Paragraph::Text() const
279 int32 count
= fTextSpans
.CountItems();
280 for (int32 i
= 0; i
< count
; i
++)
281 result
<< fTextSpans
.ItemAtFast(i
).Text();
288 Paragraph::Text(int32 start
, int32 length
) const
290 Paragraph subParagraph
= SubParagraph(start
, length
);
291 return subParagraph
.Text();
296 Paragraph::SubParagraph(int32 start
, int32 length
) const
301 if (start
== 0 && length
== Length())
304 Paragraph
result(fStyle
);
306 int32 count
= fTextSpans
.CountItems();
307 for (int32 i
= 0; i
< count
; i
++) {
308 const TextSpan
& span
= fTextSpans
.ItemAtFast(i
);
309 int32 spanLength
= span
.CountChars();
312 if (start
> spanLength
) {
313 // Skip span if its before start
318 // Remaining span length after start
320 int32 copyLength
= std::min(spanLength
, length
);
322 if (start
== 0 && length
== spanLength
)
325 result
.Append(span
.SubSpan(start
, copyLength
));
327 length
-= copyLength
;
331 // Next span is copied from its beginning
340 Paragraph::PrintToStream() const
342 int32 spanCount
= fTextSpans
.CountItems();
343 if (spanCount
== 0) {
348 for (int32 i
= 0; i
< spanCount
; i
++) {
349 const TextSpan
& span
= fTextSpans
.ItemAtFast(i
);
350 if (span
.CountChars() == 0)
351 printf(" <span/>\n");
353 BString text
= span
.Text();
354 text
.ReplaceAll("\n", "\\n");
355 printf(" <span>%s</span>\n", text
.String());
366 Paragraph::_InvalidateCachedLength()