summaryrefslogtreecommitdiffstats
path: root/core/java/android/text/SpannableStringBuilder.java
diff options
context:
space:
mode:
authorGilles Debunne <debunne@google.com>2012-04-04 15:35:30 -0700
committerGilles Debunne <debunne@google.com>2012-04-04 15:52:38 -0700
commitebd9a23817052c4d2aaa1058efa2b80b08003d4a (patch)
treed632e87ca661ed0dc3c01f857673f072f8fbfc90 /core/java/android/text/SpannableStringBuilder.java
parent75cf9e19a575c28c200c02c0ab6f83bb79f5c50d (diff)
downloadframeworks_base-ebd9a23817052c4d2aaa1058efa2b80b08003d4a.zip
frameworks_base-ebd9a23817052c4d2aaa1058efa2b80b08003d4a.tar.gz
frameworks_base-ebd9a23817052c4d2aaa1058efa2b80b08003d4a.tar.bz2
Faster and simpler replace in SSB
The original method was adding a suspicious space that was eventually removed with a series of 3 calls to change. This should not be necessary. I have tested this with various gap positions and lengths, for all replace cases I could think of. The test can not be added to the CTS as it would need to expose the internal resizeFor and moveGapTo methods. Change-Id: I194457fbcfd758fa69a7f380665cfd5ae4d3f1d4
Diffstat (limited to 'core/java/android/text/SpannableStringBuilder.java')
-rw-r--r--core/java/android/text/SpannableStringBuilder.java67
1 files changed, 21 insertions, 46 deletions
diff --git a/core/java/android/text/SpannableStringBuilder.java b/core/java/android/text/SpannableStringBuilder.java
index f7a7eb8..bb4b282 100644
--- a/core/java/android/text/SpannableStringBuilder.java
+++ b/core/java/android/text/SpannableStringBuilder.java
@@ -50,6 +50,8 @@ public class SpannableStringBuilder implements CharSequence, GetChars, Spannable
public SpannableStringBuilder(CharSequence text, int start, int end) {
int srclen = end - start;
+ if (srclen < 0) throw new StringIndexOutOfBoundsException();
+
int len = ArrayUtils.idealCharArraySize(srclen + 1);
mText = new char[len];
mGapStart = srclen;
@@ -153,7 +155,7 @@ public class SpannableStringBuilder implements CharSequence, GetChars, Spannable
if (where == mGapStart)
return;
- boolean atend = (where == length());
+ boolean atEnd = (where == length());
if (where < mGapStart) {
int overlap = mGapStart - where;
@@ -179,7 +181,7 @@ public class SpannableStringBuilder implements CharSequence, GetChars, Spannable
else if (start == where) {
int flag = (mSpanFlags[i] & START_MASK) >> START_SHIFT;
- if (flag == POINT || (atend && flag == PARAGRAPH))
+ if (flag == POINT || (atEnd && flag == PARAGRAPH))
start += mGapLength;
}
@@ -190,7 +192,7 @@ public class SpannableStringBuilder implements CharSequence, GetChars, Spannable
else if (end == where) {
int flag = (mSpanFlags[i] & END_MASK);
- if (flag == POINT || (atend && flag == PARAGRAPH))
+ if (flag == POINT || (atEnd && flag == PARAGRAPH))
end += mGapLength;
}
@@ -397,7 +399,7 @@ public class SpannableStringBuilder implements CharSequence, GetChars, Spannable
// Documentation from interface
public SpannableStringBuilder replace(final int start, final int end,
- CharSequence tb, int tbstart, int tbend) {
+ CharSequence tb, int tbstart, int tbend) {
int filtercount = mFilters.length;
for (int i = 0; i < filtercount; i++) {
CharSequence repl = mFilters[i].filter(tb, tbstart, tbend, this, start, end);
@@ -419,53 +421,26 @@ public class SpannableStringBuilder implements CharSequence, GetChars, Spannable
TextWatcher[] textWatchers = getSpans(start, start + origLen, TextWatcher.class);
sendBeforeTextChanged(textWatchers, start, origLen, newLen);
- if (origLen == 0 || newLen == 0) {
- change(start, end, tb, tbstart, tbend);
- } else {
- int selstart = Selection.getSelectionStart(this);
- int selend = Selection.getSelectionEnd(this);
-
- // XXX just make the span fixups in change() do the right thing
- // instead of this madness!
-
- checkRange("replace", start, end);
- moveGapTo(end);
-
- if (mGapLength < 2)
- resizeFor(length() + 1);
-
- for (int i = mSpanCount - 1; i >= 0; i--) {
- if (mSpanStarts[i] == mGapStart)
- mSpanStarts[i]++;
-
- if (mSpanEnds[i] == mGapStart)
- mSpanEnds[i]++;
- }
+ // Try to keep the cursor / selection at the same relative position during
+ // a text replacement. If replaced or replacement text length is zero, this
+ // is already taken care of.
+ boolean adjustSelection = origLen != 0 && newLen != 0;
+ int selstart = 0;
+ int selend = 0;
+ if (adjustSelection) {
+ selstart = Selection.getSelectionStart(this);
+ selend = Selection.getSelectionEnd(this);
+ }
- mText[mGapStart] = ' ';
- mGapStart++;
- mGapLength--;
+ checkRange("replace", start, end);
- if (mGapLength < 1) {
- new Exception("mGapLength < 1").printStackTrace();
- }
+ change(start, end, tb, tbstart, tbend);
- change(start + 1, start + 1, tb, tbstart, tbend);
- change(start, start + 1, "", 0, 0);
- change(start + newLen, start + newLen + origLen, "", 0, 0);
-
- /*
- * Special case to keep the cursor in the same position
- * if it was somewhere in the middle of the replaced region.
- * If it was at the start or the end or crossing the whole
- * replacement, it should already be where it belongs.
- * TODO: Is there some more general mechanism that could
- * accomplish this?
- */
+ if (adjustSelection) {
if (selstart > start && selstart < end) {
long off = selstart - start;
- off = off * newLen / (end - start);
+ off = off * newLen / origLen;
selstart = (int) off + start;
setSpan(false, Selection.SELECTION_START, selstart, selstart,
@@ -474,7 +449,7 @@ public class SpannableStringBuilder implements CharSequence, GetChars, Spannable
if (selend > start && selend < end) {
long off = selend - start;
- off = off * newLen / (end - start);
+ off = off * newLen / origLen;
selend = (int) off + start;
setSpan(false, Selection.SELECTION_END, selend, selend, Spanned.SPAN_POINT_POINT);