hrev46710 adds 5 changesets to branch 'master' old head: fed5e6126b1340b5f40dee2570f12c0c651f71da new head: 2c906df2174c1d0c8befc67cb0b61e789dd2eb82 overview: http://cgit.haiku-os.org/haiku/log/?qt=range&q=2c906df+%5Efed5e61 ---------------------------------------------------------------------------- ce30469: HaikuDepot: Simple TextListener interface, not yet used. b7e8a24: Paragraph: Added Remove(), untested. 13d17b5: TextDocument: Implemented Remove(), untested. Also some refactoring for ParagraphAt() into a separate ParagraphIndexFor(), used by the former, since often an index is needed. 315dee6: TextEditor: Resolve TODO and call Insert() and Remove()... ... which both already exist in the TextDocument API (stubbed out or untested as they are). 2c906df: Text stuff: Bugfixes in Insert and Remove methods Still not well tested. Also some way to trigger the layouts to adopt to the changed document paragraphs. Not yet via the new TextListener. [ Stephan Aßmus <superstippi@xxxxxx> ] ---------------------------------------------------------------------------- 11 files changed, 375 insertions(+), 18 deletions(-) src/apps/haiku-depot/Jamfile | 1 + src/apps/haiku-depot/textview/Paragraph.cpp | 57 ++++++++++ src/apps/haiku-depot/textview/Paragraph.h | 1 + src/apps/haiku-depot/textview/TextDocument.cpp | 91 +++++++++++++-- src/apps/haiku-depot/textview/TextDocument.h | 3 + .../haiku-depot/textview/TextDocumentLayout.cpp | 28 +++++ .../haiku-depot/textview/TextDocumentLayout.h | 3 + src/apps/haiku-depot/textview/TextEditor.cpp | 22 ++-- src/apps/haiku-depot/textview/TextEditor.h | 5 +- src/apps/haiku-depot/textview/TextListener.cpp | 111 +++++++++++++++++++ src/apps/haiku-depot/textview/TextListener.h | 71 ++++++++++++ ############################################################################ Commit: ce3046974c3f5296f55540cf22b82941ab10bc71 URL: http://cgit.haiku-os.org/haiku/commit/?id=ce30469 Author: Stephan Aßmus <superstippi@xxxxxx> Date: Sun Jan 19 14:49:09 2014 UTC HaikuDepot: Simple TextListener interface, not yet used. ---------------------------------------------------------------------------- diff --git a/src/apps/haiku-depot/Jamfile b/src/apps/haiku-depot/Jamfile index 1c8808a..882a5f7 100644 --- a/src/apps/haiku-depot/Jamfile +++ b/src/apps/haiku-depot/Jamfile @@ -27,6 +27,7 @@ local textDocumentSources = TextDocumentLayout.cpp TextDocumentView.cpp TextEditor.cpp + TextListener.cpp TextSelection.cpp TextSpan.cpp TextView.cpp diff --git a/src/apps/haiku-depot/textview/TextListener.cpp b/src/apps/haiku-depot/textview/TextListener.cpp new file mode 100644 index 0000000..4819b5f --- /dev/null +++ b/src/apps/haiku-depot/textview/TextListener.cpp @@ -0,0 +1,111 @@ +/* + * Copyright 2014, Stephan Aßmus <superstippi@xxxxxx>. + * All rights reserved. Distributed under the terms of the MIT License. + */ + +#include "TextListener.h" + + +// #pragma mark - TextChangeEvent + + +TextChangeEvent::TextChangeEvent(int32 firstChangedParagraph, + int32 changedParagraphCount) + : + fFirstChangedParagraph(firstChangedParagraph), + fChangedParagraphCount(changedParagraphCount) +{ +} + + +TextChangeEvent::TextChangeEvent() + : + fFirstChangedParagraph(0), + fChangedParagraphCount(0) +{ +} + + +TextChangeEvent::~TextChangeEvent() +{ +} + + +// #pragma mark - TextChangedEvent + + +TextChangingEvent::TextChangingEvent(int32 firstChangedParagraph, + int32 changedParagraphCount) + : + TextChangeEvent(firstChangedParagraph, changedParagraphCount) +{ +} + + +TextChangingEvent::TextChangingEvent() + : + TextChangeEvent() +{ +} + + +TextChangingEvent::~TextChangingEvent() +{ +} + + +void +TextChangingEvent::Cancel() +{ + fIsCanceled = true; +} + + +// #pragma mark - TextChangedEvent + + +TextChangedEvent::TextChangedEvent(int32 firstChangedParagraph, + int32 changedParagraphCount) + : + TextChangeEvent(firstChangedParagraph, changedParagraphCount) +{ +} + + +TextChangedEvent::TextChangedEvent() + : + TextChangeEvent() +{ +} + + +TextChangedEvent::~TextChangedEvent() +{ +} + + +// #pragma mark - TextListener + + +TextListener::TextListener() + : + BReferenceable() +{ +} + + +TextListener::~TextListener() +{ +} + + +void +TextListener::TextChanging(TextChangingEvent& event) +{ +} + + +void +TextListener::TextChanged(TextChangedEvent& event) +{ +} diff --git a/src/apps/haiku-depot/textview/TextListener.h b/src/apps/haiku-depot/textview/TextListener.h new file mode 100644 index 0000000..a3f8993 --- /dev/null +++ b/src/apps/haiku-depot/textview/TextListener.h @@ -0,0 +1,71 @@ +/* + * Copyright 2014, Stephan Aßmus <superstippi@xxxxxx>. + * All rights reserved. Distributed under the terms of the MIT License. + */ +#ifndef TEXT_LISTENER_H +#define TEXT_LISTENER_H + + +#include <Referenceable.h> + + +class TextChangeEvent { +protected: + TextChangeEvent(); + TextChangeEvent(int32 firstChangedParagraph, + int32 changedParagraphCount); + virtual ~TextChangeEvent(); + +public: + int32 FirstChangedParagraph() const + { return fFirstChangedParagraph; } + int32 ChangedParagraphCount() const + { return fChangedParagraphCount; } + +private: + int32 fFirstChangedParagraph; + int32 fChangedParagraphCount; +}; + + +class TextChangingEvent : public TextChangeEvent { +public: + TextChangingEvent(int32 firstChangedParagraph, + int32 changedParagraphCount); + virtual ~TextChangingEvent(); + + void Cancel(); + bool IsCanceled() const + { return fIsCanceled; } + +private: + TextChangingEvent(); + +private: + bool fIsCanceled; +}; + + +class TextChangedEvent : public TextChangeEvent { +public: + TextChangedEvent(int32 firstChangedParagraph, + int32 changedParagraphCount); + virtual ~TextChangedEvent(); + +private: + TextChangedEvent(); +}; + + +class TextListener : public BReferenceable { +public: + TextListener(); + virtual ~TextListener(); + + virtual void TextChanging(TextChangingEvent& event); + + virtual void TextChanged(TextChangedEvent& event); +}; + + +#endif // TEXT_LISTENER_H ############################################################################ Commit: b7e8a244416945ce8850b3ff148289607c1ee31f URL: http://cgit.haiku-os.org/haiku/commit/?id=b7e8a24 Author: Stephan Aßmus <superstippi@xxxxxx> Date: Sun Jan 19 14:51:45 2014 UTC Paragraph: Added Remove(), untested. ---------------------------------------------------------------------------- diff --git a/src/apps/haiku-depot/textview/Paragraph.cpp b/src/apps/haiku-depot/textview/Paragraph.cpp index df96825..2c4f401 100644 --- a/src/apps/haiku-depot/textview/Paragraph.cpp +++ b/src/apps/haiku-depot/textview/Paragraph.cpp @@ -127,6 +127,55 @@ Paragraph::Insert(int32 offset, const TextSpan& newSpan) } +bool +Paragraph::Remove(int32 offset, int32 length) +{ + if (length == 0) + return true; + + int32 index = 0; + while (index < fTextSpans.CountItems()) { + const TextSpan& span = fTextSpans.ItemAtFast(index); + if (offset - span.CharCount() < 0) + break; + offset -= span.CharCount(); + } + + if (index >= fTextSpans.CountItems()) + return false; + + TextSpan span = fTextSpans.ItemAtFast(index).SubSpan(0, offset); + fTextSpans.Replace(index, span); + index += 1; + if (index >= fTextSpans.CountItems()) + return true; + + while (length > 0) { + int32 spanLength = fTextSpans.ItemAtFast(index).CharCount(); + if (spanLength <= length) { + fTextSpans.Remove(index); + length -= spanLength; + } else { + // Reached last span + int32 removeLength = std::min(length, spanLength); + TextSpan lastSpan = fTextSpans.ItemAtFast(index).SubSpan( + removeLength, spanLength - removeLength); + // Try to merge with first span, otherwise replace span at index + if (lastSpan.Style() == span.Style()) { + span.Insert(span.CharCount(), lastSpan.Text()); + fTextSpans.Replace(index - 1, span); + } else { + fTextSpans.Replace(index, lastSpan); + } + + break; + } + } + + return true; +} + + void Paragraph::Clear() { diff --git a/src/apps/haiku-depot/textview/Paragraph.h b/src/apps/haiku-depot/textview/Paragraph.h index 98d4fd1..64ca2cb 100644 --- a/src/apps/haiku-depot/textview/Paragraph.h +++ b/src/apps/haiku-depot/textview/Paragraph.h @@ -32,6 +32,7 @@ public: bool Append(const TextSpan& span); bool Insert(int32 offset, const TextSpan& span); + bool Remove(int32 offset, int32 length); void Clear(); int32 Length() const; ############################################################################ Commit: 13d17b5b011c8ed52f2c2ec70fbebb7bc8f68c02 URL: http://cgit.haiku-os.org/haiku/commit/?id=13d17b5 Author: Stephan Aßmus <superstippi@xxxxxx> Date: Sun Jan 19 14:52:47 2014 UTC TextDocument: Implemented Remove(), untested. Also some refactoring for ParagraphAt() into a separate ParagraphIndexFor(), used by the former, since often an index is needed. ---------------------------------------------------------------------------- diff --git a/src/apps/haiku-depot/textview/TextDocument.cpp b/src/apps/haiku-depot/textview/TextDocument.cpp index c86e86f..6eba25c 100644 --- a/src/apps/haiku-depot/textview/TextDocument.cpp +++ b/src/apps/haiku-depot/textview/TextDocument.cpp @@ -95,10 +95,69 @@ TextDocument::Insert(int32 offset, const BString& text, status_t -TextDocument::Remove(int32 offset, int32 length) +TextDocument::Remove(int32 textOffset, int32 length) { - // TODO: Implement - return B_ERROR; + if (length == 0) + return B_OK; + + int32 paragraphOffset; + int32 index = ParagraphIndexFor(textOffset, paragraphOffset); + if (index < 0) + return B_BAD_VALUE; + + textOffset -= paragraphOffset; + + // The paragraph at the text offset remains, even if the offset is at + // the beginning of that paragraph. The idea is that the selection start + // stays visually in the same place. Therefore, the paragraph at that + // offset has to keep the paragraph style from that paragraph. + + Paragraph resultParagraph(ParagraphAt(index)); + int32 paragraphLength = resultParagraph.Length(); + if (textOffset == 0 && length > paragraphLength) { + length -= paragraphLength; + resultParagraph.Clear(); + } else { + int32 removeLength = std::min(length, paragraphLength - textOffset); + resultParagraph.Remove(textOffset, removeLength); + length -= removeLength; + } + + textOffset = 0; + + while (length > 0) { + const Paragraph& paragraph = ParagraphAt(index + 1); + paragraphLength = paragraph.Length(); + // Remove paragraph in any case. If some of it remains, the last + // paragraph to remove is reached, and the remaining spans are + // transfered to the result parahraph. + if (length >= paragraphLength) { + length -= paragraphLength; + fParagraphs.Remove(index); + } else { + // Last paragraph reached + int32 removedLength = std::min(length, paragraphLength); + Paragraph newParagraph(paragraph); + fParagraphs.Remove(index); + + if (!newParagraph.Remove(0, removedLength)) + return B_NO_MEMORY; + + // Transfer remaining spans to resultParagraph + const TextSpanList& textSpans = newParagraph.TextSpans(); + int32 spanCount = textSpans.CountItems(); + for (int32 i = 0; i < spanCount; i++) { + const TextSpan& span = textSpans.ItemAtFast(i); + resultParagraph.Append(span); + } + + break; + } + } + + fParagraphs.Replace(index, resultParagraph); + + return B_OK; } @@ -124,7 +183,12 @@ status_t TextDocument::Replace(int32 offset, int32 length, const BString& text, const CharacterStyle& CharacterStyle, const ParagraphStyle& paragraphStyle) { - // TODO: Implement + status_t ret = Remove(offset, length); + if (ret != B_OK) + return ret; + + // TODO: ... + return B_ERROR; } @@ -164,8 +228,8 @@ TextDocument::ParagraphStyleAt(int32 textOffset) const // #pragma mark - -const Paragraph& -TextDocument::ParagraphAt(int32 textOffset, int32& paragraphOffset) const +int32 +TextDocument::ParagraphIndexFor(int32 textOffset, int32& paragraphOffset) const { // TODO: Could binary search the Paragraphs if they were wrapped in classes // that knew there text offset in the document. @@ -177,9 +241,20 @@ TextDocument::ParagraphAt(int32 textOffset, int32& paragraphOffset) const paragraphOffset = textOffset - textLength; int32 paragraphLength = paragraph.Length(); if (textLength + paragraphLength > textOffset) - return paragraph; + return i; textLength += paragraphLength; } + return -1; +} + + +const Paragraph& +TextDocument::ParagraphAt(int32 textOffset, int32& paragraphOffset) const +{ + int32 index = ParagraphIndexFor(textOffset, paragraphOffset); + if (index >= 0) + return fParagraphs.ItemAtFast(index); + return fEmptyLastParagraph; } diff --git a/src/apps/haiku-depot/textview/TextDocument.h b/src/apps/haiku-depot/textview/TextDocument.h index 6f385f8..16154f1 100644 --- a/src/apps/haiku-depot/textview/TextDocument.h +++ b/src/apps/haiku-depot/textview/TextDocument.h @@ -55,6 +55,9 @@ public: const ParagraphList& Paragraphs() const { return fParagraphs; } + int32 ParagraphIndexFor(int32 textOffset, + int32& paragraphOffset) const; + const Paragraph& ParagraphAt(int32 textOffset, int32& paragraphOffset) const; ############################################################################ Commit: 315dee6f229823c9b926157296d76df59d4b3bb3 URL: http://cgit.haiku-os.org/haiku/commit/?id=315dee6 Author: Stephan Aßmus <superstippi@xxxxxx> Date: Sun Jan 19 14:54:45 2014 UTC TextEditor: Resolve TODO and call Insert() and Remove()... ... which both already exist in the TextDocument API (stubbed out or untested as they are). ---------------------------------------------------------------------------- diff --git a/src/apps/haiku-depot/textview/TextEditor.cpp b/src/apps/haiku-depot/textview/TextEditor.cpp index ae5948c..ec034a4 100644 --- a/src/apps/haiku-depot/textview/TextEditor.cpp +++ b/src/apps/haiku-depot/textview/TextEditor.cpp @@ -237,23 +237,23 @@ TextEditor::KeyDown(KeyEvent event) } -void +status_t TextEditor::Insert(int32 offset, const BString& string) { - if (!fEditingEnabled) - return; + if (!fEditingEnabled || fDocument.Get() == NULL) + return B_ERROR; - // TODO: ... + return fDocument->Insert(offset, string, fStyleAtCaret); } -void +status_t TextEditor::Remove(int32 offset, int32 length) { - if (!fEditingEnabled) - return; + if (!fEditingEnabled || fDocument.Get() == NULL) + return B_ERROR; - // TODO: ... + return fDocument->Remove(offset, length); } diff --git a/src/apps/haiku-depot/textview/TextEditor.h b/src/apps/haiku-depot/textview/TextEditor.h index 89bdb47..12c236c 100644 --- a/src/apps/haiku-depot/textview/TextEditor.h +++ b/src/apps/haiku-depot/textview/TextEditor.h @@ -58,8 +58,8 @@ public: virtual void KeyDown(KeyEvent event); - virtual void Insert(int32 offset, const BString& string); - virtual void Remove(int32 offset, int32 length); + virtual status_t Insert(int32 offset, const BString& string); + virtual status_t Remove(int32 offset, int32 length); void LineUp(bool select); void LineDown(bool select); @@ -84,6 +84,7 @@ private: bool updateSelectionStyle); void _UpdateStyleAtCaret(); + private: TextDocumentRef fDocument; TextDocumentLayoutRef fLayout; ############################################################################ Revision: hrev46710 Commit: 2c906df2174c1d0c8befc67cb0b61e789dd2eb82 URL: http://cgit.haiku-os.org/haiku/commit/?id=2c906df Author: Stephan Aßmus <superstippi@xxxxxx> Date: Sun Jan 19 15:33:36 2014 UTC Text stuff: Bugfixes in Insert and Remove methods Still not well tested. Also some way to trigger the layouts to adopt to the changed document paragraphs. Not yet via the new TextListener. ---------------------------------------------------------------------------- diff --git a/src/apps/haiku-depot/textview/Paragraph.cpp b/src/apps/haiku-depot/textview/Paragraph.cpp index 2c4f401..d3b8a16 100644 --- a/src/apps/haiku-depot/textview/Paragraph.cpp +++ b/src/apps/haiku-depot/textview/Paragraph.cpp @@ -92,6 +92,7 @@ Paragraph::Insert(int32 offset, const TextSpan& newSpan) if (offset - span.CharCount() < 0) break; offset -= span.CharCount(); + index++; } if (fTextSpans.CountItems() == index) @@ -139,25 +140,28 @@ Paragraph::Remove(int32 offset, int32 length) if (offset - span.CharCount() < 0) break; offset -= span.CharCount(); + index++; } if (index >= fTextSpans.CountItems()) return false; - - TextSpan span = fTextSpans.ItemAtFast(index).SubSpan(0, offset); + + TextSpan span(fTextSpans.ItemAtFast(index)); + int32 removeLength = std::min(span.CharCount() - offset, length); + span.Remove(offset, removeLength); fTextSpans.Replace(index, span); + length -= removeLength; index += 1; - if (index >= fTextSpans.CountItems()) - return true; - - while (length > 0) { + + // Remove more spans if necessary + while (length > 0 && index < fTextSpans.CountItems()) { int32 spanLength = fTextSpans.ItemAtFast(index).CharCount(); if (spanLength <= length) { fTextSpans.Remove(index); length -= spanLength; } else { // Reached last span - int32 removeLength = std::min(length, spanLength); + removeLength = std::min(length, spanLength); TextSpan lastSpan = fTextSpans.ItemAtFast(index).SubSpan( removeLength, spanLength - removeLength); // Try to merge with first span, otherwise replace span at index @@ -172,6 +176,10 @@ Paragraph::Remove(int32 offset, int32 length) } } + // See if anything from the TextSpan at offset remained + if (span.CharCount() == 0) + fTextSpans.Remove(index - 1); + return true; } diff --git a/src/apps/haiku-depot/textview/TextDocument.cpp b/src/apps/haiku-depot/textview/TextDocument.cpp index 6eba25c..0204381 100644 --- a/src/apps/haiku-depot/textview/TextDocument.cpp +++ b/src/apps/haiku-depot/textview/TextDocument.cpp @@ -125,7 +125,7 @@ TextDocument::Remove(int32 textOffset, int32 length) textOffset = 0; - while (length > 0) { + while (length > 0 && index + 1 < fParagraphs.CountItems()) { const Paragraph& paragraph = ParagraphAt(index + 1); paragraphLength = paragraph.Length(); // Remove paragraph in any case. If some of it remains, the last @@ -238,10 +238,10 @@ TextDocument::ParagraphIndexFor(int32 textOffset, int32& paragraphOffset) const int32 count = fParagraphs.CountItems(); for (int32 i = 0; i < count; i++) { const Paragraph& paragraph = fParagraphs.ItemAtFast(i); - paragraphOffset = textOffset - textLength; int32 paragraphLength = paragraph.Length(); if (textLength + paragraphLength > textOffset) return i; + paragraphOffset += paragraphLength; textLength += paragraphLength; } return -1; diff --git a/src/apps/haiku-depot/textview/TextDocumentLayout.cpp b/src/apps/haiku-depot/textview/TextDocumentLayout.cpp index afea2f6..cc98029 100644 --- a/src/apps/haiku-depot/textview/TextDocumentLayout.cpp +++ b/src/apps/haiku-depot/textview/TextDocumentLayout.cpp @@ -62,6 +62,34 @@ TextDocumentLayout::SetTextDocument(const TextDocumentRef& document) void +TextDocumentLayout::Invalidate() +{ + InvalidateParagraphs(0, fParagraphLayouts.CountItems()); +} + + +void +TextDocumentLayout::InvalidateParagraphs(int32 start, int32 count) +{ + if (start < 0 || fDocument.Get() == NULL) + return; + + const ParagraphList& paragraphs = fDocument->Paragraphs(); + + while (count > 0) { + if (start >= fParagraphLayouts.CountItems()) + break; + + const Paragraph& paragraph = paragraphs.ItemAtFast(start); + const ParagraphLayoutInfo& info = fParagraphLayouts.ItemAtFast(start); + info.layout->SetParagraph(paragraph); + + start++; + } +} + + +void TextDocumentLayout::SetWidth(float width) { if (fWidth != width) { diff --git a/src/apps/haiku-depot/textview/TextDocumentLayout.h b/src/apps/haiku-depot/textview/TextDocumentLayout.h index 8d43958..2e3df4b 100644 --- a/src/apps/haiku-depot/textview/TextDocumentLayout.h +++ b/src/apps/haiku-depot/textview/TextDocumentLayout.h @@ -78,6 +78,9 @@ public: void SetTextDocument( const TextDocumentRef& document); + void Invalidate(); + void InvalidateParagraphs(int32 start, int32 count); + void SetWidth(float width); float Width() const { return fWidth; } diff --git a/src/apps/haiku-depot/textview/TextEditor.cpp b/src/apps/haiku-depot/textview/TextEditor.cpp index ec034a4..2c803e9 100644 --- a/src/apps/haiku-depot/textview/TextEditor.cpp +++ b/src/apps/haiku-depot/textview/TextEditor.cpp @@ -243,6 +243,9 @@ TextEditor::Insert(int32 offset, const BString& string) if (!fEditingEnabled || fDocument.Get() == NULL) return B_ERROR; + // TODO: Via listener, and only affected paragraphs + fLayout->Invalidate(); + return fDocument->Insert(offset, string, fStyleAtCaret); } @@ -253,6 +256,9 @@ TextEditor::Remove(int32 offset, int32 length) if (!fEditingEnabled || fDocument.Get() == NULL) return B_ERROR; + // TODO: Via listener, and only affected paragraphs + fLayout->Invalidate(); + return fDocument->Remove(offset, length); }