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&mdash;
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&hellip;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&hellip;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&hellip;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&hellip;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