1 /* 2 * Copyright (C) 2006 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.text; 18 19 import com.android.internal.util.ArrayUtils; 20 import android.graphics.Paint; 21 import android.graphics.Canvas; 22 23 import java.lang.reflect.Array; 24 25 /** 26 * This is the class for text whose content and markup can both be changed. 27 */ 28 public class SpannableStringBuilder 29 implements CharSequence, GetChars, Spannable, Editable, Appendable, 30 GraphicsOperations 31 { 32 /** 33 * Create a new SpannableStringBuilder with empty contents 34 */ SpannableStringBuilder()35 public SpannableStringBuilder() { 36 this(""); 37 } 38 39 /** 40 * Create a new SpannableStringBuilder containing a copy of the 41 * specified text, including its spans if any. 42 */ SpannableStringBuilder(CharSequence text)43 public SpannableStringBuilder(CharSequence text) { 44 this(text, 0, text.length()); 45 } 46 47 /** 48 * Create a new SpannableStringBuilder containing a copy of the 49 * specified slice of the specified text, including its spans if any. 50 */ SpannableStringBuilder(CharSequence text, int start, int end)51 public SpannableStringBuilder(CharSequence text, int start, int end) { 52 int srclen = end - start; 53 54 int len = ArrayUtils.idealCharArraySize(srclen + 1); 55 mText = new char[len]; 56 mGapStart = srclen; 57 mGapLength = len - srclen; 58 59 TextUtils.getChars(text, start, end, mText, 0); 60 61 mSpanCount = 0; 62 int alloc = ArrayUtils.idealIntArraySize(0); 63 mSpans = new Object[alloc]; 64 mSpanStarts = new int[alloc]; 65 mSpanEnds = new int[alloc]; 66 mSpanFlags = new int[alloc]; 67 68 if (text instanceof Spanned) { 69 Spanned sp = (Spanned) text; 70 Object[] spans = sp.getSpans(start, end, Object.class); 71 72 for (int i = 0; i < spans.length; i++) { 73 if (spans[i] instanceof NoCopySpan) { 74 continue; 75 } 76 77 int st = sp.getSpanStart(spans[i]) - start; 78 int en = sp.getSpanEnd(spans[i]) - start; 79 int fl = sp.getSpanFlags(spans[i]); 80 81 if (st < 0) 82 st = 0; 83 if (st > end - start) 84 st = end - start; 85 86 if (en < 0) 87 en = 0; 88 if (en > end - start) 89 en = end - start; 90 91 setSpan(spans[i], st, en, fl); 92 } 93 } 94 } 95 valueOf(CharSequence source)96 public static SpannableStringBuilder valueOf(CharSequence source) { 97 if (source instanceof SpannableStringBuilder) { 98 return (SpannableStringBuilder) source; 99 } else { 100 return new SpannableStringBuilder(source); 101 } 102 } 103 104 /** 105 * Return the char at the specified offset within the buffer. 106 */ charAt(int where)107 public char charAt(int where) { 108 int len = length(); 109 if (where < 0) { 110 throw new IndexOutOfBoundsException("charAt: " + where + " < 0"); 111 } else if (where >= len) { 112 throw new IndexOutOfBoundsException("charAt: " + where + 113 " >= length " + len); 114 } 115 116 if (where >= mGapStart) 117 return mText[where + mGapLength]; 118 else 119 return mText[where]; 120 } 121 122 /** 123 * Return the number of chars in the buffer. 124 */ length()125 public int length() { 126 return mText.length - mGapLength; 127 } 128 resizeFor(int size)129 private void resizeFor(int size) { 130 int newlen = ArrayUtils.idealCharArraySize(size + 1); 131 char[] newtext = new char[newlen]; 132 133 int after = mText.length - (mGapStart + mGapLength); 134 135 System.arraycopy(mText, 0, newtext, 0, mGapStart); 136 System.arraycopy(mText, mText.length - after, 137 newtext, newlen - after, after); 138 139 for (int i = 0; i < mSpanCount; i++) { 140 if (mSpanStarts[i] > mGapStart) 141 mSpanStarts[i] += newlen - mText.length; 142 if (mSpanEnds[i] > mGapStart) 143 mSpanEnds[i] += newlen - mText.length; 144 } 145 146 int oldlen = mText.length; 147 mText = newtext; 148 mGapLength += mText.length - oldlen; 149 150 if (mGapLength < 1) 151 new Exception("mGapLength < 1").printStackTrace(); 152 } 153 moveGapTo(int where)154 private void moveGapTo(int where) { 155 if (where == mGapStart) 156 return; 157 158 boolean atend = (where == length()); 159 160 if (where < mGapStart) { 161 int overlap = mGapStart - where; 162 163 System.arraycopy(mText, where, 164 mText, mGapStart + mGapLength - overlap, overlap); 165 } else /* where > mGapStart */ { 166 int overlap = where - mGapStart; 167 168 System.arraycopy(mText, where + mGapLength - overlap, 169 mText, mGapStart, overlap); 170 } 171 172 // XXX be more clever 173 for (int i = 0; i < mSpanCount; i++) { 174 int start = mSpanStarts[i]; 175 int end = mSpanEnds[i]; 176 177 if (start > mGapStart) 178 start -= mGapLength; 179 if (start > where) 180 start += mGapLength; 181 else if (start == where) { 182 int flag = (mSpanFlags[i] & START_MASK) >> START_SHIFT; 183 184 if (flag == POINT || (atend && flag == PARAGRAPH)) 185 start += mGapLength; 186 } 187 188 if (end > mGapStart) 189 end -= mGapLength; 190 if (end > where) 191 end += mGapLength; 192 else if (end == where) { 193 int flag = (mSpanFlags[i] & END_MASK); 194 195 if (flag == POINT || (atend && flag == PARAGRAPH)) 196 end += mGapLength; 197 } 198 199 mSpanStarts[i] = start; 200 mSpanEnds[i] = end; 201 } 202 203 mGapStart = where; 204 } 205 206 // Documentation from interface insert(int where, CharSequence tb, int start, int end)207 public SpannableStringBuilder insert(int where, CharSequence tb, int start, int end) { 208 return replace(where, where, tb, start, end); 209 } 210 211 // Documentation from interface insert(int where, CharSequence tb)212 public SpannableStringBuilder insert(int where, CharSequence tb) { 213 return replace(where, where, tb, 0, tb.length()); 214 } 215 216 // Documentation from interface delete(int start, int end)217 public SpannableStringBuilder delete(int start, int end) { 218 SpannableStringBuilder ret = replace(start, end, "", 0, 0); 219 220 if (mGapLength > 2 * length()) 221 resizeFor(length()); 222 223 return ret; // == this 224 } 225 226 // Documentation from interface clear()227 public void clear() { 228 replace(0, length(), "", 0, 0); 229 } 230 231 // Documentation from interface clearSpans()232 public void clearSpans() { 233 for (int i = mSpanCount - 1; i >= 0; i--) { 234 Object what = mSpans[i]; 235 int ostart = mSpanStarts[i]; 236 int oend = mSpanEnds[i]; 237 238 if (ostart > mGapStart) 239 ostart -= mGapLength; 240 if (oend > mGapStart) 241 oend -= mGapLength; 242 243 mSpanCount = i; 244 mSpans[i] = null; 245 246 sendSpanRemoved(what, ostart, oend); 247 } 248 } 249 250 // Documentation from interface append(CharSequence text)251 public SpannableStringBuilder append(CharSequence text) { 252 int length = length(); 253 return replace(length, length, text, 0, text.length()); 254 } 255 256 // Documentation from interface append(CharSequence text, int start, int end)257 public SpannableStringBuilder append(CharSequence text, int start, int end) { 258 int length = length(); 259 return replace(length, length, text, start, end); 260 } 261 262 // Documentation from interface append(char text)263 public SpannableStringBuilder append(char text) { 264 return append(String.valueOf(text)); 265 } 266 change(int start, int end, CharSequence tb, int tbstart, int tbend)267 private int change(int start, int end, 268 CharSequence tb, int tbstart, int tbend) { 269 return change(true, start, end, tb, tbstart, tbend); 270 } 271 change(boolean notify, int start, int end, CharSequence tb, int tbstart, int tbend)272 private int change(boolean notify, int start, int end, 273 CharSequence tb, int tbstart, int tbend) { 274 checkRange("replace", start, end); 275 int ret = tbend - tbstart; 276 TextWatcher[] recipients = null; 277 278 if (notify) 279 recipients = sendTextWillChange(start, end - start, 280 tbend - tbstart); 281 282 for (int i = mSpanCount - 1; i >= 0; i--) { 283 if ((mSpanFlags[i] & SPAN_PARAGRAPH) == SPAN_PARAGRAPH) { 284 int st = mSpanStarts[i]; 285 if (st > mGapStart) 286 st -= mGapLength; 287 288 int en = mSpanEnds[i]; 289 if (en > mGapStart) 290 en -= mGapLength; 291 292 int ost = st; 293 int oen = en; 294 int clen = length(); 295 296 if (st > start && st <= end) { 297 for (st = end; st < clen; st++) 298 if (st > end && charAt(st - 1) == '\n') 299 break; 300 } 301 302 if (en > start && en <= end) { 303 for (en = end; en < clen; en++) 304 if (en > end && charAt(en - 1) == '\n') 305 break; 306 } 307 308 if (st != ost || en != oen) 309 setSpan(mSpans[i], st, en, mSpanFlags[i]); 310 } 311 } 312 313 moveGapTo(end); 314 315 if (tbend - tbstart >= mGapLength + (end - start)) 316 resizeFor(mText.length - mGapLength + 317 tbend - tbstart - (end - start)); 318 319 mGapStart += tbend - tbstart - (end - start); 320 mGapLength -= tbend - tbstart - (end - start); 321 322 if (mGapLength < 1) 323 new Exception("mGapLength < 1").printStackTrace(); 324 325 TextUtils.getChars(tb, tbstart, tbend, mText, start); 326 327 if (tb instanceof Spanned) { 328 Spanned sp = (Spanned) tb; 329 Object[] spans = sp.getSpans(tbstart, tbend, Object.class); 330 331 for (int i = 0; i < spans.length; i++) { 332 int st = sp.getSpanStart(spans[i]); 333 int en = sp.getSpanEnd(spans[i]); 334 335 if (st < tbstart) 336 st = tbstart; 337 if (en > tbend) 338 en = tbend; 339 340 if (getSpanStart(spans[i]) < 0) { 341 setSpan(false, spans[i], 342 st - tbstart + start, 343 en - tbstart + start, 344 sp.getSpanFlags(spans[i])); 345 } 346 } 347 } 348 349 // no need for span fixup on pure insertion 350 if (tbend > tbstart && end - start == 0) { 351 if (notify) { 352 sendTextChange(recipients, start, end - start, tbend - tbstart); 353 sendTextHasChanged(recipients); 354 } 355 356 return ret; 357 } 358 359 boolean atend = (mGapStart + mGapLength == mText.length); 360 361 for (int i = mSpanCount - 1; i >= 0; i--) { 362 if (mSpanStarts[i] >= start && 363 mSpanStarts[i] < mGapStart + mGapLength) { 364 int flag = (mSpanFlags[i] & START_MASK) >> START_SHIFT; 365 366 if (flag == POINT || (flag == PARAGRAPH && atend)) 367 mSpanStarts[i] = mGapStart + mGapLength; 368 else 369 mSpanStarts[i] = start; 370 } 371 372 if (mSpanEnds[i] >= start && 373 mSpanEnds[i] < mGapStart + mGapLength) { 374 int flag = (mSpanFlags[i] & END_MASK); 375 376 if (flag == POINT || (flag == PARAGRAPH && atend)) 377 mSpanEnds[i] = mGapStart + mGapLength; 378 else 379 mSpanEnds[i] = start; 380 } 381 382 // remove 0-length SPAN_EXCLUSIVE_EXCLUSIVE 383 // XXX send notification on removal 384 385 if (mSpanEnds[i] < mSpanStarts[i]) { 386 System.arraycopy(mSpans, i + 1, 387 mSpans, i, mSpanCount - (i + 1)); 388 System.arraycopy(mSpanStarts, i + 1, 389 mSpanStarts, i, mSpanCount - (i + 1)); 390 System.arraycopy(mSpanEnds, i + 1, 391 mSpanEnds, i, mSpanCount - (i + 1)); 392 System.arraycopy(mSpanFlags, i + 1, 393 mSpanFlags, i, mSpanCount - (i + 1)); 394 395 mSpanCount--; 396 } 397 } 398 399 if (notify) { 400 sendTextChange(recipients, start, end - start, tbend - tbstart); 401 sendTextHasChanged(recipients); 402 } 403 404 return ret; 405 } 406 407 // Documentation from interface replace(int start, int end, CharSequence tb)408 public SpannableStringBuilder replace(int start, int end, CharSequence tb) { 409 return replace(start, end, tb, 0, tb.length()); 410 } 411 412 // Documentation from interface replace(final int start, final int end, CharSequence tb, int tbstart, int tbend)413 public SpannableStringBuilder replace(final int start, final int end, 414 CharSequence tb, int tbstart, int tbend) { 415 int filtercount = mFilters.length; 416 for (int i = 0; i < filtercount; i++) { 417 CharSequence repl = mFilters[i].filter(tb, tbstart, tbend, 418 this, start, end); 419 420 if (repl != null) { 421 tb = repl; 422 tbstart = 0; 423 tbend = repl.length(); 424 } 425 } 426 427 if (end == start && tbstart == tbend) { 428 return this; 429 } 430 431 if (end == start || tbstart == tbend) { 432 change(start, end, tb, tbstart, tbend); 433 } else { 434 int selstart = Selection.getSelectionStart(this); 435 int selend = Selection.getSelectionEnd(this); 436 437 // XXX just make the span fixups in change() do the right thing 438 // instead of this madness! 439 440 checkRange("replace", start, end); 441 moveGapTo(end); 442 TextWatcher[] recipients; 443 444 recipients = sendTextWillChange(start, end - start, 445 tbend - tbstart); 446 447 int origlen = end - start; 448 449 if (mGapLength < 2) 450 resizeFor(length() + 1); 451 452 for (int i = mSpanCount - 1; i >= 0; i--) { 453 if (mSpanStarts[i] == mGapStart) 454 mSpanStarts[i]++; 455 456 if (mSpanEnds[i] == mGapStart) 457 mSpanEnds[i]++; 458 } 459 460 mText[mGapStart] = ' '; 461 mGapStart++; 462 mGapLength--; 463 464 if (mGapLength < 1) 465 new Exception("mGapLength < 1").printStackTrace(); 466 467 int oldlen = (end + 1) - start; 468 469 int inserted = change(false, start + 1, start + 1, 470 tb, tbstart, tbend); 471 change(false, start, start + 1, "", 0, 0); 472 change(false, start + inserted, start + inserted + oldlen - 1, 473 "", 0, 0); 474 475 /* 476 * Special case to keep the cursor in the same position 477 * if it was somewhere in the middle of the replaced region. 478 * If it was at the start or the end or crossing the whole 479 * replacement, it should already be where it belongs. 480 * TODO: Is there some more general mechanism that could 481 * accomplish this? 482 */ 483 if (selstart > start && selstart < end) { 484 long off = selstart - start; 485 486 off = off * inserted / (end - start); 487 selstart = (int) off + start; 488 489 setSpan(false, Selection.SELECTION_START, selstart, selstart, 490 Spanned.SPAN_POINT_POINT); 491 } 492 if (selend > start && selend < end) { 493 long off = selend - start; 494 495 off = off * inserted / (end - start); 496 selend = (int) off + start; 497 498 setSpan(false, Selection.SELECTION_END, selend, selend, 499 Spanned.SPAN_POINT_POINT); 500 } 501 502 sendTextChange(recipients, start, origlen, inserted); 503 sendTextHasChanged(recipients); 504 } 505 return this; 506 } 507 508 /** 509 * Mark the specified range of text with the specified object. 510 * The flags determine how the span will behave when text is 511 * inserted at the start or end of the span's range. 512 */ setSpan(Object what, int start, int end, int flags)513 public void setSpan(Object what, int start, int end, int flags) { 514 setSpan(true, what, start, end, flags); 515 } 516 setSpan(boolean send, Object what, int start, int end, int flags)517 private void setSpan(boolean send, 518 Object what, int start, int end, int flags) { 519 int nstart = start; 520 int nend = end; 521 522 checkRange("setSpan", start, end); 523 524 if ((flags & START_MASK) == (PARAGRAPH << START_SHIFT)) { 525 if (start != 0 && start != length()) { 526 char c = charAt(start - 1); 527 528 if (c != '\n') 529 throw new RuntimeException( 530 "PARAGRAPH span must start at paragraph boundary"); 531 } 532 } 533 534 if ((flags & END_MASK) == PARAGRAPH) { 535 if (end != 0 && end != length()) { 536 char c = charAt(end - 1); 537 538 if (c != '\n') 539 throw new RuntimeException( 540 "PARAGRAPH span must end at paragraph boundary"); 541 } 542 } 543 544 if (start > mGapStart) 545 start += mGapLength; 546 else if (start == mGapStart) { 547 int flag = (flags & START_MASK) >> START_SHIFT; 548 549 if (flag == POINT || (flag == PARAGRAPH && start == length())) 550 start += mGapLength; 551 } 552 553 if (end > mGapStart) 554 end += mGapLength; 555 else if (end == mGapStart) { 556 int flag = (flags & END_MASK); 557 558 if (flag == POINT || (flag == PARAGRAPH && end == length())) 559 end += mGapLength; 560 } 561 562 int count = mSpanCount; 563 Object[] spans = mSpans; 564 565 for (int i = 0; i < count; i++) { 566 if (spans[i] == what) { 567 int ostart = mSpanStarts[i]; 568 int oend = mSpanEnds[i]; 569 570 if (ostart > mGapStart) 571 ostart -= mGapLength; 572 if (oend > mGapStart) 573 oend -= mGapLength; 574 575 mSpanStarts[i] = start; 576 mSpanEnds[i] = end; 577 mSpanFlags[i] = flags; 578 579 if (send) 580 sendSpanChanged(what, ostart, oend, nstart, nend); 581 582 return; 583 } 584 } 585 586 if (mSpanCount + 1 >= mSpans.length) { 587 int newsize = ArrayUtils.idealIntArraySize(mSpanCount + 1); 588 Object[] newspans = new Object[newsize]; 589 int[] newspanstarts = new int[newsize]; 590 int[] newspanends = new int[newsize]; 591 int[] newspanflags = new int[newsize]; 592 593 System.arraycopy(mSpans, 0, newspans, 0, mSpanCount); 594 System.arraycopy(mSpanStarts, 0, newspanstarts, 0, mSpanCount); 595 System.arraycopy(mSpanEnds, 0, newspanends, 0, mSpanCount); 596 System.arraycopy(mSpanFlags, 0, newspanflags, 0, mSpanCount); 597 598 mSpans = newspans; 599 mSpanStarts = newspanstarts; 600 mSpanEnds = newspanends; 601 mSpanFlags = newspanflags; 602 } 603 604 mSpans[mSpanCount] = what; 605 mSpanStarts[mSpanCount] = start; 606 mSpanEnds[mSpanCount] = end; 607 mSpanFlags[mSpanCount] = flags; 608 mSpanCount++; 609 610 if (send) 611 sendSpanAdded(what, nstart, nend); 612 } 613 614 /** 615 * Remove the specified markup object from the buffer. 616 */ removeSpan(Object what)617 public void removeSpan(Object what) { 618 for (int i = mSpanCount - 1; i >= 0; i--) { 619 if (mSpans[i] == what) { 620 int ostart = mSpanStarts[i]; 621 int oend = mSpanEnds[i]; 622 623 if (ostart > mGapStart) 624 ostart -= mGapLength; 625 if (oend > mGapStart) 626 oend -= mGapLength; 627 628 int count = mSpanCount - (i + 1); 629 630 System.arraycopy(mSpans, i + 1, mSpans, i, count); 631 System.arraycopy(mSpanStarts, i + 1, mSpanStarts, i, count); 632 System.arraycopy(mSpanEnds, i + 1, mSpanEnds, i, count); 633 System.arraycopy(mSpanFlags, i + 1, mSpanFlags, i, count); 634 635 mSpanCount--; 636 mSpans[mSpanCount] = null; 637 638 sendSpanRemoved(what, ostart, oend); 639 return; 640 } 641 } 642 } 643 644 /** 645 * Return the buffer offset of the beginning of the specified 646 * markup object, or -1 if it is not attached to this buffer. 647 */ getSpanStart(Object what)648 public int getSpanStart(Object what) { 649 int count = mSpanCount; 650 Object[] spans = mSpans; 651 652 for (int i = count - 1; i >= 0; i--) { 653 if (spans[i] == what) { 654 int where = mSpanStarts[i]; 655 656 if (where > mGapStart) 657 where -= mGapLength; 658 659 return where; 660 } 661 } 662 663 return -1; 664 } 665 666 /** 667 * Return the buffer offset of the end of the specified 668 * markup object, or -1 if it is not attached to this buffer. 669 */ getSpanEnd(Object what)670 public int getSpanEnd(Object what) { 671 int count = mSpanCount; 672 Object[] spans = mSpans; 673 674 for (int i = count - 1; i >= 0; i--) { 675 if (spans[i] == what) { 676 int where = mSpanEnds[i]; 677 678 if (where > mGapStart) 679 where -= mGapLength; 680 681 return where; 682 } 683 } 684 685 return -1; 686 } 687 688 /** 689 * Return the flags of the end of the specified 690 * markup object, or 0 if it is not attached to this buffer. 691 */ getSpanFlags(Object what)692 public int getSpanFlags(Object what) { 693 int count = mSpanCount; 694 Object[] spans = mSpans; 695 696 for (int i = count - 1; i >= 0; i--) { 697 if (spans[i] == what) { 698 return mSpanFlags[i]; 699 } 700 } 701 702 return 0; 703 } 704 705 /** 706 * Return an array of the spans of the specified type that overlap 707 * the specified range of the buffer. The kind may be Object.class to get 708 * a list of all the spans regardless of type. 709 */ getSpans(int queryStart, int queryEnd, Class<T> kind)710 public <T> T[] getSpans(int queryStart, int queryEnd, Class<T> kind) { 711 int spanCount = mSpanCount; 712 Object[] spans = mSpans; 713 int[] starts = mSpanStarts; 714 int[] ends = mSpanEnds; 715 int[] flags = mSpanFlags; 716 int gapstart = mGapStart; 717 int gaplen = mGapLength; 718 719 int count = 0; 720 Object[] ret = null; 721 Object ret1 = null; 722 723 for (int i = 0; i < spanCount; i++) { 724 int spanStart = starts[i]; 725 int spanEnd = ends[i]; 726 727 if (spanStart > gapstart) { 728 spanStart -= gaplen; 729 } 730 if (spanEnd > gapstart) { 731 spanEnd -= gaplen; 732 } 733 734 if (spanStart > queryEnd) { 735 continue; 736 } 737 if (spanEnd < queryStart) { 738 continue; 739 } 740 741 if (spanStart != spanEnd && queryStart != queryEnd) { 742 if (spanStart == queryEnd) 743 continue; 744 if (spanEnd == queryStart) 745 continue; 746 } 747 748 if (kind != null && !kind.isInstance(spans[i])) { 749 continue; 750 } 751 752 if (count == 0) { 753 ret1 = spans[i]; 754 count++; 755 } else { 756 if (count == 1) { 757 ret = (Object[]) Array.newInstance(kind, spanCount - i + 1); 758 ret[0] = ret1; 759 } 760 761 int prio = flags[i] & SPAN_PRIORITY; 762 if (prio != 0) { 763 int j; 764 765 for (j = 0; j < count; j++) { 766 int p = getSpanFlags(ret[j]) & SPAN_PRIORITY; 767 768 if (prio > p) { 769 break; 770 } 771 } 772 773 System.arraycopy(ret, j, ret, j + 1, count - j); 774 ret[j] = spans[i]; 775 count++; 776 } else { 777 ret[count++] = spans[i]; 778 } 779 } 780 } 781 782 if (count == 0) { 783 return (T[]) ArrayUtils.emptyArray(kind); 784 } 785 if (count == 1) { 786 ret = (Object[]) Array.newInstance(kind, 1); 787 ret[0] = ret1; 788 return (T[]) ret; 789 } 790 if (count == ret.length) { 791 return (T[]) ret; 792 } 793 794 Object[] nret = (Object[]) Array.newInstance(kind, count); 795 System.arraycopy(ret, 0, nret, 0, count); 796 return (T[]) nret; 797 } 798 799 /** 800 * Return the next offset after <code>start</code> but less than or 801 * equal to <code>limit</code> where a span of the specified type 802 * begins or ends. 803 */ nextSpanTransition(int start, int limit, Class kind)804 public int nextSpanTransition(int start, int limit, Class kind) { 805 int count = mSpanCount; 806 Object[] spans = mSpans; 807 int[] starts = mSpanStarts; 808 int[] ends = mSpanEnds; 809 int gapstart = mGapStart; 810 int gaplen = mGapLength; 811 812 if (kind == null) { 813 kind = Object.class; 814 } 815 816 for (int i = 0; i < count; i++) { 817 int st = starts[i]; 818 int en = ends[i]; 819 820 if (st > gapstart) 821 st -= gaplen; 822 if (en > gapstart) 823 en -= gaplen; 824 825 if (st > start && st < limit && kind.isInstance(spans[i])) 826 limit = st; 827 if (en > start && en < limit && kind.isInstance(spans[i])) 828 limit = en; 829 } 830 831 return limit; 832 } 833 834 /** 835 * Return a new CharSequence containing a copy of the specified 836 * range of this buffer, including the overlapping spans. 837 */ subSequence(int start, int end)838 public CharSequence subSequence(int start, int end) { 839 return new SpannableStringBuilder(this, start, end); 840 } 841 842 /** 843 * Copy the specified range of chars from this buffer into the 844 * specified array, beginning at the specified offset. 845 */ getChars(int start, int end, char[] dest, int destoff)846 public void getChars(int start, int end, char[] dest, int destoff) { 847 checkRange("getChars", start, end); 848 849 if (end <= mGapStart) { 850 System.arraycopy(mText, start, dest, destoff, end - start); 851 } else if (start >= mGapStart) { 852 System.arraycopy(mText, start + mGapLength, 853 dest, destoff, end - start); 854 } else { 855 System.arraycopy(mText, start, dest, destoff, mGapStart - start); 856 System.arraycopy(mText, mGapStart + mGapLength, 857 dest, destoff + (mGapStart - start), 858 end - mGapStart); 859 } 860 } 861 862 /** 863 * Return a String containing a copy of the chars in this buffer. 864 */ toString()865 public String toString() { 866 int len = length(); 867 char[] buf = new char[len]; 868 869 getChars(0, len, buf, 0); 870 return new String(buf); 871 } 872 sendTextWillChange(int start, int before, int after)873 private TextWatcher[] sendTextWillChange(int start, int before, int after) { 874 TextWatcher[] recip = getSpans(start, start + before, TextWatcher.class); 875 int n = recip.length; 876 877 for (int i = 0; i < n; i++) { 878 recip[i].beforeTextChanged(this, start, before, after); 879 } 880 881 return recip; 882 } 883 sendTextChange(TextWatcher[] recip, int start, int before, int after)884 private void sendTextChange(TextWatcher[] recip, int start, int before, 885 int after) { 886 int n = recip.length; 887 888 for (int i = 0; i < n; i++) { 889 recip[i].onTextChanged(this, start, before, after); 890 } 891 } 892 sendTextHasChanged(TextWatcher[] recip)893 private void sendTextHasChanged(TextWatcher[] recip) { 894 int n = recip.length; 895 896 for (int i = 0; i < n; i++) { 897 recip[i].afterTextChanged(this); 898 } 899 } 900 sendSpanAdded(Object what, int start, int end)901 private void sendSpanAdded(Object what, int start, int end) { 902 SpanWatcher[] recip = getSpans(start, end, SpanWatcher.class); 903 int n = recip.length; 904 905 for (int i = 0; i < n; i++) { 906 recip[i].onSpanAdded(this, what, start, end); 907 } 908 } 909 sendSpanRemoved(Object what, int start, int end)910 private void sendSpanRemoved(Object what, int start, int end) { 911 SpanWatcher[] recip = getSpans(start, end, SpanWatcher.class); 912 int n = recip.length; 913 914 for (int i = 0; i < n; i++) { 915 recip[i].onSpanRemoved(this, what, start, end); 916 } 917 } 918 sendSpanChanged(Object what, int s, int e, int st, int en)919 private void sendSpanChanged(Object what, int s, int e, int st, int en) { 920 SpanWatcher[] recip = getSpans(Math.min(s, st), Math.max(e, en), 921 SpanWatcher.class); 922 int n = recip.length; 923 924 for (int i = 0; i < n; i++) { 925 recip[i].onSpanChanged(this, what, s, e, st, en); 926 } 927 } 928 region(int start, int end)929 private static String region(int start, int end) { 930 return "(" + start + " ... " + end + ")"; 931 } 932 checkRange(final String operation, int start, int end)933 private void checkRange(final String operation, int start, int end) { 934 if (end < start) { 935 throw new IndexOutOfBoundsException(operation + " " + 936 region(start, end) + 937 " has end before start"); 938 } 939 940 int len = length(); 941 942 if (start > len || end > len) { 943 throw new IndexOutOfBoundsException(operation + " " + 944 region(start, end) + 945 " ends beyond length " + len); 946 } 947 948 if (start < 0 || end < 0) { 949 throw new IndexOutOfBoundsException(operation + " " + 950 region(start, end) + 951 " starts before 0"); 952 } 953 } 954 isprint(char c)955 private boolean isprint(char c) { // XXX 956 if (c >= ' ' && c <= '~') 957 return true; 958 else 959 return false; 960 } 961 962 /* 963 private static final int startFlag(int flag) { 964 return (flag >> 4) & 0x0F; 965 } 966 967 private static final int endFlag(int flag) { 968 return flag & 0x0F; 969 } 970 971 public void dump() { // XXX 972 for (int i = 0; i < mGapStart; i++) { 973 System.out.print('|'); 974 System.out.print(' '); 975 System.out.print(isprint(mText[i]) ? mText[i] : '.'); 976 System.out.print(' '); 977 } 978 979 for (int i = mGapStart; i < mGapStart + mGapLength; i++) { 980 System.out.print('|'); 981 System.out.print('('); 982 System.out.print(isprint(mText[i]) ? mText[i] : '.'); 983 System.out.print(')'); 984 } 985 986 for (int i = mGapStart + mGapLength; i < mText.length; i++) { 987 System.out.print('|'); 988 System.out.print(' '); 989 System.out.print(isprint(mText[i]) ? mText[i] : '.'); 990 System.out.print(' '); 991 } 992 993 System.out.print('\n'); 994 995 for (int i = 0; i < mText.length + 1; i++) { 996 int found = 0; 997 int wfound = 0; 998 999 for (int j = 0; j < mSpanCount; j++) { 1000 if (mSpanStarts[j] == i) { 1001 found = 1; 1002 wfound = j; 1003 break; 1004 } 1005 1006 if (mSpanEnds[j] == i) { 1007 found = 2; 1008 wfound = j; 1009 break; 1010 } 1011 } 1012 1013 if (found == 1) { 1014 if (startFlag(mSpanFlags[wfound]) == MARK) 1015 System.out.print("( "); 1016 if (startFlag(mSpanFlags[wfound]) == PARAGRAPH) 1017 System.out.print("< "); 1018 else 1019 System.out.print("[ "); 1020 } else if (found == 2) { 1021 if (endFlag(mSpanFlags[wfound]) == POINT) 1022 System.out.print(") "); 1023 if (endFlag(mSpanFlags[wfound]) == PARAGRAPH) 1024 System.out.print("> "); 1025 else 1026 System.out.print("] "); 1027 } else { 1028 System.out.print(" "); 1029 } 1030 } 1031 1032 System.out.print("\n"); 1033 } 1034 */ 1035 1036 /** 1037 * Don't call this yourself -- exists for Canvas to use internally. 1038 * {@hide} 1039 */ drawText(Canvas c, int start, int end, float x, float y, Paint p)1040 public void drawText(Canvas c, int start, int end, 1041 float x, float y, Paint p) { 1042 checkRange("drawText", start, end); 1043 1044 if (end <= mGapStart) { 1045 c.drawText(mText, start, end - start, x, y, p); 1046 } else if (start >= mGapStart) { 1047 c.drawText(mText, start + mGapLength, end - start, x, y, p); 1048 } else { 1049 char[] buf = TextUtils.obtain(end - start); 1050 1051 getChars(start, end, buf, 0); 1052 c.drawText(buf, 0, end - start, x, y, p); 1053 TextUtils.recycle(buf); 1054 } 1055 } 1056 1057 /** 1058 * Don't call this yourself -- exists for Paint to use internally. 1059 * {@hide} 1060 */ measureText(int start, int end, Paint p)1061 public float measureText(int start, int end, Paint p) { 1062 checkRange("measureText", start, end); 1063 1064 float ret; 1065 1066 if (end <= mGapStart) { 1067 ret = p.measureText(mText, start, end - start); 1068 } else if (start >= mGapStart) { 1069 ret = p.measureText(mText, start + mGapLength, end - start); 1070 } else { 1071 char[] buf = TextUtils.obtain(end - start); 1072 1073 getChars(start, end, buf, 0); 1074 ret = p.measureText(buf, 0, end - start); 1075 TextUtils.recycle(buf); 1076 } 1077 1078 return ret; 1079 } 1080 1081 /** 1082 * Don't call this yourself -- exists for Paint to use internally. 1083 * {@hide} 1084 */ getTextWidths(int start, int end, float[] widths, Paint p)1085 public int getTextWidths(int start, int end, float[] widths, Paint p) { 1086 checkRange("getTextWidths", start, end); 1087 1088 int ret; 1089 1090 if (end <= mGapStart) { 1091 ret = p.getTextWidths(mText, start, end - start, widths); 1092 } else if (start >= mGapStart) { 1093 ret = p.getTextWidths(mText, start + mGapLength, end - start, 1094 widths); 1095 } else { 1096 char[] buf = TextUtils.obtain(end - start); 1097 1098 getChars(start, end, buf, 0); 1099 ret = p.getTextWidths(buf, 0, end - start, widths); 1100 TextUtils.recycle(buf); 1101 } 1102 1103 return ret; 1104 } 1105 1106 // Documentation from interface setFilters(InputFilter[] filters)1107 public void setFilters(InputFilter[] filters) { 1108 if (filters == null) { 1109 throw new IllegalArgumentException(); 1110 } 1111 1112 mFilters = filters; 1113 } 1114 1115 // Documentation from interface getFilters()1116 public InputFilter[] getFilters() { 1117 return mFilters; 1118 } 1119 1120 private static final InputFilter[] NO_FILTERS = new InputFilter[0]; 1121 private InputFilter[] mFilters = NO_FILTERS; 1122 1123 private char[] mText; 1124 private int mGapStart; 1125 private int mGapLength; 1126 1127 private Object[] mSpans; 1128 private int[] mSpanStarts; 1129 private int[] mSpanEnds; 1130 private int[] mSpanFlags; 1131 private int mSpanCount; 1132 1133 private static final int MARK = 1; 1134 private static final int POINT = 2; 1135 private static final int PARAGRAPH = 3; 1136 1137 private static final int START_MASK = 0xF0; 1138 private static final int END_MASK = 0x0F; 1139 private static final int START_SHIFT = 4; 1140 } 1141