Layout.java revision e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7a
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;
299f7a4442b89cc06cb8cae6992484e7ae795323abDoug Feltimport android.text.style.LineBackgroundSpan;
309f7a4442b89cc06cb8cae6992484e7ae795323abDoug Feltimport android.text.style.ParagraphStyle;
319f7a4442b89cc06cb8cae6992484e7ae795323abDoug Feltimport android.text.style.ReplacementSpan;
329f7a4442b89cc06cb8cae6992484e7ae795323abDoug Feltimport android.text.style.TabStopSpan;
339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.view.KeyEvent;
349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
359f7a4442b89cc06cb8cae6992484e7ae795323abDoug Feltimport junit.framework.Assert;
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 {
4576c0226008ee16633dc94ed4dbeadef2174f6bd9Dave Bort    private static final boolean DEBUG = false;
4671b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt    private static final ParagraphStyle[] NO_PARA_SPANS =
4771b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt        ArrayUtils.emptyArray(ParagraphStyle.class);
4876c0226008ee16633dc94ed4dbeadef2174f6bd9Dave Bort
49105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project    /* package */ static final EmojiFactory EMOJI_FACTORY =
50105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project        EmojiFactory.newAvailableInstance();
51105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project    /* package */ static final int MIN_EMOJI, MAX_EMOJI;
52105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project
53105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project    static {
54105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project        if (EMOJI_FACTORY != null) {
55105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project            MIN_EMOJI = EMOJI_FACTORY.getMinimumAndroidPua();
56105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project            MAX_EMOJI = EMOJI_FACTORY.getMaximumAndroidPua();
57105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project        } else {
58105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project            MIN_EMOJI = -1;
59105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project            MAX_EMOJI = -1;
60105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project        }
61e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    }
62c2d54f46ac13e029e6d53f7471cd9c90fe6bbfe9Eric Fischer
639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
6471b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt     * Return how wide a layout must be in order to display the
659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * specified text with one line per paragraph.
669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static float getDesiredWidth(CharSequence source,
689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                        TextPaint paint) {
699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return getDesiredWidth(source, 0, source.length(), paint);
709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
719f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt
729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
7371b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt     * Return how wide a layout must be in order to display the
749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * specified text slice with one line per paragraph.
759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static float getDesiredWidth(CharSequence source,
779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                        int start, int end,
789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                        TextPaint paint) {
799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        float need = 0;
809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        TextPaint workPaint = new TextPaint();
819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int next;
839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        for (int i = start; i <= end; i = next) {
849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            next = TextUtils.indexOf(source, '\n', i, end);
859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (next < 0)
879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                next = end;
889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
8971b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt            // note, omits trailing paragraph char
90e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            float w = measurePara(paint, workPaint,
91e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                                  source, i, next, true, null);
929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (w > need)
949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                need = w;
959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            next++;
979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return need;
1009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
1039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Subclasses of Layout use this constructor to set the display text,
1049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * width, and other standard properties.
10571b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt     * @param text the text to render
10671b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt     * @param paint the default paint for the layout.  Styles can override
10771b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt     * various attributes of the paint.
10871b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt     * @param width the wrapping width for the text.
10971b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt     * @param align whether to left, right, or center the text.  Styles can
11071b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt     * override the alignment.
11171b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt     * @param spacingMult factor by which to scale the font size to get the
11271b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt     * default line spacing
11371b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt     * @param spacingAdd amount to add to the default line spacing
1149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
1159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    protected Layout(CharSequence text, TextPaint paint,
1169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                     int width, Alignment align,
11771b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt                     float spacingMult, float spacingAdd) {
1189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (width < 0)
1199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            throw new IllegalArgumentException("Layout: " + width + " < 0");
1209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
121e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        // Ensure paint doesn't have baselineShift set.
122e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        // While normally we don't modify the paint the user passed in,
123e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        // we were already doing this in Styled.drawUniformRun with both
124e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        // baselineShift and bgColor.  We probably should reevaluate bgColor.
125e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        if (paint != null) {
126e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            paint.bgColor = 0;
127e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            paint.baselineShift = 0;
128e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        }
129e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
1309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mText = text;
1319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mPaint = paint;
1329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mWorkPaint = new TextPaint();
1339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mWidth = width;
1349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mAlignment = align;
13571b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt        mSpacingMult = spacingMult;
13671b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt        mSpacingAdd = spacingAdd;
1379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mSpannedText = text instanceof Spanned;
1389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
1419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Replace constructor properties of this Layout with new ones.  Be careful.
1429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
1439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /* package */ void replaceWith(CharSequence text, TextPaint paint,
1449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                              int width, Alignment align,
1459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                              float spacingmult, float spacingadd) {
1469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (width < 0) {
1479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            throw new IllegalArgumentException("Layout: " + width + " < 0");
1489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mText = text;
1519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mPaint = paint;
1529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mWidth = width;
1539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mAlignment = align;
1549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mSpacingMult = spacingmult;
1559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mSpacingAdd = spacingadd;
1569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mSpannedText = text instanceof Spanned;
1579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
1609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Draw this Layout on the specified Canvas.
1619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
1629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public void draw(Canvas c) {
1639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        draw(c, null, null, 0);
1649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
16771b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt     * Draw this Layout on the specified canvas, with the highlight path drawn
16871b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt     * between the background and the text.
16971b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt     *
17071b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt     * @param c the canvas
17171b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt     * @param highlight the path of the highlight or cursor; can be null
17271b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt     * @param highlightPaint the paint for the highlight
17371b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt     * @param cursorOffsetVertical the amount to temporarily translate the
17471b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt     *        canvas while rendering the highlight
1759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
17671b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt    public void draw(Canvas c, Path highlight, Paint highlightPaint,
1779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                     int cursorOffsetVertical) {
1789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int dtop, dbottom;
1799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        synchronized (sTempRect) {
1819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (!c.getClipBounds(sTempRect)) {
1829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                return;
1839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
1849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            dtop = sTempRect.top;
1869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            dbottom = sTempRect.bottom;
1879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int top = 0;
1919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int bottom = getLineTop(getLineCount());
1929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (dtop > top) {
1949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            top = dtop;
1959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (dbottom < bottom) {
1979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            bottom = dbottom;
1989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1999f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt
2009f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        int first = getLineForVertical(top);
2019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int last = getLineForVertical(bottom);
2029f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt
2039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int previousLineBottom = getLineTop(first);
2049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int previousLineEnd = getLineStart(first);
2059f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt
20671b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt        TextPaint paint = mPaint;
2079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        CharSequence buf = mText;
20871b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt        int width = mWidth;
20971b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt        boolean spannedText = mSpannedText;
2109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
21171b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt        ParagraphStyle[] spans = NO_PARA_SPANS;
2129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int spanend = 0;
2139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int textLength = 0;
2149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
21571b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt        // First, draw LineBackgroundSpans.
21671b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt        // LineBackgroundSpans know nothing about the alignment or direction of
21771b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt        // the layout or line.  XXX: Should they?
2189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (spannedText) {
2199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            textLength = buf.length();
2209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            for (int i = first; i <= last; i++) {
2219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                int start = previousLineEnd;
2229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                int end = getLineStart(i+1);
2239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                previousLineEnd = end;
2249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                int ltop = previousLineBottom;
2269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                int lbottom = getLineTop(i+1);
2279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                previousLineBottom = lbottom;
2289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                int lbaseline = lbottom - getLineDescent(i);
2299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (start >= spanend) {
2319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                   Spanned sp = (Spanned) buf;
2329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                   spanend = sp.nextSpanTransition(start, textLength,
2339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                                   LineBackgroundSpan.class);
2349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                   spans = sp.getSpans(start, spanend,
2359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                       LineBackgroundSpan.class);
2369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
2379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                for (int n = 0; n < spans.length; n++) {
2399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    LineBackgroundSpan back = (LineBackgroundSpan) spans[n];
2409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
24171b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt                    back.drawBackground(c, paint, 0, width,
2429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                       ltop, lbaseline, lbottom,
2439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                       buf, start, end,
2449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                       i);
2459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
2469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
2479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // reset to their original values
2489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            spanend = 0;
2499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            previousLineBottom = getLineTop(first);
2509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            previousLineEnd = getLineStart(first);
25171b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt            spans = NO_PARA_SPANS;
2529f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        }
2539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // There can be a highlight even without spans if we are drawing
2559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // a non-spanned transformation of a spanned editing buffer.
2569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (highlight != null) {
2579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (cursorOffsetVertical != 0) {
2589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                c.translate(0, cursorOffsetVertical);
2599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
2609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
26171b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt            c.drawPath(highlight, highlightPaint);
2629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (cursorOffsetVertical != 0) {
2649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                c.translate(0, -cursorOffsetVertical);
2659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
2669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
2679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        Alignment align = mAlignment;
2699f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt
270e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        TextLine tl = TextLine.obtain();
27171b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt        // Next draw the lines, one at a time.
27271b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt        // the baseline is the top of the following line minus the current
27371b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt        // line's descent.
2749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        for (int i = first; i <= last; i++) {
2759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int start = previousLineEnd;
2769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            previousLineEnd = getLineStart(i+1);
2789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int end = getLineVisibleEnd(i, start, previousLineEnd);
2799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int ltop = previousLineBottom;
2819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int lbottom = getLineTop(i+1);
2829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            previousLineBottom = lbottom;
2839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int lbaseline = lbottom - getLineDescent(i);
2849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
28571b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt            boolean isFirstParaLine = false;
2869f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt            if (spannedText) {
2879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (start == 0 || buf.charAt(start - 1) == '\n') {
28871b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt                    isFirstParaLine = true;
2899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
29071b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt                // New batch of paragraph styles, compute the alignment.
29171b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt                // Last alignment style wins.
2929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (start >= spanend) {
2939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    Spanned sp = (Spanned) buf;
2949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    spanend = sp.nextSpanTransition(start, textLength,
2959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                                    ParagraphStyle.class);
2969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    spans = sp.getSpans(start, spanend, ParagraphStyle.class);
2979f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt
2989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    align = mAlignment;
2999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    for (int n = spans.length-1; n >= 0; n--) {
3009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        if (spans[n] instanceof AlignmentSpan) {
3019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                            align = ((AlignmentSpan) spans[n]).getAlignment();
3029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                            break;
3039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        }
3049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    }
3059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
3069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
3079f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt
3089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int dir = getParagraphDirection(i);
3099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int left = 0;
3109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int right = mWidth;
3119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
31271b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt            // Draw all leading margin spans.  Adjust left or right according
31371b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt            // to the paragraph direction of the line.
3149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (spannedText) {
3159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                final int length = spans.length;
3169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                for (int n = 0; n < length; n++) {
3179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    if (spans[n] instanceof LeadingMarginSpan) {
3189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        LeadingMarginSpan margin = (LeadingMarginSpan) spans[n];
3199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        if (dir == DIR_RIGHT_TO_LEFT) {
3219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                            margin.drawLeadingMargin(c, paint, right, dir, ltop,
3229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                                     lbaseline, lbottom, buf,
32371b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt                                                     start, end, isFirstParaLine, this);
3249f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt
32571b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt                            right -= margin.getLeadingMargin(isFirstParaLine);
3269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        } else {
3279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                            margin.drawLeadingMargin(c, paint, left, dir, ltop,
3289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                                     lbaseline, lbottom, buf,
32971b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt                                                     start, end, isFirstParaLine, this);
3309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
33171b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt                            boolean useMargin = isFirstParaLine;
3327b5676e4d40a09ccdbc8b6f691a3d8be23e480d3Mark Wagner                            if (margin instanceof LeadingMarginSpan.LeadingMarginSpan2) {
3337b5676e4d40a09ccdbc8b6f691a3d8be23e480d3Mark Wagner                                int count = ((LeadingMarginSpan.LeadingMarginSpan2)margin).getLeadingMarginLineCount();
3347b5676e4d40a09ccdbc8b6f691a3d8be23e480d3Mark Wagner                                useMargin = count > i;
3357b5676e4d40a09ccdbc8b6f691a3d8be23e480d3Mark Wagner                            }
3367b5676e4d40a09ccdbc8b6f691a3d8be23e480d3Mark Wagner                            left += margin.getLeadingMargin(useMargin);
3379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        }
3389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    }
3399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
3409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
3419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
34271b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt            // Adjust the point at which to start rendering depending on the
34371b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt            // alignment of the paragraph.
3449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int x;
3459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (align == Alignment.ALIGN_NORMAL) {
3469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (dir == DIR_LEFT_TO_RIGHT) {
3479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    x = left;
3489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                } else {
3499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    x = right;
3509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
3519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } else {
3529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                int max = (int)getLineMax(i, spans, false);
3539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (align == Alignment.ALIGN_OPPOSITE) {
3549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    if (dir == DIR_RIGHT_TO_LEFT) {
3559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        x = left + max;
3569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    } else {
3579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        x = right - max;
3589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    }
3599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                } else {
3609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    // Alignment.ALIGN_CENTER
3619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    max = max & ~1;
3629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    int half = (right - left - max) >> 1;
3639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    if (dir == DIR_RIGHT_TO_LEFT) {
3649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        x = right - half;
3659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    } else {
3669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        x = left + half;
3679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    }
3689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
3699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
3709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            Directions directions = getLineDirections(i);
3729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            boolean hasTab = getLineContainsTab(i);
3739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (directions == DIRS_ALL_LEFT_TO_RIGHT &&
3749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    !spannedText && !hasTab) {
37576c0226008ee16633dc94ed4dbeadef2174f6bd9Dave Bort                if (DEBUG) {
3769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    Assert.assertTrue(dir == DIR_LEFT_TO_RIGHT);
3779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    Assert.assertNotNull(c);
3789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
37971b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt                // XXX: assumes there's nothing additional to be done
3809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                c.drawText(buf, start, end, x, lbaseline, paint);
3819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } else {
382e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                tl.set(paint, buf, start, end, dir, directions, hasTab, spans);
383e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                tl.draw(c, x, ltop, lbaseline, lbottom);
3849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
3859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
386e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        TextLine.recycle(tl);
3879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
3889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
3909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Return the text that is displayed by this Layout.
3919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
3929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public final CharSequence getText() {
3939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return mText;
3949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
3959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
3979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Return the base Paint properties for this layout.
3989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Do NOT change the paint, which may result in funny
3999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * drawing for this layout.
4009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
4019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public final TextPaint getPaint() {
4029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return mPaint;
4039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
4049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
4069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Return the width of this layout.
4079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
4089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public final int getWidth() {
4099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return mWidth;
4109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
4119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
4139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Return the width to which this Layout is ellipsizing, or
4149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * {@link #getWidth} if it is not doing anything special.
4159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
4169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public int getEllipsizedWidth() {
4179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return mWidth;
4189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
4199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
4219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Increase the width of this layout to the specified width.
42271b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt     * Be careful to use this only when you know it is appropriate&mdash;
4239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * it does not cause the text to reflow to use the full new width.
4249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
4259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public final void increaseWidthTo(int wid) {
4269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (wid < mWidth) {
4279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            throw new RuntimeException("attempted to reduce Layout width");
4289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
4299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mWidth = wid;
4319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
4329f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt
4339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
4349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Return the total height of this layout.
4359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
4369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public int getHeight() {
43771b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt        return getLineTop(getLineCount());
4389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
4399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
4419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Return the base alignment of this layout.
4429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
4439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public final Alignment getAlignment() {
4449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return mAlignment;
4459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
4469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
4489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Return what the text height is multiplied by to get the line height.
4499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
4509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public final float getSpacingMultiplier() {
4519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return mSpacingMult;
4529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
4539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
4559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Return the number of units of leading that are added to each line.
4569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
4579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public final float getSpacingAdd() {
4589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return mSpacingAdd;
4599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
4609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
4629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Return the number of lines of text in this layout.
4639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
4649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public abstract int getLineCount();
4659f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt
4669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
4679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Return the baseline for the specified line (0&hellip;getLineCount() - 1)
4689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * If bounds is not null, return the top, left, right, bottom extents
4699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * of the specified line in it.
4709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param line which line to examine (0..getLineCount() - 1)
4719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param bounds Optional. If not null, it returns the extent of the line
4729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @return the Y-coordinate of the baseline
4739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
4749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public int getLineBounds(int line, Rect bounds) {
4759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (bounds != null) {
4769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            bounds.left = 0;     // ???
4779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            bounds.top = getLineTop(line);
4789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            bounds.right = mWidth;   // ???
47971b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt            bounds.bottom = getLineTop(line + 1);
4809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
4819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return getLineBaseline(line);
4829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
4839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
48571b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt     * Return the vertical position of the top of the specified line
48671b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt     * (0&hellip;getLineCount()).
48771b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt     * If the specified line is equal to the line count, returns the
4889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * bottom of the last line.
4899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
4909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public abstract int getLineTop(int line);
4919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
49371b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt     * Return the descent of the specified line(0&hellip;getLineCount() - 1).
4949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
4959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public abstract int getLineDescent(int line);
4969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
49871b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt     * Return the text offset of the beginning of the specified line (
49971b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt     * 0&hellip;getLineCount()). If the specified line is equal to the line
50071b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt     * count, returns the length of the text.
5019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
5029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public abstract int getLineStart(int line);
5039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
50571b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt     * Returns the primary directionality of the paragraph containing the
50671b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt     * specified line, either 1 for left-to-right lines, or -1 for right-to-left
50771b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt     * lines (see {@link #DIR_LEFT_TO_RIGHT}, {@link #DIR_RIGHT_TO_LEFT}).
5089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
5099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public abstract int getParagraphDirection(int line);
5109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
512105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project     * Returns whether the specified line contains one or more
513105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project     * characters that need to be handled specially, like tabs
514105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project     * or emoji.
5159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
5169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public abstract boolean getLineContainsTab(int line);
5179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
51971b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt     * Returns the directional run information for the specified line.
5209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * The array alternates counts of characters in left-to-right
5219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * and right-to-left segments of the line.
52271b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt     *
52371b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt     * <p>NOTE: this is inadequate to support bidirectional text, and will change.
5249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
5259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public abstract Directions getLineDirections(int line);
5269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
5289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Returns the (negative) number of extra pixels of ascent padding in the
5299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * top line of the Layout.
5309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
5319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public abstract int getTopPadding();
5329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
5349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Returns the number of extra pixels of descent padding in the
5359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * bottom line of the Layout.
5369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
5379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public abstract int getBottomPadding();
5389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5394e0c5e55e171532760d5f51e0165563827129d4eDoug Felt
5404e0c5e55e171532760d5f51e0165563827129d4eDoug Felt    /**
5414e0c5e55e171532760d5f51e0165563827129d4eDoug Felt     * Returns true if the character at offset and the preceding character
5424e0c5e55e171532760d5f51e0165563827129d4eDoug Felt     * are at different run levels (and thus there's a split caret).
5434e0c5e55e171532760d5f51e0165563827129d4eDoug Felt     * @param offset the offset
5444e0c5e55e171532760d5f51e0165563827129d4eDoug Felt     * @return true if at a level boundary
5454e0c5e55e171532760d5f51e0165563827129d4eDoug Felt     */
5464e0c5e55e171532760d5f51e0165563827129d4eDoug Felt    private boolean isLevelBoundary(int offset) {
5479f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        int line = getLineForOffset(offset);
5484e0c5e55e171532760d5f51e0165563827129d4eDoug Felt        Directions dirs = getLineDirections(line);
5494e0c5e55e171532760d5f51e0165563827129d4eDoug Felt        if (dirs == DIRS_ALL_LEFT_TO_RIGHT || dirs == DIRS_ALL_RIGHT_TO_LEFT) {
5504e0c5e55e171532760d5f51e0165563827129d4eDoug Felt            return false;
5514e0c5e55e171532760d5f51e0165563827129d4eDoug Felt        }
5524e0c5e55e171532760d5f51e0165563827129d4eDoug Felt
5534e0c5e55e171532760d5f51e0165563827129d4eDoug Felt        int[] runs = dirs.mDirections;
5549f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        int lineStart = getLineStart(line);
5554e0c5e55e171532760d5f51e0165563827129d4eDoug Felt        int lineEnd = getLineEnd(line);
5564e0c5e55e171532760d5f51e0165563827129d4eDoug Felt        if (offset == lineStart || offset == lineEnd) {
5574e0c5e55e171532760d5f51e0165563827129d4eDoug Felt            int paraLevel = getParagraphDirection(line) == 1 ? 0 : 1;
5584e0c5e55e171532760d5f51e0165563827129d4eDoug Felt            int runIndex = offset == lineStart ? 0 : runs.length - 2;
5594e0c5e55e171532760d5f51e0165563827129d4eDoug Felt            return ((runs[runIndex + 1] >>> RUN_LEVEL_SHIFT) & RUN_LEVEL_MASK) != paraLevel;
5604e0c5e55e171532760d5f51e0165563827129d4eDoug Felt        }
5614e0c5e55e171532760d5f51e0165563827129d4eDoug Felt
5624e0c5e55e171532760d5f51e0165563827129d4eDoug Felt        offset -= lineStart;
5639f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        for (int i = 0; i < runs.length; i += 2) {
5644e0c5e55e171532760d5f51e0165563827129d4eDoug Felt            if (offset == runs[i]) {
5654e0c5e55e171532760d5f51e0165563827129d4eDoug Felt                return true;
5669f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt            }
5679f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        }
5684e0c5e55e171532760d5f51e0165563827129d4eDoug Felt        return false;
5699f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt    }
5709f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt
5719f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt    private boolean primaryIsTrailingPrevious(int offset) {
5729f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        int line = getLineForOffset(offset);
5739f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        int lineStart = getLineStart(line);
5744e0c5e55e171532760d5f51e0165563827129d4eDoug Felt        int lineEnd = getLineEnd(line);
5759f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        int[] runs = getLineDirections(line).mDirections;
5769f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt
5779f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        int levelAt = -1;
5789f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        for (int i = 0; i < runs.length; i += 2) {
5799f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt            int start = lineStart + runs[i];
5809f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt            int limit = start + (runs[i+1] & RUN_LENGTH_MASK);
5819f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt            if (limit > lineEnd) {
5829f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt                limit = lineEnd;
5839f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt            }
5849f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt            if (offset >= start && offset < limit) {
5859f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt                if (offset > start) {
5869f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt                    // Previous character is at same level, so don't use trailing.
5879f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt                    return false;
5889f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt                }
5899f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt                levelAt = (runs[i+1] >>> RUN_LEVEL_SHIFT) & RUN_LEVEL_MASK;
5909f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt                break;
5919f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt            }
5929f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        }
5939f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        if (levelAt == -1) {
5949f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt            // Offset was limit of line.
5959f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt            levelAt = getParagraphDirection(line) == 1 ? 0 : 1;
5969f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        }
5979f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt
5989f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        // At level boundary, check previous level.
5999f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        int levelBefore = -1;
6009f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        if (offset == lineStart) {
6019f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt            levelBefore = getParagraphDirection(line) == 1 ? 0 : 1;
6029f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        } else {
6039f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt            offset -= 1;
6049f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt            for (int i = 0; i < runs.length; i += 2) {
6059f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt                int start = lineStart + runs[i];
6069f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt                int limit = start + (runs[i+1] & RUN_LENGTH_MASK);
6079f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt                if (limit > lineEnd) {
6089f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt                    limit = lineEnd;
6099f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt                }
6109f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt                if (offset >= start && offset < limit) {
6119f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt                    levelBefore = (runs[i+1] >>> RUN_LEVEL_SHIFT) & RUN_LEVEL_MASK;
6129f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt                    break;
6139f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt                }
6149f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt            }
6159f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        }
6169f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt
6179f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        return levelBefore < levelAt;
6189f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt    }
6199f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt
6209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
6219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Get the primary horizontal position for the specified text offset.
6229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * This is the location where a new character would be inserted in
6239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * the paragraph's primary direction.
6249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
6259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public float getPrimaryHorizontal(int offset) {
6269f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        boolean trailing = primaryIsTrailingPrevious(offset);
6279f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        return getHorizontal(offset, trailing);
6289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
6299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
6319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Get the secondary horizontal position for the specified text offset.
6329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * This is the location where a new character would be inserted in
6339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * the direction other than the paragraph's primary direction.
6349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
6359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public float getSecondaryHorizontal(int offset) {
6369f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        boolean trailing = primaryIsTrailingPrevious(offset);
6379f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        return getHorizontal(offset, !trailing);
6389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
6399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6409f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt    private float getHorizontal(int offset, boolean trailing) {
6419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int line = getLineForOffset(offset);
6429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6439f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        return getHorizontal(offset, trailing, line);
6449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
6459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6469f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt    private float getHorizontal(int offset, boolean trailing, int line) {
6479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int start = getLineStart(line);
648e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        int end = getLineEnd(line);
6499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int dir = getParagraphDirection(line);
6509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        boolean tab = getLineContainsTab(line);
6519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        Directions directions = getLineDirections(line);
6529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        TabStopSpan[] tabs = null;
6549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (tab && mText instanceof Spanned) {
6559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            tabs = ((Spanned) mText).getSpans(start, end, TabStopSpan.class);
6569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
6579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
658e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        TextLine tl = TextLine.obtain();
659e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        tl.set(mPaint, mText, start, end, dir, directions, tab, tabs);
660e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        float wid = tl.measure(offset - start, trailing, null);
661e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        TextLine.recycle(tl);
6629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        Alignment align = getParagraphAlignment(line);
6649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int left = getParagraphLeft(line);
6659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int right = getParagraphRight(line);
6669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (align == Alignment.ALIGN_NORMAL) {
6689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (dir == DIR_RIGHT_TO_LEFT)
6699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                return right + wid;
6709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            else
6719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                return left + wid;
6729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
6739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        float max = getLineMax(line);
6759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (align == Alignment.ALIGN_OPPOSITE) {
6779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (dir == DIR_RIGHT_TO_LEFT)
6789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                return left + max + wid;
6799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            else
6809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                return right - max + wid;
6819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } else { /* align == Alignment.ALIGN_CENTER */
6829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int imax = ((int) max) & ~1;
6839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (dir == DIR_RIGHT_TO_LEFT)
6859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                return right - (((right - left) - imax) / 2) + wid;
6869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            else
6879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                return left + ((right - left) - imax) / 2 + wid;
6889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
6899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
6909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
6929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Get the leftmost position that should be exposed for horizontal
6939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * scrolling on the specified line.
6949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
6959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public float getLineLeft(int line) {
6969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int dir = getParagraphDirection(line);
6979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        Alignment align = getParagraphAlignment(line);
6989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (align == Alignment.ALIGN_NORMAL) {
7009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (dir == DIR_RIGHT_TO_LEFT)
7019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                return getParagraphRight(line) - getLineMax(line);
7029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            else
7039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                return 0;
7049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } else if (align == Alignment.ALIGN_OPPOSITE) {
7059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (dir == DIR_RIGHT_TO_LEFT)
7069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                return 0;
7079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            else
7089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                return mWidth - getLineMax(line);
7099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } else { /* align == Alignment.ALIGN_CENTER */
7109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int left = getParagraphLeft(line);
7119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int right = getParagraphRight(line);
7129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int max = ((int) getLineMax(line)) & ~1;
7139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
7149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return left + ((right - left) - max) / 2;
7159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
7169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
7179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
7189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
7199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Get the rightmost position that should be exposed for horizontal
7209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * scrolling on the specified line.
7219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
7229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public float getLineRight(int line) {
7239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int dir = getParagraphDirection(line);
7249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        Alignment align = getParagraphAlignment(line);
7259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
7269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (align == Alignment.ALIGN_NORMAL) {
7279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (dir == DIR_RIGHT_TO_LEFT)
7289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                return mWidth;
7299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            else
7309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                return getParagraphLeft(line) + getLineMax(line);
7319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } else if (align == Alignment.ALIGN_OPPOSITE) {
7329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (dir == DIR_RIGHT_TO_LEFT)
7339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                return getLineMax(line);
7349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            else
7359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                return mWidth;
7369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } else { /* align == Alignment.ALIGN_CENTER */
7379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int left = getParagraphLeft(line);
7389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int right = getParagraphRight(line);
7399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int max = ((int) getLineMax(line)) & ~1;
7409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
7419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return right - ((right - left) - max) / 2;
7429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
7439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
7449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
7459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
7469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Gets the horizontal extent of the specified line, excluding
7479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * trailing whitespace.
7489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
7499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public float getLineMax(int line) {
7509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return getLineMax(line, null, false);
7519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
7529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
7539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
7549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Gets the horizontal extent of the specified line, including
7559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * trailing whitespace.
7569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
7579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public float getLineWidth(int line) {
7589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return getLineMax(line, null, true);
7599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
7609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
7619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private float getLineMax(int line, Object[] tabs, boolean full) {
7629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int start = getLineStart(line);
763e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        int end = full ? getLineEnd(line) : getLineVisibleEnd(line);
764e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        boolean hasTabs = getLineContainsTab(line);
765e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        Directions directions = getLineDirections(line);
7669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
767e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        TextLine tl = TextLine.obtain();
768e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        tl.set(mPaint, mText, start, end, 1, directions, hasTabs, tabs);
769e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        float width = tl.metrics(null);
770e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        TextLine.recycle(tl);
771e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        return width;
7729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
7739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
7749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
7759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Get the line number corresponding to the specified vertical position.
7769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * If you ask for a position above 0, you get 0; if you ask for a position
7779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * below the bottom of the text, you get the last line.
7789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
7799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    // FIXME: It may be faster to do a linear search for layouts without many lines.
7809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public int getLineForVertical(int vertical) {
7819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int high = getLineCount(), low = -1, guess;
7829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
7839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        while (high - low > 1) {
7849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            guess = (high + low) / 2;
7859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
7869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (getLineTop(guess) > vertical)
7879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                high = guess;
7889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            else
7899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                low = guess;
7909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
7919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
7929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (low < 0)
7939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return 0;
7949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        else
7959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return low;
7969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
7979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
7989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
7999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Get the line number on which the specified text offset appears.
8009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * If you ask for a position before 0, you get 0; if you ask for a position
8019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * beyond the end of the text, you get the last line.
8029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
8039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public int getLineForOffset(int offset) {
8049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int high = getLineCount(), low = -1, guess;
8059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
8069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        while (high - low > 1) {
8079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            guess = (high + low) / 2;
8089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
8099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (getLineStart(guess) > offset)
8109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                high = guess;
8119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            else
8129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                low = guess;
8139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
8149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
8159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (low < 0)
8169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return 0;
8179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        else
8189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return low;
8199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
8209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
8219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
8229f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt     * Get the character offset on the specified line whose position is
8239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * closest to the specified horizontal position.
8249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
8259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public int getOffsetForHorizontal(int line, float horiz) {
8269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int max = getLineEnd(line) - 1;
8279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int min = getLineStart(line);
8289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        Directions dirs = getLineDirections(line);
8299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
8309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (line == getLineCount() - 1)
8319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            max++;
8329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
8339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int best = min;
8349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        float bestdist = Math.abs(getPrimaryHorizontal(best) - horiz);
8359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
8369f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        for (int i = 0; i < dirs.mDirections.length; i += 2) {
8379f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt            int here = min + dirs.mDirections[i];
8389f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt            int there = here + (dirs.mDirections[i+1] & RUN_LENGTH_MASK);
8399f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt            int swap = (dirs.mDirections[i+1] & RUN_RTL_FLAG) != 0 ? -1 : 1;
8409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
8419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (there > max)
8429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                there = max;
8439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int high = there - 1 + 1, low = here + 1 - 1, guess;
8449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
8459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            while (high - low > 1) {
8469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                guess = (high + low) / 2;
8479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                int adguess = getOffsetAtStartOf(guess);
8489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
8499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (getPrimaryHorizontal(adguess) * swap >= horiz * swap)
8509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    high = guess;
8519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                else
8529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    low = guess;
8539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
8549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
8559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (low < here + 1)
8569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                low = here + 1;
8579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
8589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (low < there) {
8599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                low = getOffsetAtStartOf(low);
8609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
8619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                float dist = Math.abs(getPrimaryHorizontal(low) - horiz);
8629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
8639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                int aft = TextUtils.getOffsetAfter(mText, low);
8649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (aft < there) {
8659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    float other = Math.abs(getPrimaryHorizontal(aft) - horiz);
8669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
8679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    if (other < dist) {
8689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        dist = other;
8699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        low = aft;
8709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    }
8719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
8729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
8739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (dist < bestdist) {
8749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    bestdist = dist;
8759f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt                    best = low;
8769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
8779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
8789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
8799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            float dist = Math.abs(getPrimaryHorizontal(here) - horiz);
8809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
8819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (dist < bestdist) {
8829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                bestdist = dist;
8839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                best = here;
8849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
8859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
8869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
8879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        float dist = Math.abs(getPrimaryHorizontal(max) - horiz);
8889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
8899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (dist < bestdist) {
8909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            bestdist = dist;
8919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            best = max;
8929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
8939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
8949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return best;
8959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
8969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
8979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
8989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Return the text offset after the last character on the specified line.
8999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
9009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public final int getLineEnd(int line) {
9019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return getLineStart(line + 1);
9029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
9039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
9049f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt    /**
9059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Return the text offset after the last visible character (so whitespace
9069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * is not counted) on the specified line.
9079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
9089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public int getLineVisibleEnd(int line) {
9099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return getLineVisibleEnd(line, getLineStart(line), getLineStart(line+1));
9109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
9119f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt
9129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private int getLineVisibleEnd(int line, int start, int end) {
91376c0226008ee16633dc94ed4dbeadef2174f6bd9Dave Bort        if (DEBUG) {
9149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            Assert.assertTrue(getLineStart(line) == start && getLineStart(line+1) == end);
9159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
9169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
9179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        CharSequence text = mText;
9189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        char ch;
9199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (line == getLineCount() - 1) {
9209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return end;
9219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
9229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
9239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        for (; end > start; end--) {
9249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            ch = text.charAt(end - 1);
9259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
9269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (ch == '\n') {
9279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                return end - 1;
9289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
9299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
9309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (ch != ' ' && ch != '\t') {
9319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                break;
9329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
9339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
9349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
9359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
9369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return end;
9379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
9389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
9399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
9409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Return the vertical position of the bottom of the specified line.
9419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
9429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public final int getLineBottom(int line) {
9439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return getLineTop(line + 1);
9449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
9459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
9469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
9479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Return the vertical position of the baseline of the specified line.
9489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
9499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public final int getLineBaseline(int line) {
9509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // getLineTop(line+1) == getLineTop(line)
9519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return getLineTop(line+1) - getLineDescent(line);
9529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
9539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
9549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
9559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Get the ascent of the text on the specified line.
9569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * The return value is negative to match the Paint.ascent() convention.
9579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
9589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public final int getLineAscent(int line) {
9599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // getLineTop(line+1) - getLineDescent(line) == getLineBaseLine(line)
9609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return getLineTop(line) - (getLineTop(line+1) - getLineDescent(line));
9619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
9629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
9639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public int getOffsetToLeftOf(int offset) {
9649f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        return getOffsetToLeftRightOf(offset, true);
9659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
9669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
9679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public int getOffsetToRightOf(int offset) {
9689f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        return getOffsetToLeftRightOf(offset, false);
9699f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt    }
9709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
9719f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt    private int getOffsetToLeftRightOf(int caret, boolean toLeft) {
9729f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        int line = getLineForOffset(caret);
9739f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        int lineStart = getLineStart(line);
9749f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        int lineEnd = getLineEnd(line);
975e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        int lineDir = getParagraphDirection(line);
976e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
977e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        boolean advance = toLeft == (lineDir == DIR_RIGHT_TO_LEFT);
978e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        if (caret == (advance ? lineEnd : lineStart)) {
979e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            // walking off line, so look at the line we're headed to
980e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            if (caret == lineStart) {
981e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                if (line > 0) {
982e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    --line;
983e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                } else {
984e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    return caret; // at very start, don't move
985e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                }
986e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            } else {
987e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                if (line < getLineCount() - 1) {
988e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    ++line;
989e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                } else {
990e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    return caret; // at very end, don't move
9919f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt                }
9929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
9939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
994e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            lineStart = getLineStart(line);
995e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            lineEnd = getLineEnd(line);
996e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            int newDir = getParagraphDirection(line);
997e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            if (newDir != lineDir) {
998e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                // unusual case.  we want to walk onto the line, but it runs
999e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                // in a different direction than this one, so we fake movement
1000e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                // in the opposite direction.
1001e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                toLeft = !toLeft;
1002e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                lineDir = newDir;
10039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
10049f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        }
10059f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt
1006e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        Directions directions = getLineDirections(line);
10079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1008e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        TextLine tl = TextLine.obtain();
1009e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        // XXX: we don't care about tabs
1010e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        tl.set(mPaint, mText, lineStart, lineEnd, lineDir, directions, false, null);
1011e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        caret = lineStart + tl.getOffsetToLeftRightOf(caret - lineStart, toLeft);
1012e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        tl = TextLine.recycle(tl);
1013e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        return caret;
10149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
10159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
10169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private int getOffsetAtStartOf(int offset) {
10179f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        // XXX this probably should skip local reorderings and
10189f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        // zero-width characters, look at callers
10199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (offset == 0)
10209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return 0;
10219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
10229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        CharSequence text = mText;
10239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        char c = text.charAt(offset);
10249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
10259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (c >= '\uDC00' && c <= '\uDFFF') {
10269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            char c1 = text.charAt(offset - 1);
10279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
10289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (c1 >= '\uD800' && c1 <= '\uDBFF')
10299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                offset -= 1;
10309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
10319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
10329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (mSpannedText) {
10339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            ReplacementSpan[] spans = ((Spanned) text).getSpans(offset, offset,
10349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                                       ReplacementSpan.class);
10359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
10369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            for (int i = 0; i < spans.length; i++) {
10379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                int start = ((Spanned) text).getSpanStart(spans[i]);
10389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                int end = ((Spanned) text).getSpanEnd(spans[i]);
10399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
10409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (start < offset && end > offset)
10419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    offset = start;
10429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
10439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
10449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
10459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return offset;
10469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
10479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
10489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
10499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Fills in the specified Path with a representation of a cursor
10509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * at the specified offset.  This will often be a vertical line
10514e0c5e55e171532760d5f51e0165563827129d4eDoug Felt     * but can be multiple discontinuous lines in text with multiple
10529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * directionalities.
10539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
10549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public void getCursorPath(int point, Path dest,
10559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                              CharSequence editingBuffer) {
10569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        dest.reset();
10579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
10589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int line = getLineForOffset(point);
10599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int top = getLineTop(line);
10609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int bottom = getLineTop(line+1);
10619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
10629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        float h1 = getPrimaryHorizontal(point) - 0.5f;
10634e0c5e55e171532760d5f51e0165563827129d4eDoug Felt        float h2 = isLevelBoundary(point) ?
10644e0c5e55e171532760d5f51e0165563827129d4eDoug Felt                    getSecondaryHorizontal(point) - 0.5f : h1;
10659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
10669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int caps = TextKeyListener.getMetaState(editingBuffer,
10679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                                KeyEvent.META_SHIFT_ON) |
10689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                   TextKeyListener.getMetaState(editingBuffer,
10699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                                TextKeyListener.META_SELECTING);
10709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int fn = TextKeyListener.getMetaState(editingBuffer,
10719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                              KeyEvent.META_ALT_ON);
10729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int dist = 0;
10739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
10749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (caps != 0 || fn != 0) {
10759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            dist = (bottom - top) >> 2;
10769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
10779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (fn != 0)
10789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                top += dist;
10799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (caps != 0)
10809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                bottom -= dist;
10819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
10829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
10839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (h1 < 0.5f)
10849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            h1 = 0.5f;
10859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (h2 < 0.5f)
10869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            h2 = 0.5f;
10879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
10889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (h1 == h2) {
10899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            dest.moveTo(h1, top);
10909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            dest.lineTo(h1, bottom);
10919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } else {
10929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            dest.moveTo(h1, top);
10939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            dest.lineTo(h1, (top + bottom) >> 1);
10949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
10959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            dest.moveTo(h2, (top + bottom) >> 1);
10969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            dest.lineTo(h2, bottom);
10979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
10989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
10999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (caps == 2) {
11009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            dest.moveTo(h2, bottom);
11019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            dest.lineTo(h2 - dist, bottom + dist);
11029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            dest.lineTo(h2, bottom);
11039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            dest.lineTo(h2 + dist, bottom + dist);
11049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } else if (caps == 1) {
11059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            dest.moveTo(h2, bottom);
11069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            dest.lineTo(h2 - dist, bottom + dist);
11079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
11089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            dest.moveTo(h2 - dist, bottom + dist - 0.5f);
11099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            dest.lineTo(h2 + dist, bottom + dist - 0.5f);
11109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
11119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            dest.moveTo(h2 + dist, bottom + dist);
11129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            dest.lineTo(h2, bottom);
11139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
11149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
11159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (fn == 2) {
11169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            dest.moveTo(h1, top);
11179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            dest.lineTo(h1 - dist, top - dist);
11189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            dest.lineTo(h1, top);
11199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            dest.lineTo(h1 + dist, top - dist);
11209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } else if (fn == 1) {
11219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            dest.moveTo(h1, top);
11229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            dest.lineTo(h1 - dist, top - dist);
11239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
11249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            dest.moveTo(h1 - dist, top - dist + 0.5f);
11259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            dest.lineTo(h1 + dist, top - dist + 0.5f);
11269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
11279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            dest.moveTo(h1 + dist, top - dist);
11289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            dest.lineTo(h1, top);
11299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
11309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
11319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
11329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private void addSelection(int line, int start, int end,
11339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                              int top, int bottom, Path dest) {
11349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int linestart = getLineStart(line);
11359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int lineend = getLineEnd(line);
11369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        Directions dirs = getLineDirections(line);
11379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
11389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (lineend > linestart && mText.charAt(lineend - 1) == '\n')
11399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            lineend--;
11409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
11419f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        for (int i = 0; i < dirs.mDirections.length; i += 2) {
11429f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt            int here = linestart + dirs.mDirections[i];
11439f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt            int there = here + (dirs.mDirections[i+1] & RUN_LENGTH_MASK);
11449f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt
11459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (there > lineend)
11469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                there = lineend;
11479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
11489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (start <= there && end >= here) {
11499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                int st = Math.max(start, here);
11509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                int en = Math.min(end, there);
11519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
11529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (st != en) {
11539f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt                    float h1 = getHorizontal(st, false, line);
11549f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt                    float h2 = getHorizontal(en, true, line);
11559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
11569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    dest.addRect(h1, top, h2, bottom, Path.Direction.CW);
11579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
11589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
11599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
11609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
11619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
11629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
11639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Fills in the specified Path with a representation of a highlight
11649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * between the specified offsets.  This will often be a rectangle
11659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * or a potentially discontinuous set of rectangles.  If the start
11669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * and end are the same, the returned path is empty.
11679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
11689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public void getSelectionPath(int start, int end, Path dest) {
11699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        dest.reset();
11709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
11719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (start == end)
11729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return;
11739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
11749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (end < start) {
11759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int temp = end;
11769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            end = start;
11779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            start = temp;
11789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
11799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
11809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int startline = getLineForOffset(start);
11819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int endline = getLineForOffset(end);
11829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
11839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int top = getLineTop(startline);
11849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int bottom = getLineBottom(endline);
11859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
11869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (startline == endline) {
11879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            addSelection(startline, start, end, top, bottom, dest);
11889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } else {
11899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            final float width = mWidth;
11909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
11919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            addSelection(startline, start, getLineEnd(startline),
11929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                         top, getLineBottom(startline), dest);
11939f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt
11949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (getParagraphDirection(startline) == DIR_RIGHT_TO_LEFT)
11959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                dest.addRect(getLineLeft(startline), top,
11969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                              0, getLineBottom(startline), Path.Direction.CW);
11979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            else
11989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                dest.addRect(getLineRight(startline), top,
11999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                              width, getLineBottom(startline), Path.Direction.CW);
12009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
12019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            for (int i = startline + 1; i < endline; i++) {
12029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                top = getLineTop(i);
12039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                bottom = getLineBottom(i);
12049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                dest.addRect(0, top, width, bottom, Path.Direction.CW);
12059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
12069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
12079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            top = getLineTop(endline);
12089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            bottom = getLineBottom(endline);
12099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
12109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            addSelection(endline, getLineStart(endline), end,
12119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                         top, bottom, dest);
12129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
12139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (getParagraphDirection(endline) == DIR_RIGHT_TO_LEFT)
12149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                dest.addRect(width, top, getLineRight(endline), bottom, Path.Direction.CW);
12159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            else
12169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                dest.addRect(0, top, getLineLeft(endline), bottom, Path.Direction.CW);
12179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
12189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
12199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
12209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
12219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Get the alignment of the specified paragraph, taking into account
12229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * markup attached to it.
12239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
12249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public final Alignment getParagraphAlignment(int line) {
12259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        Alignment align = mAlignment;
12269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
12279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (mSpannedText) {
12289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            Spanned sp = (Spanned) mText;
12299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            AlignmentSpan[] spans = sp.getSpans(getLineStart(line),
12309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                                getLineEnd(line),
12319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                                AlignmentSpan.class);
12329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
12339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int spanLength = spans.length;
12349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (spanLength > 0) {
12359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                align = spans[spanLength-1].getAlignment();
12369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
12379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
12389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
12399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return align;
12409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
12419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
12429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
12439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Get the left edge of the specified paragraph, inset by left margins.
12449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
12459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public final int getParagraphLeft(int line) {
12469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int dir = getParagraphDirection(line);
12479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
12489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int left = 0;
12499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
12509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        boolean par = false;
12519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int off = getLineStart(line);
12529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (off == 0 || mText.charAt(off - 1) == '\n')
12539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            par = true;
12549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
12559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (dir == DIR_LEFT_TO_RIGHT) {
12569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (mSpannedText) {
12579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                Spanned sp = (Spanned) mText;
12589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                LeadingMarginSpan[] spans = sp.getSpans(getLineStart(line),
12599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                                        getLineEnd(line),
12609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                                        LeadingMarginSpan.class);
12619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
12629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                for (int i = 0; i < spans.length; i++) {
12637b5676e4d40a09ccdbc8b6f691a3d8be23e480d3Mark Wagner                    boolean margin = par;
12647b5676e4d40a09ccdbc8b6f691a3d8be23e480d3Mark Wagner                    LeadingMarginSpan span = spans[i];
12657b5676e4d40a09ccdbc8b6f691a3d8be23e480d3Mark Wagner                    if (span instanceof LeadingMarginSpan.LeadingMarginSpan2) {
12667b5676e4d40a09ccdbc8b6f691a3d8be23e480d3Mark Wagner                        int count = ((LeadingMarginSpan.LeadingMarginSpan2)span).getLeadingMarginLineCount();
12677b5676e4d40a09ccdbc8b6f691a3d8be23e480d3Mark Wagner                        margin = count >= line;
12687b5676e4d40a09ccdbc8b6f691a3d8be23e480d3Mark Wagner                    }
12697b5676e4d40a09ccdbc8b6f691a3d8be23e480d3Mark Wagner                    left += span.getLeadingMargin(margin);
12709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
12719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
12729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
12739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
12749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return left;
12759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
12769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
12779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
12789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Get the right edge of the specified paragraph, inset by right margins.
12799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
12809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public final int getParagraphRight(int line) {
12819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int dir = getParagraphDirection(line);
12829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
12839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int right = mWidth;
12849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
12859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        boolean par = false;
12869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int off = getLineStart(line);
12879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (off == 0 || mText.charAt(off - 1) == '\n')
12889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            par = true;
12899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
12909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
12919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (dir == DIR_RIGHT_TO_LEFT) {
12929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (mSpannedText) {
12939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                Spanned sp = (Spanned) mText;
12949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                LeadingMarginSpan[] spans = sp.getSpans(getLineStart(line),
12959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                                        getLineEnd(line),
12969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                                        LeadingMarginSpan.class);
12979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
12989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                for (int i = 0; i < spans.length; i++) {
12999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    right -= spans[i].getLeadingMargin(par);
13009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
13019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
13029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
13039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
13049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return right;
13059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
13069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1307e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    /* package */
1308e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    static float measurePara(TextPaint paint, TextPaint workPaint,
1309e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            CharSequence text, int start, int end, boolean hasTabs,
1310e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            Object[] tabs) {
1311e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
1312e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        MeasuredText mt = MeasuredText.obtain();
1313e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        TextLine tl = TextLine.obtain();
1314e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        try {
1315e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            mt.setPara(text, start, end, DIR_REQUEST_LTR);
1316e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            Directions directions;
1317e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            if (mt.mEasy){
1318e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                directions = DIRS_ALL_LEFT_TO_RIGHT;
1319e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            } else {
1320e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                directions = AndroidBidi.directions(mt.mDir, mt.mLevels,
1321e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    0, mt.mChars, 0, mt.mLen);
13229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
1323e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            tl.set(paint, text, start, end, 1, directions, hasTabs, tabs);
1324e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            return tl.metrics(null);
1325e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        } finally {
1326e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            TextLine.recycle(tl);
1327e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            MeasuredText.recycle(mt);
13289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
13299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
13309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
133171b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt    /**
133271b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt     * Returns the position of the next tab stop after h on the line.
133371b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt     *
133471b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt     * @param text the text
133571b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt     * @param start start of the line
133671b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt     * @param end limit of the line
133771b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt     * @param h the current horizontal offset
133871b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt     * @param tabs the tabs, can be null.  If it is null, any tabs in effect
133971b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt     * on the line will be used.  If there are no tabs, a default offset
134071b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt     * will be used to compute the tab stop.
134171b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt     * @return the offset of the next tab stop.
134271b8dd71e49016e057c46a257f79162d186a3c3aDoug Felt     */
13439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /* package */ static float nextTab(CharSequence text, int start, int end,
13449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                       float h, Object[] tabs) {
13459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        float nh = Float.MAX_VALUE;
13469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        boolean alltabs = false;
13479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
13489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (text instanceof Spanned) {
13499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (tabs == null) {
13509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                tabs = ((Spanned) text).getSpans(start, end, TabStopSpan.class);
13519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                alltabs = true;
13529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
13539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
13549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            for (int i = 0; i < tabs.length; i++) {
13559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (!alltabs) {
13569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    if (!(tabs[i] instanceof TabStopSpan))
13579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        continue;
13589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
13599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
13609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                int where = ((TabStopSpan) tabs[i]).getTabStop();
13619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
13629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (where < nh && where > h)
13639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    nh = where;
13649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
13659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
13669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (nh != Float.MAX_VALUE)
13679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                return nh;
13689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
13699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
13709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return ((int) ((h + TAB_INCREMENT) / TAB_INCREMENT)) * TAB_INCREMENT;
13719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
13729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
13739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    protected final boolean isSpanned() {
13749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return mSpannedText;
13759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
13769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
13779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private void ellipsize(int start, int end, int line,
13789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                           char[] dest, int destoff) {
13799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int ellipsisCount = getEllipsisCount(line);
13809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
13819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (ellipsisCount == 0) {
13829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return;
13839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
13849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
13859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int ellipsisStart = getEllipsisStart(line);
13869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int linestart = getLineStart(line);
13879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
13889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        for (int i = ellipsisStart; i < ellipsisStart + ellipsisCount; i++) {
13899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            char c;
13909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
13919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (i == ellipsisStart) {
13929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                c = '\u2026'; // ellipsis
13939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } else {
13949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                c = '\uFEFF'; // 0-width space
13959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
13969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
13979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int a = i + linestart;
13989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
13999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (a >= start && a < end) {
14009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                dest[destoff + a - start] = c;
14019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
14029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
14039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
14049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
14059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
14069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Stores information about bidirectional (left-to-right or right-to-left)
14079f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt     * text within the layout of a line.
14089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
14099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static class Directions {
14109f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        // Directions represents directional runs within a line of text.
14119f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        // Runs are pairs of ints listed in visual order, starting from the
14129f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        // leading margin.  The first int of each pair is the offset from
14139f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        // the first character of the line to the start of the run.  The
14149f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        // second int represents both the length and level of the run.
14159f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        // The length is in the lower bits, accessed by masking with
14169f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        // DIR_LENGTH_MASK.  The level is in the higher bits, accessed
14179f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        // by shifting by DIR_LEVEL_SHIFT and masking by DIR_LEVEL_MASK.
14189f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        // To simply test for an RTL direction, test the bit using
14199f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        // DIR_RTL_FLAG, if set then the direction is rtl.
14209f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt
14219f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        /* package */ int[] mDirections;
14229f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        /* package */ Directions(int[] dirs) {
14239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mDirections = dirs;
14249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
14259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
14269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
14279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
14289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Return the offset of the first character to be ellipsized away,
14299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * relative to the start of the line.  (So 0 if the beginning of the
14309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * line is ellipsized, not getLineStart().)
14319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
14329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public abstract int getEllipsisStart(int line);
1433e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
14349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
14359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Returns the number of characters to be ellipsized away, or 0 if
14369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * no ellipsis is to take place.
14379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
14389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public abstract int getEllipsisCount(int line);
14399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
14409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /* package */ static class Ellipsizer implements CharSequence, GetChars {
14419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /* package */ CharSequence mText;
14429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /* package */ Layout mLayout;
14439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /* package */ int mWidth;
14449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /* package */ TextUtils.TruncateAt mMethod;
14459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
14469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public Ellipsizer(CharSequence s) {
14479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mText = s;
14489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
14499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
14509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public char charAt(int off) {
14519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            char[] buf = TextUtils.obtain(1);
14529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            getChars(off, off + 1, buf, 0);
14539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            char ret = buf[0];
14549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
14559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            TextUtils.recycle(buf);
14569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return ret;
14579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
14589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
14599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public void getChars(int start, int end, char[] dest, int destoff) {
14609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int line1 = mLayout.getLineForOffset(start);
14619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int line2 = mLayout.getLineForOffset(end);
14629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
14639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            TextUtils.getChars(mText, start, end, dest, destoff);
14649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
14659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            for (int i = line1; i <= line2; i++) {
14669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                mLayout.ellipsize(start, end, i, dest, destoff);
14679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
14689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
14699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
14709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public int length() {
14719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return mText.length();
14729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
14739f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt
14749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public CharSequence subSequence(int start, int end) {
14759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            char[] s = new char[end - start];
14769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            getChars(start, end, s, 0);
14779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return new String(s);
14789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
14799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
14809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public String toString() {
14819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            char[] s = new char[length()];
14829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            getChars(0, length(), s, 0);
14839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return new String(s);
14849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
14859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
14869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
14879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
14889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /* package */ static class SpannedEllipsizer
14899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    extends Ellipsizer implements Spanned {
14909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        private Spanned mSpanned;
14919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
14929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public SpannedEllipsizer(CharSequence display) {
14939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            super(display);
14949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mSpanned = (Spanned) display;
14959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
14969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
14979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public <T> T[] getSpans(int start, int end, Class<T> type) {
14989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return mSpanned.getSpans(start, end, type);
14999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
15009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
15019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public int getSpanStart(Object tag) {
15029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return mSpanned.getSpanStart(tag);
15039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
15049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
15059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public int getSpanEnd(Object tag) {
15069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return mSpanned.getSpanEnd(tag);
15079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
15089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
15099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public int getSpanFlags(Object tag) {
15109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return mSpanned.getSpanFlags(tag);
15119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
15129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
15139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public int nextSpanTransition(int start, int limit, Class type) {
15149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return mSpanned.nextSpanTransition(start, limit, type);
15159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
15169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
15179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public CharSequence subSequence(int start, int end) {
15189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            char[] s = new char[end - start];
15199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            getChars(start, end, s, 0);
15209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
15219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            SpannableString ss = new SpannableString(new String(s));
15229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            TextUtils.copySpansFrom(mSpanned, start, end, Object.class, ss, 0);
15239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return ss;
15249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
15259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
15269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
15279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private CharSequence mText;
15289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private TextPaint mPaint;
15299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /* package */ TextPaint mWorkPaint;
15309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private int mWidth;
15319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private Alignment mAlignment = Alignment.ALIGN_NORMAL;
15329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private float mSpacingMult;
15339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private float mSpacingAdd;
15349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static Rect sTempRect = new Rect();
15359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private boolean mSpannedText;
15369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
15379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static final int DIR_LEFT_TO_RIGHT = 1;
15389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static final int DIR_RIGHT_TO_LEFT = -1;
15399f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt
154020178d62cf669af18467a16d3c4c4237ed42151cDoug Felt    /* package */ static final int DIR_REQUEST_LTR = 1;
154120178d62cf669af18467a16d3c4c4237ed42151cDoug Felt    /* package */ static final int DIR_REQUEST_RTL = -1;
154220178d62cf669af18467a16d3c4c4237ed42151cDoug Felt    /* package */ static final int DIR_REQUEST_DEFAULT_LTR = 2;
154320178d62cf669af18467a16d3c4c4237ed42151cDoug Felt    /* package */ static final int DIR_REQUEST_DEFAULT_RTL = -2;
15449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
15459f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt    /* package */ static final int RUN_LENGTH_MASK = 0x03ffffff;
15469f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt    /* package */ static final int RUN_LEVEL_SHIFT = 26;
15479f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt    /* package */ static final int RUN_LEVEL_MASK = 0x3f;
15489f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt    /* package */ static final int RUN_RTL_FLAG = 1 << RUN_LEVEL_SHIFT;
15499f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt
15509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public enum Alignment {
15519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        ALIGN_NORMAL,
15529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        ALIGN_OPPOSITE,
15539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        ALIGN_CENTER,
15549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // XXX ALIGN_LEFT,
15559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // XXX ALIGN_RIGHT,
15569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
15579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
15589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final int TAB_INCREMENT = 20;
15599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
15609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /* package */ static final Directions DIRS_ALL_LEFT_TO_RIGHT =
15619f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        new Directions(new int[] { 0, RUN_LENGTH_MASK });
15629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /* package */ static final Directions DIRS_ALL_RIGHT_TO_LEFT =
15639f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        new Directions(new int[] { 0, RUN_LENGTH_MASK | RUN_RTL_FLAG });
15649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project}
15659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1566