Layout.java revision 2fba3387c31b675c419030145250e5be246c50b0
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; 34cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt 35c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Feltimport java.util.Arrays; 369f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt 379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project/** 389f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt * A base class that manages text layout in visual elements on 399f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt * the screen. 409f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt * <p>For text that will be edited, use a {@link DynamicLayout}, 419f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt * which will be updated as the text changes. 429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * For text that will not change, use a {@link StaticLayout}. 439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectpublic abstract class Layout { 4571b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt private static final ParagraphStyle[] NO_PARA_SPANS = 4671b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt ArrayUtils.emptyArray(ParagraphStyle.class); 4776c0226008ee16633dc94ed4dbeadef2174f6bd9Dave Bort 48eca5b7385a641ae5d8c4aa2b7823a4e573737376Gilles Debunne /* package */ static final EmojiFactory EMOJI_FACTORY = EmojiFactory.newAvailableInstance(); 49105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project /* package */ static final int MIN_EMOJI, MAX_EMOJI; 50105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project 51105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project static { 52105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project if (EMOJI_FACTORY != null) { 53105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project MIN_EMOJI = EMOJI_FACTORY.getMinimumAndroidPua(); 54105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project MAX_EMOJI = EMOJI_FACTORY.getMaximumAndroidPua(); 55105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project } else { 56105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project MIN_EMOJI = -1; 57105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project MAX_EMOJI = -1; 58105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project } 59e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 60c2d54f46ac13e029e6d53f7471cd9c90fe6bbfe9Eric Fischer 619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 6271b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt * Return how wide a layout must be in order to display the 639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * specified text with one line per paragraph. 649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public static float getDesiredWidth(CharSequence source, 669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project TextPaint paint) { 679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return getDesiredWidth(source, 0, source.length(), paint); 689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 699f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt 709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 7171b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt * Return how wide a layout must be in order to display the 729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * specified text slice with one line per paragraph. 739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public static float getDesiredWidth(CharSequence source, 759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int start, int end, 769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project TextPaint paint) { 779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project float need = 0; 789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int next; 809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project for (int i = start; i <= end; i = next) { 819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project next = TextUtils.indexOf(source, '\n', i, end); 829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (next < 0) 849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project next = end; 859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 8671b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt // note, omits trailing paragraph char 876c488de023a4797069673dc619c1a4096079ea9eGilles Debunne float w = measurePara(paint, source, i, next); 889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (w > need) 909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project need = w; 919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project next++; 939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return need; 969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Subclasses of Layout use this constructor to set the display text, 1009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * width, and other standard properties. 10171b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt * @param text the text to render 10271b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt * @param paint the default paint for the layout. Styles can override 10371b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt * various attributes of the paint. 10471b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt * @param width the wrapping width for the text. 10571b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt * @param align whether to left, right, or center the text. Styles can 10671b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt * override the alignment. 10771b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt * @param spacingMult factor by which to scale the font size to get the 10871b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt * default line spacing 10971b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt * @param spacingAdd amount to add to the default line spacing 1109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 1119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project protected Layout(CharSequence text, TextPaint paint, 1129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int width, Alignment align, 11371b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt float spacingMult, float spacingAdd) { 114cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt this(text, paint, width, align, TextDirectionHeuristics.FIRSTSTRONG_LTR, 115cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt spacingMult, spacingAdd); 116cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt } 117cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt 118cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt /** 119cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt * Subclasses of Layout use this constructor to set the display text, 120cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt * width, and other standard properties. 121cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt * @param text the text to render 122cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt * @param paint the default paint for the layout. Styles can override 123cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt * various attributes of the paint. 124cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt * @param width the wrapping width for the text. 125cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt * @param align whether to left, right, or center the text. Styles can 126cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt * override the alignment. 127cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt * @param spacingMult factor by which to scale the font size to get the 128cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt * default line spacing 129cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt * @param spacingAdd amount to add to the default line spacing 130cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt * 131cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt * @hide 132cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt */ 133cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt protected Layout(CharSequence text, TextPaint paint, 134cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt int width, Alignment align, TextDirectionHeuristic textDir, 135cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt float spacingMult, float spacingAdd) { 136cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt 1379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (width < 0) 1389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project throw new IllegalArgumentException("Layout: " + width + " < 0"); 1399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 140e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // Ensure paint doesn't have baselineShift set. 141e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // While normally we don't modify the paint the user passed in, 142e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // we were already doing this in Styled.drawUniformRun with both 143e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // baselineShift and bgColor. We probably should reevaluate bgColor. 144e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (paint != null) { 145e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt paint.bgColor = 0; 146e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt paint.baselineShift = 0; 147e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 148e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 1499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mText = text; 1509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mPaint = paint; 1519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mWorkPaint = new TextPaint(); 1529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mWidth = width; 1539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mAlignment = align; 15471b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt mSpacingMult = spacingMult; 15571b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt mSpacingAdd = spacingAdd; 1569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mSpannedText = text instanceof Spanned; 157cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt mTextDir = textDir; 1589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 1619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Replace constructor properties of this Layout with new ones. Be careful. 1629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 1639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /* package */ void replaceWith(CharSequence text, TextPaint paint, 1649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int width, Alignment align, 1659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project float spacingmult, float spacingadd) { 1669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (width < 0) { 1679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project throw new IllegalArgumentException("Layout: " + width + " < 0"); 1689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mText = text; 1719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mPaint = paint; 1729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mWidth = width; 1739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mAlignment = align; 1749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mSpacingMult = spacingmult; 1759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mSpacingAdd = spacingadd; 1769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mSpannedText = text instanceof Spanned; 1779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 1809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Draw this Layout on the specified Canvas. 1819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 1829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public void draw(Canvas c) { 1839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project draw(c, null, null, 0); 1849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 18771b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt * Draw this Layout on the specified canvas, with the highlight path drawn 18871b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt * between the background and the text. 18971b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt * 1906c488de023a4797069673dc619c1a4096079ea9eGilles Debunne * @param canvas the canvas 19171b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt * @param highlight the path of the highlight or cursor; can be null 19271b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt * @param highlightPaint the paint for the highlight 19371b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt * @param cursorOffsetVertical the amount to temporarily translate the 19471b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt * canvas while rendering the highlight 1959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 1966c488de023a4797069673dc619c1a4096079ea9eGilles Debunne public void draw(Canvas canvas, Path highlight, Paint highlightPaint, 1976c488de023a4797069673dc619c1a4096079ea9eGilles Debunne int cursorOffsetVertical) { 1986c488de023a4797069673dc619c1a4096079ea9eGilles Debunne final long lineRange = getLineRangeForDraw(canvas); 1996c488de023a4797069673dc619c1a4096079ea9eGilles Debunne int firstLine = TextUtils.unpackRangeStartFromLong(lineRange); 2006c488de023a4797069673dc619c1a4096079ea9eGilles Debunne int lastLine = TextUtils.unpackRangeEndFromLong(lineRange); 2016c488de023a4797069673dc619c1a4096079ea9eGilles Debunne if (lastLine < 0) return; 2026c488de023a4797069673dc619c1a4096079ea9eGilles Debunne 2036c488de023a4797069673dc619c1a4096079ea9eGilles Debunne drawBackground(canvas, highlight, highlightPaint, cursorOffsetVertical, 2046c488de023a4797069673dc619c1a4096079ea9eGilles Debunne firstLine, lastLine); 2056c488de023a4797069673dc619c1a4096079ea9eGilles Debunne drawText(canvas, firstLine, lastLine); 2066c488de023a4797069673dc619c1a4096079ea9eGilles Debunne } 2079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 2086c488de023a4797069673dc619c1a4096079ea9eGilles Debunne /** 2096c488de023a4797069673dc619c1a4096079ea9eGilles Debunne * @hide 2106c488de023a4797069673dc619c1a4096079ea9eGilles Debunne */ 2116c488de023a4797069673dc619c1a4096079ea9eGilles Debunne public void drawText(Canvas canvas, int firstLine, int lastLine) { 2126c488de023a4797069673dc619c1a4096079ea9eGilles Debunne int previousLineBottom = getLineTop(firstLine); 2136c488de023a4797069673dc619c1a4096079ea9eGilles Debunne int previousLineEnd = getLineStart(firstLine); 21471b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt ParagraphStyle[] spans = NO_PARA_SPANS; 215c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt int spanEnd = 0; 2166c488de023a4797069673dc619c1a4096079ea9eGilles Debunne TextPaint paint = mPaint; 2176c488de023a4797069673dc619c1a4096079ea9eGilles Debunne CharSequence buf = mText; 2189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 219c0ccf0c47c00942a9d0f2670600a8f2d4d7adb5bDoug Felt Alignment paraAlign = mAlignment; 220c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt TabStops tabStops = null; 221c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt boolean tabStopsIsInitialized = false; 2229f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt 223e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt TextLine tl = TextLine.obtain(); 224c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt 2256c488de023a4797069673dc619c1a4096079ea9eGilles Debunne // Draw the lines, one at a time. 2266c488de023a4797069673dc619c1a4096079ea9eGilles Debunne // The baseline is the top of the following line minus the current line's descent. 2276c488de023a4797069673dc619c1a4096079ea9eGilles Debunne for (int i = firstLine; i <= lastLine; i++) { 2289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int start = previousLineEnd; 2296c488de023a4797069673dc619c1a4096079ea9eGilles Debunne previousLineEnd = getLineStart(i + 1); 2309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int end = getLineVisibleEnd(i, start, previousLineEnd); 2319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 2329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int ltop = previousLineBottom; 2339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int lbottom = getLineTop(i+1); 2349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project previousLineBottom = lbottom; 2359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int lbaseline = lbottom - getLineDescent(i); 2369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 237c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt int dir = getParagraphDirection(i); 238c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt int left = 0; 239c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt int right = mWidth; 240c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt 2416c488de023a4797069673dc619c1a4096079ea9eGilles Debunne if (mSpannedText) { 242c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt Spanned sp = (Spanned) buf; 2436c488de023a4797069673dc619c1a4096079ea9eGilles Debunne int textLength = buf.length(); 2446c488de023a4797069673dc619c1a4096079ea9eGilles Debunne boolean isFirstParaLine = (start == 0 || buf.charAt(start - 1) == '\n'); 2450c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt 246c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt // New batch of paragraph styles, collect into spans array. 247c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt // Compute the alignment, last alignment style wins. 248c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt // Reset tabStops, we'll rebuild if we encounter a line with 249c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt // tabs. 250c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt // We expect paragraph spans to be relatively infrequent, use 251c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt // spanEnd so that we can check less frequently. Since 252c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt // paragraph styles ought to apply to entire paragraphs, we can 253c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt // just collect the ones present at the start of the paragraph. 254c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt // If spanEnd is before the end of the paragraph, that's not 255c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt // our problem. 2566c488de023a4797069673dc619c1a4096079ea9eGilles Debunne if (start >= spanEnd && (i == firstLine || isFirstParaLine)) { 257c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt spanEnd = sp.nextSpanTransition(start, textLength, 2589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project ParagraphStyle.class); 25974d31ef2b2c42b54fa1f7cf94ea955ea67ab69a0Eric Fischer spans = getParagraphSpans(sp, start, spanEnd, ParagraphStyle.class); 2609f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt 261c0ccf0c47c00942a9d0f2670600a8f2d4d7adb5bDoug Felt paraAlign = mAlignment; 2626c488de023a4797069673dc619c1a4096079ea9eGilles Debunne for (int n = spans.length - 1; n >= 0; n--) { 2639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (spans[n] instanceof AlignmentSpan) { 264c0ccf0c47c00942a9d0f2670600a8f2d4d7adb5bDoug Felt paraAlign = ((AlignmentSpan) spans[n]).getAlignment(); 2659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project break; 2669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 2679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 2680c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt 269c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt tabStopsIsInitialized = false; 2709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 2719f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt 272c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt // Draw all leading margin spans. Adjust left or right according 273c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt // to the paragraph direction of the line. 2749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project final int length = spans.length; 2759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project for (int n = 0; n < length; n++) { 2769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (spans[n] instanceof LeadingMarginSpan) { 2779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project LeadingMarginSpan margin = (LeadingMarginSpan) spans[n]; 278c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt boolean useFirstLineMargin = isFirstParaLine; 279c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt if (margin instanceof LeadingMarginSpan2) { 280c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt int count = ((LeadingMarginSpan2) margin).getLeadingMarginLineCount(); 281c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt int startLine = getLineForOffset(sp.getSpanStart(margin)); 282c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt useFirstLineMargin = i < startLine + count; 283c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt } 2849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 2859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (dir == DIR_RIGHT_TO_LEFT) { 2866c488de023a4797069673dc619c1a4096079ea9eGilles Debunne margin.drawLeadingMargin(canvas, paint, right, dir, ltop, 2879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project lbaseline, lbottom, buf, 28871b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt start, end, isFirstParaLine, this); 289c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt right -= margin.getLeadingMargin(useFirstLineMargin); 2909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } else { 2916c488de023a4797069673dc619c1a4096079ea9eGilles Debunne margin.drawLeadingMargin(canvas, paint, left, dir, ltop, 2929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project lbaseline, lbottom, buf, 29371b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt start, end, isFirstParaLine, this); 294c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt left += margin.getLeadingMargin(useFirstLineMargin); 2959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 2969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 2979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 2989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 2999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 300c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt boolean hasTabOrEmoji = getLineContainsTab(i); 301c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt // Can't tell if we have tabs for sure, currently 302c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt if (hasTabOrEmoji && !tabStopsIsInitialized) { 303c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt if (tabStops == null) { 304c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt tabStops = new TabStops(TAB_INCREMENT, spans); 305c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt } else { 306c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt tabStops.reset(TAB_INCREMENT, spans); 307c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt } 308c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt tabStopsIsInitialized = true; 309c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt } 310c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt 311c0ccf0c47c00942a9d0f2670600a8f2d4d7adb5bDoug Felt // Determine whether the line aligns to normal, opposite, or center. 312c0ccf0c47c00942a9d0f2670600a8f2d4d7adb5bDoug Felt Alignment align = paraAlign; 313c0ccf0c47c00942a9d0f2670600a8f2d4d7adb5bDoug Felt if (align == Alignment.ALIGN_LEFT) { 314c0ccf0c47c00942a9d0f2670600a8f2d4d7adb5bDoug Felt align = (dir == DIR_LEFT_TO_RIGHT) ? 315c0ccf0c47c00942a9d0f2670600a8f2d4d7adb5bDoug Felt Alignment.ALIGN_NORMAL : Alignment.ALIGN_OPPOSITE; 316c0ccf0c47c00942a9d0f2670600a8f2d4d7adb5bDoug Felt } else if (align == Alignment.ALIGN_RIGHT) { 317c0ccf0c47c00942a9d0f2670600a8f2d4d7adb5bDoug Felt align = (dir == DIR_LEFT_TO_RIGHT) ? 318c0ccf0c47c00942a9d0f2670600a8f2d4d7adb5bDoug Felt Alignment.ALIGN_OPPOSITE : Alignment.ALIGN_NORMAL; 319c0ccf0c47c00942a9d0f2670600a8f2d4d7adb5bDoug Felt } 320c0ccf0c47c00942a9d0f2670600a8f2d4d7adb5bDoug Felt 3219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int x; 3229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (align == Alignment.ALIGN_NORMAL) { 3239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (dir == DIR_LEFT_TO_RIGHT) { 3249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project x = left; 3259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } else { 3269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project x = right; 3279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 3289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } else { 329c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt int max = (int)getLineExtent(i, tabStops, false); 3309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (align == Alignment.ALIGN_OPPOSITE) { 331c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt if (dir == DIR_LEFT_TO_RIGHT) { 3329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project x = right - max; 3339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } else { 334c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt x = left - max; 3359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 336c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt } else { // Alignment.ALIGN_CENTER 337c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt max = max & ~1; 338c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt x = (right + left - max) >> 1; 3399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 3409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 3419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 3429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project Directions directions = getLineDirections(i); 3436c488de023a4797069673dc619c1a4096079ea9eGilles Debunne if (directions == DIRS_ALL_LEFT_TO_RIGHT && !mSpannedText && !hasTabOrEmoji) { 34471b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt // XXX: assumes there's nothing additional to be done 3456c488de023a4797069673dc619c1a4096079ea9eGilles Debunne canvas.drawText(buf, start, end, x, lbaseline, paint); 3469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } else { 347c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt tl.set(paint, buf, start, end, dir, directions, hasTabOrEmoji, tabStops); 3486c488de023a4797069673dc619c1a4096079ea9eGilles Debunne tl.draw(canvas, x, ltop, lbaseline, lbottom); 3499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 3509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 351c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt 352e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt TextLine.recycle(tl); 3539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 3549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 3559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 3566c488de023a4797069673dc619c1a4096079ea9eGilles Debunne * @hide 3576c488de023a4797069673dc619c1a4096079ea9eGilles Debunne */ 3586c488de023a4797069673dc619c1a4096079ea9eGilles Debunne public void drawBackground(Canvas canvas, Path highlight, Paint highlightPaint, 3596c488de023a4797069673dc619c1a4096079ea9eGilles Debunne int cursorOffsetVertical, int firstLine, int lastLine) { 3606c488de023a4797069673dc619c1a4096079ea9eGilles Debunne // First, draw LineBackgroundSpans. 3616c488de023a4797069673dc619c1a4096079ea9eGilles Debunne // LineBackgroundSpans know nothing about the alignment, margins, or 3626c488de023a4797069673dc619c1a4096079ea9eGilles Debunne // direction of the layout or line. XXX: Should they? 3636c488de023a4797069673dc619c1a4096079ea9eGilles Debunne // They are evaluated at each line. 3646c488de023a4797069673dc619c1a4096079ea9eGilles Debunne if (mSpannedText) { 365eca5b7385a641ae5d8c4aa2b7823a4e573737376Gilles Debunne if (mLineBackgroundSpans == null) { 366eca5b7385a641ae5d8c4aa2b7823a4e573737376Gilles Debunne mLineBackgroundSpans = new SpanSet<LineBackgroundSpan>(LineBackgroundSpan.class); 36760e3b3fa60a672583fe6294139feb940845efc9bGilles Debunne } 3686c488de023a4797069673dc619c1a4096079ea9eGilles Debunne 36960e3b3fa60a672583fe6294139feb940845efc9bGilles Debunne Spanned buffer = (Spanned) mText; 37060e3b3fa60a672583fe6294139feb940845efc9bGilles Debunne int textLength = buffer.length(); 371eca5b7385a641ae5d8c4aa2b7823a4e573737376Gilles Debunne mLineBackgroundSpans.init(buffer, 0, textLength); 37260e3b3fa60a672583fe6294139feb940845efc9bGilles Debunne 373eca5b7385a641ae5d8c4aa2b7823a4e573737376Gilles Debunne if (mLineBackgroundSpans.numberOfSpans > 0) { 37460e3b3fa60a672583fe6294139feb940845efc9bGilles Debunne int previousLineBottom = getLineTop(firstLine); 37560e3b3fa60a672583fe6294139feb940845efc9bGilles Debunne int previousLineEnd = getLineStart(firstLine); 37660e3b3fa60a672583fe6294139feb940845efc9bGilles Debunne ParagraphStyle[] spans = NO_PARA_SPANS; 37760e3b3fa60a672583fe6294139feb940845efc9bGilles Debunne int spansLength = 0; 37860e3b3fa60a672583fe6294139feb940845efc9bGilles Debunne TextPaint paint = mPaint; 37960e3b3fa60a672583fe6294139feb940845efc9bGilles Debunne int spanEnd = 0; 38060e3b3fa60a672583fe6294139feb940845efc9bGilles Debunne final int width = mWidth; 38160e3b3fa60a672583fe6294139feb940845efc9bGilles Debunne for (int i = firstLine; i <= lastLine; i++) { 38260e3b3fa60a672583fe6294139feb940845efc9bGilles Debunne int start = previousLineEnd; 38360e3b3fa60a672583fe6294139feb940845efc9bGilles Debunne int end = getLineStart(i + 1); 38460e3b3fa60a672583fe6294139feb940845efc9bGilles Debunne previousLineEnd = end; 38560e3b3fa60a672583fe6294139feb940845efc9bGilles Debunne 38660e3b3fa60a672583fe6294139feb940845efc9bGilles Debunne int ltop = previousLineBottom; 38760e3b3fa60a672583fe6294139feb940845efc9bGilles Debunne int lbottom = getLineTop(i + 1); 38860e3b3fa60a672583fe6294139feb940845efc9bGilles Debunne previousLineBottom = lbottom; 38960e3b3fa60a672583fe6294139feb940845efc9bGilles Debunne int lbaseline = lbottom - getLineDescent(i); 39060e3b3fa60a672583fe6294139feb940845efc9bGilles Debunne 39160e3b3fa60a672583fe6294139feb940845efc9bGilles Debunne if (start >= spanEnd) { 39260e3b3fa60a672583fe6294139feb940845efc9bGilles Debunne // These should be infrequent, so we'll use this so that 39360e3b3fa60a672583fe6294139feb940845efc9bGilles Debunne // we don't have to check as often. 394eca5b7385a641ae5d8c4aa2b7823a4e573737376Gilles Debunne spanEnd = mLineBackgroundSpans.getNextTransition(start, textLength); 39560e3b3fa60a672583fe6294139feb940845efc9bGilles Debunne // All LineBackgroundSpans on a line contribute to its background. 39660e3b3fa60a672583fe6294139feb940845efc9bGilles Debunne spansLength = 0; 39760e3b3fa60a672583fe6294139feb940845efc9bGilles Debunne // Duplication of the logic of getParagraphSpans 39860e3b3fa60a672583fe6294139feb940845efc9bGilles Debunne if (start != end || start == 0) { 39960e3b3fa60a672583fe6294139feb940845efc9bGilles Debunne // Equivalent to a getSpans(start, end), but filling the 'spans' local 40060e3b3fa60a672583fe6294139feb940845efc9bGilles Debunne // array instead to reduce memory allocation 401eca5b7385a641ae5d8c4aa2b7823a4e573737376Gilles Debunne for (int j = 0; j < mLineBackgroundSpans.numberOfSpans; j++) { 402eca5b7385a641ae5d8c4aa2b7823a4e573737376Gilles Debunne // equal test is valid since both intervals are not empty by 403eca5b7385a641ae5d8c4aa2b7823a4e573737376Gilles Debunne // construction 404eca5b7385a641ae5d8c4aa2b7823a4e573737376Gilles Debunne if (mLineBackgroundSpans.spanStarts[j] >= end || 405eca5b7385a641ae5d8c4aa2b7823a4e573737376Gilles Debunne mLineBackgroundSpans.spanEnds[j] <= start) continue; 40660e3b3fa60a672583fe6294139feb940845efc9bGilles Debunne if (spansLength == spans.length) { 40760e3b3fa60a672583fe6294139feb940845efc9bGilles Debunne // The spans array needs to be expanded 40860e3b3fa60a672583fe6294139feb940845efc9bGilles Debunne int newSize = ArrayUtils.idealObjectArraySize(2 * spansLength); 40960e3b3fa60a672583fe6294139feb940845efc9bGilles Debunne ParagraphStyle[] newSpans = new ParagraphStyle[newSize]; 41060e3b3fa60a672583fe6294139feb940845efc9bGilles Debunne System.arraycopy(spans, 0, newSpans, 0, spansLength); 41160e3b3fa60a672583fe6294139feb940845efc9bGilles Debunne spans = newSpans; 41260e3b3fa60a672583fe6294139feb940845efc9bGilles Debunne } 413eca5b7385a641ae5d8c4aa2b7823a4e573737376Gilles Debunne spans[spansLength++] = mLineBackgroundSpans.spans[j]; 41460e3b3fa60a672583fe6294139feb940845efc9bGilles Debunne } 41560e3b3fa60a672583fe6294139feb940845efc9bGilles Debunne } 41660e3b3fa60a672583fe6294139feb940845efc9bGilles Debunne } 41760e3b3fa60a672583fe6294139feb940845efc9bGilles Debunne 41860e3b3fa60a672583fe6294139feb940845efc9bGilles Debunne for (int n = 0; n < spansLength; n++) { 41960e3b3fa60a672583fe6294139feb940845efc9bGilles Debunne LineBackgroundSpan lineBackgroundSpan = (LineBackgroundSpan) spans[n]; 42060e3b3fa60a672583fe6294139feb940845efc9bGilles Debunne lineBackgroundSpan.drawBackground(canvas, paint, 0, width, 42160e3b3fa60a672583fe6294139feb940845efc9bGilles Debunne ltop, lbaseline, lbottom, 42260e3b3fa60a672583fe6294139feb940845efc9bGilles Debunne buffer, start, end, i); 42360e3b3fa60a672583fe6294139feb940845efc9bGilles Debunne } 4246c488de023a4797069673dc619c1a4096079ea9eGilles Debunne } 4256c488de023a4797069673dc619c1a4096079ea9eGilles Debunne } 426eca5b7385a641ae5d8c4aa2b7823a4e573737376Gilles Debunne mLineBackgroundSpans.recycle(); 4276c488de023a4797069673dc619c1a4096079ea9eGilles Debunne } 4286c488de023a4797069673dc619c1a4096079ea9eGilles Debunne 4296c488de023a4797069673dc619c1a4096079ea9eGilles Debunne // There can be a highlight even without spans if we are drawing 4306c488de023a4797069673dc619c1a4096079ea9eGilles Debunne // a non-spanned transformation of a spanned editing buffer. 4316c488de023a4797069673dc619c1a4096079ea9eGilles Debunne if (highlight != null) { 4326c488de023a4797069673dc619c1a4096079ea9eGilles Debunne if (cursorOffsetVertical != 0) canvas.translate(0, cursorOffsetVertical); 4336c488de023a4797069673dc619c1a4096079ea9eGilles Debunne canvas.drawPath(highlight, highlightPaint); 4346c488de023a4797069673dc619c1a4096079ea9eGilles Debunne if (cursorOffsetVertical != 0) canvas.translate(0, -cursorOffsetVertical); 4356c488de023a4797069673dc619c1a4096079ea9eGilles Debunne } 4366c488de023a4797069673dc619c1a4096079ea9eGilles Debunne } 4376c488de023a4797069673dc619c1a4096079ea9eGilles Debunne 4386c488de023a4797069673dc619c1a4096079ea9eGilles Debunne /** 4396c488de023a4797069673dc619c1a4096079ea9eGilles Debunne * @param canvas 4406c488de023a4797069673dc619c1a4096079ea9eGilles Debunne * @return The range of lines that need to be drawn, possibly empty. 4416c488de023a4797069673dc619c1a4096079ea9eGilles Debunne * @hide 4426c488de023a4797069673dc619c1a4096079ea9eGilles Debunne */ 4436c488de023a4797069673dc619c1a4096079ea9eGilles Debunne public long getLineRangeForDraw(Canvas canvas) { 4446c488de023a4797069673dc619c1a4096079ea9eGilles Debunne int dtop, dbottom; 4456c488de023a4797069673dc619c1a4096079ea9eGilles Debunne 4466c488de023a4797069673dc619c1a4096079ea9eGilles Debunne synchronized (sTempRect) { 4476c488de023a4797069673dc619c1a4096079ea9eGilles Debunne if (!canvas.getClipBounds(sTempRect)) { 4486c488de023a4797069673dc619c1a4096079ea9eGilles Debunne // Negative range end used as a special flag 4496c488de023a4797069673dc619c1a4096079ea9eGilles Debunne return TextUtils.packRangeInLong(0, -1); 4506c488de023a4797069673dc619c1a4096079ea9eGilles Debunne } 4516c488de023a4797069673dc619c1a4096079ea9eGilles Debunne 4526c488de023a4797069673dc619c1a4096079ea9eGilles Debunne dtop = sTempRect.top; 4536c488de023a4797069673dc619c1a4096079ea9eGilles Debunne dbottom = sTempRect.bottom; 4546c488de023a4797069673dc619c1a4096079ea9eGilles Debunne } 4556c488de023a4797069673dc619c1a4096079ea9eGilles Debunne 4566c488de023a4797069673dc619c1a4096079ea9eGilles Debunne final int top = Math.max(dtop, 0); 4576c488de023a4797069673dc619c1a4096079ea9eGilles Debunne final int bottom = Math.min(getLineTop(getLineCount()), dbottom); 4586c488de023a4797069673dc619c1a4096079ea9eGilles Debunne 4592fba3387c31b675c419030145250e5be246c50b0Gilles Debunne if (top >= bottom) return TextUtils.packRangeInLong(0, -1); 4606c488de023a4797069673dc619c1a4096079ea9eGilles Debunne return TextUtils.packRangeInLong(getLineForVertical(top), getLineForVertical(bottom)); 4616c488de023a4797069673dc619c1a4096079ea9eGilles Debunne } 4626c488de023a4797069673dc619c1a4096079ea9eGilles Debunne 4636c488de023a4797069673dc619c1a4096079ea9eGilles Debunne /** 464c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt * Return the start position of the line, given the left and right bounds 465c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt * of the margins. 4660c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt * 467c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt * @param line the line index 468c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt * @param left the left bounds (0, or leading margin if ltr para) 469c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt * @param right the right bounds (width, minus leading margin if rtl para) 470c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt * @return the start position of the line (to right of line if rtl para) 471c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt */ 472c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt private int getLineStartPos(int line, int left, int right) { 473c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt // Adjust the point at which to start rendering depending on the 474c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt // alignment of the paragraph. 475c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt Alignment align = getParagraphAlignment(line); 476c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt int dir = getParagraphDirection(line); 477c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt 478c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt int x; 479c0ccf0c47c00942a9d0f2670600a8f2d4d7adb5bDoug Felt if (align == Alignment.ALIGN_LEFT) { 480c0ccf0c47c00942a9d0f2670600a8f2d4d7adb5bDoug Felt x = left; 481c0ccf0c47c00942a9d0f2670600a8f2d4d7adb5bDoug Felt } else if (align == Alignment.ALIGN_NORMAL) { 482c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt if (dir == DIR_LEFT_TO_RIGHT) { 483c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt x = left; 484c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt } else { 485c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt x = right; 486c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt } 487c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt } else { 488c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt TabStops tabStops = null; 489c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt if (mSpannedText && getLineContainsTab(line)) { 490c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt Spanned spanned = (Spanned) mText; 491c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt int start = getLineStart(line); 492c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt int spanEnd = spanned.nextSpanTransition(start, spanned.length(), 493c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt TabStopSpan.class); 4946c488de023a4797069673dc619c1a4096079ea9eGilles Debunne TabStopSpan[] tabSpans = getParagraphSpans(spanned, start, spanEnd, 4956c488de023a4797069673dc619c1a4096079ea9eGilles Debunne TabStopSpan.class); 496c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt if (tabSpans.length > 0) { 497c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt tabStops = new TabStops(TAB_INCREMENT, tabSpans); 498c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt } 499c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt } 500c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt int max = (int)getLineExtent(line, tabStops, false); 501c0ccf0c47c00942a9d0f2670600a8f2d4d7adb5bDoug Felt if (align == Alignment.ALIGN_RIGHT) { 502c0ccf0c47c00942a9d0f2670600a8f2d4d7adb5bDoug Felt x = right - max; 503c0ccf0c47c00942a9d0f2670600a8f2d4d7adb5bDoug Felt } else if (align == Alignment.ALIGN_OPPOSITE) { 504c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt if (dir == DIR_LEFT_TO_RIGHT) { 505c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt x = right - max; 506c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt } else { 507c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt x = left - max; 508c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt } 509c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt } else { // Alignment.ALIGN_CENTER 510c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt max = max & ~1; 511c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt x = (left + right - max) >> 1; 512c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt } 513c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt } 514c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt return x; 515c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt } 516c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt 517c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt /** 5189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Return the text that is displayed by this Layout. 5199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 5209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public final CharSequence getText() { 5219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return mText; 5229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 5239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 5249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 5259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Return the base Paint properties for this layout. 5269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Do NOT change the paint, which may result in funny 5279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * drawing for this layout. 5289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 5299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public final TextPaint getPaint() { 5309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return mPaint; 5319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 5329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 5339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 5349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Return the width of this layout. 5359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 5369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public final int getWidth() { 5379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return mWidth; 5389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 5399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 5409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 5419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Return the width to which this Layout is ellipsizing, or 5429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * {@link #getWidth} if it is not doing anything special. 5439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 5449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public int getEllipsizedWidth() { 5459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return mWidth; 5469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 5479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 5489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 5499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Increase the width of this layout to the specified width. 55071b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt * Be careful to use this only when you know it is appropriate— 5519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * it does not cause the text to reflow to use the full new width. 5529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 5539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public final void increaseWidthTo(int wid) { 5549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (wid < mWidth) { 5559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project throw new RuntimeException("attempted to reduce Layout width"); 5569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 5579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 5589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mWidth = wid; 5599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 5609f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt 5619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 5629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Return the total height of this layout. 5639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 5649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public int getHeight() { 56571b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt return getLineTop(getLineCount()); 5669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 5679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 5689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 5699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Return the base alignment of this layout. 5709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 5719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public final Alignment getAlignment() { 5729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return mAlignment; 5739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 5749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 5759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 5769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Return what the text height is multiplied by to get the line height. 5779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 5789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public final float getSpacingMultiplier() { 5799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return mSpacingMult; 5809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 5819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 5829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 5839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Return the number of units of leading that are added to each line. 5849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 5859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public final float getSpacingAdd() { 5869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return mSpacingAdd; 5879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 5889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 5899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 590cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt * Return the heuristic used to determine paragraph text direction. 591cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt * @hide 592cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt */ 593cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt public final TextDirectionHeuristic getTextDirectionHeuristic() { 594cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt return mTextDir; 595cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt } 596cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt 597cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt /** 5989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Return the number of lines of text in this layout. 5999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 6009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public abstract int getLineCount(); 6019f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt 6029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 6039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Return the baseline for the specified line (0…getLineCount() - 1) 6049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * If bounds is not null, return the top, left, right, bottom extents 6059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * of the specified line in it. 6069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * @param line which line to examine (0..getLineCount() - 1) 6079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * @param bounds Optional. If not null, it returns the extent of the line 6089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * @return the Y-coordinate of the baseline 6099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 6109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public int getLineBounds(int line, Rect bounds) { 6119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (bounds != null) { 6129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project bounds.left = 0; // ??? 6139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project bounds.top = getLineTop(line); 6149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project bounds.right = mWidth; // ??? 61571b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt bounds.bottom = getLineTop(line + 1); 6169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 6179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return getLineBaseline(line); 6189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 6199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 6209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 62171b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt * Return the vertical position of the top of the specified line 62271b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt * (0…getLineCount()). 62371b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt * If the specified line is equal to the line count, returns the 6249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * bottom of the last line. 6259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 6269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public abstract int getLineTop(int line); 6279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 6289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 62971b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt * Return the descent of the specified line(0…getLineCount() - 1). 6309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 6319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public abstract int getLineDescent(int line); 6329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 6339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 63471b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt * Return the text offset of the beginning of the specified line ( 63571b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt * 0…getLineCount()). If the specified line is equal to the line 63671b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt * count, returns the length of the text. 6379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 6389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public abstract int getLineStart(int line); 6399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 6409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 64171b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt * Returns the primary directionality of the paragraph containing the 64271b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt * specified line, either 1 for left-to-right lines, or -1 for right-to-left 64371b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt * lines (see {@link #DIR_LEFT_TO_RIGHT}, {@link #DIR_RIGHT_TO_LEFT}). 6449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 6459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public abstract int getParagraphDirection(int line); 6469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 6479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 648105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project * Returns whether the specified line contains one or more 649105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project * characters that need to be handled specially, like tabs 650105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project * or emoji. 6519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 6529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public abstract boolean getLineContainsTab(int line); 6539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 6549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 65571b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt * Returns the directional run information for the specified line. 6569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * The array alternates counts of characters in left-to-right 6579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * and right-to-left segments of the line. 65871b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt * 65971b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt * <p>NOTE: this is inadequate to support bidirectional text, and will change. 6609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 6619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public abstract Directions getLineDirections(int line); 6629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 6639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 6649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Returns the (negative) number of extra pixels of ascent padding in the 6659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * top line of the Layout. 6669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 6679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public abstract int getTopPadding(); 6689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 6699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 6709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Returns the number of extra pixels of descent padding in the 6719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * bottom line of the Layout. 6729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 6739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public abstract int getBottomPadding(); 6749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 6754e0c5e55e171532760d5f51e0165563827129d4eDoug Felt 6764e0c5e55e171532760d5f51e0165563827129d4eDoug Felt /** 6774e0c5e55e171532760d5f51e0165563827129d4eDoug Felt * Returns true if the character at offset and the preceding character 6784e0c5e55e171532760d5f51e0165563827129d4eDoug Felt * are at different run levels (and thus there's a split caret). 6794e0c5e55e171532760d5f51e0165563827129d4eDoug Felt * @param offset the offset 6804e0c5e55e171532760d5f51e0165563827129d4eDoug Felt * @return true if at a level boundary 681f75c97e023af7d4ad9a8c129d4ea282b1c3b8f94Gilles Debunne * @hide 6824e0c5e55e171532760d5f51e0165563827129d4eDoug Felt */ 683f75c97e023af7d4ad9a8c129d4ea282b1c3b8f94Gilles Debunne public boolean isLevelBoundary(int offset) { 6849f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt int line = getLineForOffset(offset); 6854e0c5e55e171532760d5f51e0165563827129d4eDoug Felt Directions dirs = getLineDirections(line); 6864e0c5e55e171532760d5f51e0165563827129d4eDoug Felt if (dirs == DIRS_ALL_LEFT_TO_RIGHT || dirs == DIRS_ALL_RIGHT_TO_LEFT) { 6874e0c5e55e171532760d5f51e0165563827129d4eDoug Felt return false; 6884e0c5e55e171532760d5f51e0165563827129d4eDoug Felt } 6894e0c5e55e171532760d5f51e0165563827129d4eDoug Felt 6904e0c5e55e171532760d5f51e0165563827129d4eDoug Felt int[] runs = dirs.mDirections; 6919f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt int lineStart = getLineStart(line); 6924e0c5e55e171532760d5f51e0165563827129d4eDoug Felt int lineEnd = getLineEnd(line); 6934e0c5e55e171532760d5f51e0165563827129d4eDoug Felt if (offset == lineStart || offset == lineEnd) { 6944e0c5e55e171532760d5f51e0165563827129d4eDoug Felt int paraLevel = getParagraphDirection(line) == 1 ? 0 : 1; 6954e0c5e55e171532760d5f51e0165563827129d4eDoug Felt int runIndex = offset == lineStart ? 0 : runs.length - 2; 6964e0c5e55e171532760d5f51e0165563827129d4eDoug Felt return ((runs[runIndex + 1] >>> RUN_LEVEL_SHIFT) & RUN_LEVEL_MASK) != paraLevel; 6974e0c5e55e171532760d5f51e0165563827129d4eDoug Felt } 6984e0c5e55e171532760d5f51e0165563827129d4eDoug Felt 6994e0c5e55e171532760d5f51e0165563827129d4eDoug Felt offset -= lineStart; 7009f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt for (int i = 0; i < runs.length; i += 2) { 7014e0c5e55e171532760d5f51e0165563827129d4eDoug Felt if (offset == runs[i]) { 7024e0c5e55e171532760d5f51e0165563827129d4eDoug Felt return true; 7039f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt } 7049f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt } 7054e0c5e55e171532760d5f51e0165563827129d4eDoug Felt return false; 7069f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt } 7079f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt 70834d2eba560f83f4eb665cdc039cf02bf96c201daFabrice Di Meglio /** 70934d2eba560f83f4eb665cdc039cf02bf96c201daFabrice Di Meglio * Returns true if the character at offset is right to left (RTL). 71034d2eba560f83f4eb665cdc039cf02bf96c201daFabrice Di Meglio * @param offset the offset 71134d2eba560f83f4eb665cdc039cf02bf96c201daFabrice Di Meglio * @return true if the character is RTL, false if it is LTR 71234d2eba560f83f4eb665cdc039cf02bf96c201daFabrice Di Meglio */ 71334d2eba560f83f4eb665cdc039cf02bf96c201daFabrice Di Meglio public boolean isRtlCharAt(int offset) { 71434d2eba560f83f4eb665cdc039cf02bf96c201daFabrice Di Meglio int line = getLineForOffset(offset); 71534d2eba560f83f4eb665cdc039cf02bf96c201daFabrice Di Meglio Directions dirs = getLineDirections(line); 71634d2eba560f83f4eb665cdc039cf02bf96c201daFabrice Di Meglio if (dirs == DIRS_ALL_LEFT_TO_RIGHT) { 71734d2eba560f83f4eb665cdc039cf02bf96c201daFabrice Di Meglio return false; 71834d2eba560f83f4eb665cdc039cf02bf96c201daFabrice Di Meglio } 71934d2eba560f83f4eb665cdc039cf02bf96c201daFabrice Di Meglio if (dirs == DIRS_ALL_RIGHT_TO_LEFT) { 72034d2eba560f83f4eb665cdc039cf02bf96c201daFabrice Di Meglio return true; 72134d2eba560f83f4eb665cdc039cf02bf96c201daFabrice Di Meglio } 72234d2eba560f83f4eb665cdc039cf02bf96c201daFabrice Di Meglio int[] runs = dirs.mDirections; 72334d2eba560f83f4eb665cdc039cf02bf96c201daFabrice Di Meglio int lineStart = getLineStart(line); 72434d2eba560f83f4eb665cdc039cf02bf96c201daFabrice Di Meglio for (int i = 0; i < runs.length; i += 2) { 72534d2eba560f83f4eb665cdc039cf02bf96c201daFabrice Di Meglio int start = lineStart + (runs[i] & RUN_LENGTH_MASK); 72634d2eba560f83f4eb665cdc039cf02bf96c201daFabrice Di Meglio // No need to test the end as an offset after the last run should return the value 72734d2eba560f83f4eb665cdc039cf02bf96c201daFabrice Di Meglio // corresponding of the last run 72834d2eba560f83f4eb665cdc039cf02bf96c201daFabrice Di Meglio if (offset >= start) { 72934d2eba560f83f4eb665cdc039cf02bf96c201daFabrice Di Meglio int level = (runs[i+1] >>> RUN_LEVEL_SHIFT) & RUN_LEVEL_MASK; 73034d2eba560f83f4eb665cdc039cf02bf96c201daFabrice Di Meglio return ((level & 1) != 0); 73134d2eba560f83f4eb665cdc039cf02bf96c201daFabrice Di Meglio } 73234d2eba560f83f4eb665cdc039cf02bf96c201daFabrice Di Meglio } 73334d2eba560f83f4eb665cdc039cf02bf96c201daFabrice Di Meglio // Should happen only if the offset is "out of bounds" 73434d2eba560f83f4eb665cdc039cf02bf96c201daFabrice Di Meglio return false; 73534d2eba560f83f4eb665cdc039cf02bf96c201daFabrice Di Meglio } 73634d2eba560f83f4eb665cdc039cf02bf96c201daFabrice Di Meglio 7379f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt private boolean primaryIsTrailingPrevious(int offset) { 7389f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt int line = getLineForOffset(offset); 7399f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt int lineStart = getLineStart(line); 7404e0c5e55e171532760d5f51e0165563827129d4eDoug Felt int lineEnd = getLineEnd(line); 7419f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt int[] runs = getLineDirections(line).mDirections; 7429f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt 7439f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt int levelAt = -1; 7449f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt for (int i = 0; i < runs.length; i += 2) { 7459f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt int start = lineStart + runs[i]; 7469f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt int limit = start + (runs[i+1] & RUN_LENGTH_MASK); 7479f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt if (limit > lineEnd) { 7489f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt limit = lineEnd; 7499f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt } 7509f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt if (offset >= start && offset < limit) { 7519f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt if (offset > start) { 7529f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt // Previous character is at same level, so don't use trailing. 7539f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt return false; 7549f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt } 7559f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt levelAt = (runs[i+1] >>> RUN_LEVEL_SHIFT) & RUN_LEVEL_MASK; 7569f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt break; 7579f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt } 7589f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt } 7599f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt if (levelAt == -1) { 7609f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt // Offset was limit of line. 7619f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt levelAt = getParagraphDirection(line) == 1 ? 0 : 1; 7629f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt } 7639f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt 7649f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt // At level boundary, check previous level. 7659f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt int levelBefore = -1; 7669f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt if (offset == lineStart) { 7679f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt levelBefore = getParagraphDirection(line) == 1 ? 0 : 1; 7689f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt } else { 7699f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt offset -= 1; 7709f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt for (int i = 0; i < runs.length; i += 2) { 7719f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt int start = lineStart + runs[i]; 7729f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt int limit = start + (runs[i+1] & RUN_LENGTH_MASK); 7739f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt if (limit > lineEnd) { 7749f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt limit = lineEnd; 7759f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt } 7769f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt if (offset >= start && offset < limit) { 7779f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt levelBefore = (runs[i+1] >>> RUN_LEVEL_SHIFT) & RUN_LEVEL_MASK; 7789f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt break; 7799f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt } 7809f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt } 7819f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt } 7829f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt 7839f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt return levelBefore < levelAt; 7849f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt } 7859f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt 7869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 7879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Get the primary horizontal position for the specified text offset. 7889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * This is the location where a new character would be inserted in 7899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * the paragraph's primary direction. 7909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 7919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public float getPrimaryHorizontal(int offset) { 7929f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt boolean trailing = primaryIsTrailingPrevious(offset); 7939f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt return getHorizontal(offset, trailing); 7949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 7959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 7969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 7979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Get the secondary horizontal position for the specified text offset. 7989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * This is the location where a new character would be inserted in 7999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * the direction other than the paragraph's primary direction. 8009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 8019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public float getSecondaryHorizontal(int offset) { 8029f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt boolean trailing = primaryIsTrailingPrevious(offset); 8039f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt return getHorizontal(offset, !trailing); 8049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 8059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 8069f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt private float getHorizontal(int offset, boolean trailing) { 8079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int line = getLineForOffset(offset); 8089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 8099f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt return getHorizontal(offset, trailing, line); 8109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 8119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 8129f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt private float getHorizontal(int offset, boolean trailing, int line) { 8139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int start = getLineStart(line); 814e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt int end = getLineEnd(line); 8159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int dir = getParagraphDirection(line); 816c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt boolean hasTabOrEmoji = getLineContainsTab(line); 8179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project Directions directions = getLineDirections(line); 8189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 819c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt TabStops tabStops = null; 820c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt if (hasTabOrEmoji && mText instanceof Spanned) { 821c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt // Just checking this line should be good enough, tabs should be 822c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt // consistent across all lines in a paragraph. 82374d31ef2b2c42b54fa1f7cf94ea955ea67ab69a0Eric Fischer TabStopSpan[] tabs = getParagraphSpans((Spanned) mText, start, end, TabStopSpan.class); 824c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt if (tabs.length > 0) { 825c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt tabStops = new TabStops(TAB_INCREMENT, tabs); // XXX should reuse 826c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt } 8279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 8289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 829e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt TextLine tl = TextLine.obtain(); 830c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt tl.set(mPaint, mText, start, end, dir, directions, hasTabOrEmoji, tabStops); 831e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt float wid = tl.measure(offset - start, trailing, null); 832e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt TextLine.recycle(tl); 8339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 8349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int left = getParagraphLeft(line); 8359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int right = getParagraphRight(line); 8369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 837c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt return getLineStartPos(line, left, right) + wid; 8389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 8399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 8409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 8419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Get the leftmost position that should be exposed for horizontal 8429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * scrolling on the specified line. 8439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 8449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public float getLineLeft(int line) { 8459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int dir = getParagraphDirection(line); 8469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project Alignment align = getParagraphAlignment(line); 8479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 848c0ccf0c47c00942a9d0f2670600a8f2d4d7adb5bDoug Felt if (align == Alignment.ALIGN_LEFT) { 849c0ccf0c47c00942a9d0f2670600a8f2d4d7adb5bDoug Felt return 0; 850c0ccf0c47c00942a9d0f2670600a8f2d4d7adb5bDoug Felt } else if (align == Alignment.ALIGN_NORMAL) { 8519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (dir == DIR_RIGHT_TO_LEFT) 8529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return getParagraphRight(line) - getLineMax(line); 8539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project else 8549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return 0; 855c0ccf0c47c00942a9d0f2670600a8f2d4d7adb5bDoug Felt } else if (align == Alignment.ALIGN_RIGHT) { 856c0ccf0c47c00942a9d0f2670600a8f2d4d7adb5bDoug Felt return mWidth - getLineMax(line); 8579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } else if (align == Alignment.ALIGN_OPPOSITE) { 8589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (dir == DIR_RIGHT_TO_LEFT) 8599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return 0; 8609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project else 8619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return mWidth - getLineMax(line); 8629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } else { /* align == Alignment.ALIGN_CENTER */ 8639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int left = getParagraphLeft(line); 8649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int right = getParagraphRight(line); 8659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int max = ((int) getLineMax(line)) & ~1; 8669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 8679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return left + ((right - left) - max) / 2; 8689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 8699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 8709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 8719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 8729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Get the rightmost position that should be exposed for horizontal 8739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * scrolling on the specified line. 8749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 8759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public float getLineRight(int line) { 8769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int dir = getParagraphDirection(line); 8779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project Alignment align = getParagraphAlignment(line); 8789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 879c0ccf0c47c00942a9d0f2670600a8f2d4d7adb5bDoug Felt if (align == Alignment.ALIGN_LEFT) { 880c0ccf0c47c00942a9d0f2670600a8f2d4d7adb5bDoug Felt return getParagraphLeft(line) + getLineMax(line); 881c0ccf0c47c00942a9d0f2670600a8f2d4d7adb5bDoug Felt } else if (align == Alignment.ALIGN_NORMAL) { 8829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (dir == DIR_RIGHT_TO_LEFT) 8839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return mWidth; 8849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project else 8859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return getParagraphLeft(line) + getLineMax(line); 886c0ccf0c47c00942a9d0f2670600a8f2d4d7adb5bDoug Felt } else if (align == Alignment.ALIGN_RIGHT) { 887c0ccf0c47c00942a9d0f2670600a8f2d4d7adb5bDoug Felt return mWidth; 8889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } else if (align == Alignment.ALIGN_OPPOSITE) { 8899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (dir == DIR_RIGHT_TO_LEFT) 8909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return getLineMax(line); 8919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project else 8929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return mWidth; 8939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } else { /* align == Alignment.ALIGN_CENTER */ 8949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int left = getParagraphLeft(line); 8959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int right = getParagraphRight(line); 8969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int max = ((int) getLineMax(line)) & ~1; 8979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 8989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return right - ((right - left) - max) / 2; 8999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 9009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 9019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 9029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 9030c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt * Gets the unsigned horizontal extent of the specified line, including 904c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt * leading margin indent, but excluding trailing whitespace. 9059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 9069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public float getLineMax(int line) { 907c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt float margin = getParagraphLeadingMargin(line); 908c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt float signedExtent = getLineExtent(line, false); 909c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt return margin + signedExtent >= 0 ? signedExtent : -signedExtent; 9109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 9119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 9129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 913c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt * Gets the unsigned horizontal extent of the specified line, including 914c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt * leading margin indent and trailing whitespace. 9159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 9169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public float getLineWidth(int line) { 917c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt float margin = getParagraphLeadingMargin(line); 918c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt float signedExtent = getLineExtent(line, true); 919c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt return margin + signedExtent >= 0 ? signedExtent : -signedExtent; 9209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 9219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 922c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt /** 923c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt * Like {@link #getLineExtent(int,TabStops,boolean)} but determines the 924c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt * tab stops instead of using the ones passed in. 925c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt * @param line the index of the line 926c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt * @param full whether to include trailing whitespace 927c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt * @return the extent of the line 928c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt */ 929c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt private float getLineExtent(int line, boolean full) { 9309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int start = getLineStart(line); 931e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt int end = full ? getLineEnd(line) : getLineVisibleEnd(line); 932c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt 933c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt boolean hasTabsOrEmoji = getLineContainsTab(line); 934c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt TabStops tabStops = null; 935c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt if (hasTabsOrEmoji && mText instanceof Spanned) { 936c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt // Just checking this line should be good enough, tabs should be 937c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt // consistent across all lines in a paragraph. 93874d31ef2b2c42b54fa1f7cf94ea955ea67ab69a0Eric Fischer TabStopSpan[] tabs = getParagraphSpans((Spanned) mText, start, end, TabStopSpan.class); 939c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt if (tabs.length > 0) { 940c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt tabStops = new TabStops(TAB_INCREMENT, tabs); // XXX should reuse 941c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt } 942c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt } 943c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt Directions directions = getLineDirections(line); 9448059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio // Returned directions can actually be null 9458059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio if (directions == null) { 9468059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio return 0f; 9478059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio } 948c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt int dir = getParagraphDirection(line); 949c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt 950c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt TextLine tl = TextLine.obtain(); 951c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt tl.set(mPaint, mText, start, end, dir, directions, hasTabsOrEmoji, tabStops); 952c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt float width = tl.metrics(null); 953c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt TextLine.recycle(tl); 954c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt return width; 955c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt } 956c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt 957c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt /** 958c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt * Returns the signed horizontal extent of the specified line, excluding 959c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt * leading margin. If full is false, excludes trailing whitespace. 960c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt * @param line the index of the line 961c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt * @param tabStops the tab stops, can be null if we know they're not used. 962c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt * @param full whether to include trailing whitespace 963c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt * @return the extent of the text on this line 964c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt */ 965c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt private float getLineExtent(int line, TabStops tabStops, boolean full) { 966c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt int start = getLineStart(line); 967c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt int end = full ? getLineEnd(line) : getLineVisibleEnd(line); 968c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt boolean hasTabsOrEmoji = getLineContainsTab(line); 969e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt Directions directions = getLineDirections(line); 970c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt int dir = getParagraphDirection(line); 9719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 972e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt TextLine tl = TextLine.obtain(); 973c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt tl.set(mPaint, mText, start, end, dir, directions, hasTabsOrEmoji, tabStops); 974e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt float width = tl.metrics(null); 975e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt TextLine.recycle(tl); 976e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt return width; 9779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 9789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 9799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 9809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Get the line number corresponding to the specified vertical position. 9819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * If you ask for a position above 0, you get 0; if you ask for a position 9829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * below the bottom of the text, you get the last line. 9839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 9849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // FIXME: It may be faster to do a linear search for layouts without many lines. 9859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public int getLineForVertical(int vertical) { 9869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int high = getLineCount(), low = -1, guess; 9879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 9889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project while (high - low > 1) { 9899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project guess = (high + low) / 2; 9909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 9919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (getLineTop(guess) > vertical) 9929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project high = guess; 9939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project else 9949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project low = guess; 9959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 9969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 9979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (low < 0) 9989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return 0; 9999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project else 10009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return low; 10019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 10029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 10039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 10049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Get the line number on which the specified text offset appears. 10059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * If you ask for a position before 0, you get 0; if you ask for a position 10069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * beyond the end of the text, you get the last line. 10079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 10089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public int getLineForOffset(int offset) { 10099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int high = getLineCount(), low = -1, guess; 10109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 10119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project while (high - low > 1) { 10129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project guess = (high + low) / 2; 10139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 10149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (getLineStart(guess) > offset) 10159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project high = guess; 10169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project else 10179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project low = guess; 10189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 10199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 10209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (low < 0) 10219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return 0; 10229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project else 10239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return low; 10249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 10259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 10269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 10279f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt * Get the character offset on the specified line whose position is 10289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * closest to the specified horizontal position. 10299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 10309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public int getOffsetForHorizontal(int line, float horiz) { 10319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int max = getLineEnd(line) - 1; 10329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int min = getLineStart(line); 10339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project Directions dirs = getLineDirections(line); 10349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 10359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (line == getLineCount() - 1) 10369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project max++; 10379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 10389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int best = min; 10399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project float bestdist = Math.abs(getPrimaryHorizontal(best) - horiz); 10409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 10419f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt for (int i = 0; i < dirs.mDirections.length; i += 2) { 10429f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt int here = min + dirs.mDirections[i]; 10439f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt int there = here + (dirs.mDirections[i+1] & RUN_LENGTH_MASK); 10449f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt int swap = (dirs.mDirections[i+1] & RUN_RTL_FLAG) != 0 ? -1 : 1; 10459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 10469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (there > max) 10479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project there = max; 10489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int high = there - 1 + 1, low = here + 1 - 1, guess; 10499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 10509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project while (high - low > 1) { 10519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project guess = (high + low) / 2; 10529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int adguess = getOffsetAtStartOf(guess); 10539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 10549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (getPrimaryHorizontal(adguess) * swap >= horiz * swap) 10559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project high = guess; 10569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project else 10579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project low = guess; 10589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 10599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 10609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (low < here + 1) 10619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project low = here + 1; 10629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 10639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (low < there) { 10649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project low = getOffsetAtStartOf(low); 10659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 10669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project float dist = Math.abs(getPrimaryHorizontal(low) - horiz); 10679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 10689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int aft = TextUtils.getOffsetAfter(mText, low); 10699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (aft < there) { 10709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project float other = Math.abs(getPrimaryHorizontal(aft) - horiz); 10719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 10729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (other < dist) { 10739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project dist = other; 10749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project low = aft; 10759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 10769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 10779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 10789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (dist < bestdist) { 10799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project bestdist = dist; 10809f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt best = low; 10819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 10829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 10839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 10849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project float dist = Math.abs(getPrimaryHorizontal(here) - horiz); 10859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 10869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (dist < bestdist) { 10879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project bestdist = dist; 10889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project best = here; 10899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 10909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 10919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 10929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project float dist = Math.abs(getPrimaryHorizontal(max) - horiz); 10939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 10949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (dist < bestdist) { 10959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project bestdist = dist; 10969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project best = max; 10979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 10989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 10999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return best; 11009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 11019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 11029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 11039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Return the text offset after the last character on the specified line. 11049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 11059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public final int getLineEnd(int line) { 11069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return getLineStart(line + 1); 11079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 11089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 11099f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt /** 11109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Return the text offset after the last visible character (so whitespace 11119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * is not counted) on the specified line. 11129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 11139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public int getLineVisibleEnd(int line) { 11149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return getLineVisibleEnd(line, getLineStart(line), getLineStart(line+1)); 11159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 11169f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt 11179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private int getLineVisibleEnd(int line, int start, int end) { 11189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project CharSequence text = mText; 11199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project char ch; 11209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (line == getLineCount() - 1) { 11219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return end; 11229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 11239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 11249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project for (; end > start; end--) { 11259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project ch = text.charAt(end - 1); 11269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 11279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (ch == '\n') { 11289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return end - 1; 11299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 11309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 11319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (ch != ' ' && ch != '\t') { 11329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project break; 11339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 11349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 11359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 11369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 11379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return end; 11389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 11399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 11409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 11419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Return the vertical position of the bottom of the specified line. 11429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 11439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public final int getLineBottom(int line) { 11449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return getLineTop(line + 1); 11459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 11469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 11479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 11489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Return the vertical position of the baseline of the specified line. 11499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 11509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public final int getLineBaseline(int line) { 11519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // getLineTop(line+1) == getLineTop(line) 11529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return getLineTop(line+1) - getLineDescent(line); 11539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 11549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 11559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 11569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Get the ascent of the text on the specified line. 11579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * The return value is negative to match the Paint.ascent() convention. 11589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 11599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public final int getLineAscent(int line) { 11609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // getLineTop(line+1) - getLineDescent(line) == getLineBaseLine(line) 11619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return getLineTop(line) - (getLineTop(line+1) - getLineDescent(line)); 11629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 11639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 11649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public int getOffsetToLeftOf(int offset) { 11659f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt return getOffsetToLeftRightOf(offset, true); 11669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 11679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 11689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public int getOffsetToRightOf(int offset) { 11699f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt return getOffsetToLeftRightOf(offset, false); 11709f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt } 11719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 11729f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt private int getOffsetToLeftRightOf(int caret, boolean toLeft) { 11739f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt int line = getLineForOffset(caret); 11749f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt int lineStart = getLineStart(line); 11759f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt int lineEnd = getLineEnd(line); 1176e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt int lineDir = getParagraphDirection(line); 1177e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 1178162bf0f1b9fd5d78ffa801917994f6222c6efd85Gilles Debunne boolean lineChanged = false; 1179e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt boolean advance = toLeft == (lineDir == DIR_RIGHT_TO_LEFT); 1180162bf0f1b9fd5d78ffa801917994f6222c6efd85Gilles Debunne // if walking off line, look at the line we're headed to 1181162bf0f1b9fd5d78ffa801917994f6222c6efd85Gilles Debunne if (advance) { 1182162bf0f1b9fd5d78ffa801917994f6222c6efd85Gilles Debunne if (caret == lineEnd) { 1183162bf0f1b9fd5d78ffa801917994f6222c6efd85Gilles Debunne if (line < getLineCount() - 1) { 1184162bf0f1b9fd5d78ffa801917994f6222c6efd85Gilles Debunne lineChanged = true; 1185162bf0f1b9fd5d78ffa801917994f6222c6efd85Gilles Debunne ++line; 1186162bf0f1b9fd5d78ffa801917994f6222c6efd85Gilles Debunne } else { 1187162bf0f1b9fd5d78ffa801917994f6222c6efd85Gilles Debunne return caret; // at very end, don't move 1188162bf0f1b9fd5d78ffa801917994f6222c6efd85Gilles Debunne } 1189162bf0f1b9fd5d78ffa801917994f6222c6efd85Gilles Debunne } 1190162bf0f1b9fd5d78ffa801917994f6222c6efd85Gilles Debunne } else { 1191e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (caret == lineStart) { 1192e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (line > 0) { 1193162bf0f1b9fd5d78ffa801917994f6222c6efd85Gilles Debunne lineChanged = true; 1194e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt --line; 1195e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } else { 1196e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt return caret; // at very start, don't move 1197e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 11989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1199162bf0f1b9fd5d78ffa801917994f6222c6efd85Gilles Debunne } 12009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1201162bf0f1b9fd5d78ffa801917994f6222c6efd85Gilles Debunne if (lineChanged) { 1202e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt lineStart = getLineStart(line); 1203e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt lineEnd = getLineEnd(line); 1204e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt int newDir = getParagraphDirection(line); 1205e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (newDir != lineDir) { 1206e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // unusual case. we want to walk onto the line, but it runs 1207e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // in a different direction than this one, so we fake movement 1208e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // in the opposite direction. 1209e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt toLeft = !toLeft; 1210e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt lineDir = newDir; 12119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 12129f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt } 12139f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt 1214e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt Directions directions = getLineDirections(line); 12159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1216e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt TextLine tl = TextLine.obtain(); 1217e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // XXX: we don't care about tabs 1218e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt tl.set(mPaint, mText, lineStart, lineEnd, lineDir, directions, false, null); 1219e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt caret = lineStart + tl.getOffsetToLeftRightOf(caret - lineStart, toLeft); 1220e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt tl = TextLine.recycle(tl); 1221e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt return caret; 12229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 12239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 12249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private int getOffsetAtStartOf(int offset) { 12259f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt // XXX this probably should skip local reorderings and 12269f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt // zero-width characters, look at callers 12279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (offset == 0) 12289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return 0; 12299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 12309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project CharSequence text = mText; 12319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project char c = text.charAt(offset); 12329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 12339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (c >= '\uDC00' && c <= '\uDFFF') { 12349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project char c1 = text.charAt(offset - 1); 12359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 12369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (c1 >= '\uD800' && c1 <= '\uDBFF') 12379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project offset -= 1; 12389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 12399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 12409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (mSpannedText) { 12419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project ReplacementSpan[] spans = ((Spanned) text).getSpans(offset, offset, 12429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project ReplacementSpan.class); 12439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 12449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project for (int i = 0; i < spans.length; i++) { 12459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int start = ((Spanned) text).getSpanStart(spans[i]); 12469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int end = ((Spanned) text).getSpanEnd(spans[i]); 12479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 12489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (start < offset && end > offset) 12499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project offset = start; 12509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 12519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 12529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 12539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return offset; 12549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 12559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 12569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 12579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Fills in the specified Path with a representation of a cursor 12589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * at the specified offset. This will often be a vertical line 12594e0c5e55e171532760d5f51e0165563827129d4eDoug Felt * but can be multiple discontinuous lines in text with multiple 12609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * directionalities. 12619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 12629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public void getCursorPath(int point, Path dest, 12639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project CharSequence editingBuffer) { 12649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project dest.reset(); 12659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 12669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int line = getLineForOffset(point); 12679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int top = getLineTop(line); 12689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int bottom = getLineTop(line+1); 12699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 12709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project float h1 = getPrimaryHorizontal(point) - 0.5f; 1271f75c97e023af7d4ad9a8c129d4ea282b1c3b8f94Gilles Debunne float h2 = isLevelBoundary(point) ? getSecondaryHorizontal(point) - 0.5f : h1; 12729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1273497a92cc5ba2176b8a8484b0a7da040eac0e887bJeff Brown int caps = TextKeyListener.getMetaState(editingBuffer, TextKeyListener.META_SHIFT_ON) | 1274497a92cc5ba2176b8a8484b0a7da040eac0e887bJeff Brown TextKeyListener.getMetaState(editingBuffer, TextKeyListener.META_SELECTING); 1275497a92cc5ba2176b8a8484b0a7da040eac0e887bJeff Brown int fn = TextKeyListener.getMetaState(editingBuffer, TextKeyListener.META_ALT_ON); 12769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int dist = 0; 12779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 12789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (caps != 0 || fn != 0) { 12799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project dist = (bottom - top) >> 2; 12809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 12819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (fn != 0) 12829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project top += dist; 12839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (caps != 0) 12849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project bottom -= dist; 12859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 12869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 12879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (h1 < 0.5f) 12889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project h1 = 0.5f; 12899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (h2 < 0.5f) 12909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project h2 = 0.5f; 12919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 12922fb503f5102dd32a8ec391b26911528852703b90Jozef BABJAK if (Float.compare(h1, h2) == 0) { 12939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project dest.moveTo(h1, top); 12949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project dest.lineTo(h1, bottom); 12959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } else { 12969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project dest.moveTo(h1, top); 12979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project dest.lineTo(h1, (top + bottom) >> 1); 12989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 12999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project dest.moveTo(h2, (top + bottom) >> 1); 13009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project dest.lineTo(h2, bottom); 13019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 13029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 13039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (caps == 2) { 13049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project dest.moveTo(h2, bottom); 13059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project dest.lineTo(h2 - dist, bottom + dist); 13069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project dest.lineTo(h2, bottom); 13079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project dest.lineTo(h2 + dist, bottom + dist); 13089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } else if (caps == 1) { 13099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project dest.moveTo(h2, bottom); 13109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project dest.lineTo(h2 - dist, bottom + dist); 13119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 13129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project dest.moveTo(h2 - dist, bottom + dist - 0.5f); 13139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project dest.lineTo(h2 + dist, bottom + dist - 0.5f); 13149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 13159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project dest.moveTo(h2 + dist, bottom + dist); 13169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project dest.lineTo(h2, bottom); 13179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 13189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 13199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (fn == 2) { 13209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project dest.moveTo(h1, top); 13219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project dest.lineTo(h1 - dist, top - dist); 13229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project dest.lineTo(h1, top); 13239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project dest.lineTo(h1 + dist, top - dist); 13249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } else if (fn == 1) { 13259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project dest.moveTo(h1, top); 13269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project dest.lineTo(h1 - dist, top - dist); 13279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 13289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project dest.moveTo(h1 - dist, top - dist + 0.5f); 13299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project dest.lineTo(h1 + dist, top - dist + 0.5f); 13309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 13319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project dest.moveTo(h1 + dist, top - dist); 13329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project dest.lineTo(h1, top); 13339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 13349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 13359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 13369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private void addSelection(int line, int start, int end, 13379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int top, int bottom, Path dest) { 13389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int linestart = getLineStart(line); 13399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int lineend = getLineEnd(line); 13409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project Directions dirs = getLineDirections(line); 13419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 13429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (lineend > linestart && mText.charAt(lineend - 1) == '\n') 13439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project lineend--; 13449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 13459f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt for (int i = 0; i < dirs.mDirections.length; i += 2) { 13469f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt int here = linestart + dirs.mDirections[i]; 13479f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt int there = here + (dirs.mDirections[i+1] & RUN_LENGTH_MASK); 13489f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt 13499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (there > lineend) 13509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project there = lineend; 13519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 13529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (start <= there && end >= here) { 13539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int st = Math.max(start, here); 13549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int en = Math.min(end, there); 13559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 13569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (st != en) { 13579f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt float h1 = getHorizontal(st, false, line); 13589f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt float h2 = getHorizontal(en, true, line); 13599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 13603716601573f5a562f98721130e25002ad88eb164Fabrice Di Meglio float left = Math.min(h1, h2); 13613716601573f5a562f98721130e25002ad88eb164Fabrice Di Meglio float right = Math.max(h1, h2); 13623716601573f5a562f98721130e25002ad88eb164Fabrice Di Meglio 13633716601573f5a562f98721130e25002ad88eb164Fabrice Di Meglio dest.addRect(left, top, right, bottom, Path.Direction.CW); 13649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 13659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 13669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 13679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 13689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 13699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 13709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Fills in the specified Path with a representation of a highlight 13719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * between the specified offsets. This will often be a rectangle 13729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * or a potentially discontinuous set of rectangles. If the start 13739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * and end are the same, the returned path is empty. 13749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 13759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public void getSelectionPath(int start, int end, Path dest) { 13769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project dest.reset(); 13779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 13789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (start == end) 13799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return; 13809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 13819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (end < start) { 13829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int temp = end; 13839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project end = start; 13849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project start = temp; 13859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 13869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 13879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int startline = getLineForOffset(start); 13889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int endline = getLineForOffset(end); 13899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 13909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int top = getLineTop(startline); 13919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int bottom = getLineBottom(endline); 13929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 13939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (startline == endline) { 13949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project addSelection(startline, start, end, top, bottom, dest); 13959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } else { 13969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project final float width = mWidth; 13979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 13989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project addSelection(startline, start, getLineEnd(startline), 13999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project top, getLineBottom(startline), dest); 14009f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt 14019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (getParagraphDirection(startline) == DIR_RIGHT_TO_LEFT) 14029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project dest.addRect(getLineLeft(startline), top, 14039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 0, getLineBottom(startline), Path.Direction.CW); 14049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project else 14059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project dest.addRect(getLineRight(startline), top, 14069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project width, getLineBottom(startline), Path.Direction.CW); 14079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 14089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project for (int i = startline + 1; i < endline; i++) { 14099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project top = getLineTop(i); 14109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project bottom = getLineBottom(i); 14119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project dest.addRect(0, top, width, bottom, Path.Direction.CW); 14129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 14139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 14149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project top = getLineTop(endline); 14159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project bottom = getLineBottom(endline); 14169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 14179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project addSelection(endline, getLineStart(endline), end, 14189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project top, bottom, dest); 14199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 14209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (getParagraphDirection(endline) == DIR_RIGHT_TO_LEFT) 14219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project dest.addRect(width, top, getLineRight(endline), bottom, Path.Direction.CW); 14229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project else 14239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project dest.addRect(0, top, getLineLeft(endline), bottom, Path.Direction.CW); 14249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 14259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 14269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 14279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 14289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Get the alignment of the specified paragraph, taking into account 14299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * markup attached to it. 14309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 14319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public final Alignment getParagraphAlignment(int line) { 14329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project Alignment align = mAlignment; 14339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 14349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (mSpannedText) { 14359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project Spanned sp = (Spanned) mText; 143674d31ef2b2c42b54fa1f7cf94ea955ea67ab69a0Eric Fischer AlignmentSpan[] spans = getParagraphSpans(sp, getLineStart(line), 14379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project getLineEnd(line), 14389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project AlignmentSpan.class); 14399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 14409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int spanLength = spans.length; 14419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (spanLength > 0) { 14429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project align = spans[spanLength-1].getAlignment(); 14439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 14449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 14459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 14469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return align; 14479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 14489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 14499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 14509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Get the left edge of the specified paragraph, inset by left margins. 14519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 14529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public final int getParagraphLeft(int line) { 14539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int left = 0; 1454c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt int dir = getParagraphDirection(line); 1455c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt if (dir == DIR_RIGHT_TO_LEFT || !mSpannedText) { 1456c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt return left; // leading margin has no impact, or no styles 14579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1458c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt return getParagraphLeadingMargin(line); 14599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 14609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 14619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 14629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Get the right edge of the specified paragraph, inset by right margins. 14639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 14649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public final int getParagraphRight(int line) { 14659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int right = mWidth; 1466c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt int dir = getParagraphDirection(line); 1467c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt if (dir == DIR_LEFT_TO_RIGHT || !mSpannedText) { 1468c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt return right; // leading margin has no impact, or no styles 1469c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt } 1470c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt return right - getParagraphLeadingMargin(line); 1471c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt } 14729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1473c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt /** 1474c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt * Returns the effective leading margin (unsigned) for this line, 1475c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt * taking into account LeadingMarginSpan and LeadingMarginSpan2. 1476c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt * @param line the line index 1477c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt * @return the leading margin of this line 1478c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt */ 1479c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt private int getParagraphLeadingMargin(int line) { 1480c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt if (!mSpannedText) { 1481c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt return 0; 1482c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt } 1483c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt Spanned spanned = (Spanned) mText; 14840c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt 1485c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt int lineStart = getLineStart(line); 1486c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt int lineEnd = getLineEnd(line); 14870c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt int spanEnd = spanned.nextSpanTransition(lineStart, lineEnd, 1488c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt LeadingMarginSpan.class); 148974d31ef2b2c42b54fa1f7cf94ea955ea67ab69a0Eric Fischer LeadingMarginSpan[] spans = getParagraphSpans(spanned, lineStart, spanEnd, 1490c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt LeadingMarginSpan.class); 1491c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt if (spans.length == 0) { 1492c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt return 0; // no leading margin span; 1493c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt } 14940c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt 1495c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt int margin = 0; 14960c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt 14970c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt boolean isFirstParaLine = lineStart == 0 || 1498c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt spanned.charAt(lineStart - 1) == '\n'; 14990c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt 1500c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt for (int i = 0; i < spans.length; i++) { 1501c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt LeadingMarginSpan span = spans[i]; 1502c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt boolean useFirstLineMargin = isFirstParaLine; 1503c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt if (span instanceof LeadingMarginSpan2) { 1504c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt int spStart = spanned.getSpanStart(span); 1505c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt int spanLine = getLineForOffset(spStart); 1506c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt int count = ((LeadingMarginSpan2)span).getLeadingMarginLineCount(); 15070c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt useFirstLineMargin = line < spanLine + count; 15089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1509c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt margin += span.getLeadingMargin(useFirstLineMargin); 15109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 15119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1512c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt return margin; 15139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 15149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1515e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt /* package */ 15166c488de023a4797069673dc619c1a4096079ea9eGilles Debunne static float measurePara(TextPaint paint, CharSequence text, int start, int end) { 1517e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 1518e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt MeasuredText mt = MeasuredText.obtain(); 1519e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt TextLine tl = TextLine.obtain(); 1520e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt try { 1521cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt mt.setPara(text, start, end, TextDirectionHeuristics.LTR); 1522e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt Directions directions; 1523c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt int dir; 1524c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt if (mt.mEasy) { 1525e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt directions = DIRS_ALL_LEFT_TO_RIGHT; 1526c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt dir = Layout.DIR_LEFT_TO_RIGHT; 1527e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } else { 1528e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt directions = AndroidBidi.directions(mt.mDir, mt.mLevels, 1529e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 0, mt.mChars, 0, mt.mLen); 1530c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt dir = mt.mDir; 1531c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt } 1532c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt char[] chars = mt.mChars; 1533c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt int len = mt.mLen; 1534c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt boolean hasTabs = false; 1535c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt TabStops tabStops = null; 1536c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt for (int i = 0; i < len; ++i) { 1537c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt if (chars[i] == '\t') { 1538c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt hasTabs = true; 1539c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt if (text instanceof Spanned) { 1540c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt Spanned spanned = (Spanned) text; 15410c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt int spanEnd = spanned.nextSpanTransition(start, end, 1542c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt TabStopSpan.class); 154374d31ef2b2c42b54fa1f7cf94ea955ea67ab69a0Eric Fischer TabStopSpan[] spans = getParagraphSpans(spanned, start, spanEnd, 1544c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt TabStopSpan.class); 1545c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt if (spans.length > 0) { 1546c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt tabStops = new TabStops(TAB_INCREMENT, spans); 1547c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt } 1548c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt } 1549c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt break; 1550c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt } 15519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1552c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt tl.set(paint, text, start, end, dir, directions, hasTabs, tabStops); 1553e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt return tl.metrics(null); 1554e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } finally { 1555e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt TextLine.recycle(tl); 1556e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt MeasuredText.recycle(mt); 15579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 15589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 15599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 156071b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt /** 1561c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt * @hide 1562c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt */ 1563c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt /* package */ static class TabStops { 1564c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt private int[] mStops; 1565c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt private int mNumStops; 1566c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt private int mIncrement; 15670c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt 1568c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt TabStops(int increment, Object[] spans) { 1569c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt reset(increment, spans); 1570c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt } 15710c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt 1572c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt void reset(int increment, Object[] spans) { 1573c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt this.mIncrement = increment; 1574c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt 1575c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt int ns = 0; 1576c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt if (spans != null) { 1577c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt int[] stops = this.mStops; 1578c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt for (Object o : spans) { 1579c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt if (o instanceof TabStopSpan) { 1580c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt if (stops == null) { 1581c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt stops = new int[10]; 1582c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt } else if (ns == stops.length) { 1583c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt int[] nstops = new int[ns * 2]; 1584c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt for (int i = 0; i < ns; ++i) { 1585c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt nstops[i] = stops[i]; 1586c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt } 1587c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt stops = nstops; 1588c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt } 1589c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt stops[ns++] = ((TabStopSpan) o).getTabStop(); 1590c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt } 1591c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt } 1592c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt if (ns > 1) { 1593c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt Arrays.sort(stops, 0, ns); 1594c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt } 1595c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt if (stops != this.mStops) { 1596c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt this.mStops = stops; 1597c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt } 1598c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt } 1599c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt this.mNumStops = ns; 1600c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt } 16010c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt 1602c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt float nextTab(float h) { 1603c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt int ns = this.mNumStops; 1604c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt if (ns > 0) { 1605c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt int[] stops = this.mStops; 1606c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt for (int i = 0; i < ns; ++i) { 1607c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt int stop = stops[i]; 1608c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt if (stop > h) { 1609c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt return stop; 1610c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt } 1611c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt } 1612c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt } 1613c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt return nextDefaultStop(h, mIncrement); 1614c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt } 1615c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt 1616c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt public static float nextDefaultStop(float h, int inc) { 1617c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt return ((int) ((h + inc) / inc)) * inc; 1618c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt } 1619c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt } 16200c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt 1621c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt /** 162271b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt * Returns the position of the next tab stop after h on the line. 162371b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt * 162471b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt * @param text the text 162571b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt * @param start start of the line 162671b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt * @param end limit of the line 162771b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt * @param h the current horizontal offset 162871b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt * @param tabs the tabs, can be null. If it is null, any tabs in effect 162971b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt * on the line will be used. If there are no tabs, a default offset 163071b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt * will be used to compute the tab stop. 163171b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt * @return the offset of the next tab stop. 163271b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt */ 16339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /* package */ static float nextTab(CharSequence text, int start, int end, 16349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project float h, Object[] tabs) { 16359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project float nh = Float.MAX_VALUE; 16369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project boolean alltabs = false; 16379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 16389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (text instanceof Spanned) { 16399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (tabs == null) { 164074d31ef2b2c42b54fa1f7cf94ea955ea67ab69a0Eric Fischer tabs = getParagraphSpans((Spanned) text, start, end, TabStopSpan.class); 16419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project alltabs = true; 16429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 16439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 16449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project for (int i = 0; i < tabs.length; i++) { 16459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (!alltabs) { 16469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (!(tabs[i] instanceof TabStopSpan)) 16479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project continue; 16489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 16499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 16509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int where = ((TabStopSpan) tabs[i]).getTabStop(); 16519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 16529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (where < nh && where > h) 16539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project nh = where; 16549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 16559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 16569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (nh != Float.MAX_VALUE) 16579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return nh; 16589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 16599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 16609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return ((int) ((h + TAB_INCREMENT) / TAB_INCREMENT)) * TAB_INCREMENT; 16619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 16629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 16639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project protected final boolean isSpanned() { 16649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return mSpannedText; 16659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 16669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 166774d31ef2b2c42b54fa1f7cf94ea955ea67ab69a0Eric Fischer /** 166874d31ef2b2c42b54fa1f7cf94ea955ea67ab69a0Eric Fischer * Returns the same as <code>text.getSpans()</code>, except where 166974d31ef2b2c42b54fa1f7cf94ea955ea67ab69a0Eric Fischer * <code>start</code> and <code>end</code> are the same and are not 167074d31ef2b2c42b54fa1f7cf94ea955ea67ab69a0Eric Fischer * at the very beginning of the text, in which case an empty array 167174d31ef2b2c42b54fa1f7cf94ea955ea67ab69a0Eric Fischer * is returned instead. 167274d31ef2b2c42b54fa1f7cf94ea955ea67ab69a0Eric Fischer * <p> 167374d31ef2b2c42b54fa1f7cf94ea955ea67ab69a0Eric Fischer * This is needed because of the special case that <code>getSpans()</code> 167474d31ef2b2c42b54fa1f7cf94ea955ea67ab69a0Eric Fischer * on an empty range returns the spans adjacent to that range, which is 167574d31ef2b2c42b54fa1f7cf94ea955ea67ab69a0Eric Fischer * primarily for the sake of <code>TextWatchers</code> so they will get 167674d31ef2b2c42b54fa1f7cf94ea955ea67ab69a0Eric Fischer * notifications when text goes from empty to non-empty. But it also 167774d31ef2b2c42b54fa1f7cf94ea955ea67ab69a0Eric Fischer * has the unfortunate side effect that if the text ends with an empty 167874d31ef2b2c42b54fa1f7cf94ea955ea67ab69a0Eric Fischer * paragraph, that paragraph accidentally picks up the styles of the 167974d31ef2b2c42b54fa1f7cf94ea955ea67ab69a0Eric Fischer * preceding paragraph (even though those styles will not be picked up 168074d31ef2b2c42b54fa1f7cf94ea955ea67ab69a0Eric Fischer * by new text that is inserted into the empty paragraph). 168174d31ef2b2c42b54fa1f7cf94ea955ea67ab69a0Eric Fischer * <p> 168274d31ef2b2c42b54fa1f7cf94ea955ea67ab69a0Eric Fischer * The reason it just checks whether <code>start</code> and <code>end</code> 168374d31ef2b2c42b54fa1f7cf94ea955ea67ab69a0Eric Fischer * is the same is that the only time a line can contain 0 characters 168474d31ef2b2c42b54fa1f7cf94ea955ea67ab69a0Eric Fischer * is if it is the final paragraph of the Layout; otherwise any line will 168574d31ef2b2c42b54fa1f7cf94ea955ea67ab69a0Eric Fischer * contain at least one printing or newline character. The reason for the 168674d31ef2b2c42b54fa1f7cf94ea955ea67ab69a0Eric Fischer * additional check if <code>start</code> is greater than 0 is that 168774d31ef2b2c42b54fa1f7cf94ea955ea67ab69a0Eric Fischer * if the empty paragraph is the entire content of the buffer, paragraph 168874d31ef2b2c42b54fa1f7cf94ea955ea67ab69a0Eric Fischer * styles that are already applied to the buffer will apply to text that 168974d31ef2b2c42b54fa1f7cf94ea955ea67ab69a0Eric Fischer * is inserted into it. 169074d31ef2b2c42b54fa1f7cf94ea955ea67ab69a0Eric Fischer */ 1691eca5b7385a641ae5d8c4aa2b7823a4e573737376Gilles Debunne /* package */static <T> T[] getParagraphSpans(Spanned text, int start, int end, Class<T> type) { 169274d31ef2b2c42b54fa1f7cf94ea955ea67ab69a0Eric Fischer if (start == end && start > 0) { 16936c488de023a4797069673dc619c1a4096079ea9eGilles Debunne return ArrayUtils.emptyArray(type); 169474d31ef2b2c42b54fa1f7cf94ea955ea67ab69a0Eric Fischer } 169574d31ef2b2c42b54fa1f7cf94ea955ea67ab69a0Eric Fischer 169674d31ef2b2c42b54fa1f7cf94ea955ea67ab69a0Eric Fischer return text.getSpans(start, end, type); 169774d31ef2b2c42b54fa1f7cf94ea955ea67ab69a0Eric Fischer } 169874d31ef2b2c42b54fa1f7cf94ea955ea67ab69a0Eric Fischer 16999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private void ellipsize(int start, int end, int line, 17009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project char[] dest, int destoff) { 17019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int ellipsisCount = getEllipsisCount(line); 17029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 17039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (ellipsisCount == 0) { 17049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return; 17059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 17069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 17079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int ellipsisStart = getEllipsisStart(line); 17089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int linestart = getLineStart(line); 17099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 17109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project for (int i = ellipsisStart; i < ellipsisStart + ellipsisCount; i++) { 17119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project char c; 17129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 17139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (i == ellipsisStart) { 17149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project c = '\u2026'; // ellipsis 17159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } else { 17169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project c = '\uFEFF'; // 0-width space 17179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 17189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 17199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int a = i + linestart; 17209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 17219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (a >= start && a < end) { 17229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project dest[destoff + a - start] = c; 17239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 17249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 17259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 17269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 17279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 17289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Stores information about bidirectional (left-to-right or right-to-left) 17299f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt * text within the layout of a line. 17309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 17319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public static class Directions { 17329f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt // Directions represents directional runs within a line of text. 17339f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt // Runs are pairs of ints listed in visual order, starting from the 17349f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt // leading margin. The first int of each pair is the offset from 17359f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt // the first character of the line to the start of the run. The 17369f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt // second int represents both the length and level of the run. 17379f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt // The length is in the lower bits, accessed by masking with 17389f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt // DIR_LENGTH_MASK. The level is in the higher bits, accessed 17399f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt // by shifting by DIR_LEVEL_SHIFT and masking by DIR_LEVEL_MASK. 17409f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt // To simply test for an RTL direction, test the bit using 17419f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt // DIR_RTL_FLAG, if set then the direction is rtl. 17429f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt 17439f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt /* package */ int[] mDirections; 17449f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt /* package */ Directions(int[] dirs) { 17459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mDirections = dirs; 17469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 17479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 17489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 17499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 17509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Return the offset of the first character to be ellipsized away, 17519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * relative to the start of the line. (So 0 if the beginning of the 17529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * line is ellipsized, not getLineStart().) 17539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 17549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public abstract int getEllipsisStart(int line); 1755e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 17569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 17579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Returns the number of characters to be ellipsized away, or 0 if 17589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * no ellipsis is to take place. 17599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 17609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public abstract int getEllipsisCount(int line); 17619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 17629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /* package */ static class Ellipsizer implements CharSequence, GetChars { 17639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /* package */ CharSequence mText; 17649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /* package */ Layout mLayout; 17659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /* package */ int mWidth; 17669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /* package */ TextUtils.TruncateAt mMethod; 17679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 17689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public Ellipsizer(CharSequence s) { 17699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mText = s; 17709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 17719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 17729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public char charAt(int off) { 17739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project char[] buf = TextUtils.obtain(1); 17749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project getChars(off, off + 1, buf, 0); 17759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project char ret = buf[0]; 17769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 17779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project TextUtils.recycle(buf); 17789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return ret; 17799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 17809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 17819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public void getChars(int start, int end, char[] dest, int destoff) { 17829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int line1 = mLayout.getLineForOffset(start); 17839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int line2 = mLayout.getLineForOffset(end); 17849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 17859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project TextUtils.getChars(mText, start, end, dest, destoff); 17869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 17879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project for (int i = line1; i <= line2; i++) { 17889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mLayout.ellipsize(start, end, i, dest, destoff); 17899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 17909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 17919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 17929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public int length() { 17939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return mText.length(); 17949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 17959f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt 17969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public CharSequence subSequence(int start, int end) { 17979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project char[] s = new char[end - start]; 17989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project getChars(start, end, s, 0); 17999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return new String(s); 18009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 18019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1802162bf0f1b9fd5d78ffa801917994f6222c6efd85Gilles Debunne @Override 18039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public String toString() { 18049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project char[] s = new char[length()]; 18059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project getChars(0, length(), s, 0); 18069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return new String(s); 18079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 18089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 18099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 18109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 18116c488de023a4797069673dc619c1a4096079ea9eGilles Debunne /* package */ static class SpannedEllipsizer extends Ellipsizer implements Spanned { 18129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private Spanned mSpanned; 18139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 18149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public SpannedEllipsizer(CharSequence display) { 18159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project super(display); 18169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mSpanned = (Spanned) display; 18179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 18189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 18199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public <T> T[] getSpans(int start, int end, Class<T> type) { 18209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return mSpanned.getSpans(start, end, type); 18219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 18229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 18239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public int getSpanStart(Object tag) { 18249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return mSpanned.getSpanStart(tag); 18259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 18269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 18279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public int getSpanEnd(Object tag) { 18289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return mSpanned.getSpanEnd(tag); 18299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 18309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 18319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public int getSpanFlags(Object tag) { 18329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return mSpanned.getSpanFlags(tag); 18339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 18349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 18356c488de023a4797069673dc619c1a4096079ea9eGilles Debunne @SuppressWarnings("rawtypes") 18369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public int nextSpanTransition(int start, int limit, Class type) { 18379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return mSpanned.nextSpanTransition(start, limit, type); 18389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 18399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1840162bf0f1b9fd5d78ffa801917994f6222c6efd85Gilles Debunne @Override 18419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public CharSequence subSequence(int start, int end) { 18429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project char[] s = new char[end - start]; 18439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project getChars(start, end, s, 0); 18449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 18459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project SpannableString ss = new SpannableString(new String(s)); 18469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project TextUtils.copySpansFrom(mSpanned, start, end, Object.class, ss, 0); 18479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return ss; 18489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 18499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 18509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 18519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private CharSequence mText; 18529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private TextPaint mPaint; 18539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /* package */ TextPaint mWorkPaint; 18549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private int mWidth; 18559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private Alignment mAlignment = Alignment.ALIGN_NORMAL; 18569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private float mSpacingMult; 18579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private float mSpacingAdd; 1858c4d8eb6fb7c88c5c4da38b0b113c24cc4b78c0b7Romain Guy private static final Rect sTempRect = new Rect(); 18599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private boolean mSpannedText; 1860cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt private TextDirectionHeuristic mTextDir; 1861eca5b7385a641ae5d8c4aa2b7823a4e573737376Gilles Debunne private SpanSet<LineBackgroundSpan> mLineBackgroundSpans; 18629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 18639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public static final int DIR_LEFT_TO_RIGHT = 1; 18649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public static final int DIR_RIGHT_TO_LEFT = -1; 18659f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt 186620178d62cf669af18467a16d3c4c4237ed42151cDoug Felt /* package */ static final int DIR_REQUEST_LTR = 1; 186720178d62cf669af18467a16d3c4c4237ed42151cDoug Felt /* package */ static final int DIR_REQUEST_RTL = -1; 186820178d62cf669af18467a16d3c4c4237ed42151cDoug Felt /* package */ static final int DIR_REQUEST_DEFAULT_LTR = 2; 186920178d62cf669af18467a16d3c4c4237ed42151cDoug Felt /* package */ static final int DIR_REQUEST_DEFAULT_RTL = -2; 18709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 18719f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt /* package */ static final int RUN_LENGTH_MASK = 0x03ffffff; 18729f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt /* package */ static final int RUN_LEVEL_SHIFT = 26; 18739f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt /* package */ static final int RUN_LEVEL_MASK = 0x3f; 18749f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt /* package */ static final int RUN_RTL_FLAG = 1 << RUN_LEVEL_SHIFT; 18759f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt 18769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public enum Alignment { 18779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project ALIGN_NORMAL, 18789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project ALIGN_OPPOSITE, 18799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project ALIGN_CENTER, 1880c0ccf0c47c00942a9d0f2670600a8f2d4d7adb5bDoug Felt /** @hide */ 1881c0ccf0c47c00942a9d0f2670600a8f2d4d7adb5bDoug Felt ALIGN_LEFT, 1882c0ccf0c47c00942a9d0f2670600a8f2d4d7adb5bDoug Felt /** @hide */ 1883c0ccf0c47c00942a9d0f2670600a8f2d4d7adb5bDoug Felt ALIGN_RIGHT, 18849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 18859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 18869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private static final int TAB_INCREMENT = 20; 18879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 18889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /* package */ static final Directions DIRS_ALL_LEFT_TO_RIGHT = 18899f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt new Directions(new int[] { 0, RUN_LENGTH_MASK }); 18909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /* package */ static final Directions DIRS_ALL_RIGHT_TO_LEFT = 18919f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt new Directions(new int[] { 0, RUN_LENGTH_MASK | RUN_RTL_FLAG }); 18920a4db3c5270440eeb7e4e44a7029926e239ec3bdGilles Debunne 18939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project} 1894