continued work on BTextView:

* reverted my recent change that caused Mail to always show the end
  of mails 8-) and now invoke ScrollToOffset() wherever it was missing
  (mostly in keyboard-related methods)
* _DoInsertText() only moves the caret if the text has been inserted
  in front of the caret, not after it
* overhauled selection by char/word/line and their extension mechanism
  (which now behaves as the haiku shell)
* use strnlen() instead of strlen() in those versions of Insert() which
  accept a length parameter, as strlen can potentially waste a lot of
  cycles skipping the whole string, when all we want is to clamp the length.
* cleanup (some of it automatic removal of trailing whitespace)

git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@29995 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Oliver Tappe
2009-04-07 15:29:23 +00:00
parent 28a650e94d
commit 3f3ab1625c
2 changed files with 342 additions and 308 deletions
+3
View File
@@ -303,6 +303,9 @@ private:
float* outAscent, float* outDescent, float* outAscent, float* outDescent,
float* ioWidth); float* ioWidth);
int32 _FindLeftWordBoundary(int32 offset);
int32 _FindRightWordBoundary(int32 offset);
float _StyledWidth(int32 fromOffset, int32 length, float _StyledWidth(int32 fromOffset, int32 length,
float* outAscent = NULL, float* outAscent = NULL,
float* outDescent = NULL) const; float* outDescent = NULL) const;
+98 -67
View File
@@ -553,11 +553,9 @@ BTextView::MouseDown(BPoint where)
bigtime_t clickTime = system_time(); bigtime_t clickTime = system_time();
bigtime_t clickSpeed = 0; bigtime_t clickSpeed = 0;
get_click_speed(&clickSpeed); get_click_speed(&clickSpeed);
bool multipleClick = false; bool multipleClick
if (clickTime - fClickTime < clickSpeed = clickTime - fClickTime < clickSpeed
&& fClickOffset == fTrackingMouse->clickOffset) { && fClickOffset == fTrackingMouse->clickOffset;
multipleClick = true;
}
fWhere = where; fWhere = where;
@@ -570,25 +568,29 @@ BTextView::MouseDown(BPoint where)
if (region.Contains(where)) { if (region.Contains(where)) {
// Setup things for dragging // Setup things for dragging
fTrackingMouse->selectionRect = region.Frame(); fTrackingMouse->selectionRect = region.Frame();
fClickCount = 1;
fClickTime = clickTime;
return; return;
} }
} }
if (multipleClick) { if (multipleClick) {
if (fClickCount > 1) { if (fClickCount > 3) {
fClickCount = 0; fClickCount = 0;
fClickTime = 0; fClickTime = 0;
} else { } else {
fClickCount = 2; fClickCount++;
fClickTime = clickTime; fClickTime = clickTime;
} }
} else { } else if (!fTrackingMouse->shiftDown) {
// skip multiple click handling if shift is pressed, as we want to keep
// the current selection state (char, word or line) and just extend the
// selection in that case
fClickOffset = fTrackingMouse->clickOffset; fClickOffset = fTrackingMouse->clickOffset;
fClickCount = 1; fClickCount = 1;
fClickTime = clickTime; fClickTime = clickTime;
// Deselect any previously selected text // Deselect any previously selected text
if (!fTrackingMouse->shiftDown)
Select(fTrackingMouse->clickOffset, fTrackingMouse->clickOffset); Select(fTrackingMouse->clickOffset, fTrackingMouse->clickOffset);
} }
@@ -1189,10 +1191,8 @@ void
BTextView::Insert(const char *inText, int32 inLength, BTextView::Insert(const char *inText, int32 inLength,
const text_run_array *inRuns) const text_run_array *inRuns)
{ {
if (inText != NULL && inLength > 0) { if (inText != NULL && inLength > 0)
int32 realLength = strlen(inText); _DoInsertText(inText, strnlen(inText, inLength), fSelStart, inRuns);
_DoInsertText(inText, min_c(inLength, realLength), fSelStart, inRuns);
}
} }
@@ -1200,13 +1200,8 @@ void
BTextView::Insert(int32 startOffset, const char *inText, int32 inLength, BTextView::Insert(int32 startOffset, const char *inText, int32 inLength,
const text_run_array *inRuns) const text_run_array *inRuns)
{ {
CALLED(); if (inText != NULL && inLength > 0)
_DoInsertText(inText, strnlen(inText, inLength), startOffset, inRuns);
// do we really need to do anything?
if (inText != NULL && inLength > 0) {
int32 realLength = strlen(inText);
_DoInsertText(inText, min_c(inLength, realLength), startOffset, inRuns);
}
} }
@@ -1250,7 +1245,7 @@ BTextView::Delete(int32 startOffset, int32 endOffset)
fSelEnd = fSelStart = fClickOffset; fSelEnd = fSelStart = fClickOffset;
// recalc line breaks and draw what's left // recalc line breaks and draw what's left
_Refresh(startOffset, endOffset, true, true); _Refresh(startOffset, endOffset, true, false);
// draw the caret // draw the caret
if (fActive) if (fActive)
@@ -1421,6 +1416,7 @@ BTextView::Paste(BClipboard *clipboard)
Delete(); Delete();
Insert(text, len, runArray); Insert(text, len, runArray);
ScrollToOffset(fSelEnd);
} }
} }
@@ -1539,7 +1535,7 @@ BTextView::Select(int32 startOffset, int32 endOffset)
} }
} }
fSelStart = startOffset; fSelStart = startOffset;
fSelEnd = fClickOffset = endOffset; fSelEnd = endOffset;
} }
} }
@@ -1632,7 +1628,7 @@ BTextView::SetFontAndColor(int32 startOffset, int32 endOffset,
} else { } else {
// the line breaks wont change, simply redraw // the line breaks wont change, simply redraw
_RequestDrawLines(LineAt(startOffset), LineAt(endOffset), startOffset, _RequestDrawLines(LineAt(startOffset), LineAt(endOffset), startOffset,
true); false);
} }
} }
@@ -1926,31 +1922,11 @@ BTextView::OffsetAt(int32 line) const
void void
BTextView::FindWord(int32 inOffset, int32 *outFromOffset, int32 *outToOffset) BTextView::FindWord(int32 inOffset, int32 *outFromOffset, int32 *outToOffset)
{ {
int32 offset;
uint32 charType = _CharClassification(inOffset);
// check to the left
int32 previous;
for (offset = inOffset, previous = offset; offset > 0;
previous = _PreviousInitialByte(offset)) {
if (_CharClassification(previous) != charType)
break;
offset = previous;
}
if (outFromOffset) if (outFromOffset)
*outFromOffset = offset; *outFromOffset = _FindLeftWordBoundary(inOffset);
// check to the right
int32 textLen = TextLength();
for (offset = inOffset; offset < textLen;
offset = _NextInitialByte(offset)) {
if (_CharClassification(offset) != charType)
break;
}
if (outToOffset) if (outToOffset)
*outToOffset = offset; *outToOffset = _FindRightWordBoundary(inOffset);
} }
@@ -2380,7 +2356,7 @@ BTextView::SetWordWrap(bool wrap)
} }
fWrap = wrap; fWrap = wrap;
_Refresh(0, fText->Length(), true, true); _Refresh(0, fText->Length(), true, false);
if (updateOnScreen) { if (updateOnScreen) {
// show the caret, hilite the selection // show the caret, hilite the selection
@@ -3556,7 +3532,7 @@ BTextView::_Refresh(int32 fromOffset, int32 toOffset, bool erase, bool scroll)
_UpdateScrollbars(); _UpdateScrollbars();
if (scroll) if (scroll)
ScrollToSelection(); ScrollToOffset(fSelEnd);
Flush(); Flush();
} }
@@ -3608,7 +3584,7 @@ BTextView::_RecalculateLineBreaks(int32 *startLine, int32 *endLine)
newLine.ascent = 0; newLine.ascent = 0;
fLines->InsertLine(&newLine, lineIndex); fLines->InsertLine(&newLine, lineIndex);
} else { } else {
// update the exising line // update the existing line
nextLine->offset = toOffset; nextLine->offset = toOffset;
nextLine->origin = ceilf(curLine->origin + ascent + descent) + 1; nextLine->origin = ceilf(curLine->origin + ascent + descent) + 1;
@@ -3824,6 +3800,37 @@ BTextView::_FindLineBreak(int32 fromOffset, float *outAscent, float *outDescent,
} }
int32
BTextView::_FindLeftWordBoundary(int32 offset)
{
uint32 charType = _CharClassification(offset);
int32 previous;
while (offset > 0) {
previous = _PreviousInitialByte(offset);
if (_CharClassification(previous) != charType)
break;
offset = previous;
}
return offset;
}
int32
BTextView::_FindRightWordBoundary(int32 offset)
{
uint32 charType = _CharClassification(offset);
int32 textLen = TextLength();
while (offset < textLen) {
offset = _NextInitialByte(offset);
if (_CharClassification(offset) != charType)
break;
}
return offset;
}
/*! \brief Calculate the width of the text within the given limits. /*! \brief Calculate the width of the text within the given limits.
\param fromOffset The offset where to start. \param fromOffset The offset where to start.
\param length The length of the text to examine. \param length The length of the text to examine.
@@ -3916,9 +3923,6 @@ BTextView::_DoInsertText(const char *inText, int32 inLength, int32 inOffset,
if (fSelStart != fSelEnd) if (fSelStart != fSelEnd)
Select(fSelStart, fSelStart); Select(fSelStart, fSelStart);
// Don't do any check, the public methods will have adjusted
// eventual bogus values...
const int32 textLength = TextLength(); const int32 textLength = TextLength();
if (inOffset > textLength) if (inOffset > textLength)
inOffset = textLength; inOffset = textLength;
@@ -3926,12 +3930,14 @@ BTextView::_DoInsertText(const char *inText, int32 inLength, int32 inOffset,
// copy data into buffer // copy data into buffer
InsertText(inText, inLength, inOffset, inRuns); InsertText(inText, inLength, inOffset, inRuns);
// offset the caret/selection // offset the caret/selection, if the text was inserted before it
if (inOffset < fSelEnd) {
fSelStart += inLength; fSelStart += inLength;
fClickOffset = fSelEnd = fSelStart; fClickOffset = fSelEnd = fSelStart;
}
// recalc line breaks and draw the text // recalc line breaks and draw the text
_Refresh(inOffset, inOffset + inLength, true, true); _Refresh(inOffset, inOffset + inLength, true, false);
} }
@@ -4305,32 +4311,57 @@ BTextView::_PerformMouseMoved(BPoint where, uint32 code)
return true; return true;
} }
int32 oldOffset = fTrackingMouse->anchor;
int32 currentOffset = OffsetAt(where); int32 currentOffset = OffsetAt(where);
switch (fClickCount) { switch (fClickCount) {
case 0: case 3:
// triple click, select line by line // triple click, extend selection linewise
if (currentOffset <= fTrackingMouse->anchor) {
fTrackingMouse->selStart fTrackingMouse->selStart
= (*fLines)[LineAt(fTrackingMouse->selStart)]->offset; = (*fLines)[LineAt(currentOffset)]->offset;
fTrackingMouse->selEnd fTrackingMouse->selEnd
= (*fLines)[LineAt(fTrackingMouse->selEnd) + 1]->offset; = fTrackingMouse->shiftDown
? fSelEnd
: (*fLines)[LineAt(fTrackingMouse->anchor) + 1]->offset;
} else {
fTrackingMouse->selStart
= fTrackingMouse->shiftDown
? fSelStart
: (*fLines)[LineAt(fTrackingMouse->anchor)]->offset;
fTrackingMouse->selEnd
= (*fLines)[LineAt(currentOffset) + 1]->offset;
}
break; break;
case 2: case 2:
// double click, select word by word // double click, extend selection wordwise
FindWord(currentOffset, &fTrackingMouse->selStart, if (currentOffset <= fTrackingMouse->anchor) {
&fTrackingMouse->selEnd); fTrackingMouse->selStart = _FindLeftWordBoundary(currentOffset);
fTrackingMouse->selEnd
= fTrackingMouse->shiftDown
? fSelEnd
: _FindRightWordBoundary(fTrackingMouse->anchor);
} else {
fTrackingMouse->selStart
= fTrackingMouse->shiftDown
? fSelStart
: _FindLeftWordBoundary(fTrackingMouse->anchor);
fTrackingMouse->selEnd = _FindRightWordBoundary(currentOffset);
}
break; break;
default: default:
// new click, select char by char // new click, extend selection char by char
if (oldOffset < currentOffset) { if (currentOffset <= fTrackingMouse->anchor) {
fTrackingMouse->selStart = oldOffset;
fTrackingMouse->selEnd = currentOffset;
} else {
fTrackingMouse->selStart = currentOffset; fTrackingMouse->selStart = currentOffset;
fTrackingMouse->selEnd = oldOffset; fTrackingMouse->selEnd
= fTrackingMouse->shiftDown
? fSelEnd : fTrackingMouse->anchor;
} else {
fTrackingMouse->selStart
= fTrackingMouse->shiftDown
? fSelStart : fTrackingMouse->anchor;
fTrackingMouse->selEnd = currentOffset;
} }
break; break;
} }