Layout.java revision f75c97e023af7d4ad9a8c129d4ea282b1c3b8f94
19066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project/*
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
199f7a4442b89cc06cb8cae6992484e7ae795323abDoug Feltimport com.android.internal.util.ArrayUtils;
209f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt
21105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Projectimport android.emoji.EmojiFactory;
229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.graphics.Canvas;
239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.graphics.Paint;
249f7a4442b89cc06cb8cae6992484e7ae795323abDoug Feltimport android.graphics.Path;
259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.graphics.Rect;
269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.text.method.TextKeyListener;
279f7a4442b89cc06cb8cae6992484e7ae795323abDoug Feltimport android.text.style.AlignmentSpan;
289f7a4442b89cc06cb8cae6992484e7ae795323abDoug Feltimport android.text.style.LeadingMarginSpan;
29162bf0f1b9fd5d78ffa801917994f6222c6efd85Gilles Debunneimport android.text.style.LeadingMarginSpan.LeadingMarginSpan2;
309f7a4442b89cc06cb8cae6992484e7ae795323abDoug Feltimport android.text.style.LineBackgroundSpan;
319f7a4442b89cc06cb8cae6992484e7ae795323abDoug Feltimport android.text.style.ParagraphStyle;
329f7a4442b89cc06cb8cae6992484e7ae795323abDoug Feltimport android.text.style.ReplacementSpan;
339f7a4442b89cc06cb8cae6992484e7ae795323abDoug Feltimport android.text.style.TabStopSpan;
349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
35c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Feltimport java.util.Arrays;
369f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt
379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project/**
389f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt * A base class that manages text layout in visual elements on
399f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt * the screen.
409f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt * <p>For text that will be edited, use a {@link DynamicLayout},
419f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt * which will be updated as the text changes.
429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * For text that will not change, use a {@link StaticLayout}.
439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */
449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectpublic abstract class Layout {
4571b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt    private static final ParagraphStyle[] NO_PARA_SPANS =
4671b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt        ArrayUtils.emptyArray(ParagraphStyle.class);
4776c0226008ee16633dc94ed4dbeadef2174f6bd9Dave Bort
48105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project    /* package */ static final EmojiFactory EMOJI_FACTORY =
49105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project        EmojiFactory.newAvailableInstance();
50105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project    /* package */ static final int MIN_EMOJI, MAX_EMOJI;
51105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project
52105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project    static {
53105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project        if (EMOJI_FACTORY != null) {
54105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project            MIN_EMOJI = EMOJI_FACTORY.getMinimumAndroidPua();
55105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project            MAX_EMOJI = EMOJI_FACTORY.getMaximumAndroidPua();
56105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project        } else {
57105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project            MIN_EMOJI = -1;
58105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project            MAX_EMOJI = -1;
59105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project        }
60e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    }
61c2d54f46ac13e029e6d53f7471cd9c90fe6bbfe9Eric Fischer
629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
6371b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt     * Return how wide a layout must be in order to display the
649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * specified text with one line per paragraph.
659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static float getDesiredWidth(CharSequence source,
679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                        TextPaint paint) {
689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return getDesiredWidth(source, 0, source.length(), paint);
699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
709f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt
719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
7271b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt     * Return how wide a layout must be in order to display the
739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * specified text slice with one line per paragraph.
749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static float getDesiredWidth(CharSequence source,
769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                        int start, int end,
779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                        TextPaint paint) {
789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        float need = 0;
799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        TextPaint workPaint = new TextPaint();
809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int next;
829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        for (int i = start; i <= end; i = next) {
839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            next = TextUtils.indexOf(source, '\n', i, end);
849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (next < 0)
869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                next = end;
879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
8871b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt            // note, omits trailing paragraph char
89c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt            float w = measurePara(paint, workPaint, source, i, next);
909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (w > need)
929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                need = w;
939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            next++;
959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return need;
989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
1019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Subclasses of Layout use this constructor to set the display text,
1029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * width, and other standard properties.
10371b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt     * @param text the text to render
10471b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt     * @param paint the default paint for the layout.  Styles can override
10571b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt     * various attributes of the paint.
10671b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt     * @param width the wrapping width for the text.
10771b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt     * @param align whether to left, right, or center the text.  Styles can
10871b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt     * override the alignment.
10971b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt     * @param spacingMult factor by which to scale the font size to get the
11071b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt     * default line spacing
11171b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt     * @param spacingAdd amount to add to the default line spacing
1129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
1139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    protected Layout(CharSequence text, TextPaint paint,
1149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                     int width, Alignment align,
11571b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt                     float spacingMult, float spacingAdd) {
1169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (width < 0)
1179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            throw new IllegalArgumentException("Layout: " + width + " < 0");
1189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
119e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        // Ensure paint doesn't have baselineShift set.
120e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        // While normally we don't modify the paint the user passed in,
121e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        // we were already doing this in Styled.drawUniformRun with both
122e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        // baselineShift and bgColor.  We probably should reevaluate bgColor.
123e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        if (paint != null) {
124e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            paint.bgColor = 0;
125e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            paint.baselineShift = 0;
126e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        }
127e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
1289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mText = text;
1299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mPaint = paint;
1309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mWorkPaint = new TextPaint();
1319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mWidth = width;
1329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mAlignment = align;
13371b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt        mSpacingMult = spacingMult;
13471b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt        mSpacingAdd = spacingAdd;
1359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mSpannedText = text instanceof Spanned;
1369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
1399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Replace constructor properties of this Layout with new ones.  Be careful.
1409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
1419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /* package */ void replaceWith(CharSequence text, TextPaint paint,
1429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                              int width, Alignment align,
1439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                              float spacingmult, float spacingadd) {
1449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (width < 0) {
1459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            throw new IllegalArgumentException("Layout: " + width + " < 0");
1469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mText = text;
1499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mPaint = paint;
1509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mWidth = width;
1519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mAlignment = align;
1529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mSpacingMult = spacingmult;
1539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mSpacingAdd = spacingadd;
1549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mSpannedText = text instanceof Spanned;
1559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
1589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Draw this Layout on the specified Canvas.
1599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
1609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public void draw(Canvas c) {
1619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        draw(c, null, null, 0);
1629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
16571b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt     * Draw this Layout on the specified canvas, with the highlight path drawn
16671b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt     * between the background and the text.
16771b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt     *
16871b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt     * @param c the canvas
16971b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt     * @param highlight the path of the highlight or cursor; can be null
17071b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt     * @param highlightPaint the paint for the highlight
17171b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt     * @param cursorOffsetVertical the amount to temporarily translate the
17271b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt     *        canvas while rendering the highlight
1739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
17471b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt    public void draw(Canvas c, Path highlight, Paint highlightPaint,
1759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                     int cursorOffsetVertical) {
1769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int dtop, dbottom;
1779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        synchronized (sTempRect) {
1799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (!c.getClipBounds(sTempRect)) {
1809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                return;
1819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
1829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            dtop = sTempRect.top;
1849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            dbottom = sTempRect.bottom;
1859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int top = 0;
1889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int bottom = getLineTop(getLineCount());
1899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (dtop > top) {
1919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            top = dtop;
1929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (dbottom < bottom) {
1949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            bottom = dbottom;
1959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1969f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt
1979f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        int first = getLineForVertical(top);
1989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int last = getLineForVertical(bottom);
1999f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt
2009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int previousLineBottom = getLineTop(first);
2019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int previousLineEnd = getLineStart(first);
2029f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt
20371b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt        TextPaint paint = mPaint;
2049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        CharSequence buf = mText;
20571b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt        int width = mWidth;
20671b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt        boolean spannedText = mSpannedText;
2079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
20871b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt        ParagraphStyle[] spans = NO_PARA_SPANS;
209c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt        int spanEnd = 0;
2109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int textLength = 0;
2119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
21271b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt        // First, draw LineBackgroundSpans.
2130c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt        // LineBackgroundSpans know nothing about the alignment, margins, or
214c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt        // direction of the layout or line.  XXX: Should they?
215c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt        // They are evaluated at each line.
2169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (spannedText) {
217c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt            Spanned sp = (Spanned) buf;
2189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            textLength = buf.length();
2199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            for (int i = first; i <= last; i++) {
2209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                int start = previousLineEnd;
2219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                int end = getLineStart(i+1);
2229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                previousLineEnd = end;
2239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                int ltop = previousLineBottom;
2259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                int lbottom = getLineTop(i+1);
2269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                previousLineBottom = lbottom;
2279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                int lbaseline = lbottom - getLineDescent(i);
2289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
229c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt                if (start >= spanEnd) {
230c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt                    // These should be infrequent, so we'll use this so that
231c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt                    // we don't have to check as often.
2320c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                    spanEnd = sp.nextSpanTransition(start, textLength,
233c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt                            LineBackgroundSpan.class);
234c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt                    // All LineBackgroundSpans on a line contribute to its
235c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt                    // background.
23674d31ef2b2c42b54fa1f7cf94ea955ea67ab69a0Eric Fischer                   spans = getParagraphSpans(sp, start, end, LineBackgroundSpan.class);
2379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
2389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                for (int n = 0; n < spans.length; n++) {
2409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    LineBackgroundSpan back = (LineBackgroundSpan) spans[n];
2419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
24271b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt                    back.drawBackground(c, paint, 0, width,
2439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                       ltop, lbaseline, lbottom,
2449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                       buf, start, end,
2459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                       i);
2469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
2479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
2489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // reset to their original values
249c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt            spanEnd = 0;
2509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            previousLineBottom = getLineTop(first);
2519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            previousLineEnd = getLineStart(first);
25271b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt            spans = NO_PARA_SPANS;
2539f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        }
2549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // There can be a highlight even without spans if we are drawing
2569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // a non-spanned transformation of a spanned editing buffer.
2579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (highlight != null) {
2589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (cursorOffsetVertical != 0) {
2599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                c.translate(0, cursorOffsetVertical);
2609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
2619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
26271b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt            c.drawPath(highlight, highlightPaint);
2639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (cursorOffsetVertical != 0) {
2659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                c.translate(0, -cursorOffsetVertical);
2669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
2679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
2689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        Alignment align = mAlignment;
270c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt        TabStops tabStops = null;
271c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt        boolean tabStopsIsInitialized = false;
2729f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt
273e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        TextLine tl = TextLine.obtain();
274c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt
27571b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt        // Next draw the lines, one at a time.
27671b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt        // the baseline is the top of the following line minus the current
27771b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt        // line's descent.
2789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        for (int i = first; i <= last; i++) {
2799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int start = previousLineEnd;
2809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            previousLineEnd = getLineStart(i+1);
2829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int end = getLineVisibleEnd(i, start, previousLineEnd);
2839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int ltop = previousLineBottom;
2859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int lbottom = getLineTop(i+1);
2869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            previousLineBottom = lbottom;
2879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int lbaseline = lbottom - getLineDescent(i);
2889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
289c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt            int dir = getParagraphDirection(i);
290c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt            int left = 0;
291c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt            int right = mWidth;
292c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt
2939f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt            if (spannedText) {
294c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt                Spanned sp = (Spanned) buf;
295c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt                boolean isFirstParaLine = (start == 0 ||
296c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt                        buf.charAt(start - 1) == '\n');
2970c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt
298c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt                // New batch of paragraph styles, collect into spans array.
299c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt                // Compute the alignment, last alignment style wins.
300c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt                // Reset tabStops, we'll rebuild if we encounter a line with
301c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt                // tabs.
302c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt                // We expect paragraph spans to be relatively infrequent, use
303c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt                // spanEnd so that we can check less frequently.  Since
304c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt                // paragraph styles ought to apply to entire paragraphs, we can
305c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt                // just collect the ones present at the start of the paragraph.
306c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt                // If spanEnd is before the end of the paragraph, that's not
307c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt                // our problem.
308c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt                if (start >= spanEnd && (i == first || isFirstParaLine)) {
309c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt                    spanEnd = sp.nextSpanTransition(start, textLength,
3109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                                    ParagraphStyle.class);
31174d31ef2b2c42b54fa1f7cf94ea955ea67ab69a0Eric Fischer                    spans = getParagraphSpans(sp, start, spanEnd, ParagraphStyle.class);
3129f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt
3139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    align = mAlignment;
3149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    for (int n = spans.length-1; n >= 0; n--) {
3159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        if (spans[n] instanceof AlignmentSpan) {
3169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                            align = ((AlignmentSpan) spans[n]).getAlignment();
3179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                            break;
3189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        }
3199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    }
3200c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt
321c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt                    tabStopsIsInitialized = false;
3229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
3239f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt
324c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt                // Draw all leading margin spans.  Adjust left or right according
325c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt                // to the paragraph direction of the line.
3269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                final int length = spans.length;
3279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                for (int n = 0; n < length; n++) {
3289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    if (spans[n] instanceof LeadingMarginSpan) {
3299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        LeadingMarginSpan margin = (LeadingMarginSpan) spans[n];
330c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt                        boolean useFirstLineMargin = isFirstParaLine;
331c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt                        if (margin instanceof LeadingMarginSpan2) {
332c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt                            int count = ((LeadingMarginSpan2) margin).getLeadingMarginLineCount();
333c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt                            int startLine = getLineForOffset(sp.getSpanStart(margin));
334c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt                            useFirstLineMargin = i < startLine + count;
335c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt                        }
3369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        if (dir == DIR_RIGHT_TO_LEFT) {
3389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                            margin.drawLeadingMargin(c, paint, right, dir, ltop,
3399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                                     lbaseline, lbottom, buf,
34071b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt                                                     start, end, isFirstParaLine, this);
341c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt                            right -= margin.getLeadingMargin(useFirstLineMargin);
3429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        } else {
3439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                            margin.drawLeadingMargin(c, paint, left, dir, ltop,
3449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                                     lbaseline, lbottom, buf,
34571b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt                                                     start, end, isFirstParaLine, this);
346c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt                            left += margin.getLeadingMargin(useFirstLineMargin);
3479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        }
3489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    }
3499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
3509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
3519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
352c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt            boolean hasTabOrEmoji = getLineContainsTab(i);
353c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt            // Can't tell if we have tabs for sure, currently
354c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt            if (hasTabOrEmoji && !tabStopsIsInitialized) {
355c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt                if (tabStops == null) {
356c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt                    tabStops = new TabStops(TAB_INCREMENT, spans);
357c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt                } else {
358c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt                    tabStops.reset(TAB_INCREMENT, spans);
359c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt                }
360c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt                tabStopsIsInitialized = true;
361c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt            }
362c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt
3639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int x;
3649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (align == Alignment.ALIGN_NORMAL) {
3659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (dir == DIR_LEFT_TO_RIGHT) {
3669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    x = left;
3679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                } else {
3689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    x = right;
3699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
3709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } else {
371c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt                int max = (int)getLineExtent(i, tabStops, false);
3729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (align == Alignment.ALIGN_OPPOSITE) {
373c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt                    if (dir == DIR_LEFT_TO_RIGHT) {
3749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        x = right - max;
3759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    } else {
376c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt                        x = left - max;
3779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    }
378c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt                } else { // Alignment.ALIGN_CENTER
379c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt                    max = max & ~1;
380c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt                    x = (right + left - max) >> 1;
3819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
3829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
3839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            Directions directions = getLineDirections(i);
3859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (directions == DIRS_ALL_LEFT_TO_RIGHT &&
386c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt                    !spannedText && !hasTabOrEmoji) {
38771b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt                // XXX: assumes there's nothing additional to be done
3889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                c.drawText(buf, start, end, x, lbaseline, paint);
3899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } else {
390c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt                tl.set(paint, buf, start, end, dir, directions, hasTabOrEmoji, tabStops);
391e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                tl.draw(c, x, ltop, lbaseline, lbottom);
3929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
3939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
394c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt
395e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        TextLine.recycle(tl);
3969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
3979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
399c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt     * Return the start position of the line, given the left and right bounds
400c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt     * of the margins.
4010c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt     *
402c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt     * @param line the line index
403c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt     * @param left the left bounds (0, or leading margin if ltr para)
404c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt     * @param right the right bounds (width, minus leading margin if rtl para)
405c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt     * @return the start position of the line (to right of line if rtl para)
406c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt     */
407c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt    private int getLineStartPos(int line, int left, int right) {
408c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt        // Adjust the point at which to start rendering depending on the
409c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt        // alignment of the paragraph.
410c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt        Alignment align = getParagraphAlignment(line);
411c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt        int dir = getParagraphDirection(line);
412c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt
413c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt        int x;
414c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt        if (align == Alignment.ALIGN_NORMAL) {
415c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt            if (dir == DIR_LEFT_TO_RIGHT) {
416c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt                x = left;
417c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt            } else {
418c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt                x = right;
419c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt            }
420c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt        } else {
421c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt            TabStops tabStops = null;
422c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt            if (mSpannedText && getLineContainsTab(line)) {
423c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt                Spanned spanned = (Spanned) mText;
424c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt                int start = getLineStart(line);
425c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt                int spanEnd = spanned.nextSpanTransition(start, spanned.length(),
426c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt                        TabStopSpan.class);
42774d31ef2b2c42b54fa1f7cf94ea955ea67ab69a0Eric Fischer                TabStopSpan[] tabSpans = getParagraphSpans(spanned, start, spanEnd, TabStopSpan.class);
428c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt                if (tabSpans.length > 0) {
429c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt                    tabStops = new TabStops(TAB_INCREMENT, tabSpans);
430c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt                }
431c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt            }
432c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt            int max = (int)getLineExtent(line, tabStops, false);
433c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt            if (align == Alignment.ALIGN_OPPOSITE) {
434c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt                if (dir == DIR_LEFT_TO_RIGHT) {
435c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt                    x = right - max;
436c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt                } else {
437c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt                    x = left - max;
438c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt                }
439c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt            } else { // Alignment.ALIGN_CENTER
440c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt                max = max & ~1;
441c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt                x = (left + right - max) >> 1;
442c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt            }
443c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt        }
444c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt        return x;
445c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt    }
446c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt
447c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt    /**
4489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Return the text that is displayed by this Layout.
4499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
4509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public final CharSequence getText() {
4519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return mText;
4529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
4539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
4559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Return the base Paint properties for this layout.
4569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Do NOT change the paint, which may result in funny
4579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * drawing for this layout.
4589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
4599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public final TextPaint getPaint() {
4609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return mPaint;
4619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
4629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
4649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Return the width of this layout.
4659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
4669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public final int getWidth() {
4679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return mWidth;
4689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
4699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
4719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Return the width to which this Layout is ellipsizing, or
4729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * {@link #getWidth} if it is not doing anything special.
4739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
4749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public int getEllipsizedWidth() {
4759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return mWidth;
4769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
4779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
4799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Increase the width of this layout to the specified width.
48071b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt     * Be careful to use this only when you know it is appropriate&mdash;
4819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * it does not cause the text to reflow to use the full new width.
4829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
4839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public final void increaseWidthTo(int wid) {
4849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (wid < mWidth) {
4859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            throw new RuntimeException("attempted to reduce Layout width");
4869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
4879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mWidth = wid;
4899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
4909f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt
4919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
4929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Return the total height of this layout.
4939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
4949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public int getHeight() {
49571b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt        return getLineTop(getLineCount());
4969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
4979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
4999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Return the base alignment of this layout.
5009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
5019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public final Alignment getAlignment() {
5029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return mAlignment;
5039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
5049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
5069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Return what the text height is multiplied by to get the line height.
5079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
5089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public final float getSpacingMultiplier() {
5099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return mSpacingMult;
5109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
5119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
5139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Return the number of units of leading that are added to each line.
5149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
5159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public final float getSpacingAdd() {
5169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return mSpacingAdd;
5179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
5189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
5209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Return the number of lines of text in this layout.
5219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
5229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public abstract int getLineCount();
5239f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt
5249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
5259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Return the baseline for the specified line (0&hellip;getLineCount() - 1)
5269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * If bounds is not null, return the top, left, right, bottom extents
5279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * of the specified line in it.
5289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param line which line to examine (0..getLineCount() - 1)
5299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param bounds Optional. If not null, it returns the extent of the line
5309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @return the Y-coordinate of the baseline
5319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
5329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public int getLineBounds(int line, Rect bounds) {
5339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (bounds != null) {
5349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            bounds.left = 0;     // ???
5359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            bounds.top = getLineTop(line);
5369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            bounds.right = mWidth;   // ???
53771b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt            bounds.bottom = getLineTop(line + 1);
5389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
5399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return getLineBaseline(line);
5409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
5419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
54371b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt     * Return the vertical position of the top of the specified line
54471b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt     * (0&hellip;getLineCount()).
54571b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt     * If the specified line is equal to the line count, returns the
5469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * bottom of the last line.
5479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
5489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public abstract int getLineTop(int line);
5499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
55171b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt     * Return the descent of the specified line(0&hellip;getLineCount() - 1).
5529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
5539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public abstract int getLineDescent(int line);
5549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
55671b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt     * Return the text offset of the beginning of the specified line (
55771b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt     * 0&hellip;getLineCount()). If the specified line is equal to the line
55871b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt     * count, returns the length of the text.
5599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
5609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public abstract int getLineStart(int line);
5619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
56371b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt     * Returns the primary directionality of the paragraph containing the
56471b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt     * specified line, either 1 for left-to-right lines, or -1 for right-to-left
56571b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt     * lines (see {@link #DIR_LEFT_TO_RIGHT}, {@link #DIR_RIGHT_TO_LEFT}).
5669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
5679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public abstract int getParagraphDirection(int line);
5689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
570105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project     * Returns whether the specified line contains one or more
571105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project     * characters that need to be handled specially, like tabs
572105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project     * or emoji.
5739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
5749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public abstract boolean getLineContainsTab(int line);
5759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
57771b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt     * Returns the directional run information for the specified line.
5789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * The array alternates counts of characters in left-to-right
5799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * and right-to-left segments of the line.
58071b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt     *
58171b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt     * <p>NOTE: this is inadequate to support bidirectional text, and will change.
5829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
5839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public abstract Directions getLineDirections(int line);
5849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
5869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Returns the (negative) number of extra pixels of ascent padding in the
5879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * top line of the Layout.
5889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
5899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public abstract int getTopPadding();
5909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
5929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Returns the number of extra pixels of descent padding in the
5939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * bottom line of the Layout.
5949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
5959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public abstract int getBottomPadding();
5969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5974e0c5e55e171532760d5f51e0165563827129d4eDoug Felt
5984e0c5e55e171532760d5f51e0165563827129d4eDoug Felt    /**
5994e0c5e55e171532760d5f51e0165563827129d4eDoug Felt     * Returns true if the character at offset and the preceding character
6004e0c5e55e171532760d5f51e0165563827129d4eDoug Felt     * are at different run levels (and thus there's a split caret).
6014e0c5e55e171532760d5f51e0165563827129d4eDoug Felt     * @param offset the offset
6024e0c5e55e171532760d5f51e0165563827129d4eDoug Felt     * @return true if at a level boundary
603f75c97e023af7d4ad9a8c129d4ea282b1c3b8f94Gilles Debunne     * @hide
6044e0c5e55e171532760d5f51e0165563827129d4eDoug Felt     */
605f75c97e023af7d4ad9a8c129d4ea282b1c3b8f94Gilles Debunne    public boolean isLevelBoundary(int offset) {
6069f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        int line = getLineForOffset(offset);
6074e0c5e55e171532760d5f51e0165563827129d4eDoug Felt        Directions dirs = getLineDirections(line);
6084e0c5e55e171532760d5f51e0165563827129d4eDoug Felt        if (dirs == DIRS_ALL_LEFT_TO_RIGHT || dirs == DIRS_ALL_RIGHT_TO_LEFT) {
6094e0c5e55e171532760d5f51e0165563827129d4eDoug Felt            return false;
6104e0c5e55e171532760d5f51e0165563827129d4eDoug Felt        }
6114e0c5e55e171532760d5f51e0165563827129d4eDoug Felt
6124e0c5e55e171532760d5f51e0165563827129d4eDoug Felt        int[] runs = dirs.mDirections;
6139f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        int lineStart = getLineStart(line);
6144e0c5e55e171532760d5f51e0165563827129d4eDoug Felt        int lineEnd = getLineEnd(line);
6154e0c5e55e171532760d5f51e0165563827129d4eDoug Felt        if (offset == lineStart || offset == lineEnd) {
6164e0c5e55e171532760d5f51e0165563827129d4eDoug Felt            int paraLevel = getParagraphDirection(line) == 1 ? 0 : 1;
6174e0c5e55e171532760d5f51e0165563827129d4eDoug Felt            int runIndex = offset == lineStart ? 0 : runs.length - 2;
6184e0c5e55e171532760d5f51e0165563827129d4eDoug Felt            return ((runs[runIndex + 1] >>> RUN_LEVEL_SHIFT) & RUN_LEVEL_MASK) != paraLevel;
6194e0c5e55e171532760d5f51e0165563827129d4eDoug Felt        }
6204e0c5e55e171532760d5f51e0165563827129d4eDoug Felt
6214e0c5e55e171532760d5f51e0165563827129d4eDoug Felt        offset -= lineStart;
6229f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        for (int i = 0; i < runs.length; i += 2) {
6234e0c5e55e171532760d5f51e0165563827129d4eDoug Felt            if (offset == runs[i]) {
6244e0c5e55e171532760d5f51e0165563827129d4eDoug Felt                return true;
6259f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt            }
6269f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        }
6274e0c5e55e171532760d5f51e0165563827129d4eDoug Felt        return false;
6289f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt    }
6299f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt
6309f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt    private boolean primaryIsTrailingPrevious(int offset) {
6319f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        int line = getLineForOffset(offset);
6329f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        int lineStart = getLineStart(line);
6334e0c5e55e171532760d5f51e0165563827129d4eDoug Felt        int lineEnd = getLineEnd(line);
6349f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        int[] runs = getLineDirections(line).mDirections;
6359f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt
6369f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        int levelAt = -1;
6379f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        for (int i = 0; i < runs.length; i += 2) {
6389f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt            int start = lineStart + runs[i];
6399f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt            int limit = start + (runs[i+1] & RUN_LENGTH_MASK);
6409f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt            if (limit > lineEnd) {
6419f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt                limit = lineEnd;
6429f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt            }
6439f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt            if (offset >= start && offset < limit) {
6449f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt                if (offset > start) {
6459f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt                    // Previous character is at same level, so don't use trailing.
6469f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt                    return false;
6479f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt                }
6489f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt                levelAt = (runs[i+1] >>> RUN_LEVEL_SHIFT) & RUN_LEVEL_MASK;
6499f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt                break;
6509f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt            }
6519f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        }
6529f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        if (levelAt == -1) {
6539f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt            // Offset was limit of line.
6549f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt            levelAt = getParagraphDirection(line) == 1 ? 0 : 1;
6559f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        }
6569f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt
6579f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        // At level boundary, check previous level.
6589f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        int levelBefore = -1;
6599f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        if (offset == lineStart) {
6609f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt            levelBefore = getParagraphDirection(line) == 1 ? 0 : 1;
6619f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        } else {
6629f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt            offset -= 1;
6639f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt            for (int i = 0; i < runs.length; i += 2) {
6649f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt                int start = lineStart + runs[i];
6659f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt                int limit = start + (runs[i+1] & RUN_LENGTH_MASK);
6669f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt                if (limit > lineEnd) {
6679f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt                    limit = lineEnd;
6689f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt                }
6699f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt                if (offset >= start && offset < limit) {
6709f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt                    levelBefore = (runs[i+1] >>> RUN_LEVEL_SHIFT) & RUN_LEVEL_MASK;
6719f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt                    break;
6729f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt                }
6739f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt            }
6749f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        }
6759f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt
6769f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        return levelBefore < levelAt;
6779f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt    }
6789f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt
6799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
6809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Get the primary horizontal position for the specified text offset.
6819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * This is the location where a new character would be inserted in
6829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * the paragraph's primary direction.
6839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
6849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public float getPrimaryHorizontal(int offset) {
6859f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        boolean trailing = primaryIsTrailingPrevious(offset);
6869f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        return getHorizontal(offset, trailing);
6879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
6889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
6909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Get the secondary horizontal position for the specified text offset.
6919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * This is the location where a new character would be inserted in
6929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * the direction other than the paragraph's primary direction.
6939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
6949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public float getSecondaryHorizontal(int offset) {
6959f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        boolean trailing = primaryIsTrailingPrevious(offset);
6969f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        return getHorizontal(offset, !trailing);
6979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
6989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6999f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt    private float getHorizontal(int offset, boolean trailing) {
7009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int line = getLineForOffset(offset);
7019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
7029f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        return getHorizontal(offset, trailing, line);
7039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
7049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
7059f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt    private float getHorizontal(int offset, boolean trailing, int line) {
7069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int start = getLineStart(line);
707e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        int end = getLineEnd(line);
7089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int dir = getParagraphDirection(line);
709c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt        boolean hasTabOrEmoji = getLineContainsTab(line);
7109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        Directions directions = getLineDirections(line);
7119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
712c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt        TabStops tabStops = null;
713c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt        if (hasTabOrEmoji && mText instanceof Spanned) {
714c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt            // Just checking this line should be good enough, tabs should be
715c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt            // consistent across all lines in a paragraph.
71674d31ef2b2c42b54fa1f7cf94ea955ea67ab69a0Eric Fischer            TabStopSpan[] tabs = getParagraphSpans((Spanned) mText, start, end, TabStopSpan.class);
717c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt            if (tabs.length > 0) {
718c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt                tabStops = new TabStops(TAB_INCREMENT, tabs); // XXX should reuse
719c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt            }
7209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
7219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
722e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        TextLine tl = TextLine.obtain();
723c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt        tl.set(mPaint, mText, start, end, dir, directions, hasTabOrEmoji, tabStops);
724e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        float wid = tl.measure(offset - start, trailing, null);
725e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        TextLine.recycle(tl);
7269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
7279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int left = getParagraphLeft(line);
7289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int right = getParagraphRight(line);
7299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
730c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt        return getLineStartPos(line, left, right) + wid;
7319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
7329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
7339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
7349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Get the leftmost position that should be exposed for horizontal
7359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * scrolling on the specified line.
7369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
7379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public float getLineLeft(int line) {
7389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int dir = getParagraphDirection(line);
7399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        Alignment align = getParagraphAlignment(line);
7409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
7419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (align == Alignment.ALIGN_NORMAL) {
7429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (dir == DIR_RIGHT_TO_LEFT)
7439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                return getParagraphRight(line) - getLineMax(line);
7449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            else
7459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                return 0;
7469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } else if (align == Alignment.ALIGN_OPPOSITE) {
7479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (dir == DIR_RIGHT_TO_LEFT)
7489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                return 0;
7499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            else
7509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                return mWidth - getLineMax(line);
7519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } else { /* align == Alignment.ALIGN_CENTER */
7529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int left = getParagraphLeft(line);
7539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int right = getParagraphRight(line);
7549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int max = ((int) getLineMax(line)) & ~1;
7559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
7569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return left + ((right - left) - max) / 2;
7579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
7589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
7599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
7609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
7619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Get the rightmost position that should be exposed for horizontal
7629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * scrolling on the specified line.
7639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
7649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public float getLineRight(int line) {
7659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int dir = getParagraphDirection(line);
7669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        Alignment align = getParagraphAlignment(line);
7679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
7689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (align == Alignment.ALIGN_NORMAL) {
7699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (dir == DIR_RIGHT_TO_LEFT)
7709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                return mWidth;
7719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            else
7729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                return getParagraphLeft(line) + getLineMax(line);
7739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } else if (align == Alignment.ALIGN_OPPOSITE) {
7749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (dir == DIR_RIGHT_TO_LEFT)
7759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                return getLineMax(line);
7769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            else
7779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                return mWidth;
7789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } else { /* align == Alignment.ALIGN_CENTER */
7799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int left = getParagraphLeft(line);
7809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int right = getParagraphRight(line);
7819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int max = ((int) getLineMax(line)) & ~1;
7829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
7839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return right - ((right - left) - max) / 2;
7849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
7859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
7869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
7879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
7880c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt     * Gets the unsigned horizontal extent of the specified line, including
789c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt     * leading margin indent, but excluding trailing whitespace.
7909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
7919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public float getLineMax(int line) {
792c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt        float margin = getParagraphLeadingMargin(line);
793c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt        float signedExtent = getLineExtent(line, false);
794c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt        return margin + signedExtent >= 0 ? signedExtent : -signedExtent;
7959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
7969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
7979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
798c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt     * Gets the unsigned horizontal extent of the specified line, including
799c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt     * leading margin indent and trailing whitespace.
8009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
8019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public float getLineWidth(int line) {
802c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt        float margin = getParagraphLeadingMargin(line);
803c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt        float signedExtent = getLineExtent(line, true);
804c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt        return margin + signedExtent >= 0 ? signedExtent : -signedExtent;
8059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
8069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
807c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt    /**
808c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt     * Like {@link #getLineExtent(int,TabStops,boolean)} but determines the
809c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt     * tab stops instead of using the ones passed in.
810c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt     * @param line the index of the line
811c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt     * @param full whether to include trailing whitespace
812c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt     * @return the extent of the line
813c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt     */
814c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt    private float getLineExtent(int line, boolean full) {
8159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int start = getLineStart(line);
816e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        int end = full ? getLineEnd(line) : getLineVisibleEnd(line);
817c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt
818c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt        boolean hasTabsOrEmoji = getLineContainsTab(line);
819c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt        TabStops tabStops = null;
820c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt        if (hasTabsOrEmoji && mText instanceof Spanned) {
821c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt            // Just checking this line should be good enough, tabs should be
822c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt            // consistent across all lines in a paragraph.
82374d31ef2b2c42b54fa1f7cf94ea955ea67ab69a0Eric Fischer            TabStopSpan[] tabs = getParagraphSpans((Spanned) mText, start, end, TabStopSpan.class);
824c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt            if (tabs.length > 0) {
825c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt                tabStops = new TabStops(TAB_INCREMENT, tabs); // XXX should reuse
826c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt            }
827c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt        }
828c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt        Directions directions = getLineDirections(line);
829c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt        int dir = getParagraphDirection(line);
830c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt
831c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt        TextLine tl = TextLine.obtain();
832c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt        tl.set(mPaint, mText, start, end, dir, directions, hasTabsOrEmoji, tabStops);
833c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt        float width = tl.metrics(null);
834c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt        TextLine.recycle(tl);
835c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt        return width;
836c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt    }
837c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt
838c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt    /**
839c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt     * Returns the signed horizontal extent of the specified line, excluding
840c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt     * leading margin.  If full is false, excludes trailing whitespace.
841c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt     * @param line the index of the line
842c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt     * @param tabStops the tab stops, can be null if we know they're not used.
843c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt     * @param full whether to include trailing whitespace
844c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt     * @return the extent of the text on this line
845c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt     */
846c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt    private float getLineExtent(int line, TabStops tabStops, boolean full) {
847c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt        int start = getLineStart(line);
848c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt        int end = full ? getLineEnd(line) : getLineVisibleEnd(line);
849c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt        boolean hasTabsOrEmoji = getLineContainsTab(line);
850e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        Directions directions = getLineDirections(line);
851c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt        int dir = getParagraphDirection(line);
8529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
853e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        TextLine tl = TextLine.obtain();
854c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt        tl.set(mPaint, mText, start, end, dir, directions, hasTabsOrEmoji, tabStops);
855e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        float width = tl.metrics(null);
856e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        TextLine.recycle(tl);
857e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        return width;
8589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
8599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
8609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
8619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Get the line number corresponding to the specified vertical position.
8629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * If you ask for a position above 0, you get 0; if you ask for a position
8639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * below the bottom of the text, you get the last line.
8649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
8659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    // FIXME: It may be faster to do a linear search for layouts without many lines.
8669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public int getLineForVertical(int vertical) {
8679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int high = getLineCount(), low = -1, guess;
8689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
8699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        while (high - low > 1) {
8709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            guess = (high + low) / 2;
8719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
8729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (getLineTop(guess) > vertical)
8739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                high = guess;
8749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            else
8759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                low = guess;
8769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
8779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
8789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (low < 0)
8799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return 0;
8809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        else
8819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return low;
8829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
8839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
8849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
8859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Get the line number on which the specified text offset appears.
8869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * If you ask for a position before 0, you get 0; if you ask for a position
8879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * beyond the end of the text, you get the last line.
8889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
8899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public int getLineForOffset(int offset) {
8909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int high = getLineCount(), low = -1, guess;
8919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
8929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        while (high - low > 1) {
8939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            guess = (high + low) / 2;
8949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
8959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (getLineStart(guess) > offset)
8969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                high = guess;
8979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            else
8989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                low = guess;
8999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
9009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
9019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (low < 0)
9029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return 0;
9039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        else
9049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return low;
9059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
9069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
9079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
9089f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt     * Get the character offset on the specified line whose position is
9099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * closest to the specified horizontal position.
9109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
9119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public int getOffsetForHorizontal(int line, float horiz) {
9129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int max = getLineEnd(line) - 1;
9139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int min = getLineStart(line);
9149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        Directions dirs = getLineDirections(line);
9159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
9169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (line == getLineCount() - 1)
9179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            max++;
9189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
9199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int best = min;
9209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        float bestdist = Math.abs(getPrimaryHorizontal(best) - horiz);
9219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
9229f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        for (int i = 0; i < dirs.mDirections.length; i += 2) {
9239f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt            int here = min + dirs.mDirections[i];
9249f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt            int there = here + (dirs.mDirections[i+1] & RUN_LENGTH_MASK);
9259f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt            int swap = (dirs.mDirections[i+1] & RUN_RTL_FLAG) != 0 ? -1 : 1;
9269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
9279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (there > max)
9289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                there = max;
9299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int high = there - 1 + 1, low = here + 1 - 1, guess;
9309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
9319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            while (high - low > 1) {
9329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                guess = (high + low) / 2;
9339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                int adguess = getOffsetAtStartOf(guess);
9349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
9359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (getPrimaryHorizontal(adguess) * swap >= horiz * swap)
9369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    high = guess;
9379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                else
9389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    low = guess;
9399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
9409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
9419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (low < here + 1)
9429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                low = here + 1;
9439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
9449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (low < there) {
9459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                low = getOffsetAtStartOf(low);
9469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
9479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                float dist = Math.abs(getPrimaryHorizontal(low) - horiz);
9489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
9499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                int aft = TextUtils.getOffsetAfter(mText, low);
9509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (aft < there) {
9519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    float other = Math.abs(getPrimaryHorizontal(aft) - horiz);
9529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
9539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    if (other < dist) {
9549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        dist = other;
9559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        low = aft;
9569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    }
9579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
9589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
9599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (dist < bestdist) {
9609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    bestdist = dist;
9619f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt                    best = low;
9629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
9639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
9649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
9659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            float dist = Math.abs(getPrimaryHorizontal(here) - horiz);
9669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
9679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (dist < bestdist) {
9689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                bestdist = dist;
9699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                best = here;
9709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
9719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
9729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
9739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        float dist = Math.abs(getPrimaryHorizontal(max) - horiz);
9749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
9759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (dist < bestdist) {
9769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            bestdist = dist;
9779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            best = max;
9789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
9799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
9809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return best;
9819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
9829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
9839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
9849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Return the text offset after the last character on the specified line.
9859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
9869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public final int getLineEnd(int line) {
9879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return getLineStart(line + 1);
9889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
9899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
9909f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt    /**
9919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Return the text offset after the last visible character (so whitespace
9929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * is not counted) on the specified line.
9939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
9949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public int getLineVisibleEnd(int line) {
9959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return getLineVisibleEnd(line, getLineStart(line), getLineStart(line+1));
9969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
9979f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt
9989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private int getLineVisibleEnd(int line, int start, int end) {
9999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        CharSequence text = mText;
10009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        char ch;
10019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (line == getLineCount() - 1) {
10029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return end;
10039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
10049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
10059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        for (; end > start; end--) {
10069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            ch = text.charAt(end - 1);
10079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
10089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (ch == '\n') {
10099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                return end - 1;
10109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
10119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
10129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (ch != ' ' && ch != '\t') {
10139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                break;
10149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
10159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
10169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
10179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
10189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return end;
10199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
10209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
10219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
10229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Return the vertical position of the bottom of the specified line.
10239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
10249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public final int getLineBottom(int line) {
10259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return getLineTop(line + 1);
10269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
10279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
10289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
10299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Return the vertical position of the baseline of the specified line.
10309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
10319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public final int getLineBaseline(int line) {
10329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // getLineTop(line+1) == getLineTop(line)
10339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return getLineTop(line+1) - getLineDescent(line);
10349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
10359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
10369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
10379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Get the ascent of the text on the specified line.
10389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * The return value is negative to match the Paint.ascent() convention.
10399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
10409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public final int getLineAscent(int line) {
10419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // getLineTop(line+1) - getLineDescent(line) == getLineBaseLine(line)
10429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return getLineTop(line) - (getLineTop(line+1) - getLineDescent(line));
10439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
10449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
10459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public int getOffsetToLeftOf(int offset) {
10469f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        return getOffsetToLeftRightOf(offset, true);
10479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
10489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
10499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public int getOffsetToRightOf(int offset) {
10509f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        return getOffsetToLeftRightOf(offset, false);
10519f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt    }
10529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
10539f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt    private int getOffsetToLeftRightOf(int caret, boolean toLeft) {
10549f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        int line = getLineForOffset(caret);
10559f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        int lineStart = getLineStart(line);
10569f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        int lineEnd = getLineEnd(line);
1057e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        int lineDir = getParagraphDirection(line);
1058e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
1059162bf0f1b9fd5d78ffa801917994f6222c6efd85Gilles Debunne        boolean lineChanged = false;
1060e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        boolean advance = toLeft == (lineDir == DIR_RIGHT_TO_LEFT);
1061162bf0f1b9fd5d78ffa801917994f6222c6efd85Gilles Debunne        // if walking off line, look at the line we're headed to
1062162bf0f1b9fd5d78ffa801917994f6222c6efd85Gilles Debunne        if (advance) {
1063162bf0f1b9fd5d78ffa801917994f6222c6efd85Gilles Debunne            if (caret == lineEnd) {
1064162bf0f1b9fd5d78ffa801917994f6222c6efd85Gilles Debunne                if (line < getLineCount() - 1) {
1065162bf0f1b9fd5d78ffa801917994f6222c6efd85Gilles Debunne                    lineChanged = true;
1066162bf0f1b9fd5d78ffa801917994f6222c6efd85Gilles Debunne                    ++line;
1067162bf0f1b9fd5d78ffa801917994f6222c6efd85Gilles Debunne                } else {
1068162bf0f1b9fd5d78ffa801917994f6222c6efd85Gilles Debunne                    return caret; // at very end, don't move
1069162bf0f1b9fd5d78ffa801917994f6222c6efd85Gilles Debunne                }
1070162bf0f1b9fd5d78ffa801917994f6222c6efd85Gilles Debunne            }
1071162bf0f1b9fd5d78ffa801917994f6222c6efd85Gilles Debunne        } else {
1072e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            if (caret == lineStart) {
1073e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                if (line > 0) {
1074162bf0f1b9fd5d78ffa801917994f6222c6efd85Gilles Debunne                    lineChanged = true;
1075e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    --line;
1076e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                } else {
1077e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    return caret; // at very start, don't move
1078e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                }
10799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
1080162bf0f1b9fd5d78ffa801917994f6222c6efd85Gilles Debunne        }
10819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1082162bf0f1b9fd5d78ffa801917994f6222c6efd85Gilles Debunne        if (lineChanged) {
1083e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            lineStart = getLineStart(line);
1084e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            lineEnd = getLineEnd(line);
1085e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            int newDir = getParagraphDirection(line);
1086e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            if (newDir != lineDir) {
1087e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                // unusual case.  we want to walk onto the line, but it runs
1088e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                // in a different direction than this one, so we fake movement
1089e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                // in the opposite direction.
1090e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                toLeft = !toLeft;
1091e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                lineDir = newDir;
10929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
10939f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        }
10949f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt
1095e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        Directions directions = getLineDirections(line);
10969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1097e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        TextLine tl = TextLine.obtain();
1098e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        // XXX: we don't care about tabs
1099e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        tl.set(mPaint, mText, lineStart, lineEnd, lineDir, directions, false, null);
1100e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        caret = lineStart + tl.getOffsetToLeftRightOf(caret - lineStart, toLeft);
1101e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        tl = TextLine.recycle(tl);
1102e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        return caret;
11039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
11049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
11059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private int getOffsetAtStartOf(int offset) {
11069f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        // XXX this probably should skip local reorderings and
11079f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        // zero-width characters, look at callers
11089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (offset == 0)
11099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return 0;
11109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
11119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        CharSequence text = mText;
11129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        char c = text.charAt(offset);
11139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
11149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (c >= '\uDC00' && c <= '\uDFFF') {
11159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            char c1 = text.charAt(offset - 1);
11169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
11179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (c1 >= '\uD800' && c1 <= '\uDBFF')
11189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                offset -= 1;
11199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
11209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
11219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (mSpannedText) {
11229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            ReplacementSpan[] spans = ((Spanned) text).getSpans(offset, offset,
11239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                                       ReplacementSpan.class);
11249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
11259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            for (int i = 0; i < spans.length; i++) {
11269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                int start = ((Spanned) text).getSpanStart(spans[i]);
11279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                int end = ((Spanned) text).getSpanEnd(spans[i]);
11289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
11299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (start < offset && end > offset)
11309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    offset = start;
11319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
11329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
11339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
11349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return offset;
11359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
11369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
11379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
11389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Fills in the specified Path with a representation of a cursor
11399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * at the specified offset.  This will often be a vertical line
11404e0c5e55e171532760d5f51e0165563827129d4eDoug Felt     * but can be multiple discontinuous lines in text with multiple
11419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * directionalities.
11429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
11439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public void getCursorPath(int point, Path dest,
11449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                              CharSequence editingBuffer) {
11459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        dest.reset();
11469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
11479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int line = getLineForOffset(point);
11489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int top = getLineTop(line);
11499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int bottom = getLineTop(line+1);
11509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
11519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        float h1 = getPrimaryHorizontal(point) - 0.5f;
1152f75c97e023af7d4ad9a8c129d4ea282b1c3b8f94Gilles Debunne        float h2 = isLevelBoundary(point) ? getSecondaryHorizontal(point) - 0.5f : h1;
11539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1154497a92cc5ba2176b8a8484b0a7da040eac0e887bJeff Brown        int caps = TextKeyListener.getMetaState(editingBuffer, TextKeyListener.META_SHIFT_ON) |
1155497a92cc5ba2176b8a8484b0a7da040eac0e887bJeff Brown                   TextKeyListener.getMetaState(editingBuffer, TextKeyListener.META_SELECTING);
1156497a92cc5ba2176b8a8484b0a7da040eac0e887bJeff Brown        int fn = TextKeyListener.getMetaState(editingBuffer, TextKeyListener.META_ALT_ON);
11579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int dist = 0;
11589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
11599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (caps != 0 || fn != 0) {
11609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            dist = (bottom - top) >> 2;
11619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
11629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (fn != 0)
11639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                top += dist;
11649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (caps != 0)
11659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                bottom -= dist;
11669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
11679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
11689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (h1 < 0.5f)
11699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            h1 = 0.5f;
11709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (h2 < 0.5f)
11719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            h2 = 0.5f;
11729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
11739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (h1 == h2) {
11749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            dest.moveTo(h1, top);
11759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            dest.lineTo(h1, bottom);
11769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } else {
11779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            dest.moveTo(h1, top);
11789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            dest.lineTo(h1, (top + bottom) >> 1);
11799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
11809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            dest.moveTo(h2, (top + bottom) >> 1);
11819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            dest.lineTo(h2, bottom);
11829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
11839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
11849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (caps == 2) {
11859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            dest.moveTo(h2, bottom);
11869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            dest.lineTo(h2 - dist, bottom + dist);
11879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            dest.lineTo(h2, bottom);
11889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            dest.lineTo(h2 + dist, bottom + dist);
11899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } else if (caps == 1) {
11909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            dest.moveTo(h2, bottom);
11919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            dest.lineTo(h2 - dist, bottom + dist);
11929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
11939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            dest.moveTo(h2 - dist, bottom + dist - 0.5f);
11949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            dest.lineTo(h2 + dist, bottom + dist - 0.5f);
11959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
11969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            dest.moveTo(h2 + dist, bottom + dist);
11979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            dest.lineTo(h2, bottom);
11989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
11999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
12009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (fn == 2) {
12019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            dest.moveTo(h1, top);
12029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            dest.lineTo(h1 - dist, top - dist);
12039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            dest.lineTo(h1, top);
12049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            dest.lineTo(h1 + dist, top - dist);
12059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } else if (fn == 1) {
12069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            dest.moveTo(h1, top);
12079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            dest.lineTo(h1 - dist, top - dist);
12089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
12099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            dest.moveTo(h1 - dist, top - dist + 0.5f);
12109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            dest.lineTo(h1 + dist, top - dist + 0.5f);
12119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
12129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            dest.moveTo(h1 + dist, top - dist);
12139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            dest.lineTo(h1, top);
12149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
12159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
12169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
12179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private void addSelection(int line, int start, int end,
12189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                              int top, int bottom, Path dest) {
12199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int linestart = getLineStart(line);
12209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int lineend = getLineEnd(line);
12219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        Directions dirs = getLineDirections(line);
12229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
12239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (lineend > linestart && mText.charAt(lineend - 1) == '\n')
12249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            lineend--;
12259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
12269f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        for (int i = 0; i < dirs.mDirections.length; i += 2) {
12279f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt            int here = linestart + dirs.mDirections[i];
12289f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt            int there = here + (dirs.mDirections[i+1] & RUN_LENGTH_MASK);
12299f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt
12309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (there > lineend)
12319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                there = lineend;
12329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
12339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (start <= there && end >= here) {
12349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                int st = Math.max(start, here);
12359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                int en = Math.min(end, there);
12369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
12379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (st != en) {
12389f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt                    float h1 = getHorizontal(st, false, line);
12399f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt                    float h2 = getHorizontal(en, true, line);
12409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
12419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    dest.addRect(h1, top, h2, bottom, Path.Direction.CW);
12429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
12439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
12449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
12459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
12469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
12479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
12489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Fills in the specified Path with a representation of a highlight
12499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * between the specified offsets.  This will often be a rectangle
12509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * or a potentially discontinuous set of rectangles.  If the start
12519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * and end are the same, the returned path is empty.
12529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
12539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public void getSelectionPath(int start, int end, Path dest) {
12549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        dest.reset();
12559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
12569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (start == end)
12579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return;
12589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
12599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (end < start) {
12609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int temp = end;
12619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            end = start;
12629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            start = temp;
12639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
12649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
12659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int startline = getLineForOffset(start);
12669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int endline = getLineForOffset(end);
12679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
12689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int top = getLineTop(startline);
12699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int bottom = getLineBottom(endline);
12709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
12719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (startline == endline) {
12729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            addSelection(startline, start, end, top, bottom, dest);
12739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } else {
12749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            final float width = mWidth;
12759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
12769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            addSelection(startline, start, getLineEnd(startline),
12779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                         top, getLineBottom(startline), dest);
12789f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt
12799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (getParagraphDirection(startline) == DIR_RIGHT_TO_LEFT)
12809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                dest.addRect(getLineLeft(startline), top,
12819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                              0, getLineBottom(startline), Path.Direction.CW);
12829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            else
12839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                dest.addRect(getLineRight(startline), top,
12849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                              width, getLineBottom(startline), Path.Direction.CW);
12859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
12869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            for (int i = startline + 1; i < endline; i++) {
12879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                top = getLineTop(i);
12889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                bottom = getLineBottom(i);
12899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                dest.addRect(0, top, width, bottom, Path.Direction.CW);
12909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
12919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
12929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            top = getLineTop(endline);
12939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            bottom = getLineBottom(endline);
12949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
12959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            addSelection(endline, getLineStart(endline), end,
12969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                         top, bottom, dest);
12979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
12989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (getParagraphDirection(endline) == DIR_RIGHT_TO_LEFT)
12999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                dest.addRect(width, top, getLineRight(endline), bottom, Path.Direction.CW);
13009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            else
13019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                dest.addRect(0, top, getLineLeft(endline), bottom, Path.Direction.CW);
13029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
13039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
13049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
13059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
13069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Get the alignment of the specified paragraph, taking into account
13079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * markup attached to it.
13089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
13099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public final Alignment getParagraphAlignment(int line) {
13109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        Alignment align = mAlignment;
13119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
13129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (mSpannedText) {
13139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            Spanned sp = (Spanned) mText;
131474d31ef2b2c42b54fa1f7cf94ea955ea67ab69a0Eric Fischer            AlignmentSpan[] spans = getParagraphSpans(sp, getLineStart(line),
13159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                                getLineEnd(line),
13169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                                AlignmentSpan.class);
13179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
13189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int spanLength = spans.length;
13199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (spanLength > 0) {
13209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                align = spans[spanLength-1].getAlignment();
13219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
13229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
13239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
13249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return align;
13259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
13269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
13279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
13289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Get the left edge of the specified paragraph, inset by left margins.
13299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
13309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public final int getParagraphLeft(int line) {
13319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int left = 0;
1332c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt        int dir = getParagraphDirection(line);
1333c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt        if (dir == DIR_RIGHT_TO_LEFT || !mSpannedText) {
1334c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt            return left; // leading margin has no impact, or no styles
13359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1336c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt        return getParagraphLeadingMargin(line);
13379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
13389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
13399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
13409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Get the right edge of the specified paragraph, inset by right margins.
13419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
13429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public final int getParagraphRight(int line) {
13439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int right = mWidth;
1344c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt        int dir = getParagraphDirection(line);
1345c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt        if (dir == DIR_LEFT_TO_RIGHT || !mSpannedText) {
1346c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt            return right; // leading margin has no impact, or no styles
1347c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt        }
1348c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt        return right - getParagraphLeadingMargin(line);
1349c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt    }
13509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1351c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt    /**
1352c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt     * Returns the effective leading margin (unsigned) for this line,
1353c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt     * taking into account LeadingMarginSpan and LeadingMarginSpan2.
1354c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt     * @param line the line index
1355c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt     * @return the leading margin of this line
1356c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt     */
1357c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt    private int getParagraphLeadingMargin(int line) {
1358c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt        if (!mSpannedText) {
1359c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt            return 0;
1360c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt        }
1361c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt        Spanned spanned = (Spanned) mText;
13620c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt
1363c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt        int lineStart = getLineStart(line);
1364c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt        int lineEnd = getLineEnd(line);
13650c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt        int spanEnd = spanned.nextSpanTransition(lineStart, lineEnd,
1366c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt                LeadingMarginSpan.class);
136774d31ef2b2c42b54fa1f7cf94ea955ea67ab69a0Eric Fischer        LeadingMarginSpan[] spans = getParagraphSpans(spanned, lineStart, spanEnd,
1368c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt                                                LeadingMarginSpan.class);
1369c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt        if (spans.length == 0) {
1370c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt            return 0; // no leading margin span;
1371c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt        }
13720c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt
1373c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt        int margin = 0;
13740c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt
13750c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt        boolean isFirstParaLine = lineStart == 0 ||
1376c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt            spanned.charAt(lineStart - 1) == '\n';
13770c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt
1378c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt        for (int i = 0; i < spans.length; i++) {
1379c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt            LeadingMarginSpan span = spans[i];
1380c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt            boolean useFirstLineMargin = isFirstParaLine;
1381c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt            if (span instanceof LeadingMarginSpan2) {
1382c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt                int spStart = spanned.getSpanStart(span);
1383c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt                int spanLine = getLineForOffset(spStart);
1384c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt                int count = ((LeadingMarginSpan2)span).getLeadingMarginLineCount();
13850c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                useFirstLineMargin = line < spanLine + count;
13869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
1387c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt            margin += span.getLeadingMargin(useFirstLineMargin);
13889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
13899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1390c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt        return margin;
13919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
13929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1393e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    /* package */
1394e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    static float measurePara(TextPaint paint, TextPaint workPaint,
1395c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt            CharSequence text, int start, int end) {
1396e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
1397e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        MeasuredText mt = MeasuredText.obtain();
1398e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        TextLine tl = TextLine.obtain();
1399e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        try {
1400e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            mt.setPara(text, start, end, DIR_REQUEST_LTR);
1401e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            Directions directions;
1402c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt            int dir;
1403c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt            if (mt.mEasy) {
1404e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                directions = DIRS_ALL_LEFT_TO_RIGHT;
1405c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt                dir = Layout.DIR_LEFT_TO_RIGHT;
1406e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            } else {
1407e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                directions = AndroidBidi.directions(mt.mDir, mt.mLevels,
1408e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    0, mt.mChars, 0, mt.mLen);
1409c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt                dir = mt.mDir;
1410c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt            }
1411c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt            char[] chars = mt.mChars;
1412c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt            int len = mt.mLen;
1413c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt            boolean hasTabs = false;
1414c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt            TabStops tabStops = null;
1415c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt            for (int i = 0; i < len; ++i) {
1416c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt                if (chars[i] == '\t') {
1417c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt                    hasTabs = true;
1418c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt                    if (text instanceof Spanned) {
1419c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt                        Spanned spanned = (Spanned) text;
14200c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                        int spanEnd = spanned.nextSpanTransition(start, end,
1421c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt                                TabStopSpan.class);
142274d31ef2b2c42b54fa1f7cf94ea955ea67ab69a0Eric Fischer                        TabStopSpan[] spans = getParagraphSpans(spanned, start, spanEnd,
1423c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt                                TabStopSpan.class);
1424c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt                        if (spans.length > 0) {
1425c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt                            tabStops = new TabStops(TAB_INCREMENT, spans);
1426c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt                        }
1427c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt                    }
1428c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt                    break;
1429c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt                }
14309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
1431c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt            tl.set(paint, text, start, end, dir, directions, hasTabs, tabStops);
1432e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            return tl.metrics(null);
1433e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        } finally {
1434e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            TextLine.recycle(tl);
1435e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            MeasuredText.recycle(mt);
14369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
14379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
14389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
143971b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt    /**
1440c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt     * @hide
1441c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt     */
1442c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt    /* package */ static class TabStops {
1443c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt        private int[] mStops;
1444c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt        private int mNumStops;
1445c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt        private int mIncrement;
14460c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt
1447c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt        TabStops(int increment, Object[] spans) {
1448c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt            reset(increment, spans);
1449c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt        }
14500c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt
1451c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt        void reset(int increment, Object[] spans) {
1452c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt            this.mIncrement = increment;
1453c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt
1454c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt            int ns = 0;
1455c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt            if (spans != null) {
1456c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt                int[] stops = this.mStops;
1457c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt                for (Object o : spans) {
1458c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt                    if (o instanceof TabStopSpan) {
1459c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt                        if (stops == null) {
1460c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt                            stops = new int[10];
1461c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt                        } else if (ns == stops.length) {
1462c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt                            int[] nstops = new int[ns * 2];
1463c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt                            for (int i = 0; i < ns; ++i) {
1464c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt                                nstops[i] = stops[i];
1465c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt                            }
1466c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt                            stops = nstops;
1467c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt                        }
1468c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt                        stops[ns++] = ((TabStopSpan) o).getTabStop();
1469c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt                    }
1470c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt                }
1471c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt                if (ns > 1) {
1472c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt                    Arrays.sort(stops, 0, ns);
1473c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt                }
1474c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt                if (stops != this.mStops) {
1475c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt                    this.mStops = stops;
1476c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt                }
1477c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt            }
1478c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt            this.mNumStops = ns;
1479c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt        }
14800c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt
1481c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt        float nextTab(float h) {
1482c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt            int ns = this.mNumStops;
1483c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt            if (ns > 0) {
1484c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt                int[] stops = this.mStops;
1485c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt                for (int i = 0; i < ns; ++i) {
1486c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt                    int stop = stops[i];
1487c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt                    if (stop > h) {
1488c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt                        return stop;
1489c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt                    }
1490c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt                }
1491c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt            }
1492c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt            return nextDefaultStop(h, mIncrement);
1493c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt        }
1494c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt
1495c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt        public static float nextDefaultStop(float h, int inc) {
1496c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt            return ((int) ((h + inc) / inc)) * inc;
1497c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt        }
1498c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt    }
14990c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt
1500c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt    /**
150171b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt     * Returns the position of the next tab stop after h on the line.
150271b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt     *
150371b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt     * @param text the text
150471b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt     * @param start start of the line
150571b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt     * @param end limit of the line
150671b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt     * @param h the current horizontal offset
150771b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt     * @param tabs the tabs, can be null.  If it is null, any tabs in effect
150871b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt     * on the line will be used.  If there are no tabs, a default offset
150971b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt     * will be used to compute the tab stop.
151071b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt     * @return the offset of the next tab stop.
151171b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt     */
15129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /* package */ static float nextTab(CharSequence text, int start, int end,
15139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                       float h, Object[] tabs) {
15149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        float nh = Float.MAX_VALUE;
15159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        boolean alltabs = false;
15169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
15179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (text instanceof Spanned) {
15189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (tabs == null) {
151974d31ef2b2c42b54fa1f7cf94ea955ea67ab69a0Eric Fischer                tabs = getParagraphSpans((Spanned) text, start, end, TabStopSpan.class);
15209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                alltabs = true;
15219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
15229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
15239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            for (int i = 0; i < tabs.length; i++) {
15249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (!alltabs) {
15259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    if (!(tabs[i] instanceof TabStopSpan))
15269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        continue;
15279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
15289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
15299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                int where = ((TabStopSpan) tabs[i]).getTabStop();
15309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
15319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (where < nh && where > h)
15329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    nh = where;
15339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
15349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
15359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (nh != Float.MAX_VALUE)
15369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                return nh;
15379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
15389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
15399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return ((int) ((h + TAB_INCREMENT) / TAB_INCREMENT)) * TAB_INCREMENT;
15409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
15419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
15429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    protected final boolean isSpanned() {
15439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return mSpannedText;
15449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
15459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
154674d31ef2b2c42b54fa1f7cf94ea955ea67ab69a0Eric Fischer    /**
154774d31ef2b2c42b54fa1f7cf94ea955ea67ab69a0Eric Fischer     * Returns the same as <code>text.getSpans()</code>, except where
154874d31ef2b2c42b54fa1f7cf94ea955ea67ab69a0Eric Fischer     * <code>start</code> and <code>end</code> are the same and are not
154974d31ef2b2c42b54fa1f7cf94ea955ea67ab69a0Eric Fischer     * at the very beginning of the text, in which case an empty array
155074d31ef2b2c42b54fa1f7cf94ea955ea67ab69a0Eric Fischer     * is returned instead.
155174d31ef2b2c42b54fa1f7cf94ea955ea67ab69a0Eric Fischer     * <p>
155274d31ef2b2c42b54fa1f7cf94ea955ea67ab69a0Eric Fischer     * This is needed because of the special case that <code>getSpans()</code>
155374d31ef2b2c42b54fa1f7cf94ea955ea67ab69a0Eric Fischer     * on an empty range returns the spans adjacent to that range, which is
155474d31ef2b2c42b54fa1f7cf94ea955ea67ab69a0Eric Fischer     * primarily for the sake of <code>TextWatchers</code> so they will get
155574d31ef2b2c42b54fa1f7cf94ea955ea67ab69a0Eric Fischer     * notifications when text goes from empty to non-empty.  But it also
155674d31ef2b2c42b54fa1f7cf94ea955ea67ab69a0Eric Fischer     * has the unfortunate side effect that if the text ends with an empty
155774d31ef2b2c42b54fa1f7cf94ea955ea67ab69a0Eric Fischer     * paragraph, that paragraph accidentally picks up the styles of the
155874d31ef2b2c42b54fa1f7cf94ea955ea67ab69a0Eric Fischer     * preceding paragraph (even though those styles will not be picked up
155974d31ef2b2c42b54fa1f7cf94ea955ea67ab69a0Eric Fischer     * by new text that is inserted into the empty paragraph).
156074d31ef2b2c42b54fa1f7cf94ea955ea67ab69a0Eric Fischer     * <p>
156174d31ef2b2c42b54fa1f7cf94ea955ea67ab69a0Eric Fischer     * The reason it just checks whether <code>start</code> and <code>end</code>
156274d31ef2b2c42b54fa1f7cf94ea955ea67ab69a0Eric Fischer     * is the same is that the only time a line can contain 0 characters
156374d31ef2b2c42b54fa1f7cf94ea955ea67ab69a0Eric Fischer     * is if it is the final paragraph of the Layout; otherwise any line will
156474d31ef2b2c42b54fa1f7cf94ea955ea67ab69a0Eric Fischer     * contain at least one printing or newline character.  The reason for the
156574d31ef2b2c42b54fa1f7cf94ea955ea67ab69a0Eric Fischer     * additional check if <code>start</code> is greater than 0 is that
156674d31ef2b2c42b54fa1f7cf94ea955ea67ab69a0Eric Fischer     * if the empty paragraph is the entire content of the buffer, paragraph
156774d31ef2b2c42b54fa1f7cf94ea955ea67ab69a0Eric Fischer     * styles that are already applied to the buffer will apply to text that
156874d31ef2b2c42b54fa1f7cf94ea955ea67ab69a0Eric Fischer     * is inserted into it.
156974d31ef2b2c42b54fa1f7cf94ea955ea67ab69a0Eric Fischer     */
157074d31ef2b2c42b54fa1f7cf94ea955ea67ab69a0Eric Fischer    /* package */ static <T> T[] getParagraphSpans(Spanned text, int start, int end, Class<T> type) {
157174d31ef2b2c42b54fa1f7cf94ea955ea67ab69a0Eric Fischer        if (start == end && start > 0) {
157274d31ef2b2c42b54fa1f7cf94ea955ea67ab69a0Eric Fischer            return (T[]) ArrayUtils.emptyArray(type);
157374d31ef2b2c42b54fa1f7cf94ea955ea67ab69a0Eric Fischer        }
157474d31ef2b2c42b54fa1f7cf94ea955ea67ab69a0Eric Fischer
157574d31ef2b2c42b54fa1f7cf94ea955ea67ab69a0Eric Fischer        return text.getSpans(start, end, type);
157674d31ef2b2c42b54fa1f7cf94ea955ea67ab69a0Eric Fischer    }
157774d31ef2b2c42b54fa1f7cf94ea955ea67ab69a0Eric Fischer
15789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private void ellipsize(int start, int end, int line,
15799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                           char[] dest, int destoff) {
15809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int ellipsisCount = getEllipsisCount(line);
15819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
15829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (ellipsisCount == 0) {
15839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return;
15849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
15859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
15869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int ellipsisStart = getEllipsisStart(line);
15879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int linestart = getLineStart(line);
15889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
15899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        for (int i = ellipsisStart; i < ellipsisStart + ellipsisCount; i++) {
15909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            char c;
15919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
15929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (i == ellipsisStart) {
15939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                c = '\u2026'; // ellipsis
15949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } else {
15959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                c = '\uFEFF'; // 0-width space
15969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
15979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
15989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int a = i + linestart;
15999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
16009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (a >= start && a < end) {
16019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                dest[destoff + a - start] = c;
16029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
16039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
16049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
16059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
16069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
16079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Stores information about bidirectional (left-to-right or right-to-left)
16089f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt     * text within the layout of a line.
16099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
16109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static class Directions {
16119f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        // Directions represents directional runs within a line of text.
16129f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        // Runs are pairs of ints listed in visual order, starting from the
16139f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        // leading margin.  The first int of each pair is the offset from
16149f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        // the first character of the line to the start of the run.  The
16159f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        // second int represents both the length and level of the run.
16169f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        // The length is in the lower bits, accessed by masking with
16179f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        // DIR_LENGTH_MASK.  The level is in the higher bits, accessed
16189f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        // by shifting by DIR_LEVEL_SHIFT and masking by DIR_LEVEL_MASK.
16199f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        // To simply test for an RTL direction, test the bit using
16209f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        // DIR_RTL_FLAG, if set then the direction is rtl.
16219f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt
16229f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        /* package */ int[] mDirections;
16239f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        /* package */ Directions(int[] dirs) {
16249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mDirections = dirs;
16259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
16269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
16279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
16289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
16299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Return the offset of the first character to be ellipsized away,
16309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * relative to the start of the line.  (So 0 if the beginning of the
16319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * line is ellipsized, not getLineStart().)
16329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
16339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public abstract int getEllipsisStart(int line);
1634e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
16359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
16369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Returns the number of characters to be ellipsized away, or 0 if
16379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * no ellipsis is to take place.
16389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
16399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public abstract int getEllipsisCount(int line);
16409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
16419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /* package */ static class Ellipsizer implements CharSequence, GetChars {
16429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /* package */ CharSequence mText;
16439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /* package */ Layout mLayout;
16449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /* package */ int mWidth;
16459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /* package */ TextUtils.TruncateAt mMethod;
16469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
16479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public Ellipsizer(CharSequence s) {
16489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mText = s;
16499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
16509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
16519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public char charAt(int off) {
16529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            char[] buf = TextUtils.obtain(1);
16539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            getChars(off, off + 1, buf, 0);
16549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            char ret = buf[0];
16559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
16569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            TextUtils.recycle(buf);
16579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return ret;
16589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
16599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
16609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public void getChars(int start, int end, char[] dest, int destoff) {
16619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int line1 = mLayout.getLineForOffset(start);
16629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int line2 = mLayout.getLineForOffset(end);
16639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
16649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            TextUtils.getChars(mText, start, end, dest, destoff);
16659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
16669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            for (int i = line1; i <= line2; i++) {
16679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                mLayout.ellipsize(start, end, i, dest, destoff);
16689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
16699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
16709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
16719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public int length() {
16729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return mText.length();
16739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
16749f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt
16759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public CharSequence subSequence(int start, int end) {
16769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            char[] s = new char[end - start];
16779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            getChars(start, end, s, 0);
16789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return new String(s);
16799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
16809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1681162bf0f1b9fd5d78ffa801917994f6222c6efd85Gilles Debunne        @Override
16829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public String toString() {
16839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            char[] s = new char[length()];
16849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            getChars(0, length(), s, 0);
16859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return new String(s);
16869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
16879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
16889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
16899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
16909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /* package */ static class SpannedEllipsizer
16919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    extends Ellipsizer implements Spanned {
16929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        private Spanned mSpanned;
16939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
16949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public SpannedEllipsizer(CharSequence display) {
16959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            super(display);
16969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mSpanned = (Spanned) display;
16979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
16989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
16999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public <T> T[] getSpans(int start, int end, Class<T> type) {
17009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return mSpanned.getSpans(start, end, type);
17019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
17029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
17039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public int getSpanStart(Object tag) {
17049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return mSpanned.getSpanStart(tag);
17059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
17069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
17079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public int getSpanEnd(Object tag) {
17089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return mSpanned.getSpanEnd(tag);
17099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
17109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
17119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public int getSpanFlags(Object tag) {
17129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return mSpanned.getSpanFlags(tag);
17139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
17149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
17159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public int nextSpanTransition(int start, int limit, Class type) {
17169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return mSpanned.nextSpanTransition(start, limit, type);
17179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
17189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1719162bf0f1b9fd5d78ffa801917994f6222c6efd85Gilles Debunne        @Override
17209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public CharSequence subSequence(int start, int end) {
17219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            char[] s = new char[end - start];
17229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            getChars(start, end, s, 0);
17239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
17249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            SpannableString ss = new SpannableString(new String(s));
17259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            TextUtils.copySpansFrom(mSpanned, start, end, Object.class, ss, 0);
17269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return ss;
17279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
17289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
17299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
17309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private CharSequence mText;
17319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private TextPaint mPaint;
17329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /* package */ TextPaint mWorkPaint;
17339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private int mWidth;
17349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private Alignment mAlignment = Alignment.ALIGN_NORMAL;
17359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private float mSpacingMult;
17369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private float mSpacingAdd;
1737c4d8eb6fb7c88c5c4da38b0b113c24cc4b78c0b7Romain Guy    private static final Rect sTempRect = new Rect();
17389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private boolean mSpannedText;
17399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
17409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static final int DIR_LEFT_TO_RIGHT = 1;
17419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static final int DIR_RIGHT_TO_LEFT = -1;
17429f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt
174320178d62cf669af18467a16d3c4c4237ed42151cDoug Felt    /* package */ static final int DIR_REQUEST_LTR = 1;
174420178d62cf669af18467a16d3c4c4237ed42151cDoug Felt    /* package */ static final int DIR_REQUEST_RTL = -1;
174520178d62cf669af18467a16d3c4c4237ed42151cDoug Felt    /* package */ static final int DIR_REQUEST_DEFAULT_LTR = 2;
174620178d62cf669af18467a16d3c4c4237ed42151cDoug Felt    /* package */ static final int DIR_REQUEST_DEFAULT_RTL = -2;
17479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
17489f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt    /* package */ static final int RUN_LENGTH_MASK = 0x03ffffff;
17499f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt    /* package */ static final int RUN_LEVEL_SHIFT = 26;
17509f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt    /* package */ static final int RUN_LEVEL_MASK = 0x3f;
17519f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt    /* package */ static final int RUN_RTL_FLAG = 1 << RUN_LEVEL_SHIFT;
17529f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt
17539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public enum Alignment {
17549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        ALIGN_NORMAL,
17559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        ALIGN_OPPOSITE,
17569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        ALIGN_CENTER,
17579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // XXX ALIGN_LEFT,
17589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // XXX ALIGN_RIGHT,
17599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
17609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
17619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final int TAB_INCREMENT = 20;
17629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
17639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /* package */ static final Directions DIRS_ALL_LEFT_TO_RIGHT =
17649f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        new Directions(new int[] { 0, RUN_LENGTH_MASK });
17659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /* package */ static final Directions DIRS_ALL_RIGHT_TO_LEFT =
17669f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        new Directions(new int[] { 0, RUN_LENGTH_MASK | RUN_RTL_FLAG });
17670a4db3c5270440eeb7e4e44a7029926e239ec3bdGilles Debunne
17680a4db3c5270440eeb7e4e44a7029926e239ec3bdGilles Debunne    /**
17690a4db3c5270440eeb7e4e44a7029926e239ec3bdGilles Debunne     * Inform this layout that not all of its lines will be displayed, because a maximum number of
17700a4db3c5270440eeb7e4e44a7029926e239ec3bdGilles Debunne     * lines has been set on the associated TextView.
17710a4db3c5270440eeb7e4e44a7029926e239ec3bdGilles Debunne     *
17720a4db3c5270440eeb7e4e44a7029926e239ec3bdGilles Debunne     * A non positive value means that all lines are displayed.
17730a4db3c5270440eeb7e4e44a7029926e239ec3bdGilles Debunne     *
17740a4db3c5270440eeb7e4e44a7029926e239ec3bdGilles Debunne     * @param line line number of the last visible line (line numbers start at 1 for the first line)
17750a4db3c5270440eeb7e4e44a7029926e239ec3bdGilles Debunne     * @hide
17760a4db3c5270440eeb7e4e44a7029926e239ec3bdGilles Debunne     */
17770a4db3c5270440eeb7e4e44a7029926e239ec3bdGilles Debunne    public void setMaximumVisibleLineCount(int line) {}
17789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project}
1779