Layout.java revision 373b7a8d4e9dce4f71539d4dbcf627fd3e1a39da
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 478c0ccf0c47c00942a9d0f2670600a8f2d4d7adb5bDoug Felt if (align == Alignment.ALIGN_LEFT) { 479db24f0a73f4385aea104367a4bc71b78f25dbc3fFabrice Di Meglio align = (dir == DIR_LEFT_TO_RIGHT) ? Alignment.ALIGN_NORMAL : Alignment.ALIGN_OPPOSITE; 480db24f0a73f4385aea104367a4bc71b78f25dbc3fFabrice Di Meglio } else if (align == Alignment.ALIGN_RIGHT) { 481db24f0a73f4385aea104367a4bc71b78f25dbc3fFabrice Di Meglio align = (dir == DIR_LEFT_TO_RIGHT) ? Alignment.ALIGN_OPPOSITE : Alignment.ALIGN_NORMAL; 482db24f0a73f4385aea104367a4bc71b78f25dbc3fFabrice Di Meglio } 483db24f0a73f4385aea104367a4bc71b78f25dbc3fFabrice Di Meglio 484db24f0a73f4385aea104367a4bc71b78f25dbc3fFabrice Di Meglio int x; 485db24f0a73f4385aea104367a4bc71b78f25dbc3fFabrice Di Meglio if (align == Alignment.ALIGN_NORMAL) { 486c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt if (dir == DIR_LEFT_TO_RIGHT) { 487c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt x = left; 488c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt } else { 489c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt x = right; 490c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt } 491c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt } else { 492c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt TabStops tabStops = null; 493c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt if (mSpannedText && getLineContainsTab(line)) { 494c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt Spanned spanned = (Spanned) mText; 495c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt int start = getLineStart(line); 496c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt int spanEnd = spanned.nextSpanTransition(start, spanned.length(), 497c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt TabStopSpan.class); 4986c488de023a4797069673dc619c1a4096079ea9eGilles Debunne TabStopSpan[] tabSpans = getParagraphSpans(spanned, start, spanEnd, 4996c488de023a4797069673dc619c1a4096079ea9eGilles Debunne TabStopSpan.class); 500c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt if (tabSpans.length > 0) { 501c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt tabStops = new TabStops(TAB_INCREMENT, tabSpans); 502c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt } 503c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt } 504c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt int max = (int)getLineExtent(line, tabStops, false); 505db24f0a73f4385aea104367a4bc71b78f25dbc3fFabrice Di Meglio if (align == Alignment.ALIGN_OPPOSITE) { 506c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt if (dir == DIR_LEFT_TO_RIGHT) { 507c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt x = right - max; 508c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt } else { 509db24f0a73f4385aea104367a4bc71b78f25dbc3fFabrice Di Meglio // max is negative here 510c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt x = left - max; 511c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt } 512c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt } else { // Alignment.ALIGN_CENTER 513c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt max = max & ~1; 514c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt x = (left + right - max) >> 1; 515c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt } 516c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt } 517c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt return x; 518c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt } 519c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt 520c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt /** 5219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Return the text that is displayed by this Layout. 5229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 5239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public final CharSequence getText() { 5249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return mText; 5259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 5269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 5279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 5289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Return the base Paint properties for this layout. 5299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Do NOT change the paint, which may result in funny 5309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * drawing for this layout. 5319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 5329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public final TextPaint getPaint() { 5339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return mPaint; 5349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 5359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 5369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 5379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Return the width of this layout. 5389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 5399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public final int getWidth() { 5409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return mWidth; 5419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 5429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 5439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 5449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Return the width to which this Layout is ellipsizing, or 5459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * {@link #getWidth} if it is not doing anything special. 5469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 5479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public int getEllipsizedWidth() { 5489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return mWidth; 5499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 5509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 5519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 5529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Increase the width of this layout to the specified width. 55371b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt * Be careful to use this only when you know it is appropriate— 5549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * it does not cause the text to reflow to use the full new width. 5559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 5569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public final void increaseWidthTo(int wid) { 5579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (wid < mWidth) { 5589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project throw new RuntimeException("attempted to reduce Layout width"); 5599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 5609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 5619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mWidth = wid; 5629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 5639f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt 5649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 5659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Return the total height of this layout. 5669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 5679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public int getHeight() { 56871b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt return getLineTop(getLineCount()); 5699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 5709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 5719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 5729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Return the base alignment of this layout. 5739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 5749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public final Alignment getAlignment() { 5759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return mAlignment; 5769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 5779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 5789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 5799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Return what the text height is multiplied by to get the line height. 5809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 5819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public final float getSpacingMultiplier() { 5829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return mSpacingMult; 5839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 5849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 5859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 5869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Return the number of units of leading that are added to each line. 5879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 5889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public final float getSpacingAdd() { 5899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return mSpacingAdd; 5909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 5919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 5929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 593cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt * Return the heuristic used to determine paragraph text direction. 594cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt * @hide 595cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt */ 596cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt public final TextDirectionHeuristic getTextDirectionHeuristic() { 597cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt return mTextDir; 598cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt } 599cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt 600cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt /** 6019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Return the number of lines of text in this layout. 6029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 6039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public abstract int getLineCount(); 6049f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt 6059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 6069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Return the baseline for the specified line (0…getLineCount() - 1) 6079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * If bounds is not null, return the top, left, right, bottom extents 6089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * of the specified line in it. 6099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * @param line which line to examine (0..getLineCount() - 1) 6109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * @param bounds Optional. If not null, it returns the extent of the line 6119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * @return the Y-coordinate of the baseline 6129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 6139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public int getLineBounds(int line, Rect bounds) { 6149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (bounds != null) { 6159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project bounds.left = 0; // ??? 6169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project bounds.top = getLineTop(line); 6179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project bounds.right = mWidth; // ??? 61871b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt bounds.bottom = getLineTop(line + 1); 6199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 6209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return getLineBaseline(line); 6219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 6229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 6239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 62471b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt * Return the vertical position of the top of the specified line 62571b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt * (0…getLineCount()). 62671b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt * If the specified line is equal to the line count, returns the 6279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * bottom of the last line. 6289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 6299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public abstract int getLineTop(int line); 6309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 6319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 63271b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt * Return the descent of the specified line(0…getLineCount() - 1). 6339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 6349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public abstract int getLineDescent(int line); 6359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 6369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 63771b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt * Return the text offset of the beginning of the specified line ( 63871b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt * 0…getLineCount()). If the specified line is equal to the line 63971b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt * count, returns the length of the text. 6409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 6419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public abstract int getLineStart(int line); 6429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 6439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 64471b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt * Returns the primary directionality of the paragraph containing the 64571b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt * specified line, either 1 for left-to-right lines, or -1 for right-to-left 64671b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt * lines (see {@link #DIR_LEFT_TO_RIGHT}, {@link #DIR_RIGHT_TO_LEFT}). 6479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 6489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public abstract int getParagraphDirection(int line); 6499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 6509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 651105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project * Returns whether the specified line contains one or more 652105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project * characters that need to be handled specially, like tabs 653105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project * or emoji. 6549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 6559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public abstract boolean getLineContainsTab(int line); 6569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 6579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 65871b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt * Returns the directional run information for the specified line. 6599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * The array alternates counts of characters in left-to-right 6609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * and right-to-left segments of the line. 66171b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt * 66271b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt * <p>NOTE: this is inadequate to support bidirectional text, and will change. 6639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 6649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public abstract Directions getLineDirections(int line); 6659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 6669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 6679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Returns the (negative) number of extra pixels of ascent padding in the 6689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * top line of the Layout. 6699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 6709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public abstract int getTopPadding(); 6719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 6729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 6739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Returns the number of extra pixels of descent padding in the 6749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * bottom line of the Layout. 6759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 6769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public abstract int getBottomPadding(); 6779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 6784e0c5e55e171532760d5f51e0165563827129d4eDoug Felt 6794e0c5e55e171532760d5f51e0165563827129d4eDoug Felt /** 6804e0c5e55e171532760d5f51e0165563827129d4eDoug Felt * Returns true if the character at offset and the preceding character 6814e0c5e55e171532760d5f51e0165563827129d4eDoug Felt * are at different run levels (and thus there's a split caret). 6824e0c5e55e171532760d5f51e0165563827129d4eDoug Felt * @param offset the offset 6834e0c5e55e171532760d5f51e0165563827129d4eDoug Felt * @return true if at a level boundary 684f75c97e023af7d4ad9a8c129d4ea282b1c3b8f94Gilles Debunne * @hide 6854e0c5e55e171532760d5f51e0165563827129d4eDoug Felt */ 686f75c97e023af7d4ad9a8c129d4ea282b1c3b8f94Gilles Debunne public boolean isLevelBoundary(int offset) { 6879f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt int line = getLineForOffset(offset); 6884e0c5e55e171532760d5f51e0165563827129d4eDoug Felt Directions dirs = getLineDirections(line); 6894e0c5e55e171532760d5f51e0165563827129d4eDoug Felt if (dirs == DIRS_ALL_LEFT_TO_RIGHT || dirs == DIRS_ALL_RIGHT_TO_LEFT) { 6904e0c5e55e171532760d5f51e0165563827129d4eDoug Felt return false; 6914e0c5e55e171532760d5f51e0165563827129d4eDoug Felt } 6924e0c5e55e171532760d5f51e0165563827129d4eDoug Felt 6934e0c5e55e171532760d5f51e0165563827129d4eDoug Felt int[] runs = dirs.mDirections; 6949f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt int lineStart = getLineStart(line); 6954e0c5e55e171532760d5f51e0165563827129d4eDoug Felt int lineEnd = getLineEnd(line); 6964e0c5e55e171532760d5f51e0165563827129d4eDoug Felt if (offset == lineStart || offset == lineEnd) { 6974e0c5e55e171532760d5f51e0165563827129d4eDoug Felt int paraLevel = getParagraphDirection(line) == 1 ? 0 : 1; 6984e0c5e55e171532760d5f51e0165563827129d4eDoug Felt int runIndex = offset == lineStart ? 0 : runs.length - 2; 6994e0c5e55e171532760d5f51e0165563827129d4eDoug Felt return ((runs[runIndex + 1] >>> RUN_LEVEL_SHIFT) & RUN_LEVEL_MASK) != paraLevel; 7004e0c5e55e171532760d5f51e0165563827129d4eDoug Felt } 7014e0c5e55e171532760d5f51e0165563827129d4eDoug Felt 7024e0c5e55e171532760d5f51e0165563827129d4eDoug Felt offset -= lineStart; 7039f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt for (int i = 0; i < runs.length; i += 2) { 7044e0c5e55e171532760d5f51e0165563827129d4eDoug Felt if (offset == runs[i]) { 7054e0c5e55e171532760d5f51e0165563827129d4eDoug Felt return true; 7069f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt } 7079f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt } 7084e0c5e55e171532760d5f51e0165563827129d4eDoug Felt return false; 7099f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt } 7109f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt 71134d2eba560f83f4eb665cdc039cf02bf96c201daFabrice Di Meglio /** 71234d2eba560f83f4eb665cdc039cf02bf96c201daFabrice Di Meglio * Returns true if the character at offset is right to left (RTL). 71334d2eba560f83f4eb665cdc039cf02bf96c201daFabrice Di Meglio * @param offset the offset 71434d2eba560f83f4eb665cdc039cf02bf96c201daFabrice Di Meglio * @return true if the character is RTL, false if it is LTR 71534d2eba560f83f4eb665cdc039cf02bf96c201daFabrice Di Meglio */ 71634d2eba560f83f4eb665cdc039cf02bf96c201daFabrice Di Meglio public boolean isRtlCharAt(int offset) { 71734d2eba560f83f4eb665cdc039cf02bf96c201daFabrice Di Meglio int line = getLineForOffset(offset); 71834d2eba560f83f4eb665cdc039cf02bf96c201daFabrice Di Meglio Directions dirs = getLineDirections(line); 71934d2eba560f83f4eb665cdc039cf02bf96c201daFabrice Di Meglio if (dirs == DIRS_ALL_LEFT_TO_RIGHT) { 72034d2eba560f83f4eb665cdc039cf02bf96c201daFabrice Di Meglio return false; 72134d2eba560f83f4eb665cdc039cf02bf96c201daFabrice Di Meglio } 72234d2eba560f83f4eb665cdc039cf02bf96c201daFabrice Di Meglio if (dirs == DIRS_ALL_RIGHT_TO_LEFT) { 72334d2eba560f83f4eb665cdc039cf02bf96c201daFabrice Di Meglio return true; 72434d2eba560f83f4eb665cdc039cf02bf96c201daFabrice Di Meglio } 72534d2eba560f83f4eb665cdc039cf02bf96c201daFabrice Di Meglio int[] runs = dirs.mDirections; 72634d2eba560f83f4eb665cdc039cf02bf96c201daFabrice Di Meglio int lineStart = getLineStart(line); 72734d2eba560f83f4eb665cdc039cf02bf96c201daFabrice Di Meglio for (int i = 0; i < runs.length; i += 2) { 72834d2eba560f83f4eb665cdc039cf02bf96c201daFabrice Di Meglio int start = lineStart + (runs[i] & RUN_LENGTH_MASK); 72934d2eba560f83f4eb665cdc039cf02bf96c201daFabrice Di Meglio // No need to test the end as an offset after the last run should return the value 73034d2eba560f83f4eb665cdc039cf02bf96c201daFabrice Di Meglio // corresponding of the last run 73134d2eba560f83f4eb665cdc039cf02bf96c201daFabrice Di Meglio if (offset >= start) { 73234d2eba560f83f4eb665cdc039cf02bf96c201daFabrice Di Meglio int level = (runs[i+1] >>> RUN_LEVEL_SHIFT) & RUN_LEVEL_MASK; 73334d2eba560f83f4eb665cdc039cf02bf96c201daFabrice Di Meglio return ((level & 1) != 0); 73434d2eba560f83f4eb665cdc039cf02bf96c201daFabrice Di Meglio } 73534d2eba560f83f4eb665cdc039cf02bf96c201daFabrice Di Meglio } 73634d2eba560f83f4eb665cdc039cf02bf96c201daFabrice Di Meglio // Should happen only if the offset is "out of bounds" 73734d2eba560f83f4eb665cdc039cf02bf96c201daFabrice Di Meglio return false; 73834d2eba560f83f4eb665cdc039cf02bf96c201daFabrice Di Meglio } 73934d2eba560f83f4eb665cdc039cf02bf96c201daFabrice Di Meglio 7409f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt private boolean primaryIsTrailingPrevious(int offset) { 7419f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt int line = getLineForOffset(offset); 7429f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt int lineStart = getLineStart(line); 7434e0c5e55e171532760d5f51e0165563827129d4eDoug Felt int lineEnd = getLineEnd(line); 7449f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt int[] runs = getLineDirections(line).mDirections; 7459f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt 7469f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt int levelAt = -1; 7479f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt for (int i = 0; i < runs.length; i += 2) { 7489f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt int start = lineStart + runs[i]; 7499f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt int limit = start + (runs[i+1] & RUN_LENGTH_MASK); 7509f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt if (limit > lineEnd) { 7519f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt limit = lineEnd; 7529f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt } 7539f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt if (offset >= start && offset < limit) { 7549f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt if (offset > start) { 7559f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt // Previous character is at same level, so don't use trailing. 7569f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt return false; 7579f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt } 7589f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt levelAt = (runs[i+1] >>> RUN_LEVEL_SHIFT) & RUN_LEVEL_MASK; 7599f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt break; 7609f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt } 7619f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt } 7629f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt if (levelAt == -1) { 7639f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt // Offset was limit of line. 7649f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt levelAt = getParagraphDirection(line) == 1 ? 0 : 1; 7659f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt } 7669f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt 7679f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt // At level boundary, check previous level. 7689f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt int levelBefore = -1; 7699f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt if (offset == lineStart) { 7709f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt levelBefore = getParagraphDirection(line) == 1 ? 0 : 1; 7719f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt } else { 7729f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt offset -= 1; 7739f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt for (int i = 0; i < runs.length; i += 2) { 7749f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt int start = lineStart + runs[i]; 7759f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt int limit = start + (runs[i+1] & RUN_LENGTH_MASK); 7769f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt if (limit > lineEnd) { 7779f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt limit = lineEnd; 7789f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt } 7799f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt if (offset >= start && offset < limit) { 7809f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt levelBefore = (runs[i+1] >>> RUN_LEVEL_SHIFT) & RUN_LEVEL_MASK; 7819f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt break; 7829f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt } 7839f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt } 7849f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt } 7859f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt 7869f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt return levelBefore < levelAt; 7879f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt } 7889f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt 7899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 7909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Get the primary horizontal position for the specified text offset. 7919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * This is the location where a new character would be inserted in 7929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * the paragraph's primary direction. 7939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 7949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public float getPrimaryHorizontal(int offset) { 795afe8e9b6d033cb854afa3024d8198a32896a804aRaph Levien return getPrimaryHorizontal(offset, false /* not clamped */); 796afe8e9b6d033cb854afa3024d8198a32896a804aRaph Levien } 797afe8e9b6d033cb854afa3024d8198a32896a804aRaph Levien 798afe8e9b6d033cb854afa3024d8198a32896a804aRaph Levien /** 799afe8e9b6d033cb854afa3024d8198a32896a804aRaph Levien * Get the primary horizontal position for the specified text offset, but 800afe8e9b6d033cb854afa3024d8198a32896a804aRaph Levien * optionally clamp it so that it doesn't exceed the width of the layout. 801afe8e9b6d033cb854afa3024d8198a32896a804aRaph Levien * @hide 802afe8e9b6d033cb854afa3024d8198a32896a804aRaph Levien */ 803afe8e9b6d033cb854afa3024d8198a32896a804aRaph Levien public float getPrimaryHorizontal(int offset, boolean clamped) { 8049f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt boolean trailing = primaryIsTrailingPrevious(offset); 805afe8e9b6d033cb854afa3024d8198a32896a804aRaph Levien return getHorizontal(offset, trailing, clamped); 8069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 8079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 8089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 8099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Get the secondary horizontal position for the specified text offset. 8109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * This is the location where a new character would be inserted in 8119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * the direction other than the paragraph's primary direction. 8129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 8139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public float getSecondaryHorizontal(int offset) { 814afe8e9b6d033cb854afa3024d8198a32896a804aRaph Levien return getSecondaryHorizontal(offset, false /* not clamped */); 815afe8e9b6d033cb854afa3024d8198a32896a804aRaph Levien } 816afe8e9b6d033cb854afa3024d8198a32896a804aRaph Levien 817afe8e9b6d033cb854afa3024d8198a32896a804aRaph Levien /** 818afe8e9b6d033cb854afa3024d8198a32896a804aRaph Levien * Get the secondary horizontal position for the specified text offset, but 819afe8e9b6d033cb854afa3024d8198a32896a804aRaph Levien * optionally clamp it so that it doesn't exceed the width of the layout. 820afe8e9b6d033cb854afa3024d8198a32896a804aRaph Levien * @hide 821afe8e9b6d033cb854afa3024d8198a32896a804aRaph Levien */ 822afe8e9b6d033cb854afa3024d8198a32896a804aRaph Levien public float getSecondaryHorizontal(int offset, boolean clamped) { 8239f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt boolean trailing = primaryIsTrailingPrevious(offset); 824afe8e9b6d033cb854afa3024d8198a32896a804aRaph Levien return getHorizontal(offset, !trailing, clamped); 8259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 8269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 827afe8e9b6d033cb854afa3024d8198a32896a804aRaph Levien private float getHorizontal(int offset, boolean trailing, boolean clamped) { 8289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int line = getLineForOffset(offset); 8299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 830afe8e9b6d033cb854afa3024d8198a32896a804aRaph Levien return getHorizontal(offset, trailing, line, clamped); 8319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 8329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 833afe8e9b6d033cb854afa3024d8198a32896a804aRaph Levien private float getHorizontal(int offset, boolean trailing, int line, boolean clamped) { 8349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int start = getLineStart(line); 835e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt int end = getLineEnd(line); 8369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int dir = getParagraphDirection(line); 837c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt boolean hasTabOrEmoji = getLineContainsTab(line); 8389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project Directions directions = getLineDirections(line); 8399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 840c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt TabStops tabStops = null; 841c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt if (hasTabOrEmoji && mText instanceof Spanned) { 842c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt // Just checking this line should be good enough, tabs should be 843c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt // consistent across all lines in a paragraph. 84474d31ef2b2c42b54fa1f7cf94ea955ea67ab69a0Eric Fischer TabStopSpan[] tabs = getParagraphSpans((Spanned) mText, start, end, TabStopSpan.class); 845c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt if (tabs.length > 0) { 846c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt tabStops = new TabStops(TAB_INCREMENT, tabs); // XXX should reuse 847c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt } 8489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 8499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 850e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt TextLine tl = TextLine.obtain(); 851c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt tl.set(mPaint, mText, start, end, dir, directions, hasTabOrEmoji, tabStops); 852e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt float wid = tl.measure(offset - start, trailing, null); 853e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt TextLine.recycle(tl); 8549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 855afe8e9b6d033cb854afa3024d8198a32896a804aRaph Levien if (clamped && wid > mWidth) { 856afe8e9b6d033cb854afa3024d8198a32896a804aRaph Levien wid = mWidth; 857afe8e9b6d033cb854afa3024d8198a32896a804aRaph Levien } 8589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int left = getParagraphLeft(line); 8599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int right = getParagraphRight(line); 8609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 861c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt return getLineStartPos(line, left, right) + wid; 8629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 8639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 8649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 8659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Get the leftmost position that should be exposed for horizontal 8669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * scrolling on the specified line. 8679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 8689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public float getLineLeft(int line) { 8699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int dir = getParagraphDirection(line); 8709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project Alignment align = getParagraphAlignment(line); 8719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 872c0ccf0c47c00942a9d0f2670600a8f2d4d7adb5bDoug Felt if (align == Alignment.ALIGN_LEFT) { 873c0ccf0c47c00942a9d0f2670600a8f2d4d7adb5bDoug Felt return 0; 874c0ccf0c47c00942a9d0f2670600a8f2d4d7adb5bDoug Felt } else if (align == Alignment.ALIGN_NORMAL) { 8759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (dir == DIR_RIGHT_TO_LEFT) 8769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return getParagraphRight(line) - getLineMax(line); 8779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project else 8789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return 0; 879c0ccf0c47c00942a9d0f2670600a8f2d4d7adb5bDoug Felt } else if (align == Alignment.ALIGN_RIGHT) { 880c0ccf0c47c00942a9d0f2670600a8f2d4d7adb5bDoug Felt return mWidth - getLineMax(line); 8819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } else if (align == Alignment.ALIGN_OPPOSITE) { 8829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (dir == DIR_RIGHT_TO_LEFT) 8839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return 0; 8849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project else 8859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return mWidth - getLineMax(line); 8869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } else { /* align == Alignment.ALIGN_CENTER */ 8879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int left = getParagraphLeft(line); 8889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int right = getParagraphRight(line); 8899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int max = ((int) getLineMax(line)) & ~1; 8909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 8919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return left + ((right - left) - max) / 2; 8929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 8939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 8949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 8959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 8969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Get the rightmost position that should be exposed for horizontal 8979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * scrolling on the specified line. 8989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 8999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public float getLineRight(int line) { 9009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int dir = getParagraphDirection(line); 9019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project Alignment align = getParagraphAlignment(line); 9029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 903c0ccf0c47c00942a9d0f2670600a8f2d4d7adb5bDoug Felt if (align == Alignment.ALIGN_LEFT) { 904c0ccf0c47c00942a9d0f2670600a8f2d4d7adb5bDoug Felt return getParagraphLeft(line) + getLineMax(line); 905c0ccf0c47c00942a9d0f2670600a8f2d4d7adb5bDoug Felt } else if (align == Alignment.ALIGN_NORMAL) { 9069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (dir == DIR_RIGHT_TO_LEFT) 9079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return mWidth; 9089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project else 9099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return getParagraphLeft(line) + getLineMax(line); 910c0ccf0c47c00942a9d0f2670600a8f2d4d7adb5bDoug Felt } else if (align == Alignment.ALIGN_RIGHT) { 911c0ccf0c47c00942a9d0f2670600a8f2d4d7adb5bDoug Felt return mWidth; 9129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } else if (align == Alignment.ALIGN_OPPOSITE) { 9139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (dir == DIR_RIGHT_TO_LEFT) 9149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return getLineMax(line); 9159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project else 9169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return mWidth; 9179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } else { /* align == Alignment.ALIGN_CENTER */ 9189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int left = getParagraphLeft(line); 9199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int right = getParagraphRight(line); 9209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int max = ((int) getLineMax(line)) & ~1; 9219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 9229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return right - ((right - left) - max) / 2; 9239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 9249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 9259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 9269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 9270c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt * Gets the unsigned horizontal extent of the specified line, including 928c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt * leading margin indent, but excluding trailing whitespace. 9299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 9309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public float getLineMax(int line) { 931c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt float margin = getParagraphLeadingMargin(line); 932c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt float signedExtent = getLineExtent(line, false); 933c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt return margin + signedExtent >= 0 ? signedExtent : -signedExtent; 9349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 9359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 9369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 937c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt * Gets the unsigned horizontal extent of the specified line, including 938c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt * leading margin indent and trailing whitespace. 9399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 9409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public float getLineWidth(int line) { 941c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt float margin = getParagraphLeadingMargin(line); 942c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt float signedExtent = getLineExtent(line, true); 943c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt return margin + signedExtent >= 0 ? signedExtent : -signedExtent; 9449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 9459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 946c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt /** 947c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt * Like {@link #getLineExtent(int,TabStops,boolean)} but determines the 948c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt * tab stops instead of using the ones passed in. 949c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt * @param line the index of the line 950c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt * @param full whether to include trailing whitespace 951c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt * @return the extent of the line 952c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt */ 953c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt private float getLineExtent(int line, boolean full) { 9549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int start = getLineStart(line); 955e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt int end = full ? getLineEnd(line) : getLineVisibleEnd(line); 956c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt 957c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt boolean hasTabsOrEmoji = getLineContainsTab(line); 958c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt TabStops tabStops = null; 959c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt if (hasTabsOrEmoji && mText instanceof Spanned) { 960c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt // Just checking this line should be good enough, tabs should be 961c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt // consistent across all lines in a paragraph. 96274d31ef2b2c42b54fa1f7cf94ea955ea67ab69a0Eric Fischer TabStopSpan[] tabs = getParagraphSpans((Spanned) mText, start, end, TabStopSpan.class); 963c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt if (tabs.length > 0) { 964c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt tabStops = new TabStops(TAB_INCREMENT, tabs); // XXX should reuse 965c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt } 966c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt } 967c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt Directions directions = getLineDirections(line); 9688059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio // Returned directions can actually be null 9698059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio if (directions == null) { 9708059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio return 0f; 9718059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio } 972c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt int dir = getParagraphDirection(line); 973c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt 974c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt TextLine tl = TextLine.obtain(); 975c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt tl.set(mPaint, mText, start, end, dir, directions, hasTabsOrEmoji, tabStops); 976c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt float width = tl.metrics(null); 977c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt TextLine.recycle(tl); 978c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt return width; 979c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt } 980c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt 981c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt /** 982c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt * Returns the signed horizontal extent of the specified line, excluding 983c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt * leading margin. If full is false, excludes trailing whitespace. 984c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt * @param line the index of the line 985c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt * @param tabStops the tab stops, can be null if we know they're not used. 986c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt * @param full whether to include trailing whitespace 987c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt * @return the extent of the text on this line 988c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt */ 989c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt private float getLineExtent(int line, TabStops tabStops, boolean full) { 990c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt int start = getLineStart(line); 991c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt int end = full ? getLineEnd(line) : getLineVisibleEnd(line); 992c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt boolean hasTabsOrEmoji = getLineContainsTab(line); 993e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt Directions directions = getLineDirections(line); 994c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt int dir = getParagraphDirection(line); 9959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 996e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt TextLine tl = TextLine.obtain(); 997c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt tl.set(mPaint, mText, start, end, dir, directions, hasTabsOrEmoji, tabStops); 998e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt float width = tl.metrics(null); 999e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt TextLine.recycle(tl); 1000e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt return width; 10019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 10029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 10039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 10049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Get the line number corresponding to the specified vertical position. 10059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * If you ask for a position above 0, you get 0; if you ask for a position 10069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * below the bottom of the text, you get the last line. 10079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 10089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // FIXME: It may be faster to do a linear search for layouts without many lines. 10099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public int getLineForVertical(int vertical) { 10109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int high = getLineCount(), low = -1, guess; 10119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 10129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project while (high - low > 1) { 10139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project guess = (high + low) / 2; 10149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 10159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (getLineTop(guess) > vertical) 10169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project high = guess; 10179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project else 10189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project low = guess; 10199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 10209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 10219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (low < 0) 10229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return 0; 10239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project else 10249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return low; 10259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 10269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 10279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 10289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Get the line number on which the specified text offset appears. 10299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * If you ask for a position before 0, you get 0; if you ask for a position 10309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * beyond the end of the text, you get the last line. 10319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 10329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public int getLineForOffset(int offset) { 10339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int high = getLineCount(), low = -1, guess; 10349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 10359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project while (high - low > 1) { 10369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project guess = (high + low) / 2; 10379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 10389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (getLineStart(guess) > offset) 10399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project high = guess; 10409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project else 10419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project low = guess; 10429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 10439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 10449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (low < 0) 10459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return 0; 10469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project else 10479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return low; 10489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 10499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 10509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 10519f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt * Get the character offset on the specified line whose position is 10529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * closest to the specified horizontal position. 10539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 10549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public int getOffsetForHorizontal(int line, float horiz) { 10559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int max = getLineEnd(line) - 1; 10569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int min = getLineStart(line); 10579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project Directions dirs = getLineDirections(line); 10589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 10599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (line == getLineCount() - 1) 10609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project max++; 10619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 10629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int best = min; 10639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project float bestdist = Math.abs(getPrimaryHorizontal(best) - horiz); 10649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 10659f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt for (int i = 0; i < dirs.mDirections.length; i += 2) { 10669f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt int here = min + dirs.mDirections[i]; 10679f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt int there = here + (dirs.mDirections[i+1] & RUN_LENGTH_MASK); 10689f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt int swap = (dirs.mDirections[i+1] & RUN_RTL_FLAG) != 0 ? -1 : 1; 10699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 10709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (there > max) 10719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project there = max; 10729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int high = there - 1 + 1, low = here + 1 - 1, guess; 10739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 10749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project while (high - low > 1) { 10759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project guess = (high + low) / 2; 10769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int adguess = getOffsetAtStartOf(guess); 10779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 10789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (getPrimaryHorizontal(adguess) * swap >= horiz * swap) 10799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project high = guess; 10809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project else 10819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project low = guess; 10829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 10839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 10849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (low < here + 1) 10859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project low = here + 1; 10869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 10879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (low < there) { 10889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project low = getOffsetAtStartOf(low); 10899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 10909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project float dist = Math.abs(getPrimaryHorizontal(low) - horiz); 10919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 10929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int aft = TextUtils.getOffsetAfter(mText, low); 10939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (aft < there) { 10949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project float other = Math.abs(getPrimaryHorizontal(aft) - horiz); 10959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 10969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (other < dist) { 10979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project dist = other; 10989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project low = aft; 10999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 11009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 11019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 11029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (dist < bestdist) { 11039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project bestdist = dist; 11049f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt best = low; 11059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 11069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 11079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 11089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project float dist = Math.abs(getPrimaryHorizontal(here) - horiz); 11099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 11109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (dist < bestdist) { 11119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project bestdist = dist; 11129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project best = here; 11139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 11149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 11159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 11169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project float dist = Math.abs(getPrimaryHorizontal(max) - horiz); 11179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1118373b7a8d4e9dce4f71539d4dbcf627fd3e1a39daRaph Levien if (dist <= bestdist) { 11199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project bestdist = dist; 11209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project best = max; 11219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 11229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 11239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return best; 11249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 11259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 11269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 11279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Return the text offset after the last character on the specified line. 11289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 11299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public final int getLineEnd(int line) { 11309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return getLineStart(line + 1); 11319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 11329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 11339f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt /** 11349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Return the text offset after the last visible character (so whitespace 11359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * is not counted) on the specified line. 11369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 11379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public int getLineVisibleEnd(int line) { 11389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return getLineVisibleEnd(line, getLineStart(line), getLineStart(line+1)); 11399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 11409f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt 11419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private int getLineVisibleEnd(int line, int start, int end) { 11429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project CharSequence text = mText; 11439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project char ch; 11449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (line == getLineCount() - 1) { 11459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return end; 11469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 11479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 11489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project for (; end > start; end--) { 11499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project ch = text.charAt(end - 1); 11509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 11519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (ch == '\n') { 11529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return end - 1; 11539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 11549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 11559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (ch != ' ' && ch != '\t') { 11569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project break; 11579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 11589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 11599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 11609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 11619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return end; 11629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 11639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 11649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 11659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Return the vertical position of the bottom of the specified line. 11669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 11679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public final int getLineBottom(int line) { 11689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return getLineTop(line + 1); 11699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 11709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 11719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 11729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Return the vertical position of the baseline of the specified line. 11739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 11749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public final int getLineBaseline(int line) { 11759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // getLineTop(line+1) == getLineTop(line) 11769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return getLineTop(line+1) - getLineDescent(line); 11779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 11789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 11799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 11809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Get the ascent of the text on the specified line. 11819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * The return value is negative to match the Paint.ascent() convention. 11829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 11839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public final int getLineAscent(int line) { 11849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // getLineTop(line+1) - getLineDescent(line) == getLineBaseLine(line) 11859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return getLineTop(line) - (getLineTop(line+1) - getLineDescent(line)); 11869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 11879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 11889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public int getOffsetToLeftOf(int offset) { 11899f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt return getOffsetToLeftRightOf(offset, true); 11909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 11919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 11929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public int getOffsetToRightOf(int offset) { 11939f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt return getOffsetToLeftRightOf(offset, false); 11949f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt } 11959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 11969f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt private int getOffsetToLeftRightOf(int caret, boolean toLeft) { 11979f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt int line = getLineForOffset(caret); 11989f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt int lineStart = getLineStart(line); 11999f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt int lineEnd = getLineEnd(line); 1200e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt int lineDir = getParagraphDirection(line); 1201e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 1202162bf0f1b9fd5d78ffa801917994f6222c6efd85Gilles Debunne boolean lineChanged = false; 1203e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt boolean advance = toLeft == (lineDir == DIR_RIGHT_TO_LEFT); 1204162bf0f1b9fd5d78ffa801917994f6222c6efd85Gilles Debunne // if walking off line, look at the line we're headed to 1205162bf0f1b9fd5d78ffa801917994f6222c6efd85Gilles Debunne if (advance) { 1206162bf0f1b9fd5d78ffa801917994f6222c6efd85Gilles Debunne if (caret == lineEnd) { 1207162bf0f1b9fd5d78ffa801917994f6222c6efd85Gilles Debunne if (line < getLineCount() - 1) { 1208162bf0f1b9fd5d78ffa801917994f6222c6efd85Gilles Debunne lineChanged = true; 1209162bf0f1b9fd5d78ffa801917994f6222c6efd85Gilles Debunne ++line; 1210162bf0f1b9fd5d78ffa801917994f6222c6efd85Gilles Debunne } else { 1211162bf0f1b9fd5d78ffa801917994f6222c6efd85Gilles Debunne return caret; // at very end, don't move 1212162bf0f1b9fd5d78ffa801917994f6222c6efd85Gilles Debunne } 1213162bf0f1b9fd5d78ffa801917994f6222c6efd85Gilles Debunne } 1214162bf0f1b9fd5d78ffa801917994f6222c6efd85Gilles Debunne } else { 1215e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (caret == lineStart) { 1216e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (line > 0) { 1217162bf0f1b9fd5d78ffa801917994f6222c6efd85Gilles Debunne lineChanged = true; 1218e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt --line; 1219e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } else { 1220e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt return caret; // at very start, don't move 1221e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 12229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1223162bf0f1b9fd5d78ffa801917994f6222c6efd85Gilles Debunne } 12249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1225162bf0f1b9fd5d78ffa801917994f6222c6efd85Gilles Debunne if (lineChanged) { 1226e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt lineStart = getLineStart(line); 1227e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt lineEnd = getLineEnd(line); 1228e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt int newDir = getParagraphDirection(line); 1229e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (newDir != lineDir) { 1230e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // unusual case. we want to walk onto the line, but it runs 1231e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // in a different direction than this one, so we fake movement 1232e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // in the opposite direction. 1233e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt toLeft = !toLeft; 1234e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt lineDir = newDir; 12359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 12369f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt } 12379f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt 1238e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt Directions directions = getLineDirections(line); 12399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1240e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt TextLine tl = TextLine.obtain(); 1241e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // XXX: we don't care about tabs 1242e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt tl.set(mPaint, mText, lineStart, lineEnd, lineDir, directions, false, null); 1243e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt caret = lineStart + tl.getOffsetToLeftRightOf(caret - lineStart, toLeft); 1244e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt tl = TextLine.recycle(tl); 1245e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt return caret; 12469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 12479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 12489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private int getOffsetAtStartOf(int offset) { 12499f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt // XXX this probably should skip local reorderings and 12509f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt // zero-width characters, look at callers 12519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (offset == 0) 12529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return 0; 12539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 12549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project CharSequence text = mText; 12559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project char c = text.charAt(offset); 12569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 12579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (c >= '\uDC00' && c <= '\uDFFF') { 12589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project char c1 = text.charAt(offset - 1); 12599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 12609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (c1 >= '\uD800' && c1 <= '\uDBFF') 12619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project offset -= 1; 12629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 12639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 12649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (mSpannedText) { 12659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project ReplacementSpan[] spans = ((Spanned) text).getSpans(offset, offset, 12669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project ReplacementSpan.class); 12679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 12689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project for (int i = 0; i < spans.length; i++) { 12699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int start = ((Spanned) text).getSpanStart(spans[i]); 12709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int end = ((Spanned) text).getSpanEnd(spans[i]); 12719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 12729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (start < offset && end > offset) 12739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project offset = start; 12749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 12759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 12769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 12779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return offset; 12789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 12799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 12809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 1281afe8e9b6d033cb854afa3024d8198a32896a804aRaph Levien * Determine whether we should clamp cursor position. Currently it's 1282afe8e9b6d033cb854afa3024d8198a32896a804aRaph Levien * only robust for left-aligned displays. 1283afe8e9b6d033cb854afa3024d8198a32896a804aRaph Levien * @hide 1284afe8e9b6d033cb854afa3024d8198a32896a804aRaph Levien */ 1285afe8e9b6d033cb854afa3024d8198a32896a804aRaph Levien public boolean shouldClampCursor(int line) { 1286afe8e9b6d033cb854afa3024d8198a32896a804aRaph Levien // Only clamp cursor position in left-aligned displays. 1287afe8e9b6d033cb854afa3024d8198a32896a804aRaph Levien switch (getParagraphAlignment(line)) { 1288afe8e9b6d033cb854afa3024d8198a32896a804aRaph Levien case ALIGN_LEFT: 1289afe8e9b6d033cb854afa3024d8198a32896a804aRaph Levien return true; 1290afe8e9b6d033cb854afa3024d8198a32896a804aRaph Levien case ALIGN_NORMAL: 1291afe8e9b6d033cb854afa3024d8198a32896a804aRaph Levien return getParagraphDirection(line) > 0; 1292afe8e9b6d033cb854afa3024d8198a32896a804aRaph Levien default: 1293afe8e9b6d033cb854afa3024d8198a32896a804aRaph Levien return false; 1294afe8e9b6d033cb854afa3024d8198a32896a804aRaph Levien } 1295afe8e9b6d033cb854afa3024d8198a32896a804aRaph Levien 1296afe8e9b6d033cb854afa3024d8198a32896a804aRaph Levien } 1297afe8e9b6d033cb854afa3024d8198a32896a804aRaph Levien /** 12989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Fills in the specified Path with a representation of a cursor 12999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * at the specified offset. This will often be a vertical line 13004e0c5e55e171532760d5f51e0165563827129d4eDoug Felt * but can be multiple discontinuous lines in text with multiple 13019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * directionalities. 13029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 13039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public void getCursorPath(int point, Path dest, 13049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project CharSequence editingBuffer) { 13059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project dest.reset(); 13069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 13079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int line = getLineForOffset(point); 13089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int top = getLineTop(line); 13099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int bottom = getLineTop(line+1); 13109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1311afe8e9b6d033cb854afa3024d8198a32896a804aRaph Levien boolean clamped = shouldClampCursor(line); 1312afe8e9b6d033cb854afa3024d8198a32896a804aRaph Levien float h1 = getPrimaryHorizontal(point, clamped) - 0.5f; 1313afe8e9b6d033cb854afa3024d8198a32896a804aRaph Levien float h2 = isLevelBoundary(point) ? getSecondaryHorizontal(point, clamped) - 0.5f : h1; 13149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1315497a92cc5ba2176b8a8484b0a7da040eac0e887bJeff Brown int caps = TextKeyListener.getMetaState(editingBuffer, TextKeyListener.META_SHIFT_ON) | 1316497a92cc5ba2176b8a8484b0a7da040eac0e887bJeff Brown TextKeyListener.getMetaState(editingBuffer, TextKeyListener.META_SELECTING); 1317497a92cc5ba2176b8a8484b0a7da040eac0e887bJeff Brown int fn = TextKeyListener.getMetaState(editingBuffer, TextKeyListener.META_ALT_ON); 13189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int dist = 0; 13199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 13209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (caps != 0 || fn != 0) { 13219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project dist = (bottom - top) >> 2; 13229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 13239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (fn != 0) 13249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project top += dist; 13259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (caps != 0) 13269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project bottom -= dist; 13279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 13289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 13299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (h1 < 0.5f) 13309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project h1 = 0.5f; 13319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (h2 < 0.5f) 13329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project h2 = 0.5f; 13339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 13342fb503f5102dd32a8ec391b26911528852703b90Jozef BABJAK if (Float.compare(h1, h2) == 0) { 13359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project dest.moveTo(h1, top); 13369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project dest.lineTo(h1, bottom); 13379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } else { 13389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project dest.moveTo(h1, top); 13399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project dest.lineTo(h1, (top + bottom) >> 1); 13409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 13419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project dest.moveTo(h2, (top + bottom) >> 1); 13429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project dest.lineTo(h2, bottom); 13439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 13449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 13459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (caps == 2) { 13469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project dest.moveTo(h2, bottom); 13479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project dest.lineTo(h2 - dist, bottom + dist); 13489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project dest.lineTo(h2, bottom); 13499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project dest.lineTo(h2 + dist, bottom + dist); 13509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } else if (caps == 1) { 13519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project dest.moveTo(h2, bottom); 13529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project dest.lineTo(h2 - dist, bottom + dist); 13539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 13549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project dest.moveTo(h2 - dist, bottom + dist - 0.5f); 13559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project dest.lineTo(h2 + dist, bottom + dist - 0.5f); 13569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 13579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project dest.moveTo(h2 + dist, bottom + dist); 13589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project dest.lineTo(h2, bottom); 13599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 13609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 13619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (fn == 2) { 13629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project dest.moveTo(h1, top); 13639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project dest.lineTo(h1 - dist, top - dist); 13649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project dest.lineTo(h1, top); 13659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project dest.lineTo(h1 + dist, top - dist); 13669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } else if (fn == 1) { 13679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project dest.moveTo(h1, top); 13689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project dest.lineTo(h1 - dist, top - dist); 13699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 13709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project dest.moveTo(h1 - dist, top - dist + 0.5f); 13719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project dest.lineTo(h1 + dist, top - dist + 0.5f); 13729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 13739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project dest.moveTo(h1 + dist, top - dist); 13749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project dest.lineTo(h1, top); 13759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 13769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 13779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 13789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private void addSelection(int line, int start, int end, 13799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int top, int bottom, Path dest) { 13809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int linestart = getLineStart(line); 13819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int lineend = getLineEnd(line); 13829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project Directions dirs = getLineDirections(line); 13839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 13849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (lineend > linestart && mText.charAt(lineend - 1) == '\n') 13859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project lineend--; 13869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 13879f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt for (int i = 0; i < dirs.mDirections.length; i += 2) { 13889f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt int here = linestart + dirs.mDirections[i]; 13899f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt int there = here + (dirs.mDirections[i+1] & RUN_LENGTH_MASK); 13909f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt 13919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (there > lineend) 13929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project there = lineend; 13939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 13949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (start <= there && end >= here) { 13959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int st = Math.max(start, here); 13969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int en = Math.min(end, there); 13979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 13989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (st != en) { 1399afe8e9b6d033cb854afa3024d8198a32896a804aRaph Levien float h1 = getHorizontal(st, false, line, false /* not clamped */); 1400afe8e9b6d033cb854afa3024d8198a32896a804aRaph Levien float h2 = getHorizontal(en, true, line, false /* not clamped */); 14019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 14023716601573f5a562f98721130e25002ad88eb164Fabrice Di Meglio float left = Math.min(h1, h2); 14033716601573f5a562f98721130e25002ad88eb164Fabrice Di Meglio float right = Math.max(h1, h2); 14043716601573f5a562f98721130e25002ad88eb164Fabrice Di Meglio 14053716601573f5a562f98721130e25002ad88eb164Fabrice Di Meglio dest.addRect(left, top, right, bottom, Path.Direction.CW); 14069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 14079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 14089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 14099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 14109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 14119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 14129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Fills in the specified Path with a representation of a highlight 14139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * between the specified offsets. This will often be a rectangle 14149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * or a potentially discontinuous set of rectangles. If the start 14159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * and end are the same, the returned path is empty. 14169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 14179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public void getSelectionPath(int start, int end, Path dest) { 14189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project dest.reset(); 14199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 14209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (start == end) 14219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return; 14229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 14239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (end < start) { 14249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int temp = end; 14259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project end = start; 14269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project start = temp; 14279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 14289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 14299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int startline = getLineForOffset(start); 14309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int endline = getLineForOffset(end); 14319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 14329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int top = getLineTop(startline); 14339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int bottom = getLineBottom(endline); 14349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 14359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (startline == endline) { 14369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project addSelection(startline, start, end, top, bottom, dest); 14379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } else { 14389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project final float width = mWidth; 14399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 14409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project addSelection(startline, start, getLineEnd(startline), 14419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project top, getLineBottom(startline), dest); 14429f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt 14439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (getParagraphDirection(startline) == DIR_RIGHT_TO_LEFT) 14449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project dest.addRect(getLineLeft(startline), top, 14459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 0, getLineBottom(startline), Path.Direction.CW); 14469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project else 14479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project dest.addRect(getLineRight(startline), top, 14489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project width, getLineBottom(startline), Path.Direction.CW); 14499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 14509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project for (int i = startline + 1; i < endline; i++) { 14519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project top = getLineTop(i); 14529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project bottom = getLineBottom(i); 14539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project dest.addRect(0, top, width, bottom, Path.Direction.CW); 14549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 14559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 14569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project top = getLineTop(endline); 14579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project bottom = getLineBottom(endline); 14589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 14599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project addSelection(endline, getLineStart(endline), end, 14609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project top, bottom, dest); 14619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 14629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (getParagraphDirection(endline) == DIR_RIGHT_TO_LEFT) 14639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project dest.addRect(width, top, getLineRight(endline), bottom, Path.Direction.CW); 14649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project else 14659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project dest.addRect(0, top, getLineLeft(endline), bottom, Path.Direction.CW); 14669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 14679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 14689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 14699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 14709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Get the alignment of the specified paragraph, taking into account 14719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * markup attached to it. 14729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 14739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public final Alignment getParagraphAlignment(int line) { 14749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project Alignment align = mAlignment; 14759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 14769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (mSpannedText) { 14779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project Spanned sp = (Spanned) mText; 147874d31ef2b2c42b54fa1f7cf94ea955ea67ab69a0Eric Fischer AlignmentSpan[] spans = getParagraphSpans(sp, getLineStart(line), 14799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project getLineEnd(line), 14809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project AlignmentSpan.class); 14819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 14829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int spanLength = spans.length; 14839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (spanLength > 0) { 14849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project align = spans[spanLength-1].getAlignment(); 14859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 14869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 14879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 14889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return align; 14899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 14909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 14919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 14929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Get the left edge of the specified paragraph, inset by left margins. 14939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 14949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public final int getParagraphLeft(int line) { 14959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int left = 0; 1496c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt int dir = getParagraphDirection(line); 1497c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt if (dir == DIR_RIGHT_TO_LEFT || !mSpannedText) { 1498c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt return left; // leading margin has no impact, or no styles 14999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1500c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt return getParagraphLeadingMargin(line); 15019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 15029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 15039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 15049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Get the right edge of the specified paragraph, inset by right margins. 15059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 15069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public final int getParagraphRight(int line) { 15079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int right = mWidth; 1508c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt int dir = getParagraphDirection(line); 1509c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt if (dir == DIR_LEFT_TO_RIGHT || !mSpannedText) { 1510c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt return right; // leading margin has no impact, or no styles 1511c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt } 1512c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt return right - getParagraphLeadingMargin(line); 1513c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt } 15149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1515c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt /** 1516c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt * Returns the effective leading margin (unsigned) for this line, 1517c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt * taking into account LeadingMarginSpan and LeadingMarginSpan2. 1518c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt * @param line the line index 1519c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt * @return the leading margin of this line 1520c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt */ 1521c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt private int getParagraphLeadingMargin(int line) { 1522c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt if (!mSpannedText) { 1523c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt return 0; 1524c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt } 1525c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt Spanned spanned = (Spanned) mText; 15260c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt 1527c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt int lineStart = getLineStart(line); 1528c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt int lineEnd = getLineEnd(line); 15290c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt int spanEnd = spanned.nextSpanTransition(lineStart, lineEnd, 1530c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt LeadingMarginSpan.class); 153174d31ef2b2c42b54fa1f7cf94ea955ea67ab69a0Eric Fischer LeadingMarginSpan[] spans = getParagraphSpans(spanned, lineStart, spanEnd, 1532c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt LeadingMarginSpan.class); 1533c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt if (spans.length == 0) { 1534c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt return 0; // no leading margin span; 1535c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt } 15360c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt 1537c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt int margin = 0; 15380c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt 15390c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt boolean isFirstParaLine = lineStart == 0 || 1540c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt spanned.charAt(lineStart - 1) == '\n'; 15410c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt 1542c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt for (int i = 0; i < spans.length; i++) { 1543c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt LeadingMarginSpan span = spans[i]; 1544c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt boolean useFirstLineMargin = isFirstParaLine; 1545c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt if (span instanceof LeadingMarginSpan2) { 1546c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt int spStart = spanned.getSpanStart(span); 1547c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt int spanLine = getLineForOffset(spStart); 1548c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt int count = ((LeadingMarginSpan2)span).getLeadingMarginLineCount(); 15490c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt useFirstLineMargin = line < spanLine + count; 15509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1551c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt margin += span.getLeadingMargin(useFirstLineMargin); 15529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 15539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1554c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt return margin; 15559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 15569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1557e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt /* package */ 15586c488de023a4797069673dc619c1a4096079ea9eGilles Debunne static float measurePara(TextPaint paint, CharSequence text, int start, int end) { 1559e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 1560e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt MeasuredText mt = MeasuredText.obtain(); 1561e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt TextLine tl = TextLine.obtain(); 1562e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt try { 1563cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt mt.setPara(text, start, end, TextDirectionHeuristics.LTR); 1564e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt Directions directions; 1565c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt int dir; 1566c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt if (mt.mEasy) { 1567e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt directions = DIRS_ALL_LEFT_TO_RIGHT; 1568c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt dir = Layout.DIR_LEFT_TO_RIGHT; 1569e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } else { 1570e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt directions = AndroidBidi.directions(mt.mDir, mt.mLevels, 1571e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 0, mt.mChars, 0, mt.mLen); 1572c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt dir = mt.mDir; 1573c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt } 1574c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt char[] chars = mt.mChars; 1575c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt int len = mt.mLen; 1576c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt boolean hasTabs = false; 1577c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt TabStops tabStops = null; 1578c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt for (int i = 0; i < len; ++i) { 1579c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt if (chars[i] == '\t') { 1580c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt hasTabs = true; 1581c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt if (text instanceof Spanned) { 1582c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt Spanned spanned = (Spanned) text; 15830c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt int spanEnd = spanned.nextSpanTransition(start, end, 1584c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt TabStopSpan.class); 158574d31ef2b2c42b54fa1f7cf94ea955ea67ab69a0Eric Fischer TabStopSpan[] spans = getParagraphSpans(spanned, start, spanEnd, 1586c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt TabStopSpan.class); 1587c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt if (spans.length > 0) { 1588c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt tabStops = new TabStops(TAB_INCREMENT, spans); 1589c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt } 1590c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt } 1591c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt break; 1592c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt } 15939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1594c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt tl.set(paint, text, start, end, dir, directions, hasTabs, tabStops); 1595e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt return tl.metrics(null); 1596e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } finally { 1597e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt TextLine.recycle(tl); 1598e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt MeasuredText.recycle(mt); 15999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 16009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 16019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 160271b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt /** 1603c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt * @hide 1604c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt */ 1605c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt /* package */ static class TabStops { 1606c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt private int[] mStops; 1607c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt private int mNumStops; 1608c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt private int mIncrement; 16090c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt 1610c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt TabStops(int increment, Object[] spans) { 1611c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt reset(increment, spans); 1612c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt } 16130c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt 1614c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt void reset(int increment, Object[] spans) { 1615c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt this.mIncrement = increment; 1616c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt 1617c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt int ns = 0; 1618c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt if (spans != null) { 1619c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt int[] stops = this.mStops; 1620c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt for (Object o : spans) { 1621c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt if (o instanceof TabStopSpan) { 1622c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt if (stops == null) { 1623c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt stops = new int[10]; 1624c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt } else if (ns == stops.length) { 1625c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt int[] nstops = new int[ns * 2]; 1626c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt for (int i = 0; i < ns; ++i) { 1627c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt nstops[i] = stops[i]; 1628c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt } 1629c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt stops = nstops; 1630c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt } 1631c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt stops[ns++] = ((TabStopSpan) o).getTabStop(); 1632c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt } 1633c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt } 1634c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt if (ns > 1) { 1635c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt Arrays.sort(stops, 0, ns); 1636c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt } 1637c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt if (stops != this.mStops) { 1638c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt this.mStops = stops; 1639c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt } 1640c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt } 1641c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt this.mNumStops = ns; 1642c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt } 16430c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt 1644c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt float nextTab(float h) { 1645c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt int ns = this.mNumStops; 1646c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt if (ns > 0) { 1647c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt int[] stops = this.mStops; 1648c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt for (int i = 0; i < ns; ++i) { 1649c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt int stop = stops[i]; 1650c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt if (stop > h) { 1651c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt return stop; 1652c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt } 1653c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt } 1654c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt } 1655c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt return nextDefaultStop(h, mIncrement); 1656c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt } 1657c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt 1658c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt public static float nextDefaultStop(float h, int inc) { 1659c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt return ((int) ((h + inc) / inc)) * inc; 1660c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt } 1661c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt } 16620c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt 1663c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt /** 166471b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt * Returns the position of the next tab stop after h on the line. 166571b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt * 166671b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt * @param text the text 166771b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt * @param start start of the line 166871b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt * @param end limit of the line 166971b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt * @param h the current horizontal offset 167071b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt * @param tabs the tabs, can be null. If it is null, any tabs in effect 167171b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt * on the line will be used. If there are no tabs, a default offset 167271b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt * will be used to compute the tab stop. 167371b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt * @return the offset of the next tab stop. 167471b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt */ 16759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /* package */ static float nextTab(CharSequence text, int start, int end, 16769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project float h, Object[] tabs) { 16779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project float nh = Float.MAX_VALUE; 16789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project boolean alltabs = false; 16799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 16809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (text instanceof Spanned) { 16819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (tabs == null) { 168274d31ef2b2c42b54fa1f7cf94ea955ea67ab69a0Eric Fischer tabs = getParagraphSpans((Spanned) text, start, end, TabStopSpan.class); 16839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project alltabs = true; 16849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 16859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 16869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project for (int i = 0; i < tabs.length; i++) { 16879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (!alltabs) { 16889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (!(tabs[i] instanceof TabStopSpan)) 16899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project continue; 16909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 16919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 16929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int where = ((TabStopSpan) tabs[i]).getTabStop(); 16939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 16949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (where < nh && where > h) 16959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project nh = where; 16969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 16979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 16989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (nh != Float.MAX_VALUE) 16999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return nh; 17009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 17019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 17029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return ((int) ((h + TAB_INCREMENT) / TAB_INCREMENT)) * TAB_INCREMENT; 17039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 17049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 17059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project protected final boolean isSpanned() { 17069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return mSpannedText; 17079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 17089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 170974d31ef2b2c42b54fa1f7cf94ea955ea67ab69a0Eric Fischer /** 171074d31ef2b2c42b54fa1f7cf94ea955ea67ab69a0Eric Fischer * Returns the same as <code>text.getSpans()</code>, except where 171174d31ef2b2c42b54fa1f7cf94ea955ea67ab69a0Eric Fischer * <code>start</code> and <code>end</code> are the same and are not 171274d31ef2b2c42b54fa1f7cf94ea955ea67ab69a0Eric Fischer * at the very beginning of the text, in which case an empty array 171374d31ef2b2c42b54fa1f7cf94ea955ea67ab69a0Eric Fischer * is returned instead. 171474d31ef2b2c42b54fa1f7cf94ea955ea67ab69a0Eric Fischer * <p> 171574d31ef2b2c42b54fa1f7cf94ea955ea67ab69a0Eric Fischer * This is needed because of the special case that <code>getSpans()</code> 171674d31ef2b2c42b54fa1f7cf94ea955ea67ab69a0Eric Fischer * on an empty range returns the spans adjacent to that range, which is 171774d31ef2b2c42b54fa1f7cf94ea955ea67ab69a0Eric Fischer * primarily for the sake of <code>TextWatchers</code> so they will get 171874d31ef2b2c42b54fa1f7cf94ea955ea67ab69a0Eric Fischer * notifications when text goes from empty to non-empty. But it also 171974d31ef2b2c42b54fa1f7cf94ea955ea67ab69a0Eric Fischer * has the unfortunate side effect that if the text ends with an empty 172074d31ef2b2c42b54fa1f7cf94ea955ea67ab69a0Eric Fischer * paragraph, that paragraph accidentally picks up the styles of the 172174d31ef2b2c42b54fa1f7cf94ea955ea67ab69a0Eric Fischer * preceding paragraph (even though those styles will not be picked up 172274d31ef2b2c42b54fa1f7cf94ea955ea67ab69a0Eric Fischer * by new text that is inserted into the empty paragraph). 172374d31ef2b2c42b54fa1f7cf94ea955ea67ab69a0Eric Fischer * <p> 172474d31ef2b2c42b54fa1f7cf94ea955ea67ab69a0Eric Fischer * The reason it just checks whether <code>start</code> and <code>end</code> 172574d31ef2b2c42b54fa1f7cf94ea955ea67ab69a0Eric Fischer * is the same is that the only time a line can contain 0 characters 172674d31ef2b2c42b54fa1f7cf94ea955ea67ab69a0Eric Fischer * is if it is the final paragraph of the Layout; otherwise any line will 172774d31ef2b2c42b54fa1f7cf94ea955ea67ab69a0Eric Fischer * contain at least one printing or newline character. The reason for the 172874d31ef2b2c42b54fa1f7cf94ea955ea67ab69a0Eric Fischer * additional check if <code>start</code> is greater than 0 is that 172974d31ef2b2c42b54fa1f7cf94ea955ea67ab69a0Eric Fischer * if the empty paragraph is the entire content of the buffer, paragraph 173074d31ef2b2c42b54fa1f7cf94ea955ea67ab69a0Eric Fischer * styles that are already applied to the buffer will apply to text that 173174d31ef2b2c42b54fa1f7cf94ea955ea67ab69a0Eric Fischer * is inserted into it. 173274d31ef2b2c42b54fa1f7cf94ea955ea67ab69a0Eric Fischer */ 1733eca5b7385a641ae5d8c4aa2b7823a4e573737376Gilles Debunne /* package */static <T> T[] getParagraphSpans(Spanned text, int start, int end, Class<T> type) { 173474d31ef2b2c42b54fa1f7cf94ea955ea67ab69a0Eric Fischer if (start == end && start > 0) { 17356c488de023a4797069673dc619c1a4096079ea9eGilles Debunne return ArrayUtils.emptyArray(type); 173674d31ef2b2c42b54fa1f7cf94ea955ea67ab69a0Eric Fischer } 173774d31ef2b2c42b54fa1f7cf94ea955ea67ab69a0Eric Fischer 173874d31ef2b2c42b54fa1f7cf94ea955ea67ab69a0Eric Fischer return text.getSpans(start, end, type); 173974d31ef2b2c42b54fa1f7cf94ea955ea67ab69a0Eric Fischer } 174074d31ef2b2c42b54fa1f7cf94ea955ea67ab69a0Eric Fischer 17418d44fff7e62f77c3b3072a96712cc1389e63ca64Fabrice Di Meglio private char getEllipsisChar(TextUtils.TruncateAt method) { 17428d44fff7e62f77c3b3072a96712cc1389e63ca64Fabrice Di Meglio return (method == TextUtils.TruncateAt.END_SMALL) ? 17438d44fff7e62f77c3b3072a96712cc1389e63ca64Fabrice Di Meglio ELLIPSIS_TWO_DOTS[0] : 17448d44fff7e62f77c3b3072a96712cc1389e63ca64Fabrice Di Meglio ELLIPSIS_NORMAL[0]; 17458d44fff7e62f77c3b3072a96712cc1389e63ca64Fabrice Di Meglio } 17468d44fff7e62f77c3b3072a96712cc1389e63ca64Fabrice Di Meglio 17479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private void ellipsize(int start, int end, int line, 17488d44fff7e62f77c3b3072a96712cc1389e63ca64Fabrice Di Meglio char[] dest, int destoff, TextUtils.TruncateAt method) { 17499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int ellipsisCount = getEllipsisCount(line); 17509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 17519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (ellipsisCount == 0) { 17529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return; 17539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 17549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 17559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int ellipsisStart = getEllipsisStart(line); 17569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int linestart = getLineStart(line); 17579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 17589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project for (int i = ellipsisStart; i < ellipsisStart + ellipsisCount; i++) { 17599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project char c; 17609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 17619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (i == ellipsisStart) { 17628d44fff7e62f77c3b3072a96712cc1389e63ca64Fabrice Di Meglio c = getEllipsisChar(method); // ellipsis 17639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } else { 17649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project c = '\uFEFF'; // 0-width space 17659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 17669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 17679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int a = i + linestart; 17689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 17699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (a >= start && a < end) { 17709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project dest[destoff + a - start] = c; 17719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 17729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 17739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 17749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 17759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 17769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Stores information about bidirectional (left-to-right or right-to-left) 17779f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt * text within the layout of a line. 17789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 17799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public static class Directions { 17809f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt // Directions represents directional runs within a line of text. 17819f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt // Runs are pairs of ints listed in visual order, starting from the 17829f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt // leading margin. The first int of each pair is the offset from 17839f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt // the first character of the line to the start of the run. The 17849f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt // second int represents both the length and level of the run. 17859f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt // The length is in the lower bits, accessed by masking with 17869f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt // DIR_LENGTH_MASK. The level is in the higher bits, accessed 17879f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt // by shifting by DIR_LEVEL_SHIFT and masking by DIR_LEVEL_MASK. 17889f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt // To simply test for an RTL direction, test the bit using 17899f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt // DIR_RTL_FLAG, if set then the direction is rtl. 17909f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt 17919f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt /* package */ int[] mDirections; 17929f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt /* package */ Directions(int[] dirs) { 17939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mDirections = dirs; 17949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 17959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 17969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 17979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 17989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Return the offset of the first character to be ellipsized away, 17999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * relative to the start of the line. (So 0 if the beginning of the 18009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * line is ellipsized, not getLineStart().) 18019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 18029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public abstract int getEllipsisStart(int line); 1803e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 18049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 18059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Returns the number of characters to be ellipsized away, or 0 if 18069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * no ellipsis is to take place. 18079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 18089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public abstract int getEllipsisCount(int line); 18099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 18109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /* package */ static class Ellipsizer implements CharSequence, GetChars { 18119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /* package */ CharSequence mText; 18129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /* package */ Layout mLayout; 18139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /* package */ int mWidth; 18149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /* package */ TextUtils.TruncateAt mMethod; 18159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 18169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public Ellipsizer(CharSequence s) { 18179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mText = s; 18189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 18199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 18209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public char charAt(int off) { 18219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project char[] buf = TextUtils.obtain(1); 18229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project getChars(off, off + 1, buf, 0); 18239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project char ret = buf[0]; 18249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 18259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project TextUtils.recycle(buf); 18269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return ret; 18279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 18289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 18299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public void getChars(int start, int end, char[] dest, int destoff) { 18309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int line1 = mLayout.getLineForOffset(start); 18319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int line2 = mLayout.getLineForOffset(end); 18329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 18339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project TextUtils.getChars(mText, start, end, dest, destoff); 18349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 18359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project for (int i = line1; i <= line2; i++) { 18368d44fff7e62f77c3b3072a96712cc1389e63ca64Fabrice Di Meglio mLayout.ellipsize(start, end, i, dest, destoff, mMethod); 18379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 18389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 18399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 18409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public int length() { 18419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return mText.length(); 18429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 18439f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt 18449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public CharSequence subSequence(int start, int end) { 18459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project char[] s = new char[end - start]; 18469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project getChars(start, end, s, 0); 18479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return new String(s); 18489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 18499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1850162bf0f1b9fd5d78ffa801917994f6222c6efd85Gilles Debunne @Override 18519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public String toString() { 18529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project char[] s = new char[length()]; 18539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project getChars(0, length(), s, 0); 18549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return new String(s); 18559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 18569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 18579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 18589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 18596c488de023a4797069673dc619c1a4096079ea9eGilles Debunne /* package */ static class SpannedEllipsizer extends Ellipsizer implements Spanned { 18609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private Spanned mSpanned; 18619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 18629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public SpannedEllipsizer(CharSequence display) { 18639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project super(display); 18649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mSpanned = (Spanned) display; 18659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 18669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 18679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public <T> T[] getSpans(int start, int end, Class<T> type) { 18689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return mSpanned.getSpans(start, end, type); 18699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 18709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 18719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public int getSpanStart(Object tag) { 18729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return mSpanned.getSpanStart(tag); 18739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 18749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 18759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public int getSpanEnd(Object tag) { 18769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return mSpanned.getSpanEnd(tag); 18779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 18789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 18799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public int getSpanFlags(Object tag) { 18809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return mSpanned.getSpanFlags(tag); 18819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 18829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 18836c488de023a4797069673dc619c1a4096079ea9eGilles Debunne @SuppressWarnings("rawtypes") 18849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public int nextSpanTransition(int start, int limit, Class type) { 18859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return mSpanned.nextSpanTransition(start, limit, type); 18869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 18879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1888162bf0f1b9fd5d78ffa801917994f6222c6efd85Gilles Debunne @Override 18899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public CharSequence subSequence(int start, int end) { 18909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project char[] s = new char[end - start]; 18919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project getChars(start, end, s, 0); 18929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 18939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project SpannableString ss = new SpannableString(new String(s)); 18949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project TextUtils.copySpansFrom(mSpanned, start, end, Object.class, ss, 0); 18959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return ss; 18969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 18979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 18989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 18999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private CharSequence mText; 19009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private TextPaint mPaint; 19019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /* package */ TextPaint mWorkPaint; 19029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private int mWidth; 19039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private Alignment mAlignment = Alignment.ALIGN_NORMAL; 19049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private float mSpacingMult; 19059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private float mSpacingAdd; 1906c4d8eb6fb7c88c5c4da38b0b113c24cc4b78c0b7Romain Guy private static final Rect sTempRect = new Rect(); 19079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private boolean mSpannedText; 1908cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt private TextDirectionHeuristic mTextDir; 1909eca5b7385a641ae5d8c4aa2b7823a4e573737376Gilles Debunne private SpanSet<LineBackgroundSpan> mLineBackgroundSpans; 19109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 19119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public static final int DIR_LEFT_TO_RIGHT = 1; 19129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public static final int DIR_RIGHT_TO_LEFT = -1; 19139f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt 191420178d62cf669af18467a16d3c4c4237ed42151cDoug Felt /* package */ static final int DIR_REQUEST_LTR = 1; 191520178d62cf669af18467a16d3c4c4237ed42151cDoug Felt /* package */ static final int DIR_REQUEST_RTL = -1; 191620178d62cf669af18467a16d3c4c4237ed42151cDoug Felt /* package */ static final int DIR_REQUEST_DEFAULT_LTR = 2; 191720178d62cf669af18467a16d3c4c4237ed42151cDoug Felt /* package */ static final int DIR_REQUEST_DEFAULT_RTL = -2; 19189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 19199f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt /* package */ static final int RUN_LENGTH_MASK = 0x03ffffff; 19209f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt /* package */ static final int RUN_LEVEL_SHIFT = 26; 19219f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt /* package */ static final int RUN_LEVEL_MASK = 0x3f; 19229f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt /* package */ static final int RUN_RTL_FLAG = 1 << RUN_LEVEL_SHIFT; 19239f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt 19249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public enum Alignment { 19259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project ALIGN_NORMAL, 19269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project ALIGN_OPPOSITE, 19279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project ALIGN_CENTER, 1928c0ccf0c47c00942a9d0f2670600a8f2d4d7adb5bDoug Felt /** @hide */ 1929c0ccf0c47c00942a9d0f2670600a8f2d4d7adb5bDoug Felt ALIGN_LEFT, 1930c0ccf0c47c00942a9d0f2670600a8f2d4d7adb5bDoug Felt /** @hide */ 1931c0ccf0c47c00942a9d0f2670600a8f2d4d7adb5bDoug Felt ALIGN_RIGHT, 19329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 19339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 19349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private static final int TAB_INCREMENT = 20; 19359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 19369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /* package */ static final Directions DIRS_ALL_LEFT_TO_RIGHT = 19379f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt new Directions(new int[] { 0, RUN_LENGTH_MASK }); 19389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /* package */ static final Directions DIRS_ALL_RIGHT_TO_LEFT = 19399f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt new Directions(new int[] { 0, RUN_LENGTH_MASK | RUN_RTL_FLAG }); 19400a4db3c5270440eeb7e4e44a7029926e239ec3bdGilles Debunne 19418d44fff7e62f77c3b3072a96712cc1389e63ca64Fabrice Di Meglio /* package */ static final char[] ELLIPSIS_NORMAL = { '\u2026' }; // this is "..." 19428d44fff7e62f77c3b3072a96712cc1389e63ca64Fabrice Di Meglio /* package */ static final char[] ELLIPSIS_TWO_DOTS = { '\u2025' }; // this is ".." 19439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project} 1944