160e3b3fa60a672583fe6294139feb940845efc9bGilles Debunne/* 29066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Copyright (C) 2006 The Android Open Source Project 39066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * 49066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Licensed under the Apache License, Version 2.0 (the "License"); 59066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * you may not use this file except in compliance with the License. 69066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * You may obtain a copy of the License at 79066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * 89066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * http://www.apache.org/licenses/LICENSE-2.0 99066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * 109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Unless required by applicable law or agreed to in writing, software 119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * distributed under the License is distributed on an "AS IS" BASIS, 129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * See the License for the specific language governing permissions and 149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * limitations under the License. 159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectpackage android.text; 189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 19105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Projectimport android.emoji.EmojiFactory; 209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.graphics.Canvas; 219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.graphics.Paint; 229f7a4442b89cc06cb8cae6992484e7ae795323abDoug Feltimport android.graphics.Path; 239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.graphics.Rect; 249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.text.method.TextKeyListener; 259f7a4442b89cc06cb8cae6992484e7ae795323abDoug Feltimport android.text.style.AlignmentSpan; 269f7a4442b89cc06cb8cae6992484e7ae795323abDoug Feltimport android.text.style.LeadingMarginSpan; 27162bf0f1b9fd5d78ffa801917994f6222c6efd85Gilles Debunneimport android.text.style.LeadingMarginSpan.LeadingMarginSpan2; 289f7a4442b89cc06cb8cae6992484e7ae795323abDoug Feltimport android.text.style.LineBackgroundSpan; 299f7a4442b89cc06cb8cae6992484e7ae795323abDoug Feltimport android.text.style.ParagraphStyle; 309f7a4442b89cc06cb8cae6992484e7ae795323abDoug Feltimport android.text.style.ReplacementSpan; 319f7a4442b89cc06cb8cae6992484e7ae795323abDoug Feltimport android.text.style.TabStopSpan; 329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 33cb379120456d8065d742021fc5c66748fc8a11a8Doug Feltimport com.android.internal.util.ArrayUtils; 34776abc24cdd18610232a50b997cce3cffa74609bAdam Lesinskiimport com.android.internal.util.GrowingArrayUtils; 35cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt 36c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Feltimport java.util.Arrays; 379f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt 389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project/** 399f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt * A base class that manages text layout in visual elements on 409f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt * the screen. 419f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt * <p>For text that will be edited, use a {@link DynamicLayout}, 429f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt * which will be updated as the text changes. 439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * For text that will not change, use a {@link StaticLayout}. 449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectpublic abstract class Layout { 4671b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt private static final ParagraphStyle[] NO_PARA_SPANS = 4771b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt ArrayUtils.emptyArray(ParagraphStyle.class); 4876c0226008ee16633dc94ed4dbeadef2174f6bd9Dave Bort 49eca5b7385a641ae5d8c4aa2b7823a4e573737376Gilles Debunne /* package */ static final EmojiFactory EMOJI_FACTORY = EmojiFactory.newAvailableInstance(); 50105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project /* package */ static final int MIN_EMOJI, MAX_EMOJI; 51105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project 52105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project static { 53105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project if (EMOJI_FACTORY != null) { 54105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project MIN_EMOJI = EMOJI_FACTORY.getMinimumAndroidPua(); 55105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project MAX_EMOJI = EMOJI_FACTORY.getMaximumAndroidPua(); 56105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project } else { 57105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project MIN_EMOJI = -1; 58105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project MAX_EMOJI = -1; 59105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project } 60e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 61c2d54f46ac13e029e6d53f7471cd9c90fe6bbfe9Eric Fischer 629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 6371b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt * Return how wide a layout must be in order to display the 649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * specified text with one line per paragraph. 659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public static float getDesiredWidth(CharSequence source, 679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project TextPaint paint) { 689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return getDesiredWidth(source, 0, source.length(), paint); 699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 709f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt 719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 7271b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt * Return how wide a layout must be in order to display the 739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * specified text slice with one line per paragraph. 749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public static float getDesiredWidth(CharSequence source, 769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int start, int end, 779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project TextPaint paint) { 789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project float need = 0; 799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int next; 819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project for (int i = start; i <= end; i = next) { 829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project next = TextUtils.indexOf(source, '\n', i, end); 839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (next < 0) 859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project next = end; 869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 8771b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt // note, omits trailing paragraph char 886c488de023a4797069673dc619c1a4096079ea9eGilles Debunne float w = measurePara(paint, source, i, next); 899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (w > need) 919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project need = w; 929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project next++; 949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return need; 979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 1009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Subclasses of Layout use this constructor to set the display text, 1019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * width, and other standard properties. 10271b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt * @param text the text to render 10371b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt * @param paint the default paint for the layout. Styles can override 10471b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt * various attributes of the paint. 10571b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt * @param width the wrapping width for the text. 10671b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt * @param align whether to left, right, or center the text. Styles can 10771b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt * override the alignment. 10871b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt * @param spacingMult factor by which to scale the font size to get the 10971b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt * default line spacing 11071b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt * @param spacingAdd amount to add to the default line spacing 1119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 1129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project protected Layout(CharSequence text, TextPaint paint, 1139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int width, Alignment align, 11471b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt float spacingMult, float spacingAdd) { 115cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt this(text, paint, width, align, TextDirectionHeuristics.FIRSTSTRONG_LTR, 116cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt spacingMult, spacingAdd); 117cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt } 118cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt 119cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt /** 120cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt * Subclasses of Layout use this constructor to set the display text, 121cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt * width, and other standard properties. 122cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt * @param text the text to render 123cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt * @param paint the default paint for the layout. Styles can override 124cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt * various attributes of the paint. 125cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt * @param width the wrapping width for the text. 126cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt * @param align whether to left, right, or center the text. Styles can 127cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt * override the alignment. 128cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt * @param spacingMult factor by which to scale the font size to get the 129cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt * default line spacing 130cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt * @param spacingAdd amount to add to the default line spacing 131cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt * 132cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt * @hide 133cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt */ 134cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt protected Layout(CharSequence text, TextPaint paint, 135cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt int width, Alignment align, TextDirectionHeuristic textDir, 136cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt float spacingMult, float spacingAdd) { 137cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt 1389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (width < 0) 1399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project throw new IllegalArgumentException("Layout: " + width + " < 0"); 1409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 141e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // Ensure paint doesn't have baselineShift set. 142e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // While normally we don't modify the paint the user passed in, 143e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // we were already doing this in Styled.drawUniformRun with both 144e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // baselineShift and bgColor. We probably should reevaluate bgColor. 145e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (paint != null) { 146e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt paint.bgColor = 0; 147e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt paint.baselineShift = 0; 148e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 149e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 1509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mText = text; 1519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mPaint = paint; 1529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mWorkPaint = new TextPaint(); 1539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mWidth = width; 1549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mAlignment = align; 15571b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt mSpacingMult = spacingMult; 15671b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt mSpacingAdd = spacingAdd; 1579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mSpannedText = text instanceof Spanned; 158cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt mTextDir = textDir; 1599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 1629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Replace constructor properties of this Layout with new ones. Be careful. 1639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 1649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /* package */ void replaceWith(CharSequence text, TextPaint paint, 1659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int width, Alignment align, 1669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project float spacingmult, float spacingadd) { 1679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (width < 0) { 1689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project throw new IllegalArgumentException("Layout: " + width + " < 0"); 1699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mText = text; 1729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mPaint = paint; 1739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mWidth = width; 1749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mAlignment = align; 1759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mSpacingMult = spacingmult; 1769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mSpacingAdd = spacingadd; 1779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mSpannedText = text instanceof Spanned; 1789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 1819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Draw this Layout on the specified Canvas. 1829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 1839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public void draw(Canvas c) { 1849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project draw(c, null, null, 0); 1859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 18871b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt * Draw this Layout on the specified canvas, with the highlight path drawn 18971b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt * between the background and the text. 19071b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt * 1916c488de023a4797069673dc619c1a4096079ea9eGilles Debunne * @param canvas the canvas 19271b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt * @param highlight the path of the highlight or cursor; can be null 19371b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt * @param highlightPaint the paint for the highlight 19471b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt * @param cursorOffsetVertical the amount to temporarily translate the 19571b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt * canvas while rendering the highlight 1969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 1976c488de023a4797069673dc619c1a4096079ea9eGilles Debunne public void draw(Canvas canvas, Path highlight, Paint highlightPaint, 1986c488de023a4797069673dc619c1a4096079ea9eGilles Debunne int cursorOffsetVertical) { 1996c488de023a4797069673dc619c1a4096079ea9eGilles Debunne final long lineRange = getLineRangeForDraw(canvas); 2006c488de023a4797069673dc619c1a4096079ea9eGilles Debunne int firstLine = TextUtils.unpackRangeStartFromLong(lineRange); 2016c488de023a4797069673dc619c1a4096079ea9eGilles Debunne int lastLine = TextUtils.unpackRangeEndFromLong(lineRange); 2026c488de023a4797069673dc619c1a4096079ea9eGilles Debunne if (lastLine < 0) return; 2036c488de023a4797069673dc619c1a4096079ea9eGilles Debunne 2046c488de023a4797069673dc619c1a4096079ea9eGilles Debunne drawBackground(canvas, highlight, highlightPaint, cursorOffsetVertical, 2056c488de023a4797069673dc619c1a4096079ea9eGilles Debunne firstLine, lastLine); 2066c488de023a4797069673dc619c1a4096079ea9eGilles Debunne drawText(canvas, firstLine, lastLine); 2076c488de023a4797069673dc619c1a4096079ea9eGilles Debunne } 2089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 2096c488de023a4797069673dc619c1a4096079ea9eGilles Debunne /** 2106c488de023a4797069673dc619c1a4096079ea9eGilles Debunne * @hide 2116c488de023a4797069673dc619c1a4096079ea9eGilles Debunne */ 2126c488de023a4797069673dc619c1a4096079ea9eGilles Debunne public void drawText(Canvas canvas, int firstLine, int lastLine) { 2136c488de023a4797069673dc619c1a4096079ea9eGilles Debunne int previousLineBottom = getLineTop(firstLine); 2146c488de023a4797069673dc619c1a4096079ea9eGilles Debunne int previousLineEnd = getLineStart(firstLine); 21571b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt ParagraphStyle[] spans = NO_PARA_SPANS; 216c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt int spanEnd = 0; 2176c488de023a4797069673dc619c1a4096079ea9eGilles Debunne TextPaint paint = mPaint; 2186c488de023a4797069673dc619c1a4096079ea9eGilles Debunne CharSequence buf = mText; 2199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 220c0ccf0c47c00942a9d0f2670600a8f2d4d7adb5bDoug Felt Alignment paraAlign = mAlignment; 221c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt TabStops tabStops = null; 222c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt boolean tabStopsIsInitialized = false; 2239f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt 224e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt TextLine tl = TextLine.obtain(); 225c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt 2266c488de023a4797069673dc619c1a4096079ea9eGilles Debunne // Draw the lines, one at a time. 2276c488de023a4797069673dc619c1a4096079ea9eGilles Debunne // The baseline is the top of the following line minus the current line's descent. 2286c488de023a4797069673dc619c1a4096079ea9eGilles Debunne for (int i = firstLine; i <= lastLine; i++) { 2299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int start = previousLineEnd; 2306c488de023a4797069673dc619c1a4096079ea9eGilles Debunne previousLineEnd = getLineStart(i + 1); 2319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int end = getLineVisibleEnd(i, start, previousLineEnd); 2329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 2339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int ltop = previousLineBottom; 2349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int lbottom = getLineTop(i+1); 2359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project previousLineBottom = lbottom; 2369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int lbaseline = lbottom - getLineDescent(i); 2379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 238c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt int dir = getParagraphDirection(i); 239c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt int left = 0; 240c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt int right = mWidth; 241c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt 2426c488de023a4797069673dc619c1a4096079ea9eGilles Debunne if (mSpannedText) { 243c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt Spanned sp = (Spanned) buf; 2446c488de023a4797069673dc619c1a4096079ea9eGilles Debunne int textLength = buf.length(); 2456c488de023a4797069673dc619c1a4096079ea9eGilles Debunne boolean isFirstParaLine = (start == 0 || buf.charAt(start - 1) == '\n'); 2460c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt 247c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt // New batch of paragraph styles, collect into spans array. 248c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt // Compute the alignment, last alignment style wins. 249c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt // Reset tabStops, we'll rebuild if we encounter a line with 250c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt // tabs. 251c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt // We expect paragraph spans to be relatively infrequent, use 252c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt // spanEnd so that we can check less frequently. Since 253c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt // paragraph styles ought to apply to entire paragraphs, we can 254c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt // just collect the ones present at the start of the paragraph. 255c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt // If spanEnd is before the end of the paragraph, that's not 256c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt // our problem. 2576c488de023a4797069673dc619c1a4096079ea9eGilles Debunne if (start >= spanEnd && (i == firstLine || isFirstParaLine)) { 258c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt spanEnd = sp.nextSpanTransition(start, textLength, 2599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project ParagraphStyle.class); 26074d31ef2b2c42b54fa1f7cf94ea955ea67ab69a0Eric Fischer spans = getParagraphSpans(sp, start, spanEnd, ParagraphStyle.class); 2619f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt 262c0ccf0c47c00942a9d0f2670600a8f2d4d7adb5bDoug Felt paraAlign = mAlignment; 2636c488de023a4797069673dc619c1a4096079ea9eGilles Debunne for (int n = spans.length - 1; n >= 0; n--) { 2649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (spans[n] instanceof AlignmentSpan) { 265c0ccf0c47c00942a9d0f2670600a8f2d4d7adb5bDoug Felt paraAlign = ((AlignmentSpan) spans[n]).getAlignment(); 2669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project break; 2679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 2689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 2690c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt 270c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt tabStopsIsInitialized = false; 2719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 2729f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt 273c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt // Draw all leading margin spans. Adjust left or right according 274c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt // to the paragraph direction of the line. 2759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project final int length = spans.length; 276ab08c6d38ab2e575f809ca8ce4c7f095e49d258cAnish Athalye boolean useFirstLineMargin = isFirstParaLine; 277ab08c6d38ab2e575f809ca8ce4c7f095e49d258cAnish Athalye for (int n = 0; n < length; n++) { 278ab08c6d38ab2e575f809ca8ce4c7f095e49d258cAnish Athalye if (spans[n] instanceof LeadingMarginSpan2) { 279ab08c6d38ab2e575f809ca8ce4c7f095e49d258cAnish Athalye int count = ((LeadingMarginSpan2) spans[n]).getLeadingMarginLineCount(); 280ab08c6d38ab2e575f809ca8ce4c7f095e49d258cAnish Athalye int startLine = getLineForOffset(sp.getSpanStart(spans[n])); 281ab08c6d38ab2e575f809ca8ce4c7f095e49d258cAnish Athalye // if there is more than one LeadingMarginSpan2, use 282ab08c6d38ab2e575f809ca8ce4c7f095e49d258cAnish Athalye // the count that is greatest 283ab08c6d38ab2e575f809ca8ce4c7f095e49d258cAnish Athalye if (i < startLine + count) { 284ab08c6d38ab2e575f809ca8ce4c7f095e49d258cAnish Athalye useFirstLineMargin = true; 285ab08c6d38ab2e575f809ca8ce4c7f095e49d258cAnish Athalye break; 286ab08c6d38ab2e575f809ca8ce4c7f095e49d258cAnish Athalye } 287ab08c6d38ab2e575f809ca8ce4c7f095e49d258cAnish Athalye } 288ab08c6d38ab2e575f809ca8ce4c7f095e49d258cAnish Athalye } 2899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project for (int n = 0; n < length; n++) { 2909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (spans[n] instanceof LeadingMarginSpan) { 2919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project LeadingMarginSpan margin = (LeadingMarginSpan) spans[n]; 2929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (dir == DIR_RIGHT_TO_LEFT) { 2936c488de023a4797069673dc619c1a4096079ea9eGilles Debunne margin.drawLeadingMargin(canvas, paint, right, dir, ltop, 2949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project lbaseline, lbottom, buf, 29571b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt start, end, isFirstParaLine, this); 296c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt right -= margin.getLeadingMargin(useFirstLineMargin); 2979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } else { 2986c488de023a4797069673dc619c1a4096079ea9eGilles Debunne margin.drawLeadingMargin(canvas, paint, left, dir, ltop, 2999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project lbaseline, lbottom, buf, 30071b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt start, end, isFirstParaLine, this); 301c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt left += margin.getLeadingMargin(useFirstLineMargin); 3029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 3039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 3049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 3059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 3069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 307c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt boolean hasTabOrEmoji = getLineContainsTab(i); 308c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt // Can't tell if we have tabs for sure, currently 309c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt if (hasTabOrEmoji && !tabStopsIsInitialized) { 310c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt if (tabStops == null) { 311c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt tabStops = new TabStops(TAB_INCREMENT, spans); 312c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt } else { 313c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt tabStops.reset(TAB_INCREMENT, spans); 314c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt } 315c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt tabStopsIsInitialized = true; 316c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt } 317c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt 318c0ccf0c47c00942a9d0f2670600a8f2d4d7adb5bDoug Felt // Determine whether the line aligns to normal, opposite, or center. 319c0ccf0c47c00942a9d0f2670600a8f2d4d7adb5bDoug Felt Alignment align = paraAlign; 320c0ccf0c47c00942a9d0f2670600a8f2d4d7adb5bDoug Felt if (align == Alignment.ALIGN_LEFT) { 321c0ccf0c47c00942a9d0f2670600a8f2d4d7adb5bDoug Felt align = (dir == DIR_LEFT_TO_RIGHT) ? 322c0ccf0c47c00942a9d0f2670600a8f2d4d7adb5bDoug Felt Alignment.ALIGN_NORMAL : Alignment.ALIGN_OPPOSITE; 323c0ccf0c47c00942a9d0f2670600a8f2d4d7adb5bDoug Felt } else if (align == Alignment.ALIGN_RIGHT) { 324c0ccf0c47c00942a9d0f2670600a8f2d4d7adb5bDoug Felt align = (dir == DIR_LEFT_TO_RIGHT) ? 325c0ccf0c47c00942a9d0f2670600a8f2d4d7adb5bDoug Felt Alignment.ALIGN_OPPOSITE : Alignment.ALIGN_NORMAL; 326c0ccf0c47c00942a9d0f2670600a8f2d4d7adb5bDoug Felt } 327c0ccf0c47c00942a9d0f2670600a8f2d4d7adb5bDoug Felt 3289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int x; 3299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (align == Alignment.ALIGN_NORMAL) { 3309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (dir == DIR_LEFT_TO_RIGHT) { 3319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project x = left; 3329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } else { 3339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project x = right; 3349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 3359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } else { 336c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt int max = (int)getLineExtent(i, tabStops, false); 3379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (align == Alignment.ALIGN_OPPOSITE) { 338c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt if (dir == DIR_LEFT_TO_RIGHT) { 3399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project x = right - max; 3409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } else { 341c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt x = left - max; 3429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 343c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt } else { // Alignment.ALIGN_CENTER 344c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt max = max & ~1; 345c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt x = (right + left - max) >> 1; 3469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 3479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 3489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 3499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project Directions directions = getLineDirections(i); 3506c488de023a4797069673dc619c1a4096079ea9eGilles Debunne if (directions == DIRS_ALL_LEFT_TO_RIGHT && !mSpannedText && !hasTabOrEmoji) { 35171b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt // XXX: assumes there's nothing additional to be done 3526c488de023a4797069673dc619c1a4096079ea9eGilles Debunne canvas.drawText(buf, start, end, x, lbaseline, paint); 3539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } else { 354c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt tl.set(paint, buf, start, end, dir, directions, hasTabOrEmoji, tabStops); 3556c488de023a4797069673dc619c1a4096079ea9eGilles Debunne tl.draw(canvas, x, ltop, lbaseline, lbottom); 3569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 3579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 358c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt 359e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt TextLine.recycle(tl); 3609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 3619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 3629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 3636c488de023a4797069673dc619c1a4096079ea9eGilles Debunne * @hide 3646c488de023a4797069673dc619c1a4096079ea9eGilles Debunne */ 3656c488de023a4797069673dc619c1a4096079ea9eGilles Debunne public void drawBackground(Canvas canvas, Path highlight, Paint highlightPaint, 3666c488de023a4797069673dc619c1a4096079ea9eGilles Debunne int cursorOffsetVertical, int firstLine, int lastLine) { 3676c488de023a4797069673dc619c1a4096079ea9eGilles Debunne // First, draw LineBackgroundSpans. 3686c488de023a4797069673dc619c1a4096079ea9eGilles Debunne // LineBackgroundSpans know nothing about the alignment, margins, or 3696c488de023a4797069673dc619c1a4096079ea9eGilles Debunne // direction of the layout or line. XXX: Should they? 3706c488de023a4797069673dc619c1a4096079ea9eGilles Debunne // They are evaluated at each line. 3716c488de023a4797069673dc619c1a4096079ea9eGilles Debunne if (mSpannedText) { 372eca5b7385a641ae5d8c4aa2b7823a4e573737376Gilles Debunne if (mLineBackgroundSpans == null) { 373eca5b7385a641ae5d8c4aa2b7823a4e573737376Gilles Debunne mLineBackgroundSpans = new SpanSet<LineBackgroundSpan>(LineBackgroundSpan.class); 37460e3b3fa60a672583fe6294139feb940845efc9bGilles Debunne } 3756c488de023a4797069673dc619c1a4096079ea9eGilles Debunne 37660e3b3fa60a672583fe6294139feb940845efc9bGilles Debunne Spanned buffer = (Spanned) mText; 37760e3b3fa60a672583fe6294139feb940845efc9bGilles Debunne int textLength = buffer.length(); 378eca5b7385a641ae5d8c4aa2b7823a4e573737376Gilles Debunne mLineBackgroundSpans.init(buffer, 0, textLength); 37960e3b3fa60a672583fe6294139feb940845efc9bGilles Debunne 380eca5b7385a641ae5d8c4aa2b7823a4e573737376Gilles Debunne if (mLineBackgroundSpans.numberOfSpans > 0) { 38160e3b3fa60a672583fe6294139feb940845efc9bGilles Debunne int previousLineBottom = getLineTop(firstLine); 38260e3b3fa60a672583fe6294139feb940845efc9bGilles Debunne int previousLineEnd = getLineStart(firstLine); 38360e3b3fa60a672583fe6294139feb940845efc9bGilles Debunne ParagraphStyle[] spans = NO_PARA_SPANS; 38460e3b3fa60a672583fe6294139feb940845efc9bGilles Debunne int spansLength = 0; 38560e3b3fa60a672583fe6294139feb940845efc9bGilles Debunne TextPaint paint = mPaint; 38660e3b3fa60a672583fe6294139feb940845efc9bGilles Debunne int spanEnd = 0; 38760e3b3fa60a672583fe6294139feb940845efc9bGilles Debunne final int width = mWidth; 38860e3b3fa60a672583fe6294139feb940845efc9bGilles Debunne for (int i = firstLine; i <= lastLine; i++) { 38960e3b3fa60a672583fe6294139feb940845efc9bGilles Debunne int start = previousLineEnd; 39060e3b3fa60a672583fe6294139feb940845efc9bGilles Debunne int end = getLineStart(i + 1); 39160e3b3fa60a672583fe6294139feb940845efc9bGilles Debunne previousLineEnd = end; 39260e3b3fa60a672583fe6294139feb940845efc9bGilles Debunne 39360e3b3fa60a672583fe6294139feb940845efc9bGilles Debunne int ltop = previousLineBottom; 39460e3b3fa60a672583fe6294139feb940845efc9bGilles Debunne int lbottom = getLineTop(i + 1); 39560e3b3fa60a672583fe6294139feb940845efc9bGilles Debunne previousLineBottom = lbottom; 39660e3b3fa60a672583fe6294139feb940845efc9bGilles Debunne int lbaseline = lbottom - getLineDescent(i); 39760e3b3fa60a672583fe6294139feb940845efc9bGilles Debunne 39860e3b3fa60a672583fe6294139feb940845efc9bGilles Debunne if (start >= spanEnd) { 39960e3b3fa60a672583fe6294139feb940845efc9bGilles Debunne // These should be infrequent, so we'll use this so that 40060e3b3fa60a672583fe6294139feb940845efc9bGilles Debunne // we don't have to check as often. 401eca5b7385a641ae5d8c4aa2b7823a4e573737376Gilles Debunne spanEnd = mLineBackgroundSpans.getNextTransition(start, textLength); 40260e3b3fa60a672583fe6294139feb940845efc9bGilles Debunne // All LineBackgroundSpans on a line contribute to its background. 40360e3b3fa60a672583fe6294139feb940845efc9bGilles Debunne spansLength = 0; 40460e3b3fa60a672583fe6294139feb940845efc9bGilles Debunne // Duplication of the logic of getParagraphSpans 40560e3b3fa60a672583fe6294139feb940845efc9bGilles Debunne if (start != end || start == 0) { 40660e3b3fa60a672583fe6294139feb940845efc9bGilles Debunne // Equivalent to a getSpans(start, end), but filling the 'spans' local 40760e3b3fa60a672583fe6294139feb940845efc9bGilles Debunne // array instead to reduce memory allocation 408eca5b7385a641ae5d8c4aa2b7823a4e573737376Gilles Debunne for (int j = 0; j < mLineBackgroundSpans.numberOfSpans; j++) { 409eca5b7385a641ae5d8c4aa2b7823a4e573737376Gilles Debunne // equal test is valid since both intervals are not empty by 410eca5b7385a641ae5d8c4aa2b7823a4e573737376Gilles Debunne // construction 411eca5b7385a641ae5d8c4aa2b7823a4e573737376Gilles Debunne if (mLineBackgroundSpans.spanStarts[j] >= end || 412eca5b7385a641ae5d8c4aa2b7823a4e573737376Gilles Debunne mLineBackgroundSpans.spanEnds[j] <= start) continue; 413776abc24cdd18610232a50b997cce3cffa74609bAdam Lesinski spans = GrowingArrayUtils.append( 414776abc24cdd18610232a50b997cce3cffa74609bAdam Lesinski spans, spansLength, mLineBackgroundSpans.spans[j]); 415776abc24cdd18610232a50b997cce3cffa74609bAdam Lesinski spansLength++; 41660e3b3fa60a672583fe6294139feb940845efc9bGilles Debunne } 41760e3b3fa60a672583fe6294139feb940845efc9bGilles Debunne } 41860e3b3fa60a672583fe6294139feb940845efc9bGilles Debunne } 41960e3b3fa60a672583fe6294139feb940845efc9bGilles Debunne 42060e3b3fa60a672583fe6294139feb940845efc9bGilles Debunne for (int n = 0; n < spansLength; n++) { 42160e3b3fa60a672583fe6294139feb940845efc9bGilles Debunne LineBackgroundSpan lineBackgroundSpan = (LineBackgroundSpan) spans[n]; 42260e3b3fa60a672583fe6294139feb940845efc9bGilles Debunne lineBackgroundSpan.drawBackground(canvas, paint, 0, width, 42360e3b3fa60a672583fe6294139feb940845efc9bGilles Debunne ltop, lbaseline, lbottom, 42460e3b3fa60a672583fe6294139feb940845efc9bGilles Debunne buffer, start, end, i); 42560e3b3fa60a672583fe6294139feb940845efc9bGilles Debunne } 4266c488de023a4797069673dc619c1a4096079ea9eGilles Debunne } 4276c488de023a4797069673dc619c1a4096079ea9eGilles Debunne } 428eca5b7385a641ae5d8c4aa2b7823a4e573737376Gilles Debunne mLineBackgroundSpans.recycle(); 4296c488de023a4797069673dc619c1a4096079ea9eGilles Debunne } 4306c488de023a4797069673dc619c1a4096079ea9eGilles Debunne 4316c488de023a4797069673dc619c1a4096079ea9eGilles Debunne // There can be a highlight even without spans if we are drawing 4326c488de023a4797069673dc619c1a4096079ea9eGilles Debunne // a non-spanned transformation of a spanned editing buffer. 4336c488de023a4797069673dc619c1a4096079ea9eGilles Debunne if (highlight != null) { 4346c488de023a4797069673dc619c1a4096079ea9eGilles Debunne if (cursorOffsetVertical != 0) canvas.translate(0, cursorOffsetVertical); 4356c488de023a4797069673dc619c1a4096079ea9eGilles Debunne canvas.drawPath(highlight, highlightPaint); 4366c488de023a4797069673dc619c1a4096079ea9eGilles Debunne if (cursorOffsetVertical != 0) canvas.translate(0, -cursorOffsetVertical); 4376c488de023a4797069673dc619c1a4096079ea9eGilles Debunne } 4386c488de023a4797069673dc619c1a4096079ea9eGilles Debunne } 4396c488de023a4797069673dc619c1a4096079ea9eGilles Debunne 4406c488de023a4797069673dc619c1a4096079ea9eGilles Debunne /** 4416c488de023a4797069673dc619c1a4096079ea9eGilles Debunne * @param canvas 4426c488de023a4797069673dc619c1a4096079ea9eGilles Debunne * @return The range of lines that need to be drawn, possibly empty. 4436c488de023a4797069673dc619c1a4096079ea9eGilles Debunne * @hide 4446c488de023a4797069673dc619c1a4096079ea9eGilles Debunne */ 4456c488de023a4797069673dc619c1a4096079ea9eGilles Debunne public long getLineRangeForDraw(Canvas canvas) { 4466c488de023a4797069673dc619c1a4096079ea9eGilles Debunne int dtop, dbottom; 4476c488de023a4797069673dc619c1a4096079ea9eGilles Debunne 4486c488de023a4797069673dc619c1a4096079ea9eGilles Debunne synchronized (sTempRect) { 4496c488de023a4797069673dc619c1a4096079ea9eGilles Debunne if (!canvas.getClipBounds(sTempRect)) { 4506c488de023a4797069673dc619c1a4096079ea9eGilles Debunne // Negative range end used as a special flag 4516c488de023a4797069673dc619c1a4096079ea9eGilles Debunne return TextUtils.packRangeInLong(0, -1); 4526c488de023a4797069673dc619c1a4096079ea9eGilles Debunne } 4536c488de023a4797069673dc619c1a4096079ea9eGilles Debunne 4546c488de023a4797069673dc619c1a4096079ea9eGilles Debunne dtop = sTempRect.top; 4556c488de023a4797069673dc619c1a4096079ea9eGilles Debunne dbottom = sTempRect.bottom; 4566c488de023a4797069673dc619c1a4096079ea9eGilles Debunne } 4576c488de023a4797069673dc619c1a4096079ea9eGilles Debunne 4586c488de023a4797069673dc619c1a4096079ea9eGilles Debunne final int top = Math.max(dtop, 0); 4596c488de023a4797069673dc619c1a4096079ea9eGilles Debunne final int bottom = Math.min(getLineTop(getLineCount()), dbottom); 4606c488de023a4797069673dc619c1a4096079ea9eGilles Debunne 4612fba3387c31b675c419030145250e5be246c50b0Gilles Debunne if (top >= bottom) return TextUtils.packRangeInLong(0, -1); 4626c488de023a4797069673dc619c1a4096079ea9eGilles Debunne return TextUtils.packRangeInLong(getLineForVertical(top), getLineForVertical(bottom)); 4636c488de023a4797069673dc619c1a4096079ea9eGilles Debunne } 4646c488de023a4797069673dc619c1a4096079ea9eGilles Debunne 4656c488de023a4797069673dc619c1a4096079ea9eGilles Debunne /** 466c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt * Return the start position of the line, given the left and right bounds 467c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt * of the margins. 4680c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt * 469c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt * @param line the line index 470c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt * @param left the left bounds (0, or leading margin if ltr para) 471c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt * @param right the right bounds (width, minus leading margin if rtl para) 472c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt * @return the start position of the line (to right of line if rtl para) 473c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt */ 474c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt private int getLineStartPos(int line, int left, int right) { 475c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt // Adjust the point at which to start rendering depending on the 476c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt // alignment of the paragraph. 477c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt Alignment align = getParagraphAlignment(line); 478c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt int dir = getParagraphDirection(line); 479c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt 480c0ccf0c47c00942a9d0f2670600a8f2d4d7adb5bDoug Felt if (align == Alignment.ALIGN_LEFT) { 481db24f0a73f4385aea104367a4bc71b78f25dbc3fFabrice Di Meglio align = (dir == DIR_LEFT_TO_RIGHT) ? Alignment.ALIGN_NORMAL : Alignment.ALIGN_OPPOSITE; 482db24f0a73f4385aea104367a4bc71b78f25dbc3fFabrice Di Meglio } else if (align == Alignment.ALIGN_RIGHT) { 483db24f0a73f4385aea104367a4bc71b78f25dbc3fFabrice Di Meglio align = (dir == DIR_LEFT_TO_RIGHT) ? Alignment.ALIGN_OPPOSITE : Alignment.ALIGN_NORMAL; 484db24f0a73f4385aea104367a4bc71b78f25dbc3fFabrice Di Meglio } 485db24f0a73f4385aea104367a4bc71b78f25dbc3fFabrice Di Meglio 486db24f0a73f4385aea104367a4bc71b78f25dbc3fFabrice Di Meglio int x; 487db24f0a73f4385aea104367a4bc71b78f25dbc3fFabrice Di Meglio if (align == Alignment.ALIGN_NORMAL) { 488c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt if (dir == DIR_LEFT_TO_RIGHT) { 489c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt x = left; 490c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt } else { 491c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt x = right; 492c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt } 493c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt } else { 494c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt TabStops tabStops = null; 495c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt if (mSpannedText && getLineContainsTab(line)) { 496c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt Spanned spanned = (Spanned) mText; 497c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt int start = getLineStart(line); 498c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt int spanEnd = spanned.nextSpanTransition(start, spanned.length(), 499c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt TabStopSpan.class); 5006c488de023a4797069673dc619c1a4096079ea9eGilles Debunne TabStopSpan[] tabSpans = getParagraphSpans(spanned, start, spanEnd, 5016c488de023a4797069673dc619c1a4096079ea9eGilles Debunne TabStopSpan.class); 502c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt if (tabSpans.length > 0) { 503c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt tabStops = new TabStops(TAB_INCREMENT, tabSpans); 504c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt } 505c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt } 506c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt int max = (int)getLineExtent(line, tabStops, false); 507db24f0a73f4385aea104367a4bc71b78f25dbc3fFabrice Di Meglio if (align == Alignment.ALIGN_OPPOSITE) { 508c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt if (dir == DIR_LEFT_TO_RIGHT) { 509c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt x = right - max; 510c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt } else { 511db24f0a73f4385aea104367a4bc71b78f25dbc3fFabrice Di Meglio // max is negative here 512c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt x = left - max; 513c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt } 514c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt } else { // Alignment.ALIGN_CENTER 515c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt max = max & ~1; 516c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt x = (left + right - max) >> 1; 517c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt } 518c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt } 519c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt return x; 520c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt } 521c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt 522c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt /** 5239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Return the text that is displayed by this Layout. 5249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 5259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public final CharSequence getText() { 5269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return mText; 5279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 5289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 5299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 5309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Return the base Paint properties for this layout. 5319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Do NOT change the paint, which may result in funny 5329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * drawing for this layout. 5339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 5349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public final TextPaint getPaint() { 5359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return mPaint; 5369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 5379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 5389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 5399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Return the width of this layout. 5409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 5419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public final int getWidth() { 5429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return mWidth; 5439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 5449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 5459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 5469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Return the width to which this Layout is ellipsizing, or 5479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * {@link #getWidth} if it is not doing anything special. 5489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 5499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public int getEllipsizedWidth() { 5509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return mWidth; 5519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 5529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 5539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 5549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Increase the width of this layout to the specified width. 55571b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt * Be careful to use this only when you know it is appropriate— 5569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * it does not cause the text to reflow to use the full new width. 5579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 5589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public final void increaseWidthTo(int wid) { 5599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (wid < mWidth) { 5609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project throw new RuntimeException("attempted to reduce Layout width"); 5619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 5629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 5639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mWidth = wid; 5649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 5659f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt 5669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 5679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Return the total height of this layout. 5689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 5699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public int getHeight() { 57071b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt return getLineTop(getLineCount()); 5719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 5729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 5739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 5749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Return the base alignment of this layout. 5759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 5769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public final Alignment getAlignment() { 5779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return mAlignment; 5789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 5799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 5809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 5819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Return what the text height is multiplied by to get the line height. 5829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 5839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public final float getSpacingMultiplier() { 5849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return mSpacingMult; 5859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 5869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 5879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 5889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Return the number of units of leading that are added to each line. 5899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 5909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public final float getSpacingAdd() { 5919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return mSpacingAdd; 5929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 5939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 5949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 595cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt * Return the heuristic used to determine paragraph text direction. 596cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt * @hide 597cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt */ 598cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt public final TextDirectionHeuristic getTextDirectionHeuristic() { 599cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt return mTextDir; 600cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt } 601cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt 602cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt /** 6039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Return the number of lines of text in this layout. 6049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 6059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public abstract int getLineCount(); 6069f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt 6079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 6089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Return the baseline for the specified line (0…getLineCount() - 1) 6099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * If bounds is not null, return the top, left, right, bottom extents 6109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * of the specified line in it. 6119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * @param line which line to examine (0..getLineCount() - 1) 6129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * @param bounds Optional. If not null, it returns the extent of the line 6139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * @return the Y-coordinate of the baseline 6149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 6159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public int getLineBounds(int line, Rect bounds) { 6169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (bounds != null) { 6179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project bounds.left = 0; // ??? 6189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project bounds.top = getLineTop(line); 6199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project bounds.right = mWidth; // ??? 62071b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt bounds.bottom = getLineTop(line + 1); 6219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 6229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return getLineBaseline(line); 6239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 6249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 6259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 62671b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt * Return the vertical position of the top of the specified line 62771b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt * (0…getLineCount()). 62871b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt * If the specified line is equal to the line count, returns the 6299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * bottom of the last line. 6309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 6319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public abstract int getLineTop(int line); 6329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 6339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 63471b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt * Return the descent of the specified line(0…getLineCount() - 1). 6359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 6369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public abstract int getLineDescent(int line); 6379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 6389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 63971b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt * Return the text offset of the beginning of the specified line ( 64071b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt * 0…getLineCount()). If the specified line is equal to the line 64171b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt * count, returns the length of the text. 6429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 6439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public abstract int getLineStart(int line); 6449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 6459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 64671b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt * Returns the primary directionality of the paragraph containing the 64771b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt * specified line, either 1 for left-to-right lines, or -1 for right-to-left 64871b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt * lines (see {@link #DIR_LEFT_TO_RIGHT}, {@link #DIR_RIGHT_TO_LEFT}). 6499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 6509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public abstract int getParagraphDirection(int line); 6519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 6529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 653105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project * Returns whether the specified line contains one or more 654105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project * characters that need to be handled specially, like tabs 655105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project * or emoji. 6569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 6579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public abstract boolean getLineContainsTab(int line); 6589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 6599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 66071b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt * Returns the directional run information for the specified line. 6619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * The array alternates counts of characters in left-to-right 6629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * and right-to-left segments of the line. 66371b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt * 66471b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt * <p>NOTE: this is inadequate to support bidirectional text, and will change. 6659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 6669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public abstract Directions getLineDirections(int line); 6679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 6689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 6699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Returns the (negative) number of extra pixels of ascent padding in the 6709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * top line of the Layout. 6719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 6729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public abstract int getTopPadding(); 6739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 6749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 6759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Returns the number of extra pixels of descent padding in the 6769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * bottom line of the Layout. 6779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 6789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public abstract int getBottomPadding(); 6799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 6804e0c5e55e171532760d5f51e0165563827129d4eDoug Felt 6814e0c5e55e171532760d5f51e0165563827129d4eDoug Felt /** 6824e0c5e55e171532760d5f51e0165563827129d4eDoug Felt * Returns true if the character at offset and the preceding character 6834e0c5e55e171532760d5f51e0165563827129d4eDoug Felt * are at different run levels (and thus there's a split caret). 6844e0c5e55e171532760d5f51e0165563827129d4eDoug Felt * @param offset the offset 6854e0c5e55e171532760d5f51e0165563827129d4eDoug Felt * @return true if at a level boundary 686f75c97e023af7d4ad9a8c129d4ea282b1c3b8f94Gilles Debunne * @hide 6874e0c5e55e171532760d5f51e0165563827129d4eDoug Felt */ 688f75c97e023af7d4ad9a8c129d4ea282b1c3b8f94Gilles Debunne public boolean isLevelBoundary(int offset) { 6899f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt int line = getLineForOffset(offset); 6904e0c5e55e171532760d5f51e0165563827129d4eDoug Felt Directions dirs = getLineDirections(line); 6914e0c5e55e171532760d5f51e0165563827129d4eDoug Felt if (dirs == DIRS_ALL_LEFT_TO_RIGHT || dirs == DIRS_ALL_RIGHT_TO_LEFT) { 6924e0c5e55e171532760d5f51e0165563827129d4eDoug Felt return false; 6934e0c5e55e171532760d5f51e0165563827129d4eDoug Felt } 6944e0c5e55e171532760d5f51e0165563827129d4eDoug Felt 6954e0c5e55e171532760d5f51e0165563827129d4eDoug Felt int[] runs = dirs.mDirections; 6969f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt int lineStart = getLineStart(line); 6974e0c5e55e171532760d5f51e0165563827129d4eDoug Felt int lineEnd = getLineEnd(line); 6984e0c5e55e171532760d5f51e0165563827129d4eDoug Felt if (offset == lineStart || offset == lineEnd) { 6994e0c5e55e171532760d5f51e0165563827129d4eDoug Felt int paraLevel = getParagraphDirection(line) == 1 ? 0 : 1; 7004e0c5e55e171532760d5f51e0165563827129d4eDoug Felt int runIndex = offset == lineStart ? 0 : runs.length - 2; 7014e0c5e55e171532760d5f51e0165563827129d4eDoug Felt return ((runs[runIndex + 1] >>> RUN_LEVEL_SHIFT) & RUN_LEVEL_MASK) != paraLevel; 7024e0c5e55e171532760d5f51e0165563827129d4eDoug Felt } 7034e0c5e55e171532760d5f51e0165563827129d4eDoug Felt 7044e0c5e55e171532760d5f51e0165563827129d4eDoug Felt offset -= lineStart; 7059f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt for (int i = 0; i < runs.length; i += 2) { 7064e0c5e55e171532760d5f51e0165563827129d4eDoug Felt if (offset == runs[i]) { 7074e0c5e55e171532760d5f51e0165563827129d4eDoug Felt return true; 7089f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt } 7099f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt } 7104e0c5e55e171532760d5f51e0165563827129d4eDoug Felt return false; 7119f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt } 7129f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt 71334d2eba560f83f4eb665cdc039cf02bf96c201daFabrice Di Meglio /** 71434d2eba560f83f4eb665cdc039cf02bf96c201daFabrice Di Meglio * Returns true if the character at offset is right to left (RTL). 71534d2eba560f83f4eb665cdc039cf02bf96c201daFabrice Di Meglio * @param offset the offset 71634d2eba560f83f4eb665cdc039cf02bf96c201daFabrice Di Meglio * @return true if the character is RTL, false if it is LTR 71734d2eba560f83f4eb665cdc039cf02bf96c201daFabrice Di Meglio */ 71834d2eba560f83f4eb665cdc039cf02bf96c201daFabrice Di Meglio public boolean isRtlCharAt(int offset) { 71934d2eba560f83f4eb665cdc039cf02bf96c201daFabrice Di Meglio int line = getLineForOffset(offset); 72034d2eba560f83f4eb665cdc039cf02bf96c201daFabrice Di Meglio Directions dirs = getLineDirections(line); 72134d2eba560f83f4eb665cdc039cf02bf96c201daFabrice Di Meglio if (dirs == DIRS_ALL_LEFT_TO_RIGHT) { 72234d2eba560f83f4eb665cdc039cf02bf96c201daFabrice Di Meglio return false; 72334d2eba560f83f4eb665cdc039cf02bf96c201daFabrice Di Meglio } 72434d2eba560f83f4eb665cdc039cf02bf96c201daFabrice Di Meglio if (dirs == DIRS_ALL_RIGHT_TO_LEFT) { 72534d2eba560f83f4eb665cdc039cf02bf96c201daFabrice Di Meglio return true; 72634d2eba560f83f4eb665cdc039cf02bf96c201daFabrice Di Meglio } 72734d2eba560f83f4eb665cdc039cf02bf96c201daFabrice Di Meglio int[] runs = dirs.mDirections; 72834d2eba560f83f4eb665cdc039cf02bf96c201daFabrice Di Meglio int lineStart = getLineStart(line); 72934d2eba560f83f4eb665cdc039cf02bf96c201daFabrice Di Meglio for (int i = 0; i < runs.length; i += 2) { 730875062059c4932ddf5649720c26e7f59b6ab9978Raph Levien int start = lineStart + runs[i]; 731875062059c4932ddf5649720c26e7f59b6ab9978Raph Levien int limit = start + (runs[i+1] & RUN_LENGTH_MASK); 732875062059c4932ddf5649720c26e7f59b6ab9978Raph Levien if (offset >= start && offset < limit) { 73334d2eba560f83f4eb665cdc039cf02bf96c201daFabrice Di Meglio int level = (runs[i+1] >>> RUN_LEVEL_SHIFT) & RUN_LEVEL_MASK; 73434d2eba560f83f4eb665cdc039cf02bf96c201daFabrice Di Meglio return ((level & 1) != 0); 73534d2eba560f83f4eb665cdc039cf02bf96c201daFabrice Di Meglio } 73634d2eba560f83f4eb665cdc039cf02bf96c201daFabrice Di Meglio } 73734d2eba560f83f4eb665cdc039cf02bf96c201daFabrice Di Meglio // Should happen only if the offset is "out of bounds" 73834d2eba560f83f4eb665cdc039cf02bf96c201daFabrice Di Meglio return false; 73934d2eba560f83f4eb665cdc039cf02bf96c201daFabrice Di Meglio } 74034d2eba560f83f4eb665cdc039cf02bf96c201daFabrice Di Meglio 7419f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt private boolean primaryIsTrailingPrevious(int offset) { 7429f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt int line = getLineForOffset(offset); 7439f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt int lineStart = getLineStart(line); 7444e0c5e55e171532760d5f51e0165563827129d4eDoug Felt int lineEnd = getLineEnd(line); 7459f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt int[] runs = getLineDirections(line).mDirections; 7469f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt 7479f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt int levelAt = -1; 7489f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt for (int i = 0; i < runs.length; i += 2) { 7499f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt int start = lineStart + runs[i]; 7509f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt int limit = start + (runs[i+1] & RUN_LENGTH_MASK); 7519f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt if (limit > lineEnd) { 7529f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt limit = lineEnd; 7539f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt } 7549f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt if (offset >= start && offset < limit) { 7559f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt if (offset > start) { 7569f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt // Previous character is at same level, so don't use trailing. 7579f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt return false; 7589f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt } 7599f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt levelAt = (runs[i+1] >>> RUN_LEVEL_SHIFT) & RUN_LEVEL_MASK; 7609f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt break; 7619f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt } 7629f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt } 7639f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt if (levelAt == -1) { 7649f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt // Offset was limit of line. 7659f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt levelAt = getParagraphDirection(line) == 1 ? 0 : 1; 7669f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt } 7679f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt 7689f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt // At level boundary, check previous level. 7699f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt int levelBefore = -1; 7709f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt if (offset == lineStart) { 7719f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt levelBefore = getParagraphDirection(line) == 1 ? 0 : 1; 7729f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt } else { 7739f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt offset -= 1; 7749f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt for (int i = 0; i < runs.length; i += 2) { 7759f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt int start = lineStart + runs[i]; 7769f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt int limit = start + (runs[i+1] & RUN_LENGTH_MASK); 7779f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt if (limit > lineEnd) { 7789f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt limit = lineEnd; 7799f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt } 7809f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt if (offset >= start && offset < limit) { 7819f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt levelBefore = (runs[i+1] >>> RUN_LEVEL_SHIFT) & RUN_LEVEL_MASK; 7829f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt break; 7839f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt } 7849f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt } 7859f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt } 7869f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt 7879f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt return levelBefore < levelAt; 7889f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt } 7899f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt 7909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 7919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Get the primary horizontal position for the specified text offset. 7929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * This is the location where a new character would be inserted in 7939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * the paragraph's primary direction. 7949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 7959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public float getPrimaryHorizontal(int offset) { 796afe8e9b6d033cb854afa3024d8198a32896a804aRaph Levien return getPrimaryHorizontal(offset, false /* not clamped */); 797afe8e9b6d033cb854afa3024d8198a32896a804aRaph Levien } 798afe8e9b6d033cb854afa3024d8198a32896a804aRaph Levien 799afe8e9b6d033cb854afa3024d8198a32896a804aRaph Levien /** 800afe8e9b6d033cb854afa3024d8198a32896a804aRaph Levien * Get the primary horizontal position for the specified text offset, but 801afe8e9b6d033cb854afa3024d8198a32896a804aRaph Levien * optionally clamp it so that it doesn't exceed the width of the layout. 802afe8e9b6d033cb854afa3024d8198a32896a804aRaph Levien * @hide 803afe8e9b6d033cb854afa3024d8198a32896a804aRaph Levien */ 804afe8e9b6d033cb854afa3024d8198a32896a804aRaph Levien public float getPrimaryHorizontal(int offset, boolean clamped) { 8059f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt boolean trailing = primaryIsTrailingPrevious(offset); 806afe8e9b6d033cb854afa3024d8198a32896a804aRaph Levien return getHorizontal(offset, trailing, clamped); 8079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 8089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 8099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 8109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Get the secondary horizontal position for the specified text offset. 8119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * This is the location where a new character would be inserted in 8129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * the direction other than the paragraph's primary direction. 8139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 8149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public float getSecondaryHorizontal(int offset) { 815afe8e9b6d033cb854afa3024d8198a32896a804aRaph Levien return getSecondaryHorizontal(offset, false /* not clamped */); 816afe8e9b6d033cb854afa3024d8198a32896a804aRaph Levien } 817afe8e9b6d033cb854afa3024d8198a32896a804aRaph Levien 818afe8e9b6d033cb854afa3024d8198a32896a804aRaph Levien /** 819afe8e9b6d033cb854afa3024d8198a32896a804aRaph Levien * Get the secondary horizontal position for the specified text offset, but 820afe8e9b6d033cb854afa3024d8198a32896a804aRaph Levien * optionally clamp it so that it doesn't exceed the width of the layout. 821afe8e9b6d033cb854afa3024d8198a32896a804aRaph Levien * @hide 822afe8e9b6d033cb854afa3024d8198a32896a804aRaph Levien */ 823afe8e9b6d033cb854afa3024d8198a32896a804aRaph Levien public float getSecondaryHorizontal(int offset, boolean clamped) { 8249f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt boolean trailing = primaryIsTrailingPrevious(offset); 825afe8e9b6d033cb854afa3024d8198a32896a804aRaph Levien return getHorizontal(offset, !trailing, clamped); 8269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 8279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 828afe8e9b6d033cb854afa3024d8198a32896a804aRaph Levien private float getHorizontal(int offset, boolean trailing, boolean clamped) { 8299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int line = getLineForOffset(offset); 8309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 831afe8e9b6d033cb854afa3024d8198a32896a804aRaph Levien return getHorizontal(offset, trailing, line, clamped); 8329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 8339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 834afe8e9b6d033cb854afa3024d8198a32896a804aRaph Levien private float getHorizontal(int offset, boolean trailing, int line, boolean clamped) { 8359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int start = getLineStart(line); 836e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt int end = getLineEnd(line); 8379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int dir = getParagraphDirection(line); 838c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt boolean hasTabOrEmoji = getLineContainsTab(line); 8399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project Directions directions = getLineDirections(line); 8409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 841c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt TabStops tabStops = null; 842c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt if (hasTabOrEmoji && mText instanceof Spanned) { 843c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt // Just checking this line should be good enough, tabs should be 844c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt // consistent across all lines in a paragraph. 84574d31ef2b2c42b54fa1f7cf94ea955ea67ab69a0Eric Fischer TabStopSpan[] tabs = getParagraphSpans((Spanned) mText, start, end, TabStopSpan.class); 846c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt if (tabs.length > 0) { 847c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt tabStops = new TabStops(TAB_INCREMENT, tabs); // XXX should reuse 848c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt } 8499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 8509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 851e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt TextLine tl = TextLine.obtain(); 852c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt tl.set(mPaint, mText, start, end, dir, directions, hasTabOrEmoji, tabStops); 853e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt float wid = tl.measure(offset - start, trailing, null); 854e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt TextLine.recycle(tl); 8559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 856afe8e9b6d033cb854afa3024d8198a32896a804aRaph Levien if (clamped && wid > mWidth) { 857afe8e9b6d033cb854afa3024d8198a32896a804aRaph Levien wid = mWidth; 858afe8e9b6d033cb854afa3024d8198a32896a804aRaph Levien } 8599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int left = getParagraphLeft(line); 8609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int right = getParagraphRight(line); 8619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 862c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt return getLineStartPos(line, left, right) + wid; 8639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 8649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 8659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 8669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Get the leftmost position that should be exposed for horizontal 8679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * scrolling on the specified line. 8689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 8699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public float getLineLeft(int line) { 8709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int dir = getParagraphDirection(line); 8719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project Alignment align = getParagraphAlignment(line); 8729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 873c0ccf0c47c00942a9d0f2670600a8f2d4d7adb5bDoug Felt if (align == Alignment.ALIGN_LEFT) { 874c0ccf0c47c00942a9d0f2670600a8f2d4d7adb5bDoug Felt return 0; 875c0ccf0c47c00942a9d0f2670600a8f2d4d7adb5bDoug Felt } else if (align == Alignment.ALIGN_NORMAL) { 8769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (dir == DIR_RIGHT_TO_LEFT) 8779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return getParagraphRight(line) - getLineMax(line); 8789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project else 8799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return 0; 880c0ccf0c47c00942a9d0f2670600a8f2d4d7adb5bDoug Felt } else if (align == Alignment.ALIGN_RIGHT) { 881c0ccf0c47c00942a9d0f2670600a8f2d4d7adb5bDoug Felt return mWidth - getLineMax(line); 8829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } else if (align == Alignment.ALIGN_OPPOSITE) { 8839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (dir == DIR_RIGHT_TO_LEFT) 8849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return 0; 8859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project else 8869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return mWidth - getLineMax(line); 8879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } else { /* align == Alignment.ALIGN_CENTER */ 8889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int left = getParagraphLeft(line); 8899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int right = getParagraphRight(line); 8909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int max = ((int) getLineMax(line)) & ~1; 8919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 8929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return left + ((right - left) - max) / 2; 8939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 8949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 8959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 8969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 8979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Get the rightmost position that should be exposed for horizontal 8989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * scrolling on the specified line. 8999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 9009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public float getLineRight(int line) { 9019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int dir = getParagraphDirection(line); 9029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project Alignment align = getParagraphAlignment(line); 9039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 904c0ccf0c47c00942a9d0f2670600a8f2d4d7adb5bDoug Felt if (align == Alignment.ALIGN_LEFT) { 905c0ccf0c47c00942a9d0f2670600a8f2d4d7adb5bDoug Felt return getParagraphLeft(line) + getLineMax(line); 906c0ccf0c47c00942a9d0f2670600a8f2d4d7adb5bDoug Felt } else if (align == Alignment.ALIGN_NORMAL) { 9079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (dir == DIR_RIGHT_TO_LEFT) 9089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return mWidth; 9099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project else 9109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return getParagraphLeft(line) + getLineMax(line); 911c0ccf0c47c00942a9d0f2670600a8f2d4d7adb5bDoug Felt } else if (align == Alignment.ALIGN_RIGHT) { 912c0ccf0c47c00942a9d0f2670600a8f2d4d7adb5bDoug Felt return mWidth; 9139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } else if (align == Alignment.ALIGN_OPPOSITE) { 9149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (dir == DIR_RIGHT_TO_LEFT) 9159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return getLineMax(line); 9169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project else 9179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return mWidth; 9189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } else { /* align == Alignment.ALIGN_CENTER */ 9199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int left = getParagraphLeft(line); 9209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int right = getParagraphRight(line); 9219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int max = ((int) getLineMax(line)) & ~1; 9229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 9239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return right - ((right - left) - max) / 2; 9249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 9259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 9269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 9279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 9280c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt * Gets the unsigned horizontal extent of the specified line, including 929c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt * leading margin indent, but excluding trailing whitespace. 9309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 9319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public float getLineMax(int line) { 932c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt float margin = getParagraphLeadingMargin(line); 933c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt float signedExtent = getLineExtent(line, false); 934c14b3ada5cccfdd872ccf72d928fb0088f3ce00dAnish Athalye return margin + (signedExtent >= 0 ? signedExtent : -signedExtent); 9359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 9369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 9379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 938c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt * Gets the unsigned horizontal extent of the specified line, including 939c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt * leading margin indent and trailing whitespace. 9409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 9419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public float getLineWidth(int line) { 942c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt float margin = getParagraphLeadingMargin(line); 943c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt float signedExtent = getLineExtent(line, true); 944c14b3ada5cccfdd872ccf72d928fb0088f3ce00dAnish Athalye return margin + (signedExtent >= 0 ? signedExtent : -signedExtent); 9459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 9469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 947c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt /** 948c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt * Like {@link #getLineExtent(int,TabStops,boolean)} but determines the 949c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt * tab stops instead of using the ones passed in. 950c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt * @param line the index of the line 951c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt * @param full whether to include trailing whitespace 952c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt * @return the extent of the line 953c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt */ 954c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt private float getLineExtent(int line, boolean full) { 9559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int start = getLineStart(line); 956e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt int end = full ? getLineEnd(line) : getLineVisibleEnd(line); 957c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt 958c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt boolean hasTabsOrEmoji = getLineContainsTab(line); 959c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt TabStops tabStops = null; 960c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt if (hasTabsOrEmoji && mText instanceof Spanned) { 961c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt // Just checking this line should be good enough, tabs should be 962c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt // consistent across all lines in a paragraph. 96374d31ef2b2c42b54fa1f7cf94ea955ea67ab69a0Eric Fischer TabStopSpan[] tabs = getParagraphSpans((Spanned) mText, start, end, TabStopSpan.class); 964c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt if (tabs.length > 0) { 965c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt tabStops = new TabStops(TAB_INCREMENT, tabs); // XXX should reuse 966c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt } 967c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt } 968c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt Directions directions = getLineDirections(line); 9698059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio // Returned directions can actually be null 9708059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio if (directions == null) { 9718059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio return 0f; 9728059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio } 973c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt int dir = getParagraphDirection(line); 974c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt 975c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt TextLine tl = TextLine.obtain(); 976c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt tl.set(mPaint, mText, start, end, dir, directions, hasTabsOrEmoji, tabStops); 977c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt float width = tl.metrics(null); 978c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt TextLine.recycle(tl); 979c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt return width; 980c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt } 981c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt 982c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt /** 983c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt * Returns the signed horizontal extent of the specified line, excluding 984c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt * leading margin. If full is false, excludes trailing whitespace. 985c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt * @param line the index of the line 986c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt * @param tabStops the tab stops, can be null if we know they're not used. 987c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt * @param full whether to include trailing whitespace 988c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt * @return the extent of the text on this line 989c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt */ 990c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt private float getLineExtent(int line, TabStops tabStops, boolean full) { 991c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt int start = getLineStart(line); 992c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt int end = full ? getLineEnd(line) : getLineVisibleEnd(line); 993c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt boolean hasTabsOrEmoji = getLineContainsTab(line); 994e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt Directions directions = getLineDirections(line); 995c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt int dir = getParagraphDirection(line); 9969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 997e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt TextLine tl = TextLine.obtain(); 998c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt tl.set(mPaint, mText, start, end, dir, directions, hasTabsOrEmoji, tabStops); 999e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt float width = tl.metrics(null); 1000e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt TextLine.recycle(tl); 1001e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt return width; 10029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 10039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 10049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 10059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Get the line number corresponding to the specified vertical position. 10069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * If you ask for a position above 0, you get 0; if you ask for a position 10079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * below the bottom of the text, you get the last line. 10089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 10099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // FIXME: It may be faster to do a linear search for layouts without many lines. 10109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public int getLineForVertical(int vertical) { 10119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int high = getLineCount(), low = -1, guess; 10129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 10139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project while (high - low > 1) { 10149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project guess = (high + low) / 2; 10159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 10169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (getLineTop(guess) > vertical) 10179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project high = guess; 10189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project else 10199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project low = guess; 10209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 10219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 10229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (low < 0) 10239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return 0; 10249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project else 10259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return low; 10269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 10279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 10289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 10299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Get the line number on which the specified text offset appears. 10309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * If you ask for a position before 0, you get 0; if you ask for a position 10319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * beyond the end of the text, you get the last line. 10329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 10339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public int getLineForOffset(int offset) { 10349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int high = getLineCount(), low = -1, guess; 10359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 10369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project while (high - low > 1) { 10379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project guess = (high + low) / 2; 10389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 10399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (getLineStart(guess) > offset) 10409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project high = guess; 10419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project else 10429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project low = guess; 10439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 10449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 10459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (low < 0) 10469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return 0; 10479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project else 10489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return low; 10499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 10509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 10519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 10529f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt * Get the character offset on the specified line whose position is 10539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * closest to the specified horizontal position. 10549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 10559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public int getOffsetForHorizontal(int line, float horiz) { 10569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int max = getLineEnd(line) - 1; 10579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int min = getLineStart(line); 10589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project Directions dirs = getLineDirections(line); 10599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 10609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (line == getLineCount() - 1) 10619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project max++; 10629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 10639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int best = min; 10649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project float bestdist = Math.abs(getPrimaryHorizontal(best) - horiz); 10659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 10669f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt for (int i = 0; i < dirs.mDirections.length; i += 2) { 10679f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt int here = min + dirs.mDirections[i]; 10689f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt int there = here + (dirs.mDirections[i+1] & RUN_LENGTH_MASK); 10699f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt int swap = (dirs.mDirections[i+1] & RUN_RTL_FLAG) != 0 ? -1 : 1; 10709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 10719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (there > max) 10729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project there = max; 10739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int high = there - 1 + 1, low = here + 1 - 1, guess; 10749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 10759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project while (high - low > 1) { 10769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project guess = (high + low) / 2; 10779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int adguess = getOffsetAtStartOf(guess); 10789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 10799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (getPrimaryHorizontal(adguess) * swap >= horiz * swap) 10809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project high = guess; 10819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project else 10829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project low = guess; 10839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 10849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 10859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (low < here + 1) 10869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project low = here + 1; 10879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 10889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (low < there) { 10899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project low = getOffsetAtStartOf(low); 10909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 10919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project float dist = Math.abs(getPrimaryHorizontal(low) - horiz); 10929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 10939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int aft = TextUtils.getOffsetAfter(mText, low); 10949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (aft < there) { 10959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project float other = Math.abs(getPrimaryHorizontal(aft) - horiz); 10969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 10979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (other < dist) { 10989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project dist = other; 10999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project low = aft; 11009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 11019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 11029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 11039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (dist < bestdist) { 11049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project bestdist = dist; 11059f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt best = low; 11069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 11079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 11089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 11099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project float dist = Math.abs(getPrimaryHorizontal(here) - horiz); 11109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 11119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (dist < bestdist) { 11129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project bestdist = dist; 11139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project best = here; 11149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 11159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 11169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 11179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project float dist = Math.abs(getPrimaryHorizontal(max) - horiz); 11189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1119373b7a8d4e9dce4f71539d4dbcf627fd3e1a39daRaph Levien if (dist <= bestdist) { 11209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project bestdist = dist; 11219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project best = max; 11229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 11239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 11249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return best; 11259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 11269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 11279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 11289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Return the text offset after the last character on the specified line. 11299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 11309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public final int getLineEnd(int line) { 11319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return getLineStart(line + 1); 11329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 11339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 11349f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt /** 11359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Return the text offset after the last visible character (so whitespace 11369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * is not counted) on the specified line. 11379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 11389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public int getLineVisibleEnd(int line) { 11399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return getLineVisibleEnd(line, getLineStart(line), getLineStart(line+1)); 11409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 11419f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt 11429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private int getLineVisibleEnd(int line, int start, int end) { 11439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project CharSequence text = mText; 11449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project char ch; 11459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (line == getLineCount() - 1) { 11469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return end; 11479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 11489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 11499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project for (; end > start; end--) { 11509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project ch = text.charAt(end - 1); 11519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 11529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (ch == '\n') { 11539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return end - 1; 11549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 11559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 11569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (ch != ' ' && ch != '\t') { 11579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project break; 11589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 11599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 11609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 11619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 11629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return end; 11639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 11649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 11659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 11669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Return the vertical position of the bottom of the specified line. 11679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 11689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public final int getLineBottom(int line) { 11699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return getLineTop(line + 1); 11709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 11719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 11729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 11739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Return the vertical position of the baseline of the specified line. 11749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 11759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public final int getLineBaseline(int line) { 11769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // getLineTop(line+1) == getLineTop(line) 11779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return getLineTop(line+1) - getLineDescent(line); 11789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 11799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 11809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 11819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Get the ascent of the text on the specified line. 11829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * The return value is negative to match the Paint.ascent() convention. 11839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 11849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public final int getLineAscent(int line) { 11859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // getLineTop(line+1) - getLineDescent(line) == getLineBaseLine(line) 11869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return getLineTop(line) - (getLineTop(line+1) - getLineDescent(line)); 11879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 11889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 11899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public int getOffsetToLeftOf(int offset) { 11909f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt return getOffsetToLeftRightOf(offset, true); 11919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 11929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 11939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public int getOffsetToRightOf(int offset) { 11949f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt return getOffsetToLeftRightOf(offset, false); 11959f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt } 11969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 11979f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt private int getOffsetToLeftRightOf(int caret, boolean toLeft) { 11989f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt int line = getLineForOffset(caret); 11999f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt int lineStart = getLineStart(line); 12009f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt int lineEnd = getLineEnd(line); 1201e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt int lineDir = getParagraphDirection(line); 1202e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 1203162bf0f1b9fd5d78ffa801917994f6222c6efd85Gilles Debunne boolean lineChanged = false; 1204e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt boolean advance = toLeft == (lineDir == DIR_RIGHT_TO_LEFT); 1205162bf0f1b9fd5d78ffa801917994f6222c6efd85Gilles Debunne // if walking off line, look at the line we're headed to 1206162bf0f1b9fd5d78ffa801917994f6222c6efd85Gilles Debunne if (advance) { 1207162bf0f1b9fd5d78ffa801917994f6222c6efd85Gilles Debunne if (caret == lineEnd) { 1208162bf0f1b9fd5d78ffa801917994f6222c6efd85Gilles Debunne if (line < getLineCount() - 1) { 1209162bf0f1b9fd5d78ffa801917994f6222c6efd85Gilles Debunne lineChanged = true; 1210162bf0f1b9fd5d78ffa801917994f6222c6efd85Gilles Debunne ++line; 1211162bf0f1b9fd5d78ffa801917994f6222c6efd85Gilles Debunne } else { 1212162bf0f1b9fd5d78ffa801917994f6222c6efd85Gilles Debunne return caret; // at very end, don't move 1213162bf0f1b9fd5d78ffa801917994f6222c6efd85Gilles Debunne } 1214162bf0f1b9fd5d78ffa801917994f6222c6efd85Gilles Debunne } 1215162bf0f1b9fd5d78ffa801917994f6222c6efd85Gilles Debunne } else { 1216e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (caret == lineStart) { 1217e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (line > 0) { 1218162bf0f1b9fd5d78ffa801917994f6222c6efd85Gilles Debunne lineChanged = true; 1219e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt --line; 1220e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } else { 1221e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt return caret; // at very start, don't move 1222e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 12239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1224162bf0f1b9fd5d78ffa801917994f6222c6efd85Gilles Debunne } 12259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1226162bf0f1b9fd5d78ffa801917994f6222c6efd85Gilles Debunne if (lineChanged) { 1227e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt lineStart = getLineStart(line); 1228e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt lineEnd = getLineEnd(line); 1229e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt int newDir = getParagraphDirection(line); 1230e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (newDir != lineDir) { 1231e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // unusual case. we want to walk onto the line, but it runs 1232e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // in a different direction than this one, so we fake movement 1233e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // in the opposite direction. 1234e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt toLeft = !toLeft; 1235e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt lineDir = newDir; 12369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 12379f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt } 12389f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt 1239e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt Directions directions = getLineDirections(line); 12409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1241e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt TextLine tl = TextLine.obtain(); 1242e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // XXX: we don't care about tabs 1243e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt tl.set(mPaint, mText, lineStart, lineEnd, lineDir, directions, false, null); 1244e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt caret = lineStart + tl.getOffsetToLeftRightOf(caret - lineStart, toLeft); 1245e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt tl = TextLine.recycle(tl); 1246e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt return caret; 12479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 12489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 12499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private int getOffsetAtStartOf(int offset) { 12509f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt // XXX this probably should skip local reorderings and 12519f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt // zero-width characters, look at callers 12529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (offset == 0) 12539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return 0; 12549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 12559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project CharSequence text = mText; 12569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project char c = text.charAt(offset); 12579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 12589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (c >= '\uDC00' && c <= '\uDFFF') { 12599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project char c1 = text.charAt(offset - 1); 12609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 12619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (c1 >= '\uD800' && c1 <= '\uDBFF') 12629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project offset -= 1; 12639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 12649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 12659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (mSpannedText) { 12669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project ReplacementSpan[] spans = ((Spanned) text).getSpans(offset, offset, 12679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project ReplacementSpan.class); 12689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 12699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project for (int i = 0; i < spans.length; i++) { 12709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int start = ((Spanned) text).getSpanStart(spans[i]); 12719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int end = ((Spanned) text).getSpanEnd(spans[i]); 12729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 12739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (start < offset && end > offset) 12749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project offset = start; 12759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 12769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 12779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 12789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return offset; 12799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 12809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 12819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 1282afe8e9b6d033cb854afa3024d8198a32896a804aRaph Levien * Determine whether we should clamp cursor position. Currently it's 1283afe8e9b6d033cb854afa3024d8198a32896a804aRaph Levien * only robust for left-aligned displays. 1284afe8e9b6d033cb854afa3024d8198a32896a804aRaph Levien * @hide 1285afe8e9b6d033cb854afa3024d8198a32896a804aRaph Levien */ 1286afe8e9b6d033cb854afa3024d8198a32896a804aRaph Levien public boolean shouldClampCursor(int line) { 1287afe8e9b6d033cb854afa3024d8198a32896a804aRaph Levien // Only clamp cursor position in left-aligned displays. 1288afe8e9b6d033cb854afa3024d8198a32896a804aRaph Levien switch (getParagraphAlignment(line)) { 1289afe8e9b6d033cb854afa3024d8198a32896a804aRaph Levien case ALIGN_LEFT: 1290afe8e9b6d033cb854afa3024d8198a32896a804aRaph Levien return true; 1291afe8e9b6d033cb854afa3024d8198a32896a804aRaph Levien case ALIGN_NORMAL: 1292afe8e9b6d033cb854afa3024d8198a32896a804aRaph Levien return getParagraphDirection(line) > 0; 1293afe8e9b6d033cb854afa3024d8198a32896a804aRaph Levien default: 1294afe8e9b6d033cb854afa3024d8198a32896a804aRaph Levien return false; 1295afe8e9b6d033cb854afa3024d8198a32896a804aRaph Levien } 1296afe8e9b6d033cb854afa3024d8198a32896a804aRaph Levien 1297afe8e9b6d033cb854afa3024d8198a32896a804aRaph Levien } 1298afe8e9b6d033cb854afa3024d8198a32896a804aRaph Levien /** 12999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Fills in the specified Path with a representation of a cursor 13009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * at the specified offset. This will often be a vertical line 13014e0c5e55e171532760d5f51e0165563827129d4eDoug Felt * but can be multiple discontinuous lines in text with multiple 13029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * directionalities. 13039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 13049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public void getCursorPath(int point, Path dest, 13059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project CharSequence editingBuffer) { 13069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project dest.reset(); 13079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 13089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int line = getLineForOffset(point); 13099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int top = getLineTop(line); 13109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int bottom = getLineTop(line+1); 13119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1312afe8e9b6d033cb854afa3024d8198a32896a804aRaph Levien boolean clamped = shouldClampCursor(line); 1313afe8e9b6d033cb854afa3024d8198a32896a804aRaph Levien float h1 = getPrimaryHorizontal(point, clamped) - 0.5f; 1314afe8e9b6d033cb854afa3024d8198a32896a804aRaph Levien float h2 = isLevelBoundary(point) ? getSecondaryHorizontal(point, clamped) - 0.5f : h1; 13159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1316497a92cc5ba2176b8a8484b0a7da040eac0e887bJeff Brown int caps = TextKeyListener.getMetaState(editingBuffer, TextKeyListener.META_SHIFT_ON) | 1317497a92cc5ba2176b8a8484b0a7da040eac0e887bJeff Brown TextKeyListener.getMetaState(editingBuffer, TextKeyListener.META_SELECTING); 1318497a92cc5ba2176b8a8484b0a7da040eac0e887bJeff Brown int fn = TextKeyListener.getMetaState(editingBuffer, TextKeyListener.META_ALT_ON); 13199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int dist = 0; 13209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 13219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (caps != 0 || fn != 0) { 13229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project dist = (bottom - top) >> 2; 13239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 13249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (fn != 0) 13259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project top += dist; 13269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (caps != 0) 13279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project bottom -= dist; 13289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 13299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 13309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (h1 < 0.5f) 13319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project h1 = 0.5f; 13329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (h2 < 0.5f) 13339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project h2 = 0.5f; 13349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 13352fb503f5102dd32a8ec391b26911528852703b90Jozef BABJAK if (Float.compare(h1, h2) == 0) { 13369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project dest.moveTo(h1, top); 13379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project dest.lineTo(h1, bottom); 13389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } else { 13399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project dest.moveTo(h1, top); 13409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project dest.lineTo(h1, (top + bottom) >> 1); 13419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 13429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project dest.moveTo(h2, (top + bottom) >> 1); 13439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project dest.lineTo(h2, bottom); 13449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 13459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 13469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (caps == 2) { 13479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project dest.moveTo(h2, bottom); 13489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project dest.lineTo(h2 - dist, bottom + dist); 13499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project dest.lineTo(h2, bottom); 13509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project dest.lineTo(h2 + dist, bottom + dist); 13519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } else if (caps == 1) { 13529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project dest.moveTo(h2, bottom); 13539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project dest.lineTo(h2 - dist, bottom + dist); 13549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 13559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project dest.moveTo(h2 - dist, bottom + dist - 0.5f); 13569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project dest.lineTo(h2 + dist, bottom + dist - 0.5f); 13579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 13589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project dest.moveTo(h2 + dist, bottom + dist); 13599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project dest.lineTo(h2, bottom); 13609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 13619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 13629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (fn == 2) { 13639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project dest.moveTo(h1, top); 13649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project dest.lineTo(h1 - dist, top - dist); 13659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project dest.lineTo(h1, top); 13669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project dest.lineTo(h1 + dist, top - dist); 13679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } else if (fn == 1) { 13689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project dest.moveTo(h1, top); 13699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project dest.lineTo(h1 - dist, top - dist); 13709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 13719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project dest.moveTo(h1 - dist, top - dist + 0.5f); 13729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project dest.lineTo(h1 + dist, top - dist + 0.5f); 13739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 13749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project dest.moveTo(h1 + dist, top - dist); 13759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project dest.lineTo(h1, top); 13769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 13779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 13789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 13799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private void addSelection(int line, int start, int end, 13809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int top, int bottom, Path dest) { 13819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int linestart = getLineStart(line); 13829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int lineend = getLineEnd(line); 13839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project Directions dirs = getLineDirections(line); 13849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 13859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (lineend > linestart && mText.charAt(lineend - 1) == '\n') 13869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project lineend--; 13879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 13889f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt for (int i = 0; i < dirs.mDirections.length; i += 2) { 13899f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt int here = linestart + dirs.mDirections[i]; 13909f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt int there = here + (dirs.mDirections[i+1] & RUN_LENGTH_MASK); 13919f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt 13929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (there > lineend) 13939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project there = lineend; 13949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 13959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (start <= there && end >= here) { 13969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int st = Math.max(start, here); 13979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int en = Math.min(end, there); 13989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 13999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (st != en) { 1400afe8e9b6d033cb854afa3024d8198a32896a804aRaph Levien float h1 = getHorizontal(st, false, line, false /* not clamped */); 1401afe8e9b6d033cb854afa3024d8198a32896a804aRaph Levien float h2 = getHorizontal(en, true, line, false /* not clamped */); 14029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 14033716601573f5a562f98721130e25002ad88eb164Fabrice Di Meglio float left = Math.min(h1, h2); 14043716601573f5a562f98721130e25002ad88eb164Fabrice Di Meglio float right = Math.max(h1, h2); 14053716601573f5a562f98721130e25002ad88eb164Fabrice Di Meglio 14063716601573f5a562f98721130e25002ad88eb164Fabrice Di Meglio dest.addRect(left, top, right, bottom, Path.Direction.CW); 14079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 14089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 14099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 14109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 14119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 14129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 14139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Fills in the specified Path with a representation of a highlight 14149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * between the specified offsets. This will often be a rectangle 14159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * or a potentially discontinuous set of rectangles. If the start 14169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * and end are the same, the returned path is empty. 14179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 14189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public void getSelectionPath(int start, int end, Path dest) { 14199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project dest.reset(); 14209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 14219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (start == end) 14229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return; 14239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 14249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (end < start) { 14259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int temp = end; 14269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project end = start; 14279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project start = temp; 14289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 14299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 14309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int startline = getLineForOffset(start); 14319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int endline = getLineForOffset(end); 14329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 14339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int top = getLineTop(startline); 14349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int bottom = getLineBottom(endline); 14359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 14369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (startline == endline) { 14379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project addSelection(startline, start, end, top, bottom, dest); 14389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } else { 14399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project final float width = mWidth; 14409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 14419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project addSelection(startline, start, getLineEnd(startline), 14429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project top, getLineBottom(startline), dest); 14439f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt 14449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (getParagraphDirection(startline) == DIR_RIGHT_TO_LEFT) 14459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project dest.addRect(getLineLeft(startline), top, 14469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 0, getLineBottom(startline), Path.Direction.CW); 14479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project else 14489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project dest.addRect(getLineRight(startline), top, 14499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project width, getLineBottom(startline), Path.Direction.CW); 14509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 14519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project for (int i = startline + 1; i < endline; i++) { 14529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project top = getLineTop(i); 14539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project bottom = getLineBottom(i); 14549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project dest.addRect(0, top, width, bottom, Path.Direction.CW); 14559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 14569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 14579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project top = getLineTop(endline); 14589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project bottom = getLineBottom(endline); 14599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 14609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project addSelection(endline, getLineStart(endline), end, 14619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project top, bottom, dest); 14629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 14639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (getParagraphDirection(endline) == DIR_RIGHT_TO_LEFT) 14649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project dest.addRect(width, top, getLineRight(endline), bottom, Path.Direction.CW); 14659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project else 14669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project dest.addRect(0, top, getLineLeft(endline), bottom, Path.Direction.CW); 14679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 14689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 14699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 14709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 14719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Get the alignment of the specified paragraph, taking into account 14729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * markup attached to it. 14739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 14749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public final Alignment getParagraphAlignment(int line) { 14759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project Alignment align = mAlignment; 14769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 14779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (mSpannedText) { 14789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project Spanned sp = (Spanned) mText; 147974d31ef2b2c42b54fa1f7cf94ea955ea67ab69a0Eric Fischer AlignmentSpan[] spans = getParagraphSpans(sp, getLineStart(line), 14809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project getLineEnd(line), 14819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project AlignmentSpan.class); 14829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 14839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int spanLength = spans.length; 14849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (spanLength > 0) { 14859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project align = spans[spanLength-1].getAlignment(); 14869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 14879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 14889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 14899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return align; 14909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 14919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 14929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 14939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Get the left edge of the specified paragraph, inset by left margins. 14949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 14959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public final int getParagraphLeft(int line) { 14969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int left = 0; 1497c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt int dir = getParagraphDirection(line); 1498c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt if (dir == DIR_RIGHT_TO_LEFT || !mSpannedText) { 1499c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt return left; // leading margin has no impact, or no styles 15009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1501c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt return getParagraphLeadingMargin(line); 15029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 15039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 15049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 15059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Get the right edge of the specified paragraph, inset by right margins. 15069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 15079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public final int getParagraphRight(int line) { 15089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int right = mWidth; 1509c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt int dir = getParagraphDirection(line); 1510c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt if (dir == DIR_LEFT_TO_RIGHT || !mSpannedText) { 1511c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt return right; // leading margin has no impact, or no styles 1512c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt } 1513c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt return right - getParagraphLeadingMargin(line); 1514c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt } 15159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1516c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt /** 1517c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt * Returns the effective leading margin (unsigned) for this line, 1518c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt * taking into account LeadingMarginSpan and LeadingMarginSpan2. 1519c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt * @param line the line index 1520c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt * @return the leading margin of this line 1521c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt */ 1522c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt private int getParagraphLeadingMargin(int line) { 1523c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt if (!mSpannedText) { 1524c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt return 0; 1525c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt } 1526c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt Spanned spanned = (Spanned) mText; 15270c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt 1528c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt int lineStart = getLineStart(line); 1529c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt int lineEnd = getLineEnd(line); 15300c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt int spanEnd = spanned.nextSpanTransition(lineStart, lineEnd, 1531c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt LeadingMarginSpan.class); 153274d31ef2b2c42b54fa1f7cf94ea955ea67ab69a0Eric Fischer LeadingMarginSpan[] spans = getParagraphSpans(spanned, lineStart, spanEnd, 1533c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt LeadingMarginSpan.class); 1534c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt if (spans.length == 0) { 1535c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt return 0; // no leading margin span; 1536c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt } 15370c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt 1538c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt int margin = 0; 15390c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt 15400c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt boolean isFirstParaLine = lineStart == 0 || 1541c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt spanned.charAt(lineStart - 1) == '\n'; 15420c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt 1543ab08c6d38ab2e575f809ca8ce4c7f095e49d258cAnish Athalye boolean useFirstLineMargin = isFirstParaLine; 1544c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt for (int i = 0; i < spans.length; i++) { 1545ab08c6d38ab2e575f809ca8ce4c7f095e49d258cAnish Athalye if (spans[i] instanceof LeadingMarginSpan2) { 1546ab08c6d38ab2e575f809ca8ce4c7f095e49d258cAnish Athalye int spStart = spanned.getSpanStart(spans[i]); 1547c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt int spanLine = getLineForOffset(spStart); 1548ab08c6d38ab2e575f809ca8ce4c7f095e49d258cAnish Athalye int count = ((LeadingMarginSpan2) spans[i]).getLeadingMarginLineCount(); 1549ab08c6d38ab2e575f809ca8ce4c7f095e49d258cAnish Athalye // if there is more than one LeadingMarginSpan2, use the count that is greatest 1550ab08c6d38ab2e575f809ca8ce4c7f095e49d258cAnish Athalye useFirstLineMargin |= line < spanLine + count; 15519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1552ab08c6d38ab2e575f809ca8ce4c7f095e49d258cAnish Athalye } 1553ab08c6d38ab2e575f809ca8ce4c7f095e49d258cAnish Athalye for (int i = 0; i < spans.length; i++) { 1554ab08c6d38ab2e575f809ca8ce4c7f095e49d258cAnish Athalye LeadingMarginSpan span = spans[i]; 1555c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt margin += span.getLeadingMargin(useFirstLineMargin); 15569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 15579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1558c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt return margin; 15599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 15609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1561e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt /* package */ 15626c488de023a4797069673dc619c1a4096079ea9eGilles Debunne static float measurePara(TextPaint paint, CharSequence text, int start, int end) { 1563e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 1564e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt MeasuredText mt = MeasuredText.obtain(); 1565e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt TextLine tl = TextLine.obtain(); 1566e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt try { 1567cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt mt.setPara(text, start, end, TextDirectionHeuristics.LTR); 1568e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt Directions directions; 1569c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt int dir; 1570c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt if (mt.mEasy) { 1571e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt directions = DIRS_ALL_LEFT_TO_RIGHT; 1572c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt dir = Layout.DIR_LEFT_TO_RIGHT; 1573e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } else { 1574e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt directions = AndroidBidi.directions(mt.mDir, mt.mLevels, 1575e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 0, mt.mChars, 0, mt.mLen); 1576c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt dir = mt.mDir; 1577c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt } 1578c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt char[] chars = mt.mChars; 1579c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt int len = mt.mLen; 1580c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt boolean hasTabs = false; 1581c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt TabStops tabStops = null; 1582c14b3ada5cccfdd872ccf72d928fb0088f3ce00dAnish Athalye // leading margins should be taken into account when measuring a paragraph 1583c14b3ada5cccfdd872ccf72d928fb0088f3ce00dAnish Athalye int margin = 0; 1584c14b3ada5cccfdd872ccf72d928fb0088f3ce00dAnish Athalye if (text instanceof Spanned) { 1585c14b3ada5cccfdd872ccf72d928fb0088f3ce00dAnish Athalye Spanned spanned = (Spanned) text; 1586c14b3ada5cccfdd872ccf72d928fb0088f3ce00dAnish Athalye LeadingMarginSpan[] spans = getParagraphSpans(spanned, start, end, 1587c14b3ada5cccfdd872ccf72d928fb0088f3ce00dAnish Athalye LeadingMarginSpan.class); 1588c14b3ada5cccfdd872ccf72d928fb0088f3ce00dAnish Athalye for (LeadingMarginSpan lms : spans) { 1589c14b3ada5cccfdd872ccf72d928fb0088f3ce00dAnish Athalye margin += lms.getLeadingMargin(true); 1590c14b3ada5cccfdd872ccf72d928fb0088f3ce00dAnish Athalye } 1591c14b3ada5cccfdd872ccf72d928fb0088f3ce00dAnish Athalye } 1592c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt for (int i = 0; i < len; ++i) { 1593c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt if (chars[i] == '\t') { 1594c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt hasTabs = true; 1595c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt if (text instanceof Spanned) { 1596c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt Spanned spanned = (Spanned) text; 15970c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt int spanEnd = spanned.nextSpanTransition(start, end, 1598c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt TabStopSpan.class); 159974d31ef2b2c42b54fa1f7cf94ea955ea67ab69a0Eric Fischer TabStopSpan[] spans = getParagraphSpans(spanned, start, spanEnd, 1600c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt TabStopSpan.class); 1601c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt if (spans.length > 0) { 1602c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt tabStops = new TabStops(TAB_INCREMENT, spans); 1603c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt } 1604c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt } 1605c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt break; 1606c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt } 16079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1608c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt tl.set(paint, text, start, end, dir, directions, hasTabs, tabStops); 1609c14b3ada5cccfdd872ccf72d928fb0088f3ce00dAnish Athalye return margin + tl.metrics(null); 1610e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } finally { 1611e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt TextLine.recycle(tl); 1612e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt MeasuredText.recycle(mt); 16139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 16149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 16159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 161671b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt /** 1617c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt * @hide 1618c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt */ 1619c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt /* package */ static class TabStops { 1620c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt private int[] mStops; 1621c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt private int mNumStops; 1622c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt private int mIncrement; 16230c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt 1624c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt TabStops(int increment, Object[] spans) { 1625c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt reset(increment, spans); 1626c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt } 16270c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt 1628c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt void reset(int increment, Object[] spans) { 1629c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt this.mIncrement = increment; 1630c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt 1631c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt int ns = 0; 1632c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt if (spans != null) { 1633c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt int[] stops = this.mStops; 1634c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt for (Object o : spans) { 1635c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt if (o instanceof TabStopSpan) { 1636c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt if (stops == null) { 1637c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt stops = new int[10]; 1638c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt } else if (ns == stops.length) { 1639c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt int[] nstops = new int[ns * 2]; 1640c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt for (int i = 0; i < ns; ++i) { 1641c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt nstops[i] = stops[i]; 1642c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt } 1643c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt stops = nstops; 1644c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt } 1645c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt stops[ns++] = ((TabStopSpan) o).getTabStop(); 1646c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt } 1647c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt } 1648c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt if (ns > 1) { 1649c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt Arrays.sort(stops, 0, ns); 1650c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt } 1651c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt if (stops != this.mStops) { 1652c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt this.mStops = stops; 1653c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt } 1654c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt } 1655c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt this.mNumStops = ns; 1656c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt } 16570c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt 1658c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt float nextTab(float h) { 1659c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt int ns = this.mNumStops; 1660c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt if (ns > 0) { 1661c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt int[] stops = this.mStops; 1662c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt for (int i = 0; i < ns; ++i) { 1663c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt int stop = stops[i]; 1664c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt if (stop > h) { 1665c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt return stop; 1666c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt } 1667c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt } 1668c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt } 1669c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt return nextDefaultStop(h, mIncrement); 1670c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt } 1671c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt 1672c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt public static float nextDefaultStop(float h, int inc) { 1673c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt return ((int) ((h + inc) / inc)) * inc; 1674c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt } 1675c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt } 16760c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt 1677c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt /** 167871b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt * Returns the position of the next tab stop after h on the line. 167971b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt * 168071b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt * @param text the text 168171b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt * @param start start of the line 168271b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt * @param end limit of the line 168371b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt * @param h the current horizontal offset 168471b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt * @param tabs the tabs, can be null. If it is null, any tabs in effect 168571b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt * on the line will be used. If there are no tabs, a default offset 168671b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt * will be used to compute the tab stop. 168771b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt * @return the offset of the next tab stop. 168871b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt */ 16899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /* package */ static float nextTab(CharSequence text, int start, int end, 16909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project float h, Object[] tabs) { 16919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project float nh = Float.MAX_VALUE; 16929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project boolean alltabs = false; 16939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 16949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (text instanceof Spanned) { 16959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (tabs == null) { 169674d31ef2b2c42b54fa1f7cf94ea955ea67ab69a0Eric Fischer tabs = getParagraphSpans((Spanned) text, start, end, TabStopSpan.class); 16979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project alltabs = true; 16989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 16999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 17009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project for (int i = 0; i < tabs.length; i++) { 17019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (!alltabs) { 17029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (!(tabs[i] instanceof TabStopSpan)) 17039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project continue; 17049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 17059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 17069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int where = ((TabStopSpan) tabs[i]).getTabStop(); 17079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 17089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (where < nh && where > h) 17099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project nh = where; 17109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 17119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 17129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (nh != Float.MAX_VALUE) 17139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return nh; 17149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 17159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 17169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return ((int) ((h + TAB_INCREMENT) / TAB_INCREMENT)) * TAB_INCREMENT; 17179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 17189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 17199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project protected final boolean isSpanned() { 17209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return mSpannedText; 17219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 17229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 172374d31ef2b2c42b54fa1f7cf94ea955ea67ab69a0Eric Fischer /** 172474d31ef2b2c42b54fa1f7cf94ea955ea67ab69a0Eric Fischer * Returns the same as <code>text.getSpans()</code>, except where 172574d31ef2b2c42b54fa1f7cf94ea955ea67ab69a0Eric Fischer * <code>start</code> and <code>end</code> are the same and are not 172674d31ef2b2c42b54fa1f7cf94ea955ea67ab69a0Eric Fischer * at the very beginning of the text, in which case an empty array 172774d31ef2b2c42b54fa1f7cf94ea955ea67ab69a0Eric Fischer * is returned instead. 172874d31ef2b2c42b54fa1f7cf94ea955ea67ab69a0Eric Fischer * <p> 172974d31ef2b2c42b54fa1f7cf94ea955ea67ab69a0Eric Fischer * This is needed because of the special case that <code>getSpans()</code> 173074d31ef2b2c42b54fa1f7cf94ea955ea67ab69a0Eric Fischer * on an empty range returns the spans adjacent to that range, which is 173174d31ef2b2c42b54fa1f7cf94ea955ea67ab69a0Eric Fischer * primarily for the sake of <code>TextWatchers</code> so they will get 173274d31ef2b2c42b54fa1f7cf94ea955ea67ab69a0Eric Fischer * notifications when text goes from empty to non-empty. But it also 173374d31ef2b2c42b54fa1f7cf94ea955ea67ab69a0Eric Fischer * has the unfortunate side effect that if the text ends with an empty 173474d31ef2b2c42b54fa1f7cf94ea955ea67ab69a0Eric Fischer * paragraph, that paragraph accidentally picks up the styles of the 173574d31ef2b2c42b54fa1f7cf94ea955ea67ab69a0Eric Fischer * preceding paragraph (even though those styles will not be picked up 173674d31ef2b2c42b54fa1f7cf94ea955ea67ab69a0Eric Fischer * by new text that is inserted into the empty paragraph). 173774d31ef2b2c42b54fa1f7cf94ea955ea67ab69a0Eric Fischer * <p> 173874d31ef2b2c42b54fa1f7cf94ea955ea67ab69a0Eric Fischer * The reason it just checks whether <code>start</code> and <code>end</code> 173974d31ef2b2c42b54fa1f7cf94ea955ea67ab69a0Eric Fischer * is the same is that the only time a line can contain 0 characters 174074d31ef2b2c42b54fa1f7cf94ea955ea67ab69a0Eric Fischer * is if it is the final paragraph of the Layout; otherwise any line will 174174d31ef2b2c42b54fa1f7cf94ea955ea67ab69a0Eric Fischer * contain at least one printing or newline character. The reason for the 174274d31ef2b2c42b54fa1f7cf94ea955ea67ab69a0Eric Fischer * additional check if <code>start</code> is greater than 0 is that 174374d31ef2b2c42b54fa1f7cf94ea955ea67ab69a0Eric Fischer * if the empty paragraph is the entire content of the buffer, paragraph 174474d31ef2b2c42b54fa1f7cf94ea955ea67ab69a0Eric Fischer * styles that are already applied to the buffer will apply to text that 174574d31ef2b2c42b54fa1f7cf94ea955ea67ab69a0Eric Fischer * is inserted into it. 174674d31ef2b2c42b54fa1f7cf94ea955ea67ab69a0Eric Fischer */ 1747eca5b7385a641ae5d8c4aa2b7823a4e573737376Gilles Debunne /* package */static <T> T[] getParagraphSpans(Spanned text, int start, int end, Class<T> type) { 174874d31ef2b2c42b54fa1f7cf94ea955ea67ab69a0Eric Fischer if (start == end && start > 0) { 17496c488de023a4797069673dc619c1a4096079ea9eGilles Debunne return ArrayUtils.emptyArray(type); 175074d31ef2b2c42b54fa1f7cf94ea955ea67ab69a0Eric Fischer } 175174d31ef2b2c42b54fa1f7cf94ea955ea67ab69a0Eric Fischer 175274d31ef2b2c42b54fa1f7cf94ea955ea67ab69a0Eric Fischer return text.getSpans(start, end, type); 175374d31ef2b2c42b54fa1f7cf94ea955ea67ab69a0Eric Fischer } 175474d31ef2b2c42b54fa1f7cf94ea955ea67ab69a0Eric Fischer 17558d44fff7e62f77c3b3072a96712cc1389e63ca64Fabrice Di Meglio private char getEllipsisChar(TextUtils.TruncateAt method) { 17568d44fff7e62f77c3b3072a96712cc1389e63ca64Fabrice Di Meglio return (method == TextUtils.TruncateAt.END_SMALL) ? 1757d29bdb266d54b4551f42776bb790e80147a279d0Neil Fuller TextUtils.ELLIPSIS_TWO_DOTS[0] : 1758d29bdb266d54b4551f42776bb790e80147a279d0Neil Fuller TextUtils.ELLIPSIS_NORMAL[0]; 17598d44fff7e62f77c3b3072a96712cc1389e63ca64Fabrice Di Meglio } 17608d44fff7e62f77c3b3072a96712cc1389e63ca64Fabrice Di Meglio 17619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private void ellipsize(int start, int end, int line, 17628d44fff7e62f77c3b3072a96712cc1389e63ca64Fabrice Di Meglio char[] dest, int destoff, TextUtils.TruncateAt method) { 17639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int ellipsisCount = getEllipsisCount(line); 17649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 17659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (ellipsisCount == 0) { 17669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return; 17679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 17689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 17699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int ellipsisStart = getEllipsisStart(line); 17709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int linestart = getLineStart(line); 17719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 17729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project for (int i = ellipsisStart; i < ellipsisStart + ellipsisCount; i++) { 17739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project char c; 17749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 17759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (i == ellipsisStart) { 17768d44fff7e62f77c3b3072a96712cc1389e63ca64Fabrice Di Meglio c = getEllipsisChar(method); // ellipsis 17779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } else { 17789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project c = '\uFEFF'; // 0-width space 17799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 17809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 17819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int a = i + linestart; 17829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 17839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (a >= start && a < end) { 17849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project dest[destoff + a - start] = c; 17859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 17869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 17879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 17889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 17899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 17909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Stores information about bidirectional (left-to-right or right-to-left) 17919f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt * text within the layout of a line. 17929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 17939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public static class Directions { 17949f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt // Directions represents directional runs within a line of text. 17959f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt // Runs are pairs of ints listed in visual order, starting from the 17969f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt // leading margin. The first int of each pair is the offset from 17979f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt // the first character of the line to the start of the run. The 17989f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt // second int represents both the length and level of the run. 17999f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt // The length is in the lower bits, accessed by masking with 18009f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt // DIR_LENGTH_MASK. The level is in the higher bits, accessed 18019f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt // by shifting by DIR_LEVEL_SHIFT and masking by DIR_LEVEL_MASK. 18029f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt // To simply test for an RTL direction, test the bit using 18039f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt // DIR_RTL_FLAG, if set then the direction is rtl. 18049f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt 18059f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt /* package */ int[] mDirections; 18069f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt /* package */ Directions(int[] dirs) { 18079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mDirections = dirs; 18089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 18099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 18109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 18119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 18129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Return the offset of the first character to be ellipsized away, 18139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * relative to the start of the line. (So 0 if the beginning of the 18149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * line is ellipsized, not getLineStart().) 18159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 18169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public abstract int getEllipsisStart(int line); 1817e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 18189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 18199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Returns the number of characters to be ellipsized away, or 0 if 18209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * no ellipsis is to take place. 18219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 18229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public abstract int getEllipsisCount(int line); 18239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 18249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /* package */ static class Ellipsizer implements CharSequence, GetChars { 18259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /* package */ CharSequence mText; 18269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /* package */ Layout mLayout; 18279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /* package */ int mWidth; 18289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /* package */ TextUtils.TruncateAt mMethod; 18299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 18309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public Ellipsizer(CharSequence s) { 18319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mText = s; 18329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 18339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 18349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public char charAt(int off) { 18359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project char[] buf = TextUtils.obtain(1); 18369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project getChars(off, off + 1, buf, 0); 18379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project char ret = buf[0]; 18389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 18399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project TextUtils.recycle(buf); 18409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return ret; 18419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 18429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 18439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public void getChars(int start, int end, char[] dest, int destoff) { 18449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int line1 = mLayout.getLineForOffset(start); 18459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int line2 = mLayout.getLineForOffset(end); 18469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 18479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project TextUtils.getChars(mText, start, end, dest, destoff); 18489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 18499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project for (int i = line1; i <= line2; i++) { 18508d44fff7e62f77c3b3072a96712cc1389e63ca64Fabrice Di Meglio mLayout.ellipsize(start, end, i, dest, destoff, mMethod); 18519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 18529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 18539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 18549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public int length() { 18559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return mText.length(); 18569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 18579f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt 18589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public CharSequence subSequence(int start, int end) { 18599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project char[] s = new char[end - start]; 18609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project getChars(start, end, s, 0); 18619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return new String(s); 18629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 18639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1864162bf0f1b9fd5d78ffa801917994f6222c6efd85Gilles Debunne @Override 18659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public String toString() { 18669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project char[] s = new char[length()]; 18679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project getChars(0, length(), s, 0); 18689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return new String(s); 18699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 18709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 18719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 18729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 18736c488de023a4797069673dc619c1a4096079ea9eGilles Debunne /* package */ static class SpannedEllipsizer extends Ellipsizer implements Spanned { 18749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private Spanned mSpanned; 18759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 18769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public SpannedEllipsizer(CharSequence display) { 18779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project super(display); 18789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mSpanned = (Spanned) display; 18799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 18809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 18819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public <T> T[] getSpans(int start, int end, Class<T> type) { 18829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return mSpanned.getSpans(start, end, type); 18839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 18849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 18859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public int getSpanStart(Object tag) { 18869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return mSpanned.getSpanStart(tag); 18879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 18889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 18899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public int getSpanEnd(Object tag) { 18909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return mSpanned.getSpanEnd(tag); 18919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 18929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 18939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public int getSpanFlags(Object tag) { 18949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return mSpanned.getSpanFlags(tag); 18959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 18969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 18976c488de023a4797069673dc619c1a4096079ea9eGilles Debunne @SuppressWarnings("rawtypes") 18989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public int nextSpanTransition(int start, int limit, Class type) { 18999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return mSpanned.nextSpanTransition(start, limit, type); 19009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 19019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1902162bf0f1b9fd5d78ffa801917994f6222c6efd85Gilles Debunne @Override 19039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public CharSequence subSequence(int start, int end) { 19049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project char[] s = new char[end - start]; 19059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project getChars(start, end, s, 0); 19069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 19079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project SpannableString ss = new SpannableString(new String(s)); 19089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project TextUtils.copySpansFrom(mSpanned, start, end, Object.class, ss, 0); 19099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return ss; 19109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 19119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 19129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 19139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private CharSequence mText; 19149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private TextPaint mPaint; 19159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /* package */ TextPaint mWorkPaint; 19169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private int mWidth; 19179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private Alignment mAlignment = Alignment.ALIGN_NORMAL; 19189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private float mSpacingMult; 19199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private float mSpacingAdd; 1920c4d8eb6fb7c88c5c4da38b0b113c24cc4b78c0b7Romain Guy private static final Rect sTempRect = new Rect(); 19219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private boolean mSpannedText; 1922cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt private TextDirectionHeuristic mTextDir; 1923eca5b7385a641ae5d8c4aa2b7823a4e573737376Gilles Debunne private SpanSet<LineBackgroundSpan> mLineBackgroundSpans; 19249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 19259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public static final int DIR_LEFT_TO_RIGHT = 1; 19269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public static final int DIR_RIGHT_TO_LEFT = -1; 19279f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt 192820178d62cf669af18467a16d3c4c4237ed42151cDoug Felt /* package */ static final int DIR_REQUEST_LTR = 1; 192920178d62cf669af18467a16d3c4c4237ed42151cDoug Felt /* package */ static final int DIR_REQUEST_RTL = -1; 193020178d62cf669af18467a16d3c4c4237ed42151cDoug Felt /* package */ static final int DIR_REQUEST_DEFAULT_LTR = 2; 193120178d62cf669af18467a16d3c4c4237ed42151cDoug Felt /* package */ static final int DIR_REQUEST_DEFAULT_RTL = -2; 19329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 19339f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt /* package */ static final int RUN_LENGTH_MASK = 0x03ffffff; 19349f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt /* package */ static final int RUN_LEVEL_SHIFT = 26; 19359f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt /* package */ static final int RUN_LEVEL_MASK = 0x3f; 19369f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt /* package */ static final int RUN_RTL_FLAG = 1 << RUN_LEVEL_SHIFT; 19379f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt 19389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public enum Alignment { 19399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project ALIGN_NORMAL, 19409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project ALIGN_OPPOSITE, 19419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project ALIGN_CENTER, 1942c0ccf0c47c00942a9d0f2670600a8f2d4d7adb5bDoug Felt /** @hide */ 1943c0ccf0c47c00942a9d0f2670600a8f2d4d7adb5bDoug Felt ALIGN_LEFT, 1944c0ccf0c47c00942a9d0f2670600a8f2d4d7adb5bDoug Felt /** @hide */ 1945c0ccf0c47c00942a9d0f2670600a8f2d4d7adb5bDoug Felt ALIGN_RIGHT, 19469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 19479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 19489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private static final int TAB_INCREMENT = 20; 19499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 19509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /* package */ static final Directions DIRS_ALL_LEFT_TO_RIGHT = 19519f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt new Directions(new int[] { 0, RUN_LENGTH_MASK }); 19529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /* package */ static final Directions DIRS_ALL_RIGHT_TO_LEFT = 19539f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt new Directions(new int[] { 0, RUN_LENGTH_MASK | RUN_RTL_FLAG }); 19540a4db3c5270440eeb7e4e44a7029926e239ec3bdGilles Debunne 19559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project} 1956