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
This commit is contained in:
@@ -1144,7 +1144,7 @@ BTextView::SetText(const char *inText, int32 inLength,
|
|||||||
|
|
||||||
// remove data from buffer
|
// remove data from buffer
|
||||||
if (fText->Length() > 0)
|
if (fText->Length() > 0)
|
||||||
DeleteText(0, fText->Length()); // TODO: was fText->Length() - 1
|
DeleteText(0, fText->Length());
|
||||||
|
|
||||||
if (inText != NULL && inLength > 0)
|
if (inText != NULL && inLength > 0)
|
||||||
InsertText(inText, inLength, 0, inRuns);
|
InsertText(inText, inLength, 0, inRuns);
|
||||||
@@ -1944,6 +1944,9 @@ BTextView::FindWord(int32 inOffset, int32 *outFromOffset, int32 *outToOffset)
|
|||||||
bool
|
bool
|
||||||
BTextView::CanEndLine(int32 offset)
|
BTextView::CanEndLine(int32 offset)
|
||||||
{
|
{
|
||||||
|
if (offset < 0 || offset > fText->Length())
|
||||||
|
return false;
|
||||||
|
|
||||||
// TODO: This should be improved using the LocaleKit.
|
// TODO: This should be improved using the LocaleKit.
|
||||||
uint32 classification = _CharClassification(offset);
|
uint32 classification = _CharClassification(offset);
|
||||||
|
|
||||||
@@ -1961,6 +1964,13 @@ BTextView::CanEndLine(int32 offset)
|
|||||||
return false;
|
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
|
// allow wrapping after whitespace, unless more whitespace (except for
|
||||||
// newline) follows
|
// newline) follows
|
||||||
if (classification == CHAR_CLASS_WHITESPACE
|
if (classification == CHAR_CLASS_WHITESPACE
|
||||||
@@ -3869,15 +3879,13 @@ BTextView::_FindLineBreak(int32 fromOffset, float *outAscent, float *outDescent,
|
|||||||
float descent = 0.0;
|
float descent = 0.0;
|
||||||
int32 delta = 0;
|
int32 delta = 0;
|
||||||
float deltaWidth = 0.0;
|
float deltaWidth = 0.0;
|
||||||
float tabWidth = 0.0;
|
|
||||||
float strWidth = 0.0;
|
float strWidth = 0.0;
|
||||||
uchar theChar;
|
uchar theChar;
|
||||||
|
|
||||||
// wrap the text
|
// wrap the text
|
||||||
do {
|
while (offset < limit && !done) {
|
||||||
bool foundTab = false;
|
|
||||||
// find the next line break candidate
|
// find the next line break candidate
|
||||||
for ( ; (offset + delta) < limit ; delta++) {
|
for (; (offset + delta) < limit; delta++) {
|
||||||
if (CanEndLine(offset + delta)) {
|
if (CanEndLine(offset + delta)) {
|
||||||
theChar = fText->RealCharAt(offset + delta);
|
theChar = fText->RealCharAt(offset + delta);
|
||||||
if (theChar != B_SPACE && theChar != B_TAB
|
if (theChar != B_SPACE && theChar != B_TAB
|
||||||
@@ -3890,11 +3898,10 @@ BTextView::_FindLineBreak(int32 fromOffset, float *outAscent, float *outDescent,
|
|||||||
break;
|
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);
|
theChar = fText->RealCharAt(offset + delta);
|
||||||
if (theChar == B_ENTER) {
|
if (theChar == B_ENTER) {
|
||||||
// found a newline, we're done!
|
// 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) {
|
} else if (theChar != B_SPACE && theChar != B_TAB) {
|
||||||
// stop at anything else than trailing whitespace
|
// stop at anything else than trailing whitespace
|
||||||
break;
|
break;
|
||||||
} else {
|
|
||||||
// include all trailing spaces and tabs,
|
|
||||||
// but not spaces after tabs
|
|
||||||
if (theChar == B_TAB)
|
|
||||||
foundTab = true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
delta = max_c(delta, 1);
|
delta = max_c(delta, 1);
|
||||||
|
|
||||||
deltaWidth = _StyledWidth(offset, delta, &ascent, &descent);
|
deltaWidth = _TabExpandedStyledWidth(offset, delta, &ascent, &descent);
|
||||||
strWidth += deltaWidth;
|
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) {
|
if (strWidth >= *inOutWidth) {
|
||||||
// we've found where the line will wrap
|
// we've found where the line will wrap
|
||||||
bool foundNewline = done;
|
|
||||||
done = true;
|
done = true;
|
||||||
|
|
||||||
// we have included trailing whitespace in the width computation
|
// we have included trailing whitespace in the width computation
|
||||||
// above, but that is not being shown anyway, so we try again
|
// above, but that is not being shown anyway, so we try again
|
||||||
// without the trailing whitespace
|
// without the trailing whitespace
|
||||||
int32 pos = delta - 1;
|
if (delta == deltaBeforeWhitespace) {
|
||||||
theChar = fText->RealCharAt(offset + pos);
|
|
||||||
if (theChar != B_SPACE && theChar != B_TAB && theChar != B_ENTER) {
|
|
||||||
// there is no trailing whitespace, no point in trying
|
// there is no trailing whitespace, no point in trying
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// reset string width to start of current run ...
|
// reset string width to start of current run ...
|
||||||
strWidth -= (deltaWidth + tabWidth);
|
strWidth -= deltaWidth;
|
||||||
// ... 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--;
|
|
||||||
}
|
|
||||||
// ... and compute the resulting width (of visible characters)
|
// ... and compute the resulting width (of visible characters)
|
||||||
strWidth += _StyledWidth(offset, pos + 1, &ascent, &descent);
|
strWidth += _StyledWidth(offset, deltaBeforeWhitespace, NULL, NULL);
|
||||||
if (strWidth >= *inOutWidth) {
|
if (strWidth >= *inOutWidth) {
|
||||||
// width of visible characters exceeds line, we need to wrap
|
// width of visible characters exceeds line, we need to wrap
|
||||||
// before the current "word"
|
// before the current "word"
|
||||||
break;
|
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);
|
*outAscent = max_c(ascent, *outAscent);
|
||||||
@@ -3990,8 +3948,7 @@ BTextView::_FindLineBreak(int32 fromOffset, float *outAscent, float *outDescent,
|
|||||||
|
|
||||||
offset += delta;
|
offset += delta;
|
||||||
delta = 0;
|
delta = 0;
|
||||||
|
}
|
||||||
} while (offset < limit && !done);
|
|
||||||
|
|
||||||
if (offset - fromOffset < 1) {
|
if (offset - fromOffset < 1) {
|
||||||
// there weren't any words that fit entirely in this line
|
// there weren't any words that fit entirely in this line
|
||||||
|
|||||||
@@ -129,21 +129,22 @@ TextGapBuffer::MoveGapTo(int32 toIndex)
|
|||||||
{
|
{
|
||||||
if (toIndex == fGapIndex)
|
if (toIndex == fGapIndex)
|
||||||
return;
|
return;
|
||||||
|
if (toIndex > fItemCount) {
|
||||||
|
debugger("MoveGapTo: invalid toIndex supplied");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
long gapEndIndex = fGapIndex + fGapCount;
|
int32 srcIndex = 0;
|
||||||
long srcIndex = 0;
|
int32 dstIndex = 0;
|
||||||
long dstIndex = 0;
|
int32 count = 0;
|
||||||
long count = 0;
|
|
||||||
if (toIndex > fGapIndex) {
|
if (toIndex > fGapIndex) {
|
||||||
long trailGapCount = fBufferCount - gapEndIndex;
|
srcIndex = fGapIndex + fGapCount;
|
||||||
srcIndex = toIndex + (gapEndIndex - toIndex);
|
dstIndex = fGapIndex;
|
||||||
dstIndex = fGapIndex;
|
count = toIndex - fGapIndex;
|
||||||
count = fGapCount + (toIndex - srcIndex);
|
|
||||||
count = (count > trailGapCount) ? trailGapCount : count;
|
|
||||||
} else {
|
} else {
|
||||||
srcIndex = toIndex;
|
srcIndex = toIndex;
|
||||||
dstIndex = toIndex + (gapEndIndex - fGapIndex);
|
dstIndex = toIndex + fGapCount;
|
||||||
count = gapEndIndex - dstIndex;
|
count = fGapIndex- toIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (count > 0)
|
if (count > 0)
|
||||||
|
|||||||
@@ -33,11 +33,11 @@ virtual ~TextGapBuffer();
|
|||||||
|
|
||||||
bool FindChar(char inChar, int32 fromIndex, int32 *ioDelta);
|
bool FindChar(char inChar, int32 fromIndex, int32 *ioDelta);
|
||||||
|
|
||||||
const char *Text();
|
const char *Text();
|
||||||
const char *RealText();
|
const char *RealText();
|
||||||
int32 Length() const;
|
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);
|
void GetString(int32 offset, int32 length, char *buffer);
|
||||||
|
|
||||||
char RealCharAt(int32 offset) const;
|
char RealCharAt(int32 offset) const;
|
||||||
@@ -50,11 +50,11 @@ virtual ~TextGapBuffer();
|
|||||||
protected:
|
protected:
|
||||||
int32 fExtraCount; // when realloc()-ing
|
int32 fExtraCount; // when realloc()-ing
|
||||||
int32 fItemCount; // logical count
|
int32 fItemCount; // logical count
|
||||||
char *fBuffer; // allocated memory
|
char * fBuffer; // allocated memory
|
||||||
int32 fBufferCount; // physical count
|
int32 fBufferCount; // physical count
|
||||||
int32 fGapIndex; // gap position
|
int32 fGapIndex; // gap position
|
||||||
int32 fGapCount; // gap count
|
int32 fGapCount; // gap count
|
||||||
char *fScratchBuffer; // for GetString
|
char * fScratchBuffer; // for GetString
|
||||||
int32 fScratchSize; // scratch size
|
int32 fScratchSize; // scratch size
|
||||||
bool fPasswordMode;
|
bool fPasswordMode;
|
||||||
};
|
};
|
||||||
@@ -68,9 +68,15 @@ TextGapBuffer::Length() const
|
|||||||
|
|
||||||
|
|
||||||
inline char
|
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
|
} // namespace BPrivate
|
||||||
|
|||||||
Reference in New Issue
Block a user