From 5c4e7641a732891a776f88c3c46b237de9220f88 Mon Sep 17 00:00:00 2001 From: Michael Lotz Date: Thu, 15 Apr 2010 16:12:16 +0000 Subject: [PATCH] zooey+mmlr: * Large cleanup in _FindLineBreak(), be a bit more clever by storing the found offsets instead of calculating them multiple times, use _TabExpandedStyledWidth() instead of _StyledWidth() and manual tab calculation which was also broken (it assumed tabs were consequtive which was possibly not the case). * Modified CanEndLine() to also return true when going from non-whitespace to whitespace and the other way around which is more logical (so we'd break after the word and not after word + whitespace, even though we actually do that in the end by eating whitespace after words in _FindLineBreak()). * The TextGapBuffer is not necessarily 0 terminated, so RealCharAt() needs to check if the index is at the end of the string and return 0 in that case. Before it would access the buffer "out by one" which could've lead to a crash. * Simplified the gap moving calculations by removing some no-op calculations. * Added debugger calls in TextGapBuffer in case of invalid use. * Some minor cleanup. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@36304 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- src/kits/interface/TextView.cpp | 89 +++++-------------- .../textview_support/TextGapBuffer.cpp | 23 ++--- .../textview_support/TextGapBuffer.h | 20 +++-- 3 files changed, 48 insertions(+), 84 deletions(-) diff --git a/src/kits/interface/TextView.cpp b/src/kits/interface/TextView.cpp index 2750541100..5dcdaa502a 100644 --- a/src/kits/interface/TextView.cpp +++ b/src/kits/interface/TextView.cpp @@ -1144,7 +1144,7 @@ BTextView::SetText(const char *inText, int32 inLength, // remove data from buffer if (fText->Length() > 0) - DeleteText(0, fText->Length()); // TODO: was fText->Length() - 1 + DeleteText(0, fText->Length()); if (inText != NULL && inLength > 0) InsertText(inText, inLength, 0, inRuns); @@ -1944,6 +1944,9 @@ BTextView::FindWord(int32 inOffset, int32 *outFromOffset, int32 *outToOffset) bool BTextView::CanEndLine(int32 offset) { + if (offset < 0 || offset > fText->Length()) + return false; + // TODO: This should be improved using the LocaleKit. uint32 classification = _CharClassification(offset); @@ -1961,6 +1964,13 @@ BTextView::CanEndLine(int32 offset) return false; } + if ((classification == CHAR_CLASS_WHITESPACE + && nextClassification != CHAR_CLASS_WHITESPACE) + || (classification != CHAR_CLASS_WHITESPACE + && nextClassification == CHAR_CLASS_WHITESPACE)) { + return true; + } + // allow wrapping after whitespace, unless more whitespace (except for // newline) follows if (classification == CHAR_CLASS_WHITESPACE @@ -3869,15 +3879,13 @@ BTextView::_FindLineBreak(int32 fromOffset, float *outAscent, float *outDescent, float descent = 0.0; int32 delta = 0; float deltaWidth = 0.0; - float tabWidth = 0.0; float strWidth = 0.0; uchar theChar; // wrap the text - do { - bool foundTab = false; + while (offset < limit && !done) { // find the next line break candidate - for ( ; (offset + delta) < limit ; delta++) { + for (; (offset + delta) < limit; delta++) { if (CanEndLine(offset + delta)) { theChar = fText->RealCharAt(offset + delta); if (theChar != B_SPACE && theChar != B_TAB @@ -3890,11 +3898,10 @@ BTextView::_FindLineBreak(int32 fromOffset, float *outAscent, float *outDescent, break; } } - // now skip over trailing whitespace, if any - for ( ; (offset + delta) < limit; delta++) { - if (!CanEndLine(offset + delta)) - break; + int32 deltaBeforeWhitespace = delta; + // now skip over trailing whitespace, if any + for (; (offset + delta) < limit; delta++) { theChar = fText->RealCharAt(offset + delta); if (theChar == B_ENTER) { // found a newline, we're done! @@ -3904,85 +3911,36 @@ BTextView::_FindLineBreak(int32 fromOffset, float *outAscent, float *outDescent, } else if (theChar != B_SPACE && theChar != B_TAB) { // stop at anything else than trailing whitespace break; - } else { - // include all trailing spaces and tabs, - // but not spaces after tabs - if (theChar == B_TAB) - foundTab = true; } } + delta = max_c(delta, 1); - deltaWidth = _StyledWidth(offset, delta, &ascent, &descent); + deltaWidth = _TabExpandedStyledWidth(offset, delta, &ascent, &descent); strWidth += deltaWidth; - if (!foundTab) - tabWidth = 0.0; - else { - int32 tabCount = 0; - for (int32 i = delta - 1; fText->RealCharAt(offset + i) == B_TAB; - i--) { - tabCount++; - } - - tabWidth = _ActualTabWidth(strWidth); - if (tabCount > 1) - tabWidth += ((tabCount - 1) * fTabWidth); - strWidth += tabWidth; - } - if (strWidth >= *inOutWidth) { // we've found where the line will wrap - bool foundNewline = done; done = true; // we have included trailing whitespace in the width computation // above, but that is not being shown anyway, so we try again // without the trailing whitespace - int32 pos = delta - 1; - theChar = fText->RealCharAt(offset + pos); - if (theChar != B_SPACE && theChar != B_TAB && theChar != B_ENTER) { + if (delta == deltaBeforeWhitespace) { // there is no trailing whitespace, no point in trying break; } // reset string width to start of current run ... - strWidth -= (deltaWidth + tabWidth); - // ... skip back all trailing whitespace ... - while (offset + pos > offset) { - theChar = fText->RealCharAt(offset + pos); - if (theChar != B_SPACE && theChar != B_TAB - && theChar != B_ENTER) - break; - pos--; - } + strWidth -= deltaWidth; + // ... and compute the resulting width (of visible characters) - strWidth += _StyledWidth(offset, pos + 1, &ascent, &descent); + strWidth += _StyledWidth(offset, deltaBeforeWhitespace, NULL, NULL); if (strWidth >= *inOutWidth) { // width of visible characters exceeds line, we need to wrap // before the current "word" break; } - - // we can include the current "word" on this line, but we need - // to eat the trailing whitespace, such that we do not encounter - // it again during the next run - // TODO: can this ever be entered? - if (!foundNewline) { - while (offset + delta < limit) { - const char realChar = fText->RealCharAt(offset + delta); - if (realChar != B_SPACE && realChar != B_TAB) - break; - - delta++; - } - if (offset + delta < limit - && fText->RealCharAt(offset + delta) == B_ENTER) - delta++; - } - // get the ascent and descent of the current word, including all - // trailing whitespace - _StyledWidth(offset, delta, &ascent, &descent); } *outAscent = max_c(ascent, *outAscent); @@ -3990,8 +3948,7 @@ BTextView::_FindLineBreak(int32 fromOffset, float *outAscent, float *outDescent, offset += delta; delta = 0; - - } while (offset < limit && !done); + } if (offset - fromOffset < 1) { // there weren't any words that fit entirely in this line diff --git a/src/kits/interface/textview_support/TextGapBuffer.cpp b/src/kits/interface/textview_support/TextGapBuffer.cpp index d36ff7475e..683cb90ba7 100644 --- a/src/kits/interface/textview_support/TextGapBuffer.cpp +++ b/src/kits/interface/textview_support/TextGapBuffer.cpp @@ -129,21 +129,22 @@ TextGapBuffer::MoveGapTo(int32 toIndex) { if (toIndex == fGapIndex) return; + if (toIndex > fItemCount) { + debugger("MoveGapTo: invalid toIndex supplied"); + return; + } - long gapEndIndex = fGapIndex + fGapCount; - long srcIndex = 0; - long dstIndex = 0; - long count = 0; + int32 srcIndex = 0; + int32 dstIndex = 0; + int32 count = 0; if (toIndex > fGapIndex) { - long trailGapCount = fBufferCount - gapEndIndex; - srcIndex = toIndex + (gapEndIndex - toIndex); - dstIndex = fGapIndex; - count = fGapCount + (toIndex - srcIndex); - count = (count > trailGapCount) ? trailGapCount : count; + srcIndex = fGapIndex + fGapCount; + dstIndex = fGapIndex; + count = toIndex - fGapIndex; } else { srcIndex = toIndex; - dstIndex = toIndex + (gapEndIndex - fGapIndex); - count = gapEndIndex - dstIndex; + dstIndex = toIndex + fGapCount; + count = fGapIndex- toIndex; } if (count > 0) diff --git a/src/kits/interface/textview_support/TextGapBuffer.h b/src/kits/interface/textview_support/TextGapBuffer.h index 8f4702f4f7..38f575c534 100644 --- a/src/kits/interface/textview_support/TextGapBuffer.h +++ b/src/kits/interface/textview_support/TextGapBuffer.h @@ -33,11 +33,11 @@ virtual ~TextGapBuffer(); bool FindChar(char inChar, int32 fromIndex, int32 *ioDelta); - const char *Text(); - const char *RealText(); + const char *Text(); + const char *RealText(); int32 Length() const; - const char *GetString(int32 fromOffset, int32 *numBytes); + const char *GetString(int32 fromOffset, int32 *numBytes); void GetString(int32 offset, int32 length, char *buffer); char RealCharAt(int32 offset) const; @@ -50,11 +50,11 @@ virtual ~TextGapBuffer(); protected: int32 fExtraCount; // when realloc()-ing int32 fItemCount; // logical count - char *fBuffer; // allocated memory + char * fBuffer; // allocated memory int32 fBufferCount; // physical count int32 fGapIndex; // gap position int32 fGapCount; // gap count - char *fScratchBuffer; // for GetString + char * fScratchBuffer; // for GetString int32 fScratchSize; // scratch size bool fPasswordMode; }; @@ -68,9 +68,15 @@ TextGapBuffer::Length() const inline char -TextGapBuffer::RealCharAt(long index) const +TextGapBuffer::RealCharAt(int32 index) const { - return (index < fGapIndex) ? fBuffer[index] : fBuffer[index + fGapCount]; + if (index < 0 || index >= fItemCount) { + if (index != fItemCount) + debugger("RealCharAt: invalid index supplied"); + return 0; + } + + return index < fGapIndex ? fBuffer[index] : fBuffer[index + fGapCount]; } } // namespace BPrivate