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 1939b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levienimport android.annotation.IntDef; 209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.graphics.Canvas; 219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.graphics.Paint; 229f7a4442b89cc06cb8cae6992484e7ae795323abDoug Feltimport android.graphics.Path; 239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.graphics.Rect; 249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.text.method.TextKeyListener; 259f7a4442b89cc06cb8cae6992484e7ae795323abDoug Feltimport android.text.style.AlignmentSpan; 269f7a4442b89cc06cb8cae6992484e7ae795323abDoug Feltimport android.text.style.LeadingMarginSpan; 27162bf0f1b9fd5d78ffa801917994f6222c6efd85Gilles Debunneimport android.text.style.LeadingMarginSpan.LeadingMarginSpan2; 289f7a4442b89cc06cb8cae6992484e7ae795323abDoug Feltimport android.text.style.LineBackgroundSpan; 299f7a4442b89cc06cb8cae6992484e7ae795323abDoug Feltimport android.text.style.ParagraphStyle; 309f7a4442b89cc06cb8cae6992484e7ae795323abDoug Feltimport android.text.style.ReplacementSpan; 319f7a4442b89cc06cb8cae6992484e7ae795323abDoug Feltimport android.text.style.TabStopSpan; 329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 33cb379120456d8065d742021fc5c66748fc8a11a8Doug Feltimport com.android.internal.util.ArrayUtils; 34776abc24cdd18610232a50b997cce3cffa74609bAdam Lesinskiimport com.android.internal.util.GrowingArrayUtils; 35cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt 3639b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levienimport java.lang.annotation.Retention; 3739b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levienimport java.lang.annotation.RetentionPolicy; 38c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Feltimport java.util.Arrays; 399f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt 409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project/** 419f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt * A base class that manages text layout in visual elements on 429f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt * the screen. 439f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt * <p>For text that will be edited, use a {@link DynamicLayout}, 449f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt * which will be updated as the text changes. 459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * For text that will not change, use a {@link StaticLayout}. 469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectpublic abstract class Layout { 4839b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien /** @hide */ 4939b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien @IntDef({BREAK_STRATEGY_SIMPLE, BREAK_STRATEGY_HIGH_QUALITY, BREAK_STRATEGY_BALANCED}) 5039b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien @Retention(RetentionPolicy.SOURCE) 5139b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien public @interface BreakStrategy {} 5239b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien 5339b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien /** 5439b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien * Value for break strategy indicating simple line breaking. Automatic hyphens are not added 5539b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien * (though soft hyphens are respected), and modifying text generally doesn't affect the layout 5639b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien * before it (which yields a more consistent user experience when editing), but layout may not 5739b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien * be the highest quality. 5839b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien */ 5939b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien public static final int BREAK_STRATEGY_SIMPLE = 0; 6039b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien 6139b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien /** 6239b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien * Value for break strategy indicating high quality line breaking, including automatic 6339b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien * hyphenation and doing whole-paragraph optimization of line breaks. 6439b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien */ 6539b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien public static final int BREAK_STRATEGY_HIGH_QUALITY = 1; 6639b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien 6739b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien /** 6839b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien * Value for break strategy indicating balanced line breaking. The breaks are chosen to 6939b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien * make all lines as close to the same length as possible, including automatic hyphenation. 7039b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien */ 7139b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien public static final int BREAK_STRATEGY_BALANCED = 2; 7239b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien 7395c7a13f2ac4f31ed3aaec9b47b9a29a3dbca978Roozbeh Pournader /** @hide */ 7495c7a13f2ac4f31ed3aaec9b47b9a29a3dbca978Roozbeh Pournader @IntDef({HYPHENATION_FREQUENCY_NORMAL, HYPHENATION_FREQUENCY_FULL, 7595c7a13f2ac4f31ed3aaec9b47b9a29a3dbca978Roozbeh Pournader HYPHENATION_FREQUENCY_NONE}) 7695c7a13f2ac4f31ed3aaec9b47b9a29a3dbca978Roozbeh Pournader @Retention(RetentionPolicy.SOURCE) 7795c7a13f2ac4f31ed3aaec9b47b9a29a3dbca978Roozbeh Pournader public @interface HyphenationFrequency {} 7895c7a13f2ac4f31ed3aaec9b47b9a29a3dbca978Roozbeh Pournader 7995c7a13f2ac4f31ed3aaec9b47b9a29a3dbca978Roozbeh Pournader /** 8095c7a13f2ac4f31ed3aaec9b47b9a29a3dbca978Roozbeh Pournader * Value for hyphenation frequency indicating no automatic hyphenation. Useful 8195c7a13f2ac4f31ed3aaec9b47b9a29a3dbca978Roozbeh Pournader * for backward compatibility, and for cases where the automatic hyphenation algorithm results 8295c7a13f2ac4f31ed3aaec9b47b9a29a3dbca978Roozbeh Pournader * in incorrect hyphenation. Mid-word breaks may still happen when a word is wider than the 8395c7a13f2ac4f31ed3aaec9b47b9a29a3dbca978Roozbeh Pournader * layout and there is otherwise no valid break. Soft hyphens are ignored and will not be used 8495c7a13f2ac4f31ed3aaec9b47b9a29a3dbca978Roozbeh Pournader * as suggestions for potential line breaks. 8595c7a13f2ac4f31ed3aaec9b47b9a29a3dbca978Roozbeh Pournader */ 8695c7a13f2ac4f31ed3aaec9b47b9a29a3dbca978Roozbeh Pournader public static final int HYPHENATION_FREQUENCY_NONE = 0; 8795c7a13f2ac4f31ed3aaec9b47b9a29a3dbca978Roozbeh Pournader 8895c7a13f2ac4f31ed3aaec9b47b9a29a3dbca978Roozbeh Pournader /** 8995c7a13f2ac4f31ed3aaec9b47b9a29a3dbca978Roozbeh Pournader * Value for hyphenation frequency indicating a light amount of automatic hyphenation, which 9095c7a13f2ac4f31ed3aaec9b47b9a29a3dbca978Roozbeh Pournader * is a conservative default. Useful for informal cases, such as short sentences or chat 9195c7a13f2ac4f31ed3aaec9b47b9a29a3dbca978Roozbeh Pournader * messages. 9295c7a13f2ac4f31ed3aaec9b47b9a29a3dbca978Roozbeh Pournader */ 9395c7a13f2ac4f31ed3aaec9b47b9a29a3dbca978Roozbeh Pournader public static final int HYPHENATION_FREQUENCY_NORMAL = 1; 9495c7a13f2ac4f31ed3aaec9b47b9a29a3dbca978Roozbeh Pournader 9595c7a13f2ac4f31ed3aaec9b47b9a29a3dbca978Roozbeh Pournader /** 9695c7a13f2ac4f31ed3aaec9b47b9a29a3dbca978Roozbeh Pournader * Value for hyphenation frequency indicating the full amount of automatic hyphenation, typical 9795c7a13f2ac4f31ed3aaec9b47b9a29a3dbca978Roozbeh Pournader * in typography. Useful for running text and where it's important to put the maximum amount of 9895c7a13f2ac4f31ed3aaec9b47b9a29a3dbca978Roozbeh Pournader * text in a screen with limited space. 9995c7a13f2ac4f31ed3aaec9b47b9a29a3dbca978Roozbeh Pournader */ 10095c7a13f2ac4f31ed3aaec9b47b9a29a3dbca978Roozbeh Pournader public static final int HYPHENATION_FREQUENCY_FULL = 2; 10195c7a13f2ac4f31ed3aaec9b47b9a29a3dbca978Roozbeh Pournader 10271b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt private static final ParagraphStyle[] NO_PARA_SPANS = 10371b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt ArrayUtils.emptyArray(ParagraphStyle.class); 10476c0226008ee16633dc94ed4dbeadef2174f6bd9Dave Bort 1059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 10671b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt * Return how wide a layout must be in order to display the 1079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * specified text with one line per paragraph. 1089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 1099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public static float getDesiredWidth(CharSequence source, 1109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project TextPaint paint) { 1119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return getDesiredWidth(source, 0, source.length(), paint); 1129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1139f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt 1149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 11571b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt * Return how wide a layout must be in order to display the 1169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * specified text slice with one line per paragraph. 1179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 1189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public static float getDesiredWidth(CharSequence source, 1199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int start, int end, 1209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project TextPaint paint) { 1219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project float need = 0; 1229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int next; 1249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project for (int i = start; i <= end; i = next) { 1259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project next = TextUtils.indexOf(source, '\n', i, end); 1269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (next < 0) 1289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project next = end; 1299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 13071b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt // note, omits trailing paragraph char 1316c488de023a4797069673dc619c1a4096079ea9eGilles Debunne float w = measurePara(paint, source, i, next); 1329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (w > need) 1349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project need = w; 1359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project next++; 1379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return need; 1409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 1439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Subclasses of Layout use this constructor to set the display text, 1449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * width, and other standard properties. 14571b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt * @param text the text to render 14671b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt * @param paint the default paint for the layout. Styles can override 14771b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt * various attributes of the paint. 14871b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt * @param width the wrapping width for the text. 14971b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt * @param align whether to left, right, or center the text. Styles can 15071b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt * override the alignment. 15171b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt * @param spacingMult factor by which to scale the font size to get the 15271b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt * default line spacing 15371b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt * @param spacingAdd amount to add to the default line spacing 1549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 1559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project protected Layout(CharSequence text, TextPaint paint, 1569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int width, Alignment align, 15771b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt float spacingMult, float spacingAdd) { 158cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt this(text, paint, width, align, TextDirectionHeuristics.FIRSTSTRONG_LTR, 159cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt spacingMult, spacingAdd); 160cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt } 161cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt 162cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt /** 163cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt * Subclasses of Layout use this constructor to set the display text, 164cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt * width, and other standard properties. 165cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt * @param text the text to render 166cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt * @param paint the default paint for the layout. Styles can override 167cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt * various attributes of the paint. 168cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt * @param width the wrapping width for the text. 169cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt * @param align whether to left, right, or center the text. Styles can 170cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt * override the alignment. 171cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt * @param spacingMult factor by which to scale the font size to get the 172cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt * default line spacing 173cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt * @param spacingAdd amount to add to the default line spacing 174cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt * 175cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt * @hide 176cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt */ 177cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt protected Layout(CharSequence text, TextPaint paint, 178cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt int width, Alignment align, TextDirectionHeuristic textDir, 179cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt float spacingMult, float spacingAdd) { 180cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt 1819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (width < 0) 1829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project throw new IllegalArgumentException("Layout: " + width + " < 0"); 1839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 184e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // Ensure paint doesn't have baselineShift set. 185e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // While normally we don't modify the paint the user passed in, 186e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // we were already doing this in Styled.drawUniformRun with both 187e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // baselineShift and bgColor. We probably should reevaluate bgColor. 188e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (paint != null) { 189e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt paint.bgColor = 0; 190e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt paint.baselineShift = 0; 191e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 192e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 1939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mText = text; 1949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mPaint = paint; 1959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mWidth = width; 1969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mAlignment = align; 19771b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt mSpacingMult = spacingMult; 19871b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt mSpacingAdd = spacingAdd; 1999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mSpannedText = text instanceof Spanned; 200cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt mTextDir = textDir; 2019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 2029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 2039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 2049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Replace constructor properties of this Layout with new ones. Be careful. 2059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 2069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /* package */ void replaceWith(CharSequence text, TextPaint paint, 2079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int width, Alignment align, 2089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project float spacingmult, float spacingadd) { 2099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (width < 0) { 2109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project throw new IllegalArgumentException("Layout: " + width + " < 0"); 2119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 2129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 2139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mText = text; 2149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mPaint = paint; 2159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mWidth = width; 2169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mAlignment = align; 2179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mSpacingMult = spacingmult; 2189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mSpacingAdd = spacingadd; 2199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mSpannedText = text instanceof Spanned; 2209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 2219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 2229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 2239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Draw this Layout on the specified Canvas. 2249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 2259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public void draw(Canvas c) { 2269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project draw(c, null, null, 0); 2279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 2289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 2299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 23071b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt * Draw this Layout on the specified canvas, with the highlight path drawn 23171b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt * between the background and the text. 23271b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt * 2336c488de023a4797069673dc619c1a4096079ea9eGilles Debunne * @param canvas the canvas 23471b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt * @param highlight the path of the highlight or cursor; can be null 23571b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt * @param highlightPaint the paint for the highlight 23671b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt * @param cursorOffsetVertical the amount to temporarily translate the 23771b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt * canvas while rendering the highlight 2389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 2396c488de023a4797069673dc619c1a4096079ea9eGilles Debunne public void draw(Canvas canvas, Path highlight, Paint highlightPaint, 2406c488de023a4797069673dc619c1a4096079ea9eGilles Debunne int cursorOffsetVertical) { 2416c488de023a4797069673dc619c1a4096079ea9eGilles Debunne final long lineRange = getLineRangeForDraw(canvas); 2426c488de023a4797069673dc619c1a4096079ea9eGilles Debunne int firstLine = TextUtils.unpackRangeStartFromLong(lineRange); 2436c488de023a4797069673dc619c1a4096079ea9eGilles Debunne int lastLine = TextUtils.unpackRangeEndFromLong(lineRange); 2446c488de023a4797069673dc619c1a4096079ea9eGilles Debunne if (lastLine < 0) return; 2456c488de023a4797069673dc619c1a4096079ea9eGilles Debunne 2466c488de023a4797069673dc619c1a4096079ea9eGilles Debunne drawBackground(canvas, highlight, highlightPaint, cursorOffsetVertical, 2476c488de023a4797069673dc619c1a4096079ea9eGilles Debunne firstLine, lastLine); 2486c488de023a4797069673dc619c1a4096079ea9eGilles Debunne drawText(canvas, firstLine, lastLine); 2496c488de023a4797069673dc619c1a4096079ea9eGilles Debunne } 2509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 2516c488de023a4797069673dc619c1a4096079ea9eGilles Debunne /** 2526c488de023a4797069673dc619c1a4096079ea9eGilles Debunne * @hide 2536c488de023a4797069673dc619c1a4096079ea9eGilles Debunne */ 2546c488de023a4797069673dc619c1a4096079ea9eGilles Debunne public void drawText(Canvas canvas, int firstLine, int lastLine) { 2556c488de023a4797069673dc619c1a4096079ea9eGilles Debunne int previousLineBottom = getLineTop(firstLine); 2566c488de023a4797069673dc619c1a4096079ea9eGilles Debunne int previousLineEnd = getLineStart(firstLine); 25771b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt ParagraphStyle[] spans = NO_PARA_SPANS; 258c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt int spanEnd = 0; 2596c488de023a4797069673dc619c1a4096079ea9eGilles Debunne TextPaint paint = mPaint; 2606c488de023a4797069673dc619c1a4096079ea9eGilles Debunne CharSequence buf = mText; 2619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 262c0ccf0c47c00942a9d0f2670600a8f2d4d7adb5bDoug Felt Alignment paraAlign = mAlignment; 263c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt TabStops tabStops = null; 264c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt boolean tabStopsIsInitialized = false; 2659f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt 266e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt TextLine tl = TextLine.obtain(); 267c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt 2686c488de023a4797069673dc619c1a4096079ea9eGilles Debunne // Draw the lines, one at a time. 2696c488de023a4797069673dc619c1a4096079ea9eGilles Debunne // The baseline is the top of the following line minus the current line's descent. 27026d443aee4ee5a8791417b4ca09e8c78ba8dc78bRaph Levien for (int lineNum = firstLine; lineNum <= lastLine; lineNum++) { 2719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int start = previousLineEnd; 27226d443aee4ee5a8791417b4ca09e8c78ba8dc78bRaph Levien previousLineEnd = getLineStart(lineNum + 1); 27326d443aee4ee5a8791417b4ca09e8c78ba8dc78bRaph Levien int end = getLineVisibleEnd(lineNum, start, previousLineEnd); 2749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 2759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int ltop = previousLineBottom; 27626d443aee4ee5a8791417b4ca09e8c78ba8dc78bRaph Levien int lbottom = getLineTop(lineNum + 1); 2779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project previousLineBottom = lbottom; 27826d443aee4ee5a8791417b4ca09e8c78ba8dc78bRaph Levien int lbaseline = lbottom - getLineDescent(lineNum); 2799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 28026d443aee4ee5a8791417b4ca09e8c78ba8dc78bRaph Levien int dir = getParagraphDirection(lineNum); 281c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt int left = 0; 282c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt int right = mWidth; 283c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt 2846c488de023a4797069673dc619c1a4096079ea9eGilles Debunne if (mSpannedText) { 285c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt Spanned sp = (Spanned) buf; 2866c488de023a4797069673dc619c1a4096079ea9eGilles Debunne int textLength = buf.length(); 2876c488de023a4797069673dc619c1a4096079ea9eGilles Debunne boolean isFirstParaLine = (start == 0 || buf.charAt(start - 1) == '\n'); 2880c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt 289c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt // New batch of paragraph styles, collect into spans array. 290c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt // Compute the alignment, last alignment style wins. 291c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt // Reset tabStops, we'll rebuild if we encounter a line with 292c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt // tabs. 293c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt // We expect paragraph spans to be relatively infrequent, use 294c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt // spanEnd so that we can check less frequently. Since 295c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt // paragraph styles ought to apply to entire paragraphs, we can 296c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt // just collect the ones present at the start of the paragraph. 297c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt // If spanEnd is before the end of the paragraph, that's not 298c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt // our problem. 29926d443aee4ee5a8791417b4ca09e8c78ba8dc78bRaph Levien if (start >= spanEnd && (lineNum == firstLine || isFirstParaLine)) { 300c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt spanEnd = sp.nextSpanTransition(start, textLength, 3019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project ParagraphStyle.class); 30274d31ef2b2c42b54fa1f7cf94ea955ea67ab69a0Eric Fischer spans = getParagraphSpans(sp, start, spanEnd, ParagraphStyle.class); 3039f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt 304c0ccf0c47c00942a9d0f2670600a8f2d4d7adb5bDoug Felt paraAlign = mAlignment; 3056c488de023a4797069673dc619c1a4096079ea9eGilles Debunne for (int n = spans.length - 1; n >= 0; n--) { 3069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (spans[n] instanceof AlignmentSpan) { 307c0ccf0c47c00942a9d0f2670600a8f2d4d7adb5bDoug Felt paraAlign = ((AlignmentSpan) spans[n]).getAlignment(); 3089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project break; 3099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 3109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 3110c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt 312c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt tabStopsIsInitialized = false; 3139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 3149f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt 315c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt // Draw all leading margin spans. Adjust left or right according 316c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt // to the paragraph direction of the line. 3179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project final int length = spans.length; 318ab08c6d38ab2e575f809ca8ce4c7f095e49d258cAnish Athalye boolean useFirstLineMargin = isFirstParaLine; 319ab08c6d38ab2e575f809ca8ce4c7f095e49d258cAnish Athalye for (int n = 0; n < length; n++) { 320ab08c6d38ab2e575f809ca8ce4c7f095e49d258cAnish Athalye if (spans[n] instanceof LeadingMarginSpan2) { 321ab08c6d38ab2e575f809ca8ce4c7f095e49d258cAnish Athalye int count = ((LeadingMarginSpan2) spans[n]).getLeadingMarginLineCount(); 322ab08c6d38ab2e575f809ca8ce4c7f095e49d258cAnish Athalye int startLine = getLineForOffset(sp.getSpanStart(spans[n])); 323ab08c6d38ab2e575f809ca8ce4c7f095e49d258cAnish Athalye // if there is more than one LeadingMarginSpan2, use 324ab08c6d38ab2e575f809ca8ce4c7f095e49d258cAnish Athalye // the count that is greatest 32526d443aee4ee5a8791417b4ca09e8c78ba8dc78bRaph Levien if (lineNum < startLine + count) { 326ab08c6d38ab2e575f809ca8ce4c7f095e49d258cAnish Athalye useFirstLineMargin = true; 327ab08c6d38ab2e575f809ca8ce4c7f095e49d258cAnish Athalye break; 328ab08c6d38ab2e575f809ca8ce4c7f095e49d258cAnish Athalye } 329ab08c6d38ab2e575f809ca8ce4c7f095e49d258cAnish Athalye } 330ab08c6d38ab2e575f809ca8ce4c7f095e49d258cAnish Athalye } 3319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project for (int n = 0; n < length; n++) { 3329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (spans[n] instanceof LeadingMarginSpan) { 3339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project LeadingMarginSpan margin = (LeadingMarginSpan) spans[n]; 3349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (dir == DIR_RIGHT_TO_LEFT) { 3356c488de023a4797069673dc619c1a4096079ea9eGilles Debunne margin.drawLeadingMargin(canvas, paint, right, dir, ltop, 3369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project lbaseline, lbottom, buf, 33771b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt start, end, isFirstParaLine, this); 338c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt right -= margin.getLeadingMargin(useFirstLineMargin); 3399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } else { 3406c488de023a4797069673dc619c1a4096079ea9eGilles Debunne margin.drawLeadingMargin(canvas, paint, left, dir, ltop, 3419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project lbaseline, lbottom, buf, 34271b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt start, end, isFirstParaLine, this); 343c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt left += margin.getLeadingMargin(useFirstLineMargin); 3449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 3459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 3469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 3479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 3489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 349112d9c7f116bec0a52badde81bd778e59e88cb63Roozbeh Pournader boolean hasTab = getLineContainsTab(lineNum); 350c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt // Can't tell if we have tabs for sure, currently 351112d9c7f116bec0a52badde81bd778e59e88cb63Roozbeh Pournader if (hasTab && !tabStopsIsInitialized) { 352c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt if (tabStops == null) { 353c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt tabStops = new TabStops(TAB_INCREMENT, spans); 354c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt } else { 355c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt tabStops.reset(TAB_INCREMENT, spans); 356c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt } 357c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt tabStopsIsInitialized = true; 358c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt } 359c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt 360c0ccf0c47c00942a9d0f2670600a8f2d4d7adb5bDoug Felt // Determine whether the line aligns to normal, opposite, or center. 361c0ccf0c47c00942a9d0f2670600a8f2d4d7adb5bDoug Felt Alignment align = paraAlign; 362c0ccf0c47c00942a9d0f2670600a8f2d4d7adb5bDoug Felt if (align == Alignment.ALIGN_LEFT) { 363c0ccf0c47c00942a9d0f2670600a8f2d4d7adb5bDoug Felt align = (dir == DIR_LEFT_TO_RIGHT) ? 364c0ccf0c47c00942a9d0f2670600a8f2d4d7adb5bDoug Felt Alignment.ALIGN_NORMAL : Alignment.ALIGN_OPPOSITE; 365c0ccf0c47c00942a9d0f2670600a8f2d4d7adb5bDoug Felt } else if (align == Alignment.ALIGN_RIGHT) { 366c0ccf0c47c00942a9d0f2670600a8f2d4d7adb5bDoug Felt align = (dir == DIR_LEFT_TO_RIGHT) ? 367c0ccf0c47c00942a9d0f2670600a8f2d4d7adb5bDoug Felt Alignment.ALIGN_OPPOSITE : Alignment.ALIGN_NORMAL; 368c0ccf0c47c00942a9d0f2670600a8f2d4d7adb5bDoug Felt } 369c0ccf0c47c00942a9d0f2670600a8f2d4d7adb5bDoug Felt 3709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int x; 3719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (align == Alignment.ALIGN_NORMAL) { 3729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (dir == DIR_LEFT_TO_RIGHT) { 3732ea5290ffb9efe0a7c187fb1177ef8ecd089803cRaph Levien x = left + getIndentAdjust(lineNum, Alignment.ALIGN_LEFT); 3749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } else { 3752ea5290ffb9efe0a7c187fb1177ef8ecd089803cRaph Levien x = right + getIndentAdjust(lineNum, Alignment.ALIGN_RIGHT); 3769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 3779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } else { 37826d443aee4ee5a8791417b4ca09e8c78ba8dc78bRaph Levien int max = (int)getLineExtent(lineNum, tabStops, false); 3799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (align == Alignment.ALIGN_OPPOSITE) { 380c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt if (dir == DIR_LEFT_TO_RIGHT) { 3812ea5290ffb9efe0a7c187fb1177ef8ecd089803cRaph Levien x = right - max + getIndentAdjust(lineNum, Alignment.ALIGN_RIGHT); 3829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } else { 3832ea5290ffb9efe0a7c187fb1177ef8ecd089803cRaph Levien x = left - max + getIndentAdjust(lineNum, Alignment.ALIGN_LEFT); 3849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 385c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt } else { // Alignment.ALIGN_CENTER 386c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt max = max & ~1; 3872ea5290ffb9efe0a7c187fb1177ef8ecd089803cRaph Levien x = ((right + left - max) >> 1) + 3882ea5290ffb9efe0a7c187fb1177ef8ecd089803cRaph Levien getIndentAdjust(lineNum, Alignment.ALIGN_CENTER); 3899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 3909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 3919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 39226d443aee4ee5a8791417b4ca09e8c78ba8dc78bRaph Levien paint.setHyphenEdit(getHyphen(lineNum)); 39326d443aee4ee5a8791417b4ca09e8c78ba8dc78bRaph Levien Directions directions = getLineDirections(lineNum); 394112d9c7f116bec0a52badde81bd778e59e88cb63Roozbeh Pournader if (directions == DIRS_ALL_LEFT_TO_RIGHT && !mSpannedText && !hasTab) { 39571b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt // XXX: assumes there's nothing additional to be done 3966c488de023a4797069673dc619c1a4096079ea9eGilles Debunne canvas.drawText(buf, start, end, x, lbaseline, paint); 3979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } else { 398112d9c7f116bec0a52badde81bd778e59e88cb63Roozbeh Pournader tl.set(paint, buf, start, end, dir, directions, hasTab, tabStops); 3996c488de023a4797069673dc619c1a4096079ea9eGilles Debunne tl.draw(canvas, x, ltop, lbaseline, lbottom); 4009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 4019a174dda4227f602fd0b5f69909339b433a70f59Raph Levien paint.setHyphenEdit(0); 4029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 403c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt 404e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt TextLine.recycle(tl); 4059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 4069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 4079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 4086c488de023a4797069673dc619c1a4096079ea9eGilles Debunne * @hide 4096c488de023a4797069673dc619c1a4096079ea9eGilles Debunne */ 4106c488de023a4797069673dc619c1a4096079ea9eGilles Debunne public void drawBackground(Canvas canvas, Path highlight, Paint highlightPaint, 4116c488de023a4797069673dc619c1a4096079ea9eGilles Debunne int cursorOffsetVertical, int firstLine, int lastLine) { 4126c488de023a4797069673dc619c1a4096079ea9eGilles Debunne // First, draw LineBackgroundSpans. 4136c488de023a4797069673dc619c1a4096079ea9eGilles Debunne // LineBackgroundSpans know nothing about the alignment, margins, or 4146c488de023a4797069673dc619c1a4096079ea9eGilles Debunne // direction of the layout or line. XXX: Should they? 4156c488de023a4797069673dc619c1a4096079ea9eGilles Debunne // They are evaluated at each line. 4166c488de023a4797069673dc619c1a4096079ea9eGilles Debunne if (mSpannedText) { 417eca5b7385a641ae5d8c4aa2b7823a4e573737376Gilles Debunne if (mLineBackgroundSpans == null) { 418eca5b7385a641ae5d8c4aa2b7823a4e573737376Gilles Debunne mLineBackgroundSpans = new SpanSet<LineBackgroundSpan>(LineBackgroundSpan.class); 41960e3b3fa60a672583fe6294139feb940845efc9bGilles Debunne } 4206c488de023a4797069673dc619c1a4096079ea9eGilles Debunne 42160e3b3fa60a672583fe6294139feb940845efc9bGilles Debunne Spanned buffer = (Spanned) mText; 42260e3b3fa60a672583fe6294139feb940845efc9bGilles Debunne int textLength = buffer.length(); 423eca5b7385a641ae5d8c4aa2b7823a4e573737376Gilles Debunne mLineBackgroundSpans.init(buffer, 0, textLength); 42460e3b3fa60a672583fe6294139feb940845efc9bGilles Debunne 425eca5b7385a641ae5d8c4aa2b7823a4e573737376Gilles Debunne if (mLineBackgroundSpans.numberOfSpans > 0) { 42660e3b3fa60a672583fe6294139feb940845efc9bGilles Debunne int previousLineBottom = getLineTop(firstLine); 42760e3b3fa60a672583fe6294139feb940845efc9bGilles Debunne int previousLineEnd = getLineStart(firstLine); 42860e3b3fa60a672583fe6294139feb940845efc9bGilles Debunne ParagraphStyle[] spans = NO_PARA_SPANS; 42960e3b3fa60a672583fe6294139feb940845efc9bGilles Debunne int spansLength = 0; 43060e3b3fa60a672583fe6294139feb940845efc9bGilles Debunne TextPaint paint = mPaint; 43160e3b3fa60a672583fe6294139feb940845efc9bGilles Debunne int spanEnd = 0; 43260e3b3fa60a672583fe6294139feb940845efc9bGilles Debunne final int width = mWidth; 43360e3b3fa60a672583fe6294139feb940845efc9bGilles Debunne for (int i = firstLine; i <= lastLine; i++) { 43460e3b3fa60a672583fe6294139feb940845efc9bGilles Debunne int start = previousLineEnd; 43560e3b3fa60a672583fe6294139feb940845efc9bGilles Debunne int end = getLineStart(i + 1); 43660e3b3fa60a672583fe6294139feb940845efc9bGilles Debunne previousLineEnd = end; 43760e3b3fa60a672583fe6294139feb940845efc9bGilles Debunne 43860e3b3fa60a672583fe6294139feb940845efc9bGilles Debunne int ltop = previousLineBottom; 43960e3b3fa60a672583fe6294139feb940845efc9bGilles Debunne int lbottom = getLineTop(i + 1); 44060e3b3fa60a672583fe6294139feb940845efc9bGilles Debunne previousLineBottom = lbottom; 44160e3b3fa60a672583fe6294139feb940845efc9bGilles Debunne int lbaseline = lbottom - getLineDescent(i); 44260e3b3fa60a672583fe6294139feb940845efc9bGilles Debunne 44360e3b3fa60a672583fe6294139feb940845efc9bGilles Debunne if (start >= spanEnd) { 44460e3b3fa60a672583fe6294139feb940845efc9bGilles Debunne // These should be infrequent, so we'll use this so that 44560e3b3fa60a672583fe6294139feb940845efc9bGilles Debunne // we don't have to check as often. 446eca5b7385a641ae5d8c4aa2b7823a4e573737376Gilles Debunne spanEnd = mLineBackgroundSpans.getNextTransition(start, textLength); 44760e3b3fa60a672583fe6294139feb940845efc9bGilles Debunne // All LineBackgroundSpans on a line contribute to its background. 44860e3b3fa60a672583fe6294139feb940845efc9bGilles Debunne spansLength = 0; 44960e3b3fa60a672583fe6294139feb940845efc9bGilles Debunne // Duplication of the logic of getParagraphSpans 45060e3b3fa60a672583fe6294139feb940845efc9bGilles Debunne if (start != end || start == 0) { 45160e3b3fa60a672583fe6294139feb940845efc9bGilles Debunne // Equivalent to a getSpans(start, end), but filling the 'spans' local 45260e3b3fa60a672583fe6294139feb940845efc9bGilles Debunne // array instead to reduce memory allocation 453eca5b7385a641ae5d8c4aa2b7823a4e573737376Gilles Debunne for (int j = 0; j < mLineBackgroundSpans.numberOfSpans; j++) { 454eca5b7385a641ae5d8c4aa2b7823a4e573737376Gilles Debunne // equal test is valid since both intervals are not empty by 455eca5b7385a641ae5d8c4aa2b7823a4e573737376Gilles Debunne // construction 456eca5b7385a641ae5d8c4aa2b7823a4e573737376Gilles Debunne if (mLineBackgroundSpans.spanStarts[j] >= end || 457eca5b7385a641ae5d8c4aa2b7823a4e573737376Gilles Debunne mLineBackgroundSpans.spanEnds[j] <= start) continue; 458776abc24cdd18610232a50b997cce3cffa74609bAdam Lesinski spans = GrowingArrayUtils.append( 459776abc24cdd18610232a50b997cce3cffa74609bAdam Lesinski spans, spansLength, mLineBackgroundSpans.spans[j]); 460776abc24cdd18610232a50b997cce3cffa74609bAdam Lesinski spansLength++; 46160e3b3fa60a672583fe6294139feb940845efc9bGilles Debunne } 46260e3b3fa60a672583fe6294139feb940845efc9bGilles Debunne } 46360e3b3fa60a672583fe6294139feb940845efc9bGilles Debunne } 46460e3b3fa60a672583fe6294139feb940845efc9bGilles Debunne 46560e3b3fa60a672583fe6294139feb940845efc9bGilles Debunne for (int n = 0; n < spansLength; n++) { 46660e3b3fa60a672583fe6294139feb940845efc9bGilles Debunne LineBackgroundSpan lineBackgroundSpan = (LineBackgroundSpan) spans[n]; 46760e3b3fa60a672583fe6294139feb940845efc9bGilles Debunne lineBackgroundSpan.drawBackground(canvas, paint, 0, width, 46860e3b3fa60a672583fe6294139feb940845efc9bGilles Debunne ltop, lbaseline, lbottom, 46960e3b3fa60a672583fe6294139feb940845efc9bGilles Debunne buffer, start, end, i); 47060e3b3fa60a672583fe6294139feb940845efc9bGilles Debunne } 4716c488de023a4797069673dc619c1a4096079ea9eGilles Debunne } 4726c488de023a4797069673dc619c1a4096079ea9eGilles Debunne } 473eca5b7385a641ae5d8c4aa2b7823a4e573737376Gilles Debunne mLineBackgroundSpans.recycle(); 4746c488de023a4797069673dc619c1a4096079ea9eGilles Debunne } 4756c488de023a4797069673dc619c1a4096079ea9eGilles Debunne 4766c488de023a4797069673dc619c1a4096079ea9eGilles Debunne // There can be a highlight even without spans if we are drawing 4776c488de023a4797069673dc619c1a4096079ea9eGilles Debunne // a non-spanned transformation of a spanned editing buffer. 4786c488de023a4797069673dc619c1a4096079ea9eGilles Debunne if (highlight != null) { 4796c488de023a4797069673dc619c1a4096079ea9eGilles Debunne if (cursorOffsetVertical != 0) canvas.translate(0, cursorOffsetVertical); 4806c488de023a4797069673dc619c1a4096079ea9eGilles Debunne canvas.drawPath(highlight, highlightPaint); 4816c488de023a4797069673dc619c1a4096079ea9eGilles Debunne if (cursorOffsetVertical != 0) canvas.translate(0, -cursorOffsetVertical); 4826c488de023a4797069673dc619c1a4096079ea9eGilles Debunne } 4836c488de023a4797069673dc619c1a4096079ea9eGilles Debunne } 4846c488de023a4797069673dc619c1a4096079ea9eGilles Debunne 4856c488de023a4797069673dc619c1a4096079ea9eGilles Debunne /** 4866c488de023a4797069673dc619c1a4096079ea9eGilles Debunne * @param canvas 4876c488de023a4797069673dc619c1a4096079ea9eGilles Debunne * @return The range of lines that need to be drawn, possibly empty. 4886c488de023a4797069673dc619c1a4096079ea9eGilles Debunne * @hide 4896c488de023a4797069673dc619c1a4096079ea9eGilles Debunne */ 4906c488de023a4797069673dc619c1a4096079ea9eGilles Debunne public long getLineRangeForDraw(Canvas canvas) { 4916c488de023a4797069673dc619c1a4096079ea9eGilles Debunne int dtop, dbottom; 4926c488de023a4797069673dc619c1a4096079ea9eGilles Debunne 4936c488de023a4797069673dc619c1a4096079ea9eGilles Debunne synchronized (sTempRect) { 4946c488de023a4797069673dc619c1a4096079ea9eGilles Debunne if (!canvas.getClipBounds(sTempRect)) { 4956c488de023a4797069673dc619c1a4096079ea9eGilles Debunne // Negative range end used as a special flag 4966c488de023a4797069673dc619c1a4096079ea9eGilles Debunne return TextUtils.packRangeInLong(0, -1); 4976c488de023a4797069673dc619c1a4096079ea9eGilles Debunne } 4986c488de023a4797069673dc619c1a4096079ea9eGilles Debunne 4996c488de023a4797069673dc619c1a4096079ea9eGilles Debunne dtop = sTempRect.top; 5006c488de023a4797069673dc619c1a4096079ea9eGilles Debunne dbottom = sTempRect.bottom; 5016c488de023a4797069673dc619c1a4096079ea9eGilles Debunne } 5026c488de023a4797069673dc619c1a4096079ea9eGilles Debunne 5036c488de023a4797069673dc619c1a4096079ea9eGilles Debunne final int top = Math.max(dtop, 0); 5046c488de023a4797069673dc619c1a4096079ea9eGilles Debunne final int bottom = Math.min(getLineTop(getLineCount()), dbottom); 5056c488de023a4797069673dc619c1a4096079ea9eGilles Debunne 5062fba3387c31b675c419030145250e5be246c50b0Gilles Debunne if (top >= bottom) return TextUtils.packRangeInLong(0, -1); 5076c488de023a4797069673dc619c1a4096079ea9eGilles Debunne return TextUtils.packRangeInLong(getLineForVertical(top), getLineForVertical(bottom)); 5086c488de023a4797069673dc619c1a4096079ea9eGilles Debunne } 5096c488de023a4797069673dc619c1a4096079ea9eGilles Debunne 5106c488de023a4797069673dc619c1a4096079ea9eGilles Debunne /** 511c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt * Return the start position of the line, given the left and right bounds 512c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt * of the margins. 5130c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt * 514c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt * @param line the line index 515c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt * @param left the left bounds (0, or leading margin if ltr para) 516c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt * @param right the right bounds (width, minus leading margin if rtl para) 517c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt * @return the start position of the line (to right of line if rtl para) 518c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt */ 519c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt private int getLineStartPos(int line, int left, int right) { 520c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt // Adjust the point at which to start rendering depending on the 521c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt // alignment of the paragraph. 522c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt Alignment align = getParagraphAlignment(line); 523c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt int dir = getParagraphDirection(line); 524c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt 525c0ccf0c47c00942a9d0f2670600a8f2d4d7adb5bDoug Felt if (align == Alignment.ALIGN_LEFT) { 526db24f0a73f4385aea104367a4bc71b78f25dbc3fFabrice Di Meglio align = (dir == DIR_LEFT_TO_RIGHT) ? Alignment.ALIGN_NORMAL : Alignment.ALIGN_OPPOSITE; 527db24f0a73f4385aea104367a4bc71b78f25dbc3fFabrice Di Meglio } else if (align == Alignment.ALIGN_RIGHT) { 528db24f0a73f4385aea104367a4bc71b78f25dbc3fFabrice Di Meglio align = (dir == DIR_LEFT_TO_RIGHT) ? Alignment.ALIGN_OPPOSITE : Alignment.ALIGN_NORMAL; 529db24f0a73f4385aea104367a4bc71b78f25dbc3fFabrice Di Meglio } 530db24f0a73f4385aea104367a4bc71b78f25dbc3fFabrice Di Meglio 531db24f0a73f4385aea104367a4bc71b78f25dbc3fFabrice Di Meglio int x; 532db24f0a73f4385aea104367a4bc71b78f25dbc3fFabrice Di Meglio if (align == Alignment.ALIGN_NORMAL) { 533c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt if (dir == DIR_LEFT_TO_RIGHT) { 5342ea5290ffb9efe0a7c187fb1177ef8ecd089803cRaph Levien x = left + getIndentAdjust(line, Alignment.ALIGN_LEFT); 535c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt } else { 5362ea5290ffb9efe0a7c187fb1177ef8ecd089803cRaph Levien x = right + getIndentAdjust(line, Alignment.ALIGN_RIGHT); 537c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt } 538c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt } else { 539c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt TabStops tabStops = null; 540c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt if (mSpannedText && getLineContainsTab(line)) { 541c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt Spanned spanned = (Spanned) mText; 542c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt int start = getLineStart(line); 543c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt int spanEnd = spanned.nextSpanTransition(start, spanned.length(), 544c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt TabStopSpan.class); 5456c488de023a4797069673dc619c1a4096079ea9eGilles Debunne TabStopSpan[] tabSpans = getParagraphSpans(spanned, start, spanEnd, 5466c488de023a4797069673dc619c1a4096079ea9eGilles Debunne TabStopSpan.class); 547c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt if (tabSpans.length > 0) { 548c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt tabStops = new TabStops(TAB_INCREMENT, tabSpans); 549c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt } 550c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt } 551c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt int max = (int)getLineExtent(line, tabStops, false); 552db24f0a73f4385aea104367a4bc71b78f25dbc3fFabrice Di Meglio if (align == Alignment.ALIGN_OPPOSITE) { 553c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt if (dir == DIR_LEFT_TO_RIGHT) { 5542ea5290ffb9efe0a7c187fb1177ef8ecd089803cRaph Levien x = right - max + getIndentAdjust(line, Alignment.ALIGN_RIGHT); 555c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt } else { 556db24f0a73f4385aea104367a4bc71b78f25dbc3fFabrice Di Meglio // max is negative here 5572ea5290ffb9efe0a7c187fb1177ef8ecd089803cRaph Levien x = left - max + getIndentAdjust(line, Alignment.ALIGN_LEFT); 558c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt } 559c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt } else { // Alignment.ALIGN_CENTER 560c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt max = max & ~1; 5612ea5290ffb9efe0a7c187fb1177ef8ecd089803cRaph Levien x = (left + right - max) >> 1 + getIndentAdjust(line, Alignment.ALIGN_CENTER); 562c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt } 563c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt } 564c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt return x; 565c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt } 566c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt 567c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt /** 5689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Return the text that is displayed by this Layout. 5699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 5709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public final CharSequence getText() { 5719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return mText; 5729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 5739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 5749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 5759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Return the base Paint properties for this layout. 5769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Do NOT change the paint, which may result in funny 5779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * drawing for this layout. 5789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 5799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public final TextPaint getPaint() { 5809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return mPaint; 5819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 5829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 5839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 5849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Return the width of this layout. 5859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 5869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public final int getWidth() { 5879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return mWidth; 5889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 5899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 5909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 5919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Return the width to which this Layout is ellipsizing, or 5929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * {@link #getWidth} if it is not doing anything special. 5939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 5949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public int getEllipsizedWidth() { 5959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return mWidth; 5969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 5979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 5989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 5999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Increase the width of this layout to the specified width. 60071b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt * Be careful to use this only when you know it is appropriate— 6019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * it does not cause the text to reflow to use the full new width. 6029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 6039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public final void increaseWidthTo(int wid) { 6049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (wid < mWidth) { 6059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project throw new RuntimeException("attempted to reduce Layout width"); 6069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 6079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 6089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mWidth = wid; 6099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 6109f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt 6119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 6129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Return the total height of this layout. 6139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 6149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public int getHeight() { 61571b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt return getLineTop(getLineCount()); 6169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 6179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 6189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 6199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Return the base alignment of this layout. 6209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 6219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public final Alignment getAlignment() { 6229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return mAlignment; 6239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 6249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 6259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 6269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Return what the text height is multiplied by to get the line height. 6279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 6289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public final float getSpacingMultiplier() { 6299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return mSpacingMult; 6309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 6319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 6329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 6339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Return the number of units of leading that are added to each line. 6349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 6359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public final float getSpacingAdd() { 6369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return mSpacingAdd; 6379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 6389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 6399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 640cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt * Return the heuristic used to determine paragraph text direction. 641cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt * @hide 642cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt */ 643cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt public final TextDirectionHeuristic getTextDirectionHeuristic() { 644cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt return mTextDir; 645cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt } 646cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt 647cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt /** 6489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Return the number of lines of text in this layout. 6499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 6509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public abstract int getLineCount(); 6519f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt 6529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 6539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Return the baseline for the specified line (0…getLineCount() - 1) 6549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * If bounds is not null, return the top, left, right, bottom extents 6559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * of the specified line in it. 6569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * @param line which line to examine (0..getLineCount() - 1) 6579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * @param bounds Optional. If not null, it returns the extent of the line 6589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * @return the Y-coordinate of the baseline 6599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 6609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public int getLineBounds(int line, Rect bounds) { 6619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (bounds != null) { 6629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project bounds.left = 0; // ??? 6639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project bounds.top = getLineTop(line); 6649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project bounds.right = mWidth; // ??? 66571b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt bounds.bottom = getLineTop(line + 1); 6669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 6679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return getLineBaseline(line); 6689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 6699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 6709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 67171b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt * Return the vertical position of the top of the specified line 67271b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt * (0…getLineCount()). 67371b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt * If the specified line is equal to the line count, returns the 6749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * bottom of the last line. 6759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 6769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public abstract int getLineTop(int line); 6779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 6789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 67971b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt * Return the descent of the specified line(0…getLineCount() - 1). 6809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 6819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public abstract int getLineDescent(int line); 6829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 6839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 68471b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt * Return the text offset of the beginning of the specified line ( 68571b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt * 0…getLineCount()). If the specified line is equal to the line 68671b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt * count, returns the length of the text. 6879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 6889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public abstract int getLineStart(int line); 6899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 6909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 69171b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt * Returns the primary directionality of the paragraph containing the 69271b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt * specified line, either 1 for left-to-right lines, or -1 for right-to-left 69371b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt * lines (see {@link #DIR_LEFT_TO_RIGHT}, {@link #DIR_RIGHT_TO_LEFT}). 6949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 6959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public abstract int getParagraphDirection(int line); 6969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 6979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 698105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project * Returns whether the specified line contains one or more 699112d9c7f116bec0a52badde81bd778e59e88cb63Roozbeh Pournader * characters that need to be handled specially, like tabs. 7009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 7019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public abstract boolean getLineContainsTab(int line); 7029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 7039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 70471b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt * Returns the directional run information for the specified line. 7059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * The array alternates counts of characters in left-to-right 7069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * and right-to-left segments of the line. 70771b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt * 70871b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt * <p>NOTE: this is inadequate to support bidirectional text, and will change. 7099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 7109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public abstract Directions getLineDirections(int line); 7119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 7129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 7139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Returns the (negative) number of extra pixels of ascent padding in the 7149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * top line of the Layout. 7159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 7169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public abstract int getTopPadding(); 7179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 7189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 7199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Returns the number of extra pixels of descent padding in the 7209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * bottom line of the Layout. 7219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 7229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public abstract int getBottomPadding(); 7239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 72426d443aee4ee5a8791417b4ca09e8c78ba8dc78bRaph Levien /** 72526d443aee4ee5a8791417b4ca09e8c78ba8dc78bRaph Levien * Returns the hyphen edit for a line. 72626d443aee4ee5a8791417b4ca09e8c78ba8dc78bRaph Levien * 72726d443aee4ee5a8791417b4ca09e8c78ba8dc78bRaph Levien * @hide 72826d443aee4ee5a8791417b4ca09e8c78ba8dc78bRaph Levien */ 72926d443aee4ee5a8791417b4ca09e8c78ba8dc78bRaph Levien public int getHyphen(int line) { 73026d443aee4ee5a8791417b4ca09e8c78ba8dc78bRaph Levien return 0; 73126d443aee4ee5a8791417b4ca09e8c78ba8dc78bRaph Levien } 73226d443aee4ee5a8791417b4ca09e8c78ba8dc78bRaph Levien 7332ea5290ffb9efe0a7c187fb1177ef8ecd089803cRaph Levien /** 7342ea5290ffb9efe0a7c187fb1177ef8ecd089803cRaph Levien * Returns the left indent for a line. 7352ea5290ffb9efe0a7c187fb1177ef8ecd089803cRaph Levien * 7362ea5290ffb9efe0a7c187fb1177ef8ecd089803cRaph Levien * @hide 7372ea5290ffb9efe0a7c187fb1177ef8ecd089803cRaph Levien */ 7382ea5290ffb9efe0a7c187fb1177ef8ecd089803cRaph Levien public int getIndentAdjust(int line, Alignment alignment) { 7392ea5290ffb9efe0a7c187fb1177ef8ecd089803cRaph Levien return 0; 7402ea5290ffb9efe0a7c187fb1177ef8ecd089803cRaph Levien } 7414e0c5e55e171532760d5f51e0165563827129d4eDoug Felt 7424e0c5e55e171532760d5f51e0165563827129d4eDoug Felt /** 7434e0c5e55e171532760d5f51e0165563827129d4eDoug Felt * Returns true if the character at offset and the preceding character 7444e0c5e55e171532760d5f51e0165563827129d4eDoug Felt * are at different run levels (and thus there's a split caret). 7454e0c5e55e171532760d5f51e0165563827129d4eDoug Felt * @param offset the offset 7464e0c5e55e171532760d5f51e0165563827129d4eDoug Felt * @return true if at a level boundary 747f75c97e023af7d4ad9a8c129d4ea282b1c3b8f94Gilles Debunne * @hide 7484e0c5e55e171532760d5f51e0165563827129d4eDoug Felt */ 749f75c97e023af7d4ad9a8c129d4ea282b1c3b8f94Gilles Debunne public boolean isLevelBoundary(int offset) { 7509f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt int line = getLineForOffset(offset); 7514e0c5e55e171532760d5f51e0165563827129d4eDoug Felt Directions dirs = getLineDirections(line); 7524e0c5e55e171532760d5f51e0165563827129d4eDoug Felt if (dirs == DIRS_ALL_LEFT_TO_RIGHT || dirs == DIRS_ALL_RIGHT_TO_LEFT) { 7534e0c5e55e171532760d5f51e0165563827129d4eDoug Felt return false; 7544e0c5e55e171532760d5f51e0165563827129d4eDoug Felt } 7554e0c5e55e171532760d5f51e0165563827129d4eDoug Felt 7564e0c5e55e171532760d5f51e0165563827129d4eDoug Felt int[] runs = dirs.mDirections; 7579f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt int lineStart = getLineStart(line); 7584e0c5e55e171532760d5f51e0165563827129d4eDoug Felt int lineEnd = getLineEnd(line); 7594e0c5e55e171532760d5f51e0165563827129d4eDoug Felt if (offset == lineStart || offset == lineEnd) { 7604e0c5e55e171532760d5f51e0165563827129d4eDoug Felt int paraLevel = getParagraphDirection(line) == 1 ? 0 : 1; 7614e0c5e55e171532760d5f51e0165563827129d4eDoug Felt int runIndex = offset == lineStart ? 0 : runs.length - 2; 7624e0c5e55e171532760d5f51e0165563827129d4eDoug Felt return ((runs[runIndex + 1] >>> RUN_LEVEL_SHIFT) & RUN_LEVEL_MASK) != paraLevel; 7634e0c5e55e171532760d5f51e0165563827129d4eDoug Felt } 7644e0c5e55e171532760d5f51e0165563827129d4eDoug Felt 7654e0c5e55e171532760d5f51e0165563827129d4eDoug Felt offset -= lineStart; 7669f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt for (int i = 0; i < runs.length; i += 2) { 7674e0c5e55e171532760d5f51e0165563827129d4eDoug Felt if (offset == runs[i]) { 7684e0c5e55e171532760d5f51e0165563827129d4eDoug Felt return true; 7699f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt } 7709f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt } 7714e0c5e55e171532760d5f51e0165563827129d4eDoug Felt return false; 7729f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt } 7739f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt 77434d2eba560f83f4eb665cdc039cf02bf96c201daFabrice Di Meglio /** 77534d2eba560f83f4eb665cdc039cf02bf96c201daFabrice Di Meglio * Returns true if the character at offset is right to left (RTL). 77634d2eba560f83f4eb665cdc039cf02bf96c201daFabrice Di Meglio * @param offset the offset 77734d2eba560f83f4eb665cdc039cf02bf96c201daFabrice Di Meglio * @return true if the character is RTL, false if it is LTR 77834d2eba560f83f4eb665cdc039cf02bf96c201daFabrice Di Meglio */ 77934d2eba560f83f4eb665cdc039cf02bf96c201daFabrice Di Meglio public boolean isRtlCharAt(int offset) { 78034d2eba560f83f4eb665cdc039cf02bf96c201daFabrice Di Meglio int line = getLineForOffset(offset); 78134d2eba560f83f4eb665cdc039cf02bf96c201daFabrice Di Meglio Directions dirs = getLineDirections(line); 78234d2eba560f83f4eb665cdc039cf02bf96c201daFabrice Di Meglio if (dirs == DIRS_ALL_LEFT_TO_RIGHT) { 78334d2eba560f83f4eb665cdc039cf02bf96c201daFabrice Di Meglio return false; 78434d2eba560f83f4eb665cdc039cf02bf96c201daFabrice Di Meglio } 78534d2eba560f83f4eb665cdc039cf02bf96c201daFabrice Di Meglio if (dirs == DIRS_ALL_RIGHT_TO_LEFT) { 78634d2eba560f83f4eb665cdc039cf02bf96c201daFabrice Di Meglio return true; 78734d2eba560f83f4eb665cdc039cf02bf96c201daFabrice Di Meglio } 78834d2eba560f83f4eb665cdc039cf02bf96c201daFabrice Di Meglio int[] runs = dirs.mDirections; 78934d2eba560f83f4eb665cdc039cf02bf96c201daFabrice Di Meglio int lineStart = getLineStart(line); 79034d2eba560f83f4eb665cdc039cf02bf96c201daFabrice Di Meglio for (int i = 0; i < runs.length; i += 2) { 791875062059c4932ddf5649720c26e7f59b6ab9978Raph Levien int start = lineStart + runs[i]; 792875062059c4932ddf5649720c26e7f59b6ab9978Raph Levien int limit = start + (runs[i+1] & RUN_LENGTH_MASK); 793875062059c4932ddf5649720c26e7f59b6ab9978Raph Levien if (offset >= start && offset < limit) { 79434d2eba560f83f4eb665cdc039cf02bf96c201daFabrice Di Meglio int level = (runs[i+1] >>> RUN_LEVEL_SHIFT) & RUN_LEVEL_MASK; 79534d2eba560f83f4eb665cdc039cf02bf96c201daFabrice Di Meglio return ((level & 1) != 0); 79634d2eba560f83f4eb665cdc039cf02bf96c201daFabrice Di Meglio } 79734d2eba560f83f4eb665cdc039cf02bf96c201daFabrice Di Meglio } 79834d2eba560f83f4eb665cdc039cf02bf96c201daFabrice Di Meglio // Should happen only if the offset is "out of bounds" 79934d2eba560f83f4eb665cdc039cf02bf96c201daFabrice Di Meglio return false; 80034d2eba560f83f4eb665cdc039cf02bf96c201daFabrice Di Meglio } 80134d2eba560f83f4eb665cdc039cf02bf96c201daFabrice Di Meglio 802f0bb87b7c40efeeaee58d4c7b767961c9800463eKeisuke Kuroyanagi /** 803f0bb87b7c40efeeaee58d4c7b767961c9800463eKeisuke Kuroyanagi * Returns the range of the run that the character at offset belongs to. 804f0bb87b7c40efeeaee58d4c7b767961c9800463eKeisuke Kuroyanagi * @param offset the offset 805f0bb87b7c40efeeaee58d4c7b767961c9800463eKeisuke Kuroyanagi * @return The range of the run 806f0bb87b7c40efeeaee58d4c7b767961c9800463eKeisuke Kuroyanagi * @hide 807f0bb87b7c40efeeaee58d4c7b767961c9800463eKeisuke Kuroyanagi */ 808f0bb87b7c40efeeaee58d4c7b767961c9800463eKeisuke Kuroyanagi public long getRunRange(int offset) { 809f0bb87b7c40efeeaee58d4c7b767961c9800463eKeisuke Kuroyanagi int line = getLineForOffset(offset); 810f0bb87b7c40efeeaee58d4c7b767961c9800463eKeisuke Kuroyanagi Directions dirs = getLineDirections(line); 811f0bb87b7c40efeeaee58d4c7b767961c9800463eKeisuke Kuroyanagi if (dirs == DIRS_ALL_LEFT_TO_RIGHT || dirs == DIRS_ALL_RIGHT_TO_LEFT) { 812f0bb87b7c40efeeaee58d4c7b767961c9800463eKeisuke Kuroyanagi return TextUtils.packRangeInLong(0, getLineEnd(line)); 813f0bb87b7c40efeeaee58d4c7b767961c9800463eKeisuke Kuroyanagi } 814f0bb87b7c40efeeaee58d4c7b767961c9800463eKeisuke Kuroyanagi int[] runs = dirs.mDirections; 815f0bb87b7c40efeeaee58d4c7b767961c9800463eKeisuke Kuroyanagi int lineStart = getLineStart(line); 816f0bb87b7c40efeeaee58d4c7b767961c9800463eKeisuke Kuroyanagi for (int i = 0; i < runs.length; i += 2) { 817f0bb87b7c40efeeaee58d4c7b767961c9800463eKeisuke Kuroyanagi int start = lineStart + runs[i]; 818f0bb87b7c40efeeaee58d4c7b767961c9800463eKeisuke Kuroyanagi int limit = start + (runs[i+1] & RUN_LENGTH_MASK); 819f0bb87b7c40efeeaee58d4c7b767961c9800463eKeisuke Kuroyanagi if (offset >= start && offset < limit) { 820f0bb87b7c40efeeaee58d4c7b767961c9800463eKeisuke Kuroyanagi return TextUtils.packRangeInLong(start, limit); 821f0bb87b7c40efeeaee58d4c7b767961c9800463eKeisuke Kuroyanagi } 822f0bb87b7c40efeeaee58d4c7b767961c9800463eKeisuke Kuroyanagi } 823f0bb87b7c40efeeaee58d4c7b767961c9800463eKeisuke Kuroyanagi // Should happen only if the offset is "out of bounds" 824f0bb87b7c40efeeaee58d4c7b767961c9800463eKeisuke Kuroyanagi return TextUtils.packRangeInLong(0, getLineEnd(line)); 825f0bb87b7c40efeeaee58d4c7b767961c9800463eKeisuke Kuroyanagi } 826f0bb87b7c40efeeaee58d4c7b767961c9800463eKeisuke Kuroyanagi 8279f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt private boolean primaryIsTrailingPrevious(int offset) { 8289f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt int line = getLineForOffset(offset); 8299f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt int lineStart = getLineStart(line); 8304e0c5e55e171532760d5f51e0165563827129d4eDoug Felt int lineEnd = getLineEnd(line); 8319f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt int[] runs = getLineDirections(line).mDirections; 8329f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt 8339f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt int levelAt = -1; 8349f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt for (int i = 0; i < runs.length; i += 2) { 8359f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt int start = lineStart + runs[i]; 8369f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt int limit = start + (runs[i+1] & RUN_LENGTH_MASK); 8379f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt if (limit > lineEnd) { 8389f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt limit = lineEnd; 8399f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt } 8409f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt if (offset >= start && offset < limit) { 8419f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt if (offset > start) { 8429f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt // Previous character is at same level, so don't use trailing. 8439f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt return false; 8449f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt } 8459f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt levelAt = (runs[i+1] >>> RUN_LEVEL_SHIFT) & RUN_LEVEL_MASK; 8469f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt break; 8479f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt } 8489f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt } 8499f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt if (levelAt == -1) { 8509f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt // Offset was limit of line. 8519f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt levelAt = getParagraphDirection(line) == 1 ? 0 : 1; 8529f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt } 8539f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt 8549f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt // At level boundary, check previous level. 8559f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt int levelBefore = -1; 8569f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt if (offset == lineStart) { 8579f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt levelBefore = getParagraphDirection(line) == 1 ? 0 : 1; 8589f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt } else { 8599f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt offset -= 1; 8609f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt for (int i = 0; i < runs.length; i += 2) { 8619f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt int start = lineStart + runs[i]; 8629f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt int limit = start + (runs[i+1] & RUN_LENGTH_MASK); 8639f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt if (limit > lineEnd) { 8649f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt limit = lineEnd; 8659f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt } 8669f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt if (offset >= start && offset < limit) { 8679f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt levelBefore = (runs[i+1] >>> RUN_LEVEL_SHIFT) & RUN_LEVEL_MASK; 8689f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt break; 8699f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt } 8709f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt } 8719f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt } 8729f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt 8739f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt return levelBefore < levelAt; 8749f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt } 8759f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt 8769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 8779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Get the primary horizontal position for the specified text offset. 8789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * This is the location where a new character would be inserted in 8799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * the paragraph's primary direction. 8809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 8819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public float getPrimaryHorizontal(int offset) { 882afe8e9b6d033cb854afa3024d8198a32896a804aRaph Levien return getPrimaryHorizontal(offset, false /* not clamped */); 883afe8e9b6d033cb854afa3024d8198a32896a804aRaph Levien } 884afe8e9b6d033cb854afa3024d8198a32896a804aRaph Levien 885afe8e9b6d033cb854afa3024d8198a32896a804aRaph Levien /** 886afe8e9b6d033cb854afa3024d8198a32896a804aRaph Levien * Get the primary horizontal position for the specified text offset, but 887afe8e9b6d033cb854afa3024d8198a32896a804aRaph Levien * optionally clamp it so that it doesn't exceed the width of the layout. 888afe8e9b6d033cb854afa3024d8198a32896a804aRaph Levien * @hide 889afe8e9b6d033cb854afa3024d8198a32896a804aRaph Levien */ 890afe8e9b6d033cb854afa3024d8198a32896a804aRaph Levien public float getPrimaryHorizontal(int offset, boolean clamped) { 8919f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt boolean trailing = primaryIsTrailingPrevious(offset); 892afe8e9b6d033cb854afa3024d8198a32896a804aRaph Levien return getHorizontal(offset, trailing, clamped); 8939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 8949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 8959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 8969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Get the secondary horizontal position for the specified text offset. 8979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * This is the location where a new character would be inserted in 8989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * the direction other than the paragraph's primary direction. 8999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 9009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public float getSecondaryHorizontal(int offset) { 901afe8e9b6d033cb854afa3024d8198a32896a804aRaph Levien return getSecondaryHorizontal(offset, false /* not clamped */); 902afe8e9b6d033cb854afa3024d8198a32896a804aRaph Levien } 903afe8e9b6d033cb854afa3024d8198a32896a804aRaph Levien 904afe8e9b6d033cb854afa3024d8198a32896a804aRaph Levien /** 905afe8e9b6d033cb854afa3024d8198a32896a804aRaph Levien * Get the secondary horizontal position for the specified text offset, but 906afe8e9b6d033cb854afa3024d8198a32896a804aRaph Levien * optionally clamp it so that it doesn't exceed the width of the layout. 907afe8e9b6d033cb854afa3024d8198a32896a804aRaph Levien * @hide 908afe8e9b6d033cb854afa3024d8198a32896a804aRaph Levien */ 909afe8e9b6d033cb854afa3024d8198a32896a804aRaph Levien public float getSecondaryHorizontal(int offset, boolean clamped) { 9109f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt boolean trailing = primaryIsTrailingPrevious(offset); 911afe8e9b6d033cb854afa3024d8198a32896a804aRaph Levien return getHorizontal(offset, !trailing, clamped); 9129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 9139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 914f0bb87b7c40efeeaee58d4c7b767961c9800463eKeisuke Kuroyanagi private float getHorizontal(int offset, boolean primary) { 915f0bb87b7c40efeeaee58d4c7b767961c9800463eKeisuke Kuroyanagi return primary ? getPrimaryHorizontal(offset) : getSecondaryHorizontal(offset); 916f0bb87b7c40efeeaee58d4c7b767961c9800463eKeisuke Kuroyanagi } 917f0bb87b7c40efeeaee58d4c7b767961c9800463eKeisuke Kuroyanagi 918afe8e9b6d033cb854afa3024d8198a32896a804aRaph Levien private float getHorizontal(int offset, boolean trailing, boolean clamped) { 9199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int line = getLineForOffset(offset); 9209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 921afe8e9b6d033cb854afa3024d8198a32896a804aRaph Levien return getHorizontal(offset, trailing, line, clamped); 9229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 9239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 924afe8e9b6d033cb854afa3024d8198a32896a804aRaph Levien private float getHorizontal(int offset, boolean trailing, int line, boolean clamped) { 9259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int start = getLineStart(line); 926e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt int end = getLineEnd(line); 9279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int dir = getParagraphDirection(line); 928112d9c7f116bec0a52badde81bd778e59e88cb63Roozbeh Pournader boolean hasTab = getLineContainsTab(line); 9299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project Directions directions = getLineDirections(line); 9309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 931c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt TabStops tabStops = null; 932112d9c7f116bec0a52badde81bd778e59e88cb63Roozbeh Pournader if (hasTab && mText instanceof Spanned) { 933c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt // Just checking this line should be good enough, tabs should be 934c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt // consistent across all lines in a paragraph. 93574d31ef2b2c42b54fa1f7cf94ea955ea67ab69a0Eric Fischer TabStopSpan[] tabs = getParagraphSpans((Spanned) mText, start, end, TabStopSpan.class); 936c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt if (tabs.length > 0) { 937c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt tabStops = new TabStops(TAB_INCREMENT, tabs); // XXX should reuse 938c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt } 9399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 9409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 941e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt TextLine tl = TextLine.obtain(); 942112d9c7f116bec0a52badde81bd778e59e88cb63Roozbeh Pournader tl.set(mPaint, mText, start, end, dir, directions, hasTab, tabStops); 943e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt float wid = tl.measure(offset - start, trailing, null); 944e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt TextLine.recycle(tl); 9459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 946afe8e9b6d033cb854afa3024d8198a32896a804aRaph Levien if (clamped && wid > mWidth) { 947afe8e9b6d033cb854afa3024d8198a32896a804aRaph Levien wid = mWidth; 948afe8e9b6d033cb854afa3024d8198a32896a804aRaph Levien } 9499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int left = getParagraphLeft(line); 9509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int right = getParagraphRight(line); 9519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 952c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt return getLineStartPos(line, left, right) + wid; 9539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 9549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 9559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 9569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Get the leftmost position that should be exposed for horizontal 9579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * scrolling on the specified line. 9589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 9599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public float getLineLeft(int line) { 9609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int dir = getParagraphDirection(line); 9619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project Alignment align = getParagraphAlignment(line); 9629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 963c0ccf0c47c00942a9d0f2670600a8f2d4d7adb5bDoug Felt if (align == Alignment.ALIGN_LEFT) { 964c0ccf0c47c00942a9d0f2670600a8f2d4d7adb5bDoug Felt return 0; 965c0ccf0c47c00942a9d0f2670600a8f2d4d7adb5bDoug Felt } else if (align == Alignment.ALIGN_NORMAL) { 9669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (dir == DIR_RIGHT_TO_LEFT) 9679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return getParagraphRight(line) - getLineMax(line); 9689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project else 9699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return 0; 970c0ccf0c47c00942a9d0f2670600a8f2d4d7adb5bDoug Felt } else if (align == Alignment.ALIGN_RIGHT) { 971c0ccf0c47c00942a9d0f2670600a8f2d4d7adb5bDoug Felt return mWidth - getLineMax(line); 9729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } else if (align == Alignment.ALIGN_OPPOSITE) { 9739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (dir == DIR_RIGHT_TO_LEFT) 9749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return 0; 9759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project else 9769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return mWidth - getLineMax(line); 9779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } else { /* align == Alignment.ALIGN_CENTER */ 9789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int left = getParagraphLeft(line); 9799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int right = getParagraphRight(line); 9809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int max = ((int) getLineMax(line)) & ~1; 9819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 9829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return left + ((right - left) - max) / 2; 9839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 9849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 9859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 9869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 9879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Get the rightmost position that should be exposed for horizontal 9889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * scrolling on the specified line. 9899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 9909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public float getLineRight(int line) { 9919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int dir = getParagraphDirection(line); 9929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project Alignment align = getParagraphAlignment(line); 9939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 994c0ccf0c47c00942a9d0f2670600a8f2d4d7adb5bDoug Felt if (align == Alignment.ALIGN_LEFT) { 995c0ccf0c47c00942a9d0f2670600a8f2d4d7adb5bDoug Felt return getParagraphLeft(line) + getLineMax(line); 996c0ccf0c47c00942a9d0f2670600a8f2d4d7adb5bDoug Felt } else if (align == Alignment.ALIGN_NORMAL) { 9979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (dir == DIR_RIGHT_TO_LEFT) 9989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return mWidth; 9999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project else 10009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return getParagraphLeft(line) + getLineMax(line); 1001c0ccf0c47c00942a9d0f2670600a8f2d4d7adb5bDoug Felt } else if (align == Alignment.ALIGN_RIGHT) { 1002c0ccf0c47c00942a9d0f2670600a8f2d4d7adb5bDoug Felt return mWidth; 10039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } else if (align == Alignment.ALIGN_OPPOSITE) { 10049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (dir == DIR_RIGHT_TO_LEFT) 10059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return getLineMax(line); 10069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project else 10079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return mWidth; 10089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } else { /* align == Alignment.ALIGN_CENTER */ 10099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int left = getParagraphLeft(line); 10109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int right = getParagraphRight(line); 10119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int max = ((int) getLineMax(line)) & ~1; 10129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 10139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return right - ((right - left) - max) / 2; 10149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 10159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 10169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 10179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 10180c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt * Gets the unsigned horizontal extent of the specified line, including 1019c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt * leading margin indent, but excluding trailing whitespace. 10209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 10219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public float getLineMax(int line) { 1022c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt float margin = getParagraphLeadingMargin(line); 1023c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt float signedExtent = getLineExtent(line, false); 1024c14b3ada5cccfdd872ccf72d928fb0088f3ce00dAnish Athalye return margin + (signedExtent >= 0 ? signedExtent : -signedExtent); 10259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 10269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 10279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 1028c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt * Gets the unsigned horizontal extent of the specified line, including 1029c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt * leading margin indent and trailing whitespace. 10309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 10319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public float getLineWidth(int line) { 1032c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt float margin = getParagraphLeadingMargin(line); 1033c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt float signedExtent = getLineExtent(line, true); 1034c14b3ada5cccfdd872ccf72d928fb0088f3ce00dAnish Athalye return margin + (signedExtent >= 0 ? signedExtent : -signedExtent); 10359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 10369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1037c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt /** 1038c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt * Like {@link #getLineExtent(int,TabStops,boolean)} but determines the 1039c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt * tab stops instead of using the ones passed in. 1040c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt * @param line the index of the line 1041c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt * @param full whether to include trailing whitespace 1042c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt * @return the extent of the line 1043c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt */ 1044c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt private float getLineExtent(int line, boolean full) { 10459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int start = getLineStart(line); 1046e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt int end = full ? getLineEnd(line) : getLineVisibleEnd(line); 1047c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt 1048112d9c7f116bec0a52badde81bd778e59e88cb63Roozbeh Pournader boolean hasTabs = getLineContainsTab(line); 1049c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt TabStops tabStops = null; 1050112d9c7f116bec0a52badde81bd778e59e88cb63Roozbeh Pournader if (hasTabs && mText instanceof Spanned) { 1051c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt // Just checking this line should be good enough, tabs should be 1052c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt // consistent across all lines in a paragraph. 105374d31ef2b2c42b54fa1f7cf94ea955ea67ab69a0Eric Fischer TabStopSpan[] tabs = getParagraphSpans((Spanned) mText, start, end, TabStopSpan.class); 1054c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt if (tabs.length > 0) { 1055c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt tabStops = new TabStops(TAB_INCREMENT, tabs); // XXX should reuse 1056c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt } 1057c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt } 1058c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt Directions directions = getLineDirections(line); 10598059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio // Returned directions can actually be null 10608059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio if (directions == null) { 10618059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio return 0f; 10628059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio } 1063c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt int dir = getParagraphDirection(line); 1064c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt 1065c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt TextLine tl = TextLine.obtain(); 1066112d9c7f116bec0a52badde81bd778e59e88cb63Roozbeh Pournader tl.set(mPaint, mText, start, end, dir, directions, hasTabs, tabStops); 1067c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt float width = tl.metrics(null); 1068c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt TextLine.recycle(tl); 1069c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt return width; 1070c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt } 1071c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt 1072c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt /** 1073c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt * Returns the signed horizontal extent of the specified line, excluding 1074c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt * leading margin. If full is false, excludes trailing whitespace. 1075c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt * @param line the index of the line 1076c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt * @param tabStops the tab stops, can be null if we know they're not used. 1077c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt * @param full whether to include trailing whitespace 1078c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt * @return the extent of the text on this line 1079c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt */ 1080c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt private float getLineExtent(int line, TabStops tabStops, boolean full) { 1081c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt int start = getLineStart(line); 1082c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt int end = full ? getLineEnd(line) : getLineVisibleEnd(line); 1083112d9c7f116bec0a52badde81bd778e59e88cb63Roozbeh Pournader boolean hasTabs = getLineContainsTab(line); 1084e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt Directions directions = getLineDirections(line); 1085c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt int dir = getParagraphDirection(line); 10869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1087e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt TextLine tl = TextLine.obtain(); 1088112d9c7f116bec0a52badde81bd778e59e88cb63Roozbeh Pournader tl.set(mPaint, mText, start, end, dir, directions, hasTabs, tabStops); 1089e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt float width = tl.metrics(null); 1090e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt TextLine.recycle(tl); 1091e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt return width; 10929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 10939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 10949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 10959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Get the line number corresponding to the specified vertical position. 10969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * If you ask for a position above 0, you get 0; if you ask for a position 10979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * below the bottom of the text, you get the last line. 10989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 10999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // FIXME: It may be faster to do a linear search for layouts without many lines. 11009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public int getLineForVertical(int vertical) { 11019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int high = getLineCount(), low = -1, guess; 11029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 11039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project while (high - low > 1) { 11049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project guess = (high + low) / 2; 11059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 11069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (getLineTop(guess) > vertical) 11079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project high = guess; 11089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project else 11099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project low = guess; 11109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 11119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 11129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (low < 0) 11139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return 0; 11149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project else 11159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return low; 11169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 11179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 11189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 11199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Get the line number on which the specified text offset appears. 11209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * If you ask for a position before 0, you get 0; if you ask for a position 11219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * beyond the end of the text, you get the last line. 11229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 11239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public int getLineForOffset(int offset) { 11249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int high = getLineCount(), low = -1, guess; 11259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 11269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project while (high - low > 1) { 11279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project guess = (high + low) / 2; 11289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 11299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (getLineStart(guess) > offset) 11309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project high = guess; 11319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project else 11329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project low = guess; 11339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 11349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 11359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (low < 0) 11369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return 0; 11379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project else 11389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return low; 11399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 11409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 11419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 11429f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt * Get the character offset on the specified line whose position is 11439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * closest to the specified horizontal position. 11449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 11459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public int getOffsetForHorizontal(int line, float horiz) { 1146f0bb87b7c40efeeaee58d4c7b767961c9800463eKeisuke Kuroyanagi return getOffsetForHorizontal(line, horiz, true); 1147f0bb87b7c40efeeaee58d4c7b767961c9800463eKeisuke Kuroyanagi } 1148f0bb87b7c40efeeaee58d4c7b767961c9800463eKeisuke Kuroyanagi 1149f0bb87b7c40efeeaee58d4c7b767961c9800463eKeisuke Kuroyanagi /** 1150f0bb87b7c40efeeaee58d4c7b767961c9800463eKeisuke Kuroyanagi * Get the character offset on the specified line whose position is 1151f0bb87b7c40efeeaee58d4c7b767961c9800463eKeisuke Kuroyanagi * closest to the specified horizontal position. 1152f0bb87b7c40efeeaee58d4c7b767961c9800463eKeisuke Kuroyanagi * 1153f0bb87b7c40efeeaee58d4c7b767961c9800463eKeisuke Kuroyanagi * @param line the line used to find the closest offset 1154f0bb87b7c40efeeaee58d4c7b767961c9800463eKeisuke Kuroyanagi * @param horiz the horizontal position used to find the closest offset 1155f0bb87b7c40efeeaee58d4c7b767961c9800463eKeisuke Kuroyanagi * @param primary whether to use the primary position or secondary position to find the offset 1156f0bb87b7c40efeeaee58d4c7b767961c9800463eKeisuke Kuroyanagi * 1157f0bb87b7c40efeeaee58d4c7b767961c9800463eKeisuke Kuroyanagi * @hide 1158f0bb87b7c40efeeaee58d4c7b767961c9800463eKeisuke Kuroyanagi */ 1159f0bb87b7c40efeeaee58d4c7b767961c9800463eKeisuke Kuroyanagi public int getOffsetForHorizontal(int line, float horiz, boolean primary) { 1160edb27f14ec03d9f4e1dd3959e81f58d4f1389971Raph Levien // TODO: use Paint.getOffsetForAdvance to avoid binary search 116100ad16d1cd24b788262ab4f62935e720a392da6dKeisuke Kuroyanagi final int lineEndOffset = getLineEnd(line); 11625bff01d72f2a49add74dd36c8d1ed5038611f20bKeisuke Kuroyanagi final int lineStartOffset = getLineStart(line); 11635bff01d72f2a49add74dd36c8d1ed5038611f20bKeisuke Kuroyanagi 11645bff01d72f2a49add74dd36c8d1ed5038611f20bKeisuke Kuroyanagi Directions dirs = getLineDirections(line); 11655bff01d72f2a49add74dd36c8d1ed5038611f20bKeisuke Kuroyanagi 11665bff01d72f2a49add74dd36c8d1ed5038611f20bKeisuke Kuroyanagi TextLine tl = TextLine.obtain(); 11675bff01d72f2a49add74dd36c8d1ed5038611f20bKeisuke Kuroyanagi // XXX: we don't care about tabs as we just use TextLine#getOffsetToLeftRightOf here. 11685bff01d72f2a49add74dd36c8d1ed5038611f20bKeisuke Kuroyanagi tl.set(mPaint, mText, lineStartOffset, lineEndOffset, getParagraphDirection(line), dirs, 11695bff01d72f2a49add74dd36c8d1ed5038611f20bKeisuke Kuroyanagi false, null); 11705bff01d72f2a49add74dd36c8d1ed5038611f20bKeisuke Kuroyanagi 117100ad16d1cd24b788262ab4f62935e720a392da6dKeisuke Kuroyanagi final int max; 117200ad16d1cd24b788262ab4f62935e720a392da6dKeisuke Kuroyanagi if (line == getLineCount() - 1) { 117300ad16d1cd24b788262ab4f62935e720a392da6dKeisuke Kuroyanagi max = lineEndOffset; 117400ad16d1cd24b788262ab4f62935e720a392da6dKeisuke Kuroyanagi } else { 11755bff01d72f2a49add74dd36c8d1ed5038611f20bKeisuke Kuroyanagi max = tl.getOffsetToLeftRightOf(lineEndOffset - lineStartOffset, 11765bff01d72f2a49add74dd36c8d1ed5038611f20bKeisuke Kuroyanagi !isRtlCharAt(lineEndOffset - 1)) + lineStartOffset; 117700ad16d1cd24b788262ab4f62935e720a392da6dKeisuke Kuroyanagi } 11785bff01d72f2a49add74dd36c8d1ed5038611f20bKeisuke Kuroyanagi int best = lineStartOffset; 1179f0bb87b7c40efeeaee58d4c7b767961c9800463eKeisuke Kuroyanagi float bestdist = Math.abs(getHorizontal(best, primary) - horiz); 11809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 11819f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt for (int i = 0; i < dirs.mDirections.length; i += 2) { 11825bff01d72f2a49add74dd36c8d1ed5038611f20bKeisuke Kuroyanagi int here = lineStartOffset + dirs.mDirections[i]; 11839f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt int there = here + (dirs.mDirections[i+1] & RUN_LENGTH_MASK); 11845bff01d72f2a49add74dd36c8d1ed5038611f20bKeisuke Kuroyanagi boolean isRtl = (dirs.mDirections[i+1] & RUN_RTL_FLAG) != 0; 11855bff01d72f2a49add74dd36c8d1ed5038611f20bKeisuke Kuroyanagi int swap = isRtl ? -1 : 1; 11869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 11879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (there > max) 11889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project there = max; 11899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int high = there - 1 + 1, low = here + 1 - 1, guess; 11909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 11919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project while (high - low > 1) { 11929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project guess = (high + low) / 2; 11939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int adguess = getOffsetAtStartOf(guess); 11949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1195f0bb87b7c40efeeaee58d4c7b767961c9800463eKeisuke Kuroyanagi if (getHorizontal(adguess, primary) * swap >= horiz * swap) 11969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project high = guess; 11979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project else 11989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project low = guess; 11999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 12009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 12019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (low < here + 1) 12029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project low = here + 1; 12039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 12049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (low < there) { 12055bff01d72f2a49add74dd36c8d1ed5038611f20bKeisuke Kuroyanagi int aft = tl.getOffsetToLeftRightOf(low - lineStartOffset, isRtl) + lineStartOffset; 12065bff01d72f2a49add74dd36c8d1ed5038611f20bKeisuke Kuroyanagi low = tl.getOffsetToLeftRightOf(aft - lineStartOffset, !isRtl) + lineStartOffset; 12075bff01d72f2a49add74dd36c8d1ed5038611f20bKeisuke Kuroyanagi if (low >= here && low < there) { 1208f0bb87b7c40efeeaee58d4c7b767961c9800463eKeisuke Kuroyanagi float dist = Math.abs(getHorizontal(low, primary) - horiz); 12095bff01d72f2a49add74dd36c8d1ed5038611f20bKeisuke Kuroyanagi if (aft < there) { 1210f0bb87b7c40efeeaee58d4c7b767961c9800463eKeisuke Kuroyanagi float other = Math.abs(getHorizontal(aft, primary) - horiz); 12115bff01d72f2a49add74dd36c8d1ed5038611f20bKeisuke Kuroyanagi 12125bff01d72f2a49add74dd36c8d1ed5038611f20bKeisuke Kuroyanagi if (other < dist) { 12135bff01d72f2a49add74dd36c8d1ed5038611f20bKeisuke Kuroyanagi dist = other; 12145bff01d72f2a49add74dd36c8d1ed5038611f20bKeisuke Kuroyanagi low = aft; 12155bff01d72f2a49add74dd36c8d1ed5038611f20bKeisuke Kuroyanagi } 12169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 12179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 12185bff01d72f2a49add74dd36c8d1ed5038611f20bKeisuke Kuroyanagi if (dist < bestdist) { 12195bff01d72f2a49add74dd36c8d1ed5038611f20bKeisuke Kuroyanagi bestdist = dist; 12205bff01d72f2a49add74dd36c8d1ed5038611f20bKeisuke Kuroyanagi best = low; 12215bff01d72f2a49add74dd36c8d1ed5038611f20bKeisuke Kuroyanagi } 12229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 12239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 12249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1225f0bb87b7c40efeeaee58d4c7b767961c9800463eKeisuke Kuroyanagi float dist = Math.abs(getHorizontal(here, primary) - horiz); 12269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 12279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (dist < bestdist) { 12289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project bestdist = dist; 12299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project best = here; 12309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 12319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 12329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1233f0bb87b7c40efeeaee58d4c7b767961c9800463eKeisuke Kuroyanagi float dist = Math.abs(getHorizontal(max, primary) - horiz); 12349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1235373b7a8d4e9dce4f71539d4dbcf627fd3e1a39daRaph Levien if (dist <= bestdist) { 12369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project bestdist = dist; 12379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project best = max; 12389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 12399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 12405bff01d72f2a49add74dd36c8d1ed5038611f20bKeisuke Kuroyanagi TextLine.recycle(tl); 12419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return best; 12429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 12439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 12449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 12459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Return the text offset after the last character on the specified line. 12469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 12479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public final int getLineEnd(int line) { 12489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return getLineStart(line + 1); 12499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 12509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 12519f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt /** 12529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Return the text offset after the last visible character (so whitespace 12539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * is not counted) on the specified line. 12549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 12559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public int getLineVisibleEnd(int line) { 12569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return getLineVisibleEnd(line, getLineStart(line), getLineStart(line+1)); 12579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 12589f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt 12599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private int getLineVisibleEnd(int line, int start, int end) { 12609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project CharSequence text = mText; 12619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project char ch; 12629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (line == getLineCount() - 1) { 12639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return end; 12649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 12659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 12669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project for (; end > start; end--) { 12679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project ch = text.charAt(end - 1); 12689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 12699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (ch == '\n') { 12709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return end - 1; 12719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 12729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1273c94f742f7e07a3b86c8f603836c19638472b3e83Raph Levien // Note: keep this in sync with Minikin LineBreaker::isLineEndSpace() 1274c94f742f7e07a3b86c8f603836c19638472b3e83Raph Levien if (!(ch == ' ' || ch == '\t' || ch == 0x1680 || 1275c94f742f7e07a3b86c8f603836c19638472b3e83Raph Levien (0x2000 <= ch && ch <= 0x200A && ch != 0x2007) || 1276c94f742f7e07a3b86c8f603836c19638472b3e83Raph Levien ch == 0x205F || ch == 0x3000)) { 12779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project break; 12789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 12799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 12809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 12819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 12829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return end; 12839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 12849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 12859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 12869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Return the vertical position of the bottom of the specified line. 12879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 12889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public final int getLineBottom(int line) { 12899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return getLineTop(line + 1); 12909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 12919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 12929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 12939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Return the vertical position of the baseline of the specified line. 12949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 12959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public final int getLineBaseline(int line) { 12969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // getLineTop(line+1) == getLineTop(line) 12979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return getLineTop(line+1) - getLineDescent(line); 12989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 12999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 13009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 13019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Get the ascent of the text on the specified line. 13029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * The return value is negative to match the Paint.ascent() convention. 13039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 13049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public final int getLineAscent(int line) { 13059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // getLineTop(line+1) - getLineDescent(line) == getLineBaseLine(line) 13069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return getLineTop(line) - (getLineTop(line+1) - getLineDescent(line)); 13079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 13089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 13099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public int getOffsetToLeftOf(int offset) { 13109f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt return getOffsetToLeftRightOf(offset, true); 13119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 13129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 13139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public int getOffsetToRightOf(int offset) { 13149f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt return getOffsetToLeftRightOf(offset, false); 13159f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt } 13169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 13179f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt private int getOffsetToLeftRightOf(int caret, boolean toLeft) { 13189f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt int line = getLineForOffset(caret); 13199f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt int lineStart = getLineStart(line); 13209f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt int lineEnd = getLineEnd(line); 1321e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt int lineDir = getParagraphDirection(line); 1322e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 1323162bf0f1b9fd5d78ffa801917994f6222c6efd85Gilles Debunne boolean lineChanged = false; 1324e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt boolean advance = toLeft == (lineDir == DIR_RIGHT_TO_LEFT); 1325162bf0f1b9fd5d78ffa801917994f6222c6efd85Gilles Debunne // if walking off line, look at the line we're headed to 1326162bf0f1b9fd5d78ffa801917994f6222c6efd85Gilles Debunne if (advance) { 1327162bf0f1b9fd5d78ffa801917994f6222c6efd85Gilles Debunne if (caret == lineEnd) { 1328162bf0f1b9fd5d78ffa801917994f6222c6efd85Gilles Debunne if (line < getLineCount() - 1) { 1329162bf0f1b9fd5d78ffa801917994f6222c6efd85Gilles Debunne lineChanged = true; 1330162bf0f1b9fd5d78ffa801917994f6222c6efd85Gilles Debunne ++line; 1331162bf0f1b9fd5d78ffa801917994f6222c6efd85Gilles Debunne } else { 1332162bf0f1b9fd5d78ffa801917994f6222c6efd85Gilles Debunne return caret; // at very end, don't move 1333162bf0f1b9fd5d78ffa801917994f6222c6efd85Gilles Debunne } 1334162bf0f1b9fd5d78ffa801917994f6222c6efd85Gilles Debunne } 1335162bf0f1b9fd5d78ffa801917994f6222c6efd85Gilles Debunne } else { 1336e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (caret == lineStart) { 1337e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (line > 0) { 1338162bf0f1b9fd5d78ffa801917994f6222c6efd85Gilles Debunne lineChanged = true; 1339e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt --line; 1340e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } else { 1341e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt return caret; // at very start, don't move 1342e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 13439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1344162bf0f1b9fd5d78ffa801917994f6222c6efd85Gilles Debunne } 13459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1346162bf0f1b9fd5d78ffa801917994f6222c6efd85Gilles Debunne if (lineChanged) { 1347e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt lineStart = getLineStart(line); 1348e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt lineEnd = getLineEnd(line); 1349e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt int newDir = getParagraphDirection(line); 1350e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (newDir != lineDir) { 1351e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // unusual case. we want to walk onto the line, but it runs 1352e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // in a different direction than this one, so we fake movement 1353e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // in the opposite direction. 1354e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt toLeft = !toLeft; 1355e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt lineDir = newDir; 13569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 13579f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt } 13589f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt 1359e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt Directions directions = getLineDirections(line); 13609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1361e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt TextLine tl = TextLine.obtain(); 1362e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // XXX: we don't care about tabs 1363e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt tl.set(mPaint, mText, lineStart, lineEnd, lineDir, directions, false, null); 1364e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt caret = lineStart + tl.getOffsetToLeftRightOf(caret - lineStart, toLeft); 1365e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt tl = TextLine.recycle(tl); 1366e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt return caret; 13679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 13689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 13699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private int getOffsetAtStartOf(int offset) { 13709f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt // XXX this probably should skip local reorderings and 13719f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt // zero-width characters, look at callers 13729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (offset == 0) 13739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return 0; 13749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 13759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project CharSequence text = mText; 13769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project char c = text.charAt(offset); 13779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 13789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (c >= '\uDC00' && c <= '\uDFFF') { 13799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project char c1 = text.charAt(offset - 1); 13809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 13819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (c1 >= '\uD800' && c1 <= '\uDBFF') 13829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project offset -= 1; 13839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 13849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 13859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (mSpannedText) { 13869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project ReplacementSpan[] spans = ((Spanned) text).getSpans(offset, offset, 13879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project ReplacementSpan.class); 13889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 13899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project for (int i = 0; i < spans.length; i++) { 13909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int start = ((Spanned) text).getSpanStart(spans[i]); 13919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int end = ((Spanned) text).getSpanEnd(spans[i]); 13929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 13939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (start < offset && end > offset) 13949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project offset = start; 13959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 13969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 13979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 13989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return offset; 13999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 14009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 14019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 1402afe8e9b6d033cb854afa3024d8198a32896a804aRaph Levien * Determine whether we should clamp cursor position. Currently it's 1403afe8e9b6d033cb854afa3024d8198a32896a804aRaph Levien * only robust for left-aligned displays. 1404afe8e9b6d033cb854afa3024d8198a32896a804aRaph Levien * @hide 1405afe8e9b6d033cb854afa3024d8198a32896a804aRaph Levien */ 1406afe8e9b6d033cb854afa3024d8198a32896a804aRaph Levien public boolean shouldClampCursor(int line) { 1407afe8e9b6d033cb854afa3024d8198a32896a804aRaph Levien // Only clamp cursor position in left-aligned displays. 1408afe8e9b6d033cb854afa3024d8198a32896a804aRaph Levien switch (getParagraphAlignment(line)) { 1409afe8e9b6d033cb854afa3024d8198a32896a804aRaph Levien case ALIGN_LEFT: 1410afe8e9b6d033cb854afa3024d8198a32896a804aRaph Levien return true; 1411afe8e9b6d033cb854afa3024d8198a32896a804aRaph Levien case ALIGN_NORMAL: 1412afe8e9b6d033cb854afa3024d8198a32896a804aRaph Levien return getParagraphDirection(line) > 0; 1413afe8e9b6d033cb854afa3024d8198a32896a804aRaph Levien default: 1414afe8e9b6d033cb854afa3024d8198a32896a804aRaph Levien return false; 1415afe8e9b6d033cb854afa3024d8198a32896a804aRaph Levien } 1416afe8e9b6d033cb854afa3024d8198a32896a804aRaph Levien 1417afe8e9b6d033cb854afa3024d8198a32896a804aRaph Levien } 1418afe8e9b6d033cb854afa3024d8198a32896a804aRaph Levien /** 14199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Fills in the specified Path with a representation of a cursor 14209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * at the specified offset. This will often be a vertical line 14214e0c5e55e171532760d5f51e0165563827129d4eDoug Felt * but can be multiple discontinuous lines in text with multiple 14229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * directionalities. 14239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 14249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public void getCursorPath(int point, Path dest, 14259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project CharSequence editingBuffer) { 14269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project dest.reset(); 14279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 14289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int line = getLineForOffset(point); 14299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int top = getLineTop(line); 14309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int bottom = getLineTop(line+1); 14319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1432afe8e9b6d033cb854afa3024d8198a32896a804aRaph Levien boolean clamped = shouldClampCursor(line); 1433afe8e9b6d033cb854afa3024d8198a32896a804aRaph Levien float h1 = getPrimaryHorizontal(point, clamped) - 0.5f; 1434afe8e9b6d033cb854afa3024d8198a32896a804aRaph Levien float h2 = isLevelBoundary(point) ? getSecondaryHorizontal(point, clamped) - 0.5f : h1; 14359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1436497a92cc5ba2176b8a8484b0a7da040eac0e887bJeff Brown int caps = TextKeyListener.getMetaState(editingBuffer, TextKeyListener.META_SHIFT_ON) | 1437497a92cc5ba2176b8a8484b0a7da040eac0e887bJeff Brown TextKeyListener.getMetaState(editingBuffer, TextKeyListener.META_SELECTING); 1438497a92cc5ba2176b8a8484b0a7da040eac0e887bJeff Brown int fn = TextKeyListener.getMetaState(editingBuffer, TextKeyListener.META_ALT_ON); 14399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int dist = 0; 14409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 14419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (caps != 0 || fn != 0) { 14429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project dist = (bottom - top) >> 2; 14439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 14449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (fn != 0) 14459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project top += dist; 14469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (caps != 0) 14479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project bottom -= dist; 14489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 14499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 14509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (h1 < 0.5f) 14519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project h1 = 0.5f; 14529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (h2 < 0.5f) 14539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project h2 = 0.5f; 14549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 14552fb503f5102dd32a8ec391b26911528852703b90Jozef BABJAK if (Float.compare(h1, h2) == 0) { 14569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project dest.moveTo(h1, top); 14579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project dest.lineTo(h1, bottom); 14589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } else { 14599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project dest.moveTo(h1, top); 14609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project dest.lineTo(h1, (top + bottom) >> 1); 14619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 14629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project dest.moveTo(h2, (top + bottom) >> 1); 14639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project dest.lineTo(h2, bottom); 14649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 14659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 14669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (caps == 2) { 14679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project dest.moveTo(h2, bottom); 14689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project dest.lineTo(h2 - dist, bottom + dist); 14699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project dest.lineTo(h2, bottom); 14709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project dest.lineTo(h2 + dist, bottom + dist); 14719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } else if (caps == 1) { 14729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project dest.moveTo(h2, bottom); 14739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project dest.lineTo(h2 - dist, bottom + dist); 14749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 14759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project dest.moveTo(h2 - dist, bottom + dist - 0.5f); 14769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project dest.lineTo(h2 + dist, bottom + dist - 0.5f); 14779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 14789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project dest.moveTo(h2 + dist, bottom + dist); 14799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project dest.lineTo(h2, bottom); 14809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 14819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 14829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (fn == 2) { 14839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project dest.moveTo(h1, top); 14849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project dest.lineTo(h1 - dist, top - dist); 14859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project dest.lineTo(h1, top); 14869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project dest.lineTo(h1 + dist, top - dist); 14879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } else if (fn == 1) { 14889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project dest.moveTo(h1, top); 14899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project dest.lineTo(h1 - dist, top - dist); 14909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 14919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project dest.moveTo(h1 - dist, top - dist + 0.5f); 14929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project dest.lineTo(h1 + dist, top - dist + 0.5f); 14939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 14949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project dest.moveTo(h1 + dist, top - dist); 14959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project dest.lineTo(h1, top); 14969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 14979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 14989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 14999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private void addSelection(int line, int start, int end, 15009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int top, int bottom, Path dest) { 15019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int linestart = getLineStart(line); 15029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int lineend = getLineEnd(line); 15039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project Directions dirs = getLineDirections(line); 15049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 15059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (lineend > linestart && mText.charAt(lineend - 1) == '\n') 15069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project lineend--; 15079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 15089f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt for (int i = 0; i < dirs.mDirections.length; i += 2) { 15099f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt int here = linestart + dirs.mDirections[i]; 15109f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt int there = here + (dirs.mDirections[i+1] & RUN_LENGTH_MASK); 15119f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt 15129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (there > lineend) 15139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project there = lineend; 15149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 15159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (start <= there && end >= here) { 15169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int st = Math.max(start, here); 15179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int en = Math.min(end, there); 15189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 15199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (st != en) { 1520afe8e9b6d033cb854afa3024d8198a32896a804aRaph Levien float h1 = getHorizontal(st, false, line, false /* not clamped */); 1521afe8e9b6d033cb854afa3024d8198a32896a804aRaph Levien float h2 = getHorizontal(en, true, line, false /* not clamped */); 15229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 15233716601573f5a562f98721130e25002ad88eb164Fabrice Di Meglio float left = Math.min(h1, h2); 15243716601573f5a562f98721130e25002ad88eb164Fabrice Di Meglio float right = Math.max(h1, h2); 15253716601573f5a562f98721130e25002ad88eb164Fabrice Di Meglio 15263716601573f5a562f98721130e25002ad88eb164Fabrice Di Meglio dest.addRect(left, top, right, bottom, Path.Direction.CW); 15279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 15289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 15299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 15309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 15319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 15329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 15339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Fills in the specified Path with a representation of a highlight 15349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * between the specified offsets. This will often be a rectangle 15359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * or a potentially discontinuous set of rectangles. If the start 15369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * and end are the same, the returned path is empty. 15379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 15389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public void getSelectionPath(int start, int end, Path dest) { 15399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project dest.reset(); 15409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 15419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (start == end) 15429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return; 15439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 15449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (end < start) { 15459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int temp = end; 15469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project end = start; 15479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project start = temp; 15489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 15499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 15509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int startline = getLineForOffset(start); 15519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int endline = getLineForOffset(end); 15529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 15539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int top = getLineTop(startline); 15549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int bottom = getLineBottom(endline); 15559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 15569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (startline == endline) { 15579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project addSelection(startline, start, end, top, bottom, dest); 15589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } else { 15599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project final float width = mWidth; 15609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 15619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project addSelection(startline, start, getLineEnd(startline), 15629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project top, getLineBottom(startline), dest); 15639f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt 15649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (getParagraphDirection(startline) == DIR_RIGHT_TO_LEFT) 15659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project dest.addRect(getLineLeft(startline), top, 15669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 0, getLineBottom(startline), Path.Direction.CW); 15679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project else 15689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project dest.addRect(getLineRight(startline), top, 15699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project width, getLineBottom(startline), Path.Direction.CW); 15709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 15719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project for (int i = startline + 1; i < endline; i++) { 15729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project top = getLineTop(i); 15739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project bottom = getLineBottom(i); 15749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project dest.addRect(0, top, width, bottom, Path.Direction.CW); 15759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 15769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 15779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project top = getLineTop(endline); 15789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project bottom = getLineBottom(endline); 15799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 15809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project addSelection(endline, getLineStart(endline), end, 15819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project top, bottom, dest); 15829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 15839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (getParagraphDirection(endline) == DIR_RIGHT_TO_LEFT) 15849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project dest.addRect(width, top, getLineRight(endline), bottom, Path.Direction.CW); 15859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project else 15869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project dest.addRect(0, top, getLineLeft(endline), bottom, Path.Direction.CW); 15879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 15889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 15899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 15909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 15919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Get the alignment of the specified paragraph, taking into account 15929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * markup attached to it. 15939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 15949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public final Alignment getParagraphAlignment(int line) { 15959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project Alignment align = mAlignment; 15969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 15979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (mSpannedText) { 15989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project Spanned sp = (Spanned) mText; 159974d31ef2b2c42b54fa1f7cf94ea955ea67ab69a0Eric Fischer AlignmentSpan[] spans = getParagraphSpans(sp, getLineStart(line), 16009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project getLineEnd(line), 16019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project AlignmentSpan.class); 16029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 16039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int spanLength = spans.length; 16049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (spanLength > 0) { 16059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project align = spans[spanLength-1].getAlignment(); 16069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 16079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 16089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 16099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return align; 16109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 16119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 16129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 16139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Get the left edge of the specified paragraph, inset by left margins. 16149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 16159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public final int getParagraphLeft(int line) { 16169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int left = 0; 1617c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt int dir = getParagraphDirection(line); 1618c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt if (dir == DIR_RIGHT_TO_LEFT || !mSpannedText) { 1619c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt return left; // leading margin has no impact, or no styles 16209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1621c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt return getParagraphLeadingMargin(line); 16229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 16239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 16249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 16259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Get the right edge of the specified paragraph, inset by right margins. 16269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 16279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public final int getParagraphRight(int line) { 16289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int right = mWidth; 1629c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt int dir = getParagraphDirection(line); 1630c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt if (dir == DIR_LEFT_TO_RIGHT || !mSpannedText) { 1631c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt return right; // leading margin has no impact, or no styles 1632c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt } 1633c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt return right - getParagraphLeadingMargin(line); 1634c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt } 16359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1636c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt /** 1637c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt * Returns the effective leading margin (unsigned) for this line, 1638c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt * taking into account LeadingMarginSpan and LeadingMarginSpan2. 1639c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt * @param line the line index 1640c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt * @return the leading margin of this line 1641c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt */ 1642c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt private int getParagraphLeadingMargin(int line) { 1643c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt if (!mSpannedText) { 1644c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt return 0; 1645c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt } 1646c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt Spanned spanned = (Spanned) mText; 16470c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt 1648c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt int lineStart = getLineStart(line); 1649c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt int lineEnd = getLineEnd(line); 16500c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt int spanEnd = spanned.nextSpanTransition(lineStart, lineEnd, 1651c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt LeadingMarginSpan.class); 165274d31ef2b2c42b54fa1f7cf94ea955ea67ab69a0Eric Fischer LeadingMarginSpan[] spans = getParagraphSpans(spanned, lineStart, spanEnd, 1653c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt LeadingMarginSpan.class); 1654c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt if (spans.length == 0) { 1655c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt return 0; // no leading margin span; 1656c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt } 16570c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt 1658c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt int margin = 0; 16590c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt 16600c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt boolean isFirstParaLine = lineStart == 0 || 1661c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt spanned.charAt(lineStart - 1) == '\n'; 16620c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt 1663ab08c6d38ab2e575f809ca8ce4c7f095e49d258cAnish Athalye boolean useFirstLineMargin = isFirstParaLine; 1664c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt for (int i = 0; i < spans.length; i++) { 1665ab08c6d38ab2e575f809ca8ce4c7f095e49d258cAnish Athalye if (spans[i] instanceof LeadingMarginSpan2) { 1666ab08c6d38ab2e575f809ca8ce4c7f095e49d258cAnish Athalye int spStart = spanned.getSpanStart(spans[i]); 1667c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt int spanLine = getLineForOffset(spStart); 1668ab08c6d38ab2e575f809ca8ce4c7f095e49d258cAnish Athalye int count = ((LeadingMarginSpan2) spans[i]).getLeadingMarginLineCount(); 1669ab08c6d38ab2e575f809ca8ce4c7f095e49d258cAnish Athalye // if there is more than one LeadingMarginSpan2, use the count that is greatest 1670ab08c6d38ab2e575f809ca8ce4c7f095e49d258cAnish Athalye useFirstLineMargin |= line < spanLine + count; 16719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1672ab08c6d38ab2e575f809ca8ce4c7f095e49d258cAnish Athalye } 1673ab08c6d38ab2e575f809ca8ce4c7f095e49d258cAnish Athalye for (int i = 0; i < spans.length; i++) { 1674ab08c6d38ab2e575f809ca8ce4c7f095e49d258cAnish Athalye LeadingMarginSpan span = spans[i]; 1675c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt margin += span.getLeadingMargin(useFirstLineMargin); 16769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 16779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1678c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt return margin; 16799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 16809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1681e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt /* package */ 16826c488de023a4797069673dc619c1a4096079ea9eGilles Debunne static float measurePara(TextPaint paint, CharSequence text, int start, int end) { 1683e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 1684e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt MeasuredText mt = MeasuredText.obtain(); 1685e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt TextLine tl = TextLine.obtain(); 1686e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt try { 168770616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien mt.setPara(text, start, end, TextDirectionHeuristics.LTR, null); 1688e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt Directions directions; 1689c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt int dir; 1690c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt if (mt.mEasy) { 1691e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt directions = DIRS_ALL_LEFT_TO_RIGHT; 1692c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt dir = Layout.DIR_LEFT_TO_RIGHT; 1693e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } else { 1694e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt directions = AndroidBidi.directions(mt.mDir, mt.mLevels, 1695e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 0, mt.mChars, 0, mt.mLen); 1696c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt dir = mt.mDir; 1697c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt } 1698c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt char[] chars = mt.mChars; 1699c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt int len = mt.mLen; 1700c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt boolean hasTabs = false; 1701c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt TabStops tabStops = null; 1702c14b3ada5cccfdd872ccf72d928fb0088f3ce00dAnish Athalye // leading margins should be taken into account when measuring a paragraph 1703c14b3ada5cccfdd872ccf72d928fb0088f3ce00dAnish Athalye int margin = 0; 1704c14b3ada5cccfdd872ccf72d928fb0088f3ce00dAnish Athalye if (text instanceof Spanned) { 1705c14b3ada5cccfdd872ccf72d928fb0088f3ce00dAnish Athalye Spanned spanned = (Spanned) text; 1706c14b3ada5cccfdd872ccf72d928fb0088f3ce00dAnish Athalye LeadingMarginSpan[] spans = getParagraphSpans(spanned, start, end, 1707c14b3ada5cccfdd872ccf72d928fb0088f3ce00dAnish Athalye LeadingMarginSpan.class); 1708c14b3ada5cccfdd872ccf72d928fb0088f3ce00dAnish Athalye for (LeadingMarginSpan lms : spans) { 1709c14b3ada5cccfdd872ccf72d928fb0088f3ce00dAnish Athalye margin += lms.getLeadingMargin(true); 1710c14b3ada5cccfdd872ccf72d928fb0088f3ce00dAnish Athalye } 1711c14b3ada5cccfdd872ccf72d928fb0088f3ce00dAnish Athalye } 1712c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt for (int i = 0; i < len; ++i) { 1713c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt if (chars[i] == '\t') { 1714c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt hasTabs = true; 1715c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt if (text instanceof Spanned) { 1716c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt Spanned spanned = (Spanned) text; 17170c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt int spanEnd = spanned.nextSpanTransition(start, end, 1718c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt TabStopSpan.class); 171974d31ef2b2c42b54fa1f7cf94ea955ea67ab69a0Eric Fischer TabStopSpan[] spans = getParagraphSpans(spanned, start, spanEnd, 1720c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt TabStopSpan.class); 1721c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt if (spans.length > 0) { 1722c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt tabStops = new TabStops(TAB_INCREMENT, spans); 1723c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt } 1724c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt } 1725c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt break; 1726c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt } 17279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1728c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt tl.set(paint, text, start, end, dir, directions, hasTabs, tabStops); 1729c14b3ada5cccfdd872ccf72d928fb0088f3ce00dAnish Athalye return margin + tl.metrics(null); 1730e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } finally { 1731e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt TextLine.recycle(tl); 1732e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt MeasuredText.recycle(mt); 17339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 17349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 17359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 173671b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt /** 1737c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt * @hide 1738c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt */ 1739c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt /* package */ static class TabStops { 1740c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt private int[] mStops; 1741c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt private int mNumStops; 1742c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt private int mIncrement; 17430c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt 1744c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt TabStops(int increment, Object[] spans) { 1745c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt reset(increment, spans); 1746c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt } 17470c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt 1748c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt void reset(int increment, Object[] spans) { 1749c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt this.mIncrement = increment; 1750c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt 1751c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt int ns = 0; 1752c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt if (spans != null) { 1753c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt int[] stops = this.mStops; 1754c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt for (Object o : spans) { 1755c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt if (o instanceof TabStopSpan) { 1756c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt if (stops == null) { 1757c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt stops = new int[10]; 1758c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt } else if (ns == stops.length) { 1759c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt int[] nstops = new int[ns * 2]; 1760c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt for (int i = 0; i < ns; ++i) { 1761c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt nstops[i] = stops[i]; 1762c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt } 1763c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt stops = nstops; 1764c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt } 1765c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt stops[ns++] = ((TabStopSpan) o).getTabStop(); 1766c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt } 1767c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt } 1768c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt if (ns > 1) { 1769c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt Arrays.sort(stops, 0, ns); 1770c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt } 1771c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt if (stops != this.mStops) { 1772c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt this.mStops = stops; 1773c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt } 1774c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt } 1775c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt this.mNumStops = ns; 1776c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt } 17770c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt 1778c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt float nextTab(float h) { 1779c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt int ns = this.mNumStops; 1780c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt if (ns > 0) { 1781c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt int[] stops = this.mStops; 1782c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt for (int i = 0; i < ns; ++i) { 1783c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt int stop = stops[i]; 1784c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt if (stop > h) { 1785c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt return stop; 1786c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt } 1787c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt } 1788c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt } 1789c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt return nextDefaultStop(h, mIncrement); 1790c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt } 1791c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt 1792c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt public static float nextDefaultStop(float h, int inc) { 1793c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt return ((int) ((h + inc) / inc)) * inc; 1794c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt } 1795c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt } 17960c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt 1797c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt /** 179871b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt * Returns the position of the next tab stop after h on the line. 179971b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt * 180071b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt * @param text the text 180171b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt * @param start start of the line 180271b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt * @param end limit of the line 180371b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt * @param h the current horizontal offset 180471b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt * @param tabs the tabs, can be null. If it is null, any tabs in effect 180571b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt * on the line will be used. If there are no tabs, a default offset 180671b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt * will be used to compute the tab stop. 180771b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt * @return the offset of the next tab stop. 180871b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt */ 18099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /* package */ static float nextTab(CharSequence text, int start, int end, 18109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project float h, Object[] tabs) { 18119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project float nh = Float.MAX_VALUE; 18129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project boolean alltabs = false; 18139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 18149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (text instanceof Spanned) { 18159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (tabs == null) { 181674d31ef2b2c42b54fa1f7cf94ea955ea67ab69a0Eric Fischer tabs = getParagraphSpans((Spanned) text, start, end, TabStopSpan.class); 18179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project alltabs = true; 18189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 18199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 18209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project for (int i = 0; i < tabs.length; i++) { 18219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (!alltabs) { 18229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (!(tabs[i] instanceof TabStopSpan)) 18239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project continue; 18249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 18259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 18269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int where = ((TabStopSpan) tabs[i]).getTabStop(); 18279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 18289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (where < nh && where > h) 18299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project nh = where; 18309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 18319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 18329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (nh != Float.MAX_VALUE) 18339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return nh; 18349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 18359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 18369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return ((int) ((h + TAB_INCREMENT) / TAB_INCREMENT)) * TAB_INCREMENT; 18379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 18389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 18399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project protected final boolean isSpanned() { 18409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return mSpannedText; 18419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 18429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 184374d31ef2b2c42b54fa1f7cf94ea955ea67ab69a0Eric Fischer /** 184474d31ef2b2c42b54fa1f7cf94ea955ea67ab69a0Eric Fischer * Returns the same as <code>text.getSpans()</code>, except where 184574d31ef2b2c42b54fa1f7cf94ea955ea67ab69a0Eric Fischer * <code>start</code> and <code>end</code> are the same and are not 184674d31ef2b2c42b54fa1f7cf94ea955ea67ab69a0Eric Fischer * at the very beginning of the text, in which case an empty array 184774d31ef2b2c42b54fa1f7cf94ea955ea67ab69a0Eric Fischer * is returned instead. 184874d31ef2b2c42b54fa1f7cf94ea955ea67ab69a0Eric Fischer * <p> 184974d31ef2b2c42b54fa1f7cf94ea955ea67ab69a0Eric Fischer * This is needed because of the special case that <code>getSpans()</code> 185074d31ef2b2c42b54fa1f7cf94ea955ea67ab69a0Eric Fischer * on an empty range returns the spans adjacent to that range, which is 185174d31ef2b2c42b54fa1f7cf94ea955ea67ab69a0Eric Fischer * primarily for the sake of <code>TextWatchers</code> so they will get 185274d31ef2b2c42b54fa1f7cf94ea955ea67ab69a0Eric Fischer * notifications when text goes from empty to non-empty. But it also 185374d31ef2b2c42b54fa1f7cf94ea955ea67ab69a0Eric Fischer * has the unfortunate side effect that if the text ends with an empty 185474d31ef2b2c42b54fa1f7cf94ea955ea67ab69a0Eric Fischer * paragraph, that paragraph accidentally picks up the styles of the 185574d31ef2b2c42b54fa1f7cf94ea955ea67ab69a0Eric Fischer * preceding paragraph (even though those styles will not be picked up 185674d31ef2b2c42b54fa1f7cf94ea955ea67ab69a0Eric Fischer * by new text that is inserted into the empty paragraph). 185774d31ef2b2c42b54fa1f7cf94ea955ea67ab69a0Eric Fischer * <p> 185874d31ef2b2c42b54fa1f7cf94ea955ea67ab69a0Eric Fischer * The reason it just checks whether <code>start</code> and <code>end</code> 185974d31ef2b2c42b54fa1f7cf94ea955ea67ab69a0Eric Fischer * is the same is that the only time a line can contain 0 characters 186074d31ef2b2c42b54fa1f7cf94ea955ea67ab69a0Eric Fischer * is if it is the final paragraph of the Layout; otherwise any line will 186174d31ef2b2c42b54fa1f7cf94ea955ea67ab69a0Eric Fischer * contain at least one printing or newline character. The reason for the 186274d31ef2b2c42b54fa1f7cf94ea955ea67ab69a0Eric Fischer * additional check if <code>start</code> is greater than 0 is that 186374d31ef2b2c42b54fa1f7cf94ea955ea67ab69a0Eric Fischer * if the empty paragraph is the entire content of the buffer, paragraph 186474d31ef2b2c42b54fa1f7cf94ea955ea67ab69a0Eric Fischer * styles that are already applied to the buffer will apply to text that 186574d31ef2b2c42b54fa1f7cf94ea955ea67ab69a0Eric Fischer * is inserted into it. 186674d31ef2b2c42b54fa1f7cf94ea955ea67ab69a0Eric Fischer */ 1867eca5b7385a641ae5d8c4aa2b7823a4e573737376Gilles Debunne /* package */static <T> T[] getParagraphSpans(Spanned text, int start, int end, Class<T> type) { 186874d31ef2b2c42b54fa1f7cf94ea955ea67ab69a0Eric Fischer if (start == end && start > 0) { 18696c488de023a4797069673dc619c1a4096079ea9eGilles Debunne return ArrayUtils.emptyArray(type); 187074d31ef2b2c42b54fa1f7cf94ea955ea67ab69a0Eric Fischer } 187174d31ef2b2c42b54fa1f7cf94ea955ea67ab69a0Eric Fischer 1872fa05ba0b0d39fae1d2cb3d98fbee0aef6a9fed88Siyamed Sinir if(text instanceof SpannableStringBuilder) { 1873fa05ba0b0d39fae1d2cb3d98fbee0aef6a9fed88Siyamed Sinir return ((SpannableStringBuilder) text).getSpans(start, end, type, false); 1874fa05ba0b0d39fae1d2cb3d98fbee0aef6a9fed88Siyamed Sinir } else { 1875fa05ba0b0d39fae1d2cb3d98fbee0aef6a9fed88Siyamed Sinir return text.getSpans(start, end, type); 1876fa05ba0b0d39fae1d2cb3d98fbee0aef6a9fed88Siyamed Sinir } 187774d31ef2b2c42b54fa1f7cf94ea955ea67ab69a0Eric Fischer } 187874d31ef2b2c42b54fa1f7cf94ea955ea67ab69a0Eric Fischer 18798d44fff7e62f77c3b3072a96712cc1389e63ca64Fabrice Di Meglio private char getEllipsisChar(TextUtils.TruncateAt method) { 18808d44fff7e62f77c3b3072a96712cc1389e63ca64Fabrice Di Meglio return (method == TextUtils.TruncateAt.END_SMALL) ? 1881d29bdb266d54b4551f42776bb790e80147a279d0Neil Fuller TextUtils.ELLIPSIS_TWO_DOTS[0] : 1882d29bdb266d54b4551f42776bb790e80147a279d0Neil Fuller TextUtils.ELLIPSIS_NORMAL[0]; 18838d44fff7e62f77c3b3072a96712cc1389e63ca64Fabrice Di Meglio } 18848d44fff7e62f77c3b3072a96712cc1389e63ca64Fabrice Di Meglio 18859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private void ellipsize(int start, int end, int line, 18868d44fff7e62f77c3b3072a96712cc1389e63ca64Fabrice Di Meglio char[] dest, int destoff, TextUtils.TruncateAt method) { 18879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int ellipsisCount = getEllipsisCount(line); 18889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 18899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (ellipsisCount == 0) { 18909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return; 18919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 18929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 18939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int ellipsisStart = getEllipsisStart(line); 18949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int linestart = getLineStart(line); 18959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 18969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project for (int i = ellipsisStart; i < ellipsisStart + ellipsisCount; i++) { 18979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project char c; 18989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 18999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (i == ellipsisStart) { 19008d44fff7e62f77c3b3072a96712cc1389e63ca64Fabrice Di Meglio c = getEllipsisChar(method); // ellipsis 19019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } else { 19029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project c = '\uFEFF'; // 0-width space 19039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 19049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 19059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int a = i + linestart; 19069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 19079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (a >= start && a < end) { 19089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project dest[destoff + a - start] = c; 19099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 19109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 19119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 19129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 19139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 19149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Stores information about bidirectional (left-to-right or right-to-left) 19159f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt * text within the layout of a line. 19169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 19179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public static class Directions { 19189f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt // Directions represents directional runs within a line of text. 19199f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt // Runs are pairs of ints listed in visual order, starting from the 19209f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt // leading margin. The first int of each pair is the offset from 19219f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt // the first character of the line to the start of the run. The 19229f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt // second int represents both the length and level of the run. 19239f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt // The length is in the lower bits, accessed by masking with 19249f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt // DIR_LENGTH_MASK. The level is in the higher bits, accessed 19259f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt // by shifting by DIR_LEVEL_SHIFT and masking by DIR_LEVEL_MASK. 19269f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt // To simply test for an RTL direction, test the bit using 19279f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt // DIR_RTL_FLAG, if set then the direction is rtl. 19289f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt 19299f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt /* package */ int[] mDirections; 19309f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt /* package */ Directions(int[] dirs) { 19319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mDirections = dirs; 19329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 19339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 19349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 19359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 19369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Return the offset of the first character to be ellipsized away, 19379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * relative to the start of the line. (So 0 if the beginning of the 19389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * line is ellipsized, not getLineStart().) 19399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 19409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public abstract int getEllipsisStart(int line); 1941e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 19429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 19439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Returns the number of characters to be ellipsized away, or 0 if 19449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * no ellipsis is to take place. 19459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 19469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public abstract int getEllipsisCount(int line); 19479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 19489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /* package */ static class Ellipsizer implements CharSequence, GetChars { 19499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /* package */ CharSequence mText; 19509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /* package */ Layout mLayout; 19519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /* package */ int mWidth; 19529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /* package */ TextUtils.TruncateAt mMethod; 19539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 19549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public Ellipsizer(CharSequence s) { 19559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mText = s; 19569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 19579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 19589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public char charAt(int off) { 19599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project char[] buf = TextUtils.obtain(1); 19609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project getChars(off, off + 1, buf, 0); 19619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project char ret = buf[0]; 19629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 19639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project TextUtils.recycle(buf); 19649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return ret; 19659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 19669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 19679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public void getChars(int start, int end, char[] dest, int destoff) { 19689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int line1 = mLayout.getLineForOffset(start); 19699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int line2 = mLayout.getLineForOffset(end); 19709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 19719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project TextUtils.getChars(mText, start, end, dest, destoff); 19729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 19739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project for (int i = line1; i <= line2; i++) { 19748d44fff7e62f77c3b3072a96712cc1389e63ca64Fabrice Di Meglio mLayout.ellipsize(start, end, i, dest, destoff, mMethod); 19759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 19769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 19779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 19789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public int length() { 19799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return mText.length(); 19809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 19819f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt 19829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public CharSequence subSequence(int start, int end) { 19839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project char[] s = new char[end - start]; 19849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project getChars(start, end, s, 0); 19859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return new String(s); 19869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 19879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1988162bf0f1b9fd5d78ffa801917994f6222c6efd85Gilles Debunne @Override 19899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public String toString() { 19909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project char[] s = new char[length()]; 19919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project getChars(0, length(), s, 0); 19929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return new String(s); 19939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 19949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 19959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 19969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 19976c488de023a4797069673dc619c1a4096079ea9eGilles Debunne /* package */ static class SpannedEllipsizer extends Ellipsizer implements Spanned { 19989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private Spanned mSpanned; 19999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 20009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public SpannedEllipsizer(CharSequence display) { 20019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project super(display); 20029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mSpanned = (Spanned) display; 20039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 20049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 20059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public <T> T[] getSpans(int start, int end, Class<T> type) { 20069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return mSpanned.getSpans(start, end, type); 20079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 20089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 20099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public int getSpanStart(Object tag) { 20109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return mSpanned.getSpanStart(tag); 20119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 20129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 20139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public int getSpanEnd(Object tag) { 20149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return mSpanned.getSpanEnd(tag); 20159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 20169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 20179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public int getSpanFlags(Object tag) { 20189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return mSpanned.getSpanFlags(tag); 20199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 20209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 20216c488de023a4797069673dc619c1a4096079ea9eGilles Debunne @SuppressWarnings("rawtypes") 20229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public int nextSpanTransition(int start, int limit, Class type) { 20239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return mSpanned.nextSpanTransition(start, limit, type); 20249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 20259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 2026162bf0f1b9fd5d78ffa801917994f6222c6efd85Gilles Debunne @Override 20279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public CharSequence subSequence(int start, int end) { 20289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project char[] s = new char[end - start]; 20299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project getChars(start, end, s, 0); 20309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 20319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project SpannableString ss = new SpannableString(new String(s)); 20329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project TextUtils.copySpansFrom(mSpanned, start, end, Object.class, ss, 0); 20339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return ss; 20349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 20359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 20369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 20379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private CharSequence mText; 20389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private TextPaint mPaint; 20399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private int mWidth; 20409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private Alignment mAlignment = Alignment.ALIGN_NORMAL; 20419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private float mSpacingMult; 20429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private float mSpacingAdd; 2043c4d8eb6fb7c88c5c4da38b0b113c24cc4b78c0b7Romain Guy private static final Rect sTempRect = new Rect(); 20449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private boolean mSpannedText; 2045cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt private TextDirectionHeuristic mTextDir; 2046eca5b7385a641ae5d8c4aa2b7823a4e573737376Gilles Debunne private SpanSet<LineBackgroundSpan> mLineBackgroundSpans; 20479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 20489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public static final int DIR_LEFT_TO_RIGHT = 1; 20499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public static final int DIR_RIGHT_TO_LEFT = -1; 20509f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt 205120178d62cf669af18467a16d3c4c4237ed42151cDoug Felt /* package */ static final int DIR_REQUEST_LTR = 1; 205220178d62cf669af18467a16d3c4c4237ed42151cDoug Felt /* package */ static final int DIR_REQUEST_RTL = -1; 205320178d62cf669af18467a16d3c4c4237ed42151cDoug Felt /* package */ static final int DIR_REQUEST_DEFAULT_LTR = 2; 205420178d62cf669af18467a16d3c4c4237ed42151cDoug Felt /* package */ static final int DIR_REQUEST_DEFAULT_RTL = -2; 20559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 20569f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt /* package */ static final int RUN_LENGTH_MASK = 0x03ffffff; 20579f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt /* package */ static final int RUN_LEVEL_SHIFT = 26; 20589f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt /* package */ static final int RUN_LEVEL_MASK = 0x3f; 20599f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt /* package */ static final int RUN_RTL_FLAG = 1 << RUN_LEVEL_SHIFT; 20609f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt 20619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public enum Alignment { 20629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project ALIGN_NORMAL, 20639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project ALIGN_OPPOSITE, 20649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project ALIGN_CENTER, 2065c0ccf0c47c00942a9d0f2670600a8f2d4d7adb5bDoug Felt /** @hide */ 2066c0ccf0c47c00942a9d0f2670600a8f2d4d7adb5bDoug Felt ALIGN_LEFT, 2067c0ccf0c47c00942a9d0f2670600a8f2d4d7adb5bDoug Felt /** @hide */ 2068c0ccf0c47c00942a9d0f2670600a8f2d4d7adb5bDoug Felt ALIGN_RIGHT, 20699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 20709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 20719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private static final int TAB_INCREMENT = 20; 20729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 20739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /* package */ static final Directions DIRS_ALL_LEFT_TO_RIGHT = 20749f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt new Directions(new int[] { 0, RUN_LENGTH_MASK }); 20759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /* package */ static final Directions DIRS_ALL_RIGHT_TO_LEFT = 20769f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt new Directions(new int[] { 0, RUN_LENGTH_MASK | RUN_RTL_FLAG }); 20770a4db3c5270440eeb7e4e44a7029926e239ec3bdGilles Debunne 20789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project} 2079