repository_infos: Enable automatic updates on the main Haiku repostiory.
[haiku.git] / src / apps / haikudepot / textview / Paragraph.cpp
blobd1d9649af48608e8fe0b2176bff06b25147966a6
1 /*
2 * Copyright 2013-2014, Stephan Aßmus <superstippi@gmx.de>.
3 * All rights reserved. Distributed under the terms of the MIT License.
4 */
6 #include "Paragraph.h"
8 #include <algorithm>
9 #include <stdio.h>
12 Paragraph::Paragraph()
14 fStyle()
19 Paragraph::Paragraph(const ParagraphStyle& style)
21 fStyle(style),
22 fTextSpans(),
23 fCachedLength(-1)
28 Paragraph::Paragraph(const Paragraph& other)
30 fStyle(other.fStyle),
31 fTextSpans(other.fTextSpans),
32 fCachedLength(other.fCachedLength)
37 Paragraph&
38 Paragraph::operator=(const Paragraph& other)
40 fStyle = other.fStyle;
41 fTextSpans = other.fTextSpans;
42 fCachedLength = other.fCachedLength;
44 return *this;
48 bool
49 Paragraph::operator==(const Paragraph& other) const
51 if (this == &other)
52 return true;
54 return fStyle == other.fStyle
55 && fTextSpans == other.fTextSpans;
59 bool
60 Paragraph::operator!=(const Paragraph& other) const
62 return !(*this == other);
66 void
67 Paragraph::SetStyle(const ParagraphStyle& style)
69 fStyle = style;
73 bool
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);
91 bool
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());
102 fTextSpans.Remove();
103 return fTextSpans.Add(TextSpan(text, span.Style()));
106 return fTextSpans.Add(span);
110 bool
111 Paragraph::Insert(int32 offset, const TextSpan& newSpan)
113 _InvalidateCachedLength();
115 int32 index = 0;
116 while (index < fTextSpans.CountItems()) {
117 const TextSpan& span = fTextSpans.ItemAtFast(index);
118 if (offset - span.CountChars() < 0)
119 break;
120 offset -= span.CountChars();
121 index++;
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);
134 if (offset == 0) {
135 if (index > 0) {
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);
147 // Split the span,
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);
157 bool
158 Paragraph::Remove(int32 offset, int32 length)
160 if (length == 0)
161 return true;
163 _InvalidateCachedLength();
165 int32 index = 0;
166 while (index < fTextSpans.CountItems()) {
167 const TextSpan& span = fTextSpans.ItemAtFast(index);
168 if (offset - span.CountChars() < 0)
169 break;
170 offset -= span.CountChars();
171 index++;
174 if (index >= fTextSpans.CountItems())
175 return false;
177 TextSpan span(fTextSpans.ItemAtFast(index));
178 int32 removeLength = std::min(span.CountChars() - offset, length);
179 span.Remove(offset, removeLength);
180 length -= removeLength;
181 index += 1;
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;
189 } else {
190 // Reached last span
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);
198 } else {
199 fTextSpans.Replace(index, lastSpan);
202 break;
206 // See if anything from the TextSpan at offset remained, keep it as empty
207 // span if it is the last remaining span.
208 index--;
209 if (span.CountChars() > 0 || fTextSpans.CountItems() == 1) {
210 fTextSpans.Replace(index, span);
211 } else {
212 fTextSpans.Remove(index);
213 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()) {
221 span = span1;
222 span.Append(span2.Text());
223 fTextSpans.Replace(index, span);
224 fTextSpans.Remove(index + 1);
228 return true;
232 void
233 Paragraph::Clear()
235 fTextSpans.Clear();
239 int32
240 Paragraph::Length() const
242 if (fCachedLength >= 0)
243 return fCachedLength;
245 int32 length = 0;
246 for (int32 i = fTextSpans.CountItems() - 1; i >= 0; i--) {
247 const TextSpan& span = fTextSpans.ItemAtFast(i);
248 length += span.CountChars();
251 fCachedLength = length;
252 return length;
256 bool
257 Paragraph::IsEmpty() const
259 return fTextSpans.CountItems() == 0;
263 bool
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;
274 BString
275 Paragraph::Text() const
277 BString result;
279 int32 count = fTextSpans.CountItems();
280 for (int32 i = 0; i < count; i++)
281 result << fTextSpans.ItemAtFast(i).Text();
283 return result;
287 BString
288 Paragraph::Text(int32 start, int32 length) const
290 Paragraph subParagraph = SubParagraph(start, length);
291 return subParagraph.Text();
295 Paragraph
296 Paragraph::SubParagraph(int32 start, int32 length) const
298 if (start < 0)
299 start = 0;
301 if (start == 0 && length == Length())
302 return *this;
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();
310 if (spanLength == 0)
311 continue;
312 if (start > spanLength) {
313 // Skip span if its before start
314 start -= spanLength;
315 continue;
318 // Remaining span length after start
319 spanLength -= start;
320 int32 copyLength = std::min(spanLength, length);
322 if (start == 0 && length == spanLength)
323 result.Append(span);
324 else
325 result.Append(span.SubSpan(start, copyLength));
327 length -= copyLength;
328 if (length == 0)
329 break;
331 // Next span is copied from its beginning
332 start = 0;
335 return result;
339 void
340 Paragraph::PrintToStream() const
342 int32 spanCount = fTextSpans.CountItems();
343 if (spanCount == 0) {
344 printf(" <p/>\n");
345 return;
347 printf(" <p>\n");
348 for (int32 i = 0; i < spanCount; i++) {
349 const TextSpan& span = fTextSpans.ItemAtFast(i);
350 if (span.CountChars() == 0)
351 printf(" <span/>\n");
352 else {
353 BString text = span.Text();
354 text.ReplaceAll("\n", "\\n");
355 printf(" <span>%s</span>\n", text.String());
358 printf(" </p>\n");
362 // #pragma mark -
365 void
366 Paragraph::_InvalidateCachedLength()
368 fCachedLength = -1;