OSDN Git Service

Frameworks/base: Remove unused field
[android-x86/frameworks-base.git] / core / java / android / text / BoringLayout.java
1 /*
2  * Copyright (C) 2006 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 package android.text;
18
19 import android.graphics.Canvas;
20 import android.graphics.Paint;
21 import android.graphics.Path;
22 import android.text.style.ParagraphStyle;
23
24 /**
25  * A BoringLayout is a very simple Layout implementation for text that
26  * fits on a single line and is all left-to-right characters.
27  * You will probably never want to make one of these yourself;
28  * if you do, be sure to call {@link #isBoring} first to make sure
29  * the text meets the criteria.
30  * <p>This class is used by widgets to control text layout. You should not need
31  * to use this class directly unless you are implementing your own widget
32  * or custom display object, in which case
33  * you are encouraged to use a Layout instead of calling
34  * {@link android.graphics.Canvas#drawText(java.lang.CharSequence, int, int, float, float, android.graphics.Paint)
35  *  Canvas.drawText()} directly.</p>
36  */
37 public class BoringLayout extends Layout implements TextUtils.EllipsizeCallback {
38     public static BoringLayout make(CharSequence source,
39                         TextPaint paint, int outerwidth,
40                         Alignment align,
41                         float spacingmult, float spacingadd,
42                         BoringLayout.Metrics metrics, boolean includepad) {
43         return new BoringLayout(source, paint, outerwidth, align,
44                                 spacingmult, spacingadd, metrics,
45                                 includepad);
46     }
47
48     public static BoringLayout make(CharSequence source,
49                         TextPaint paint, int outerwidth,
50                         Alignment align,
51                         float spacingmult, float spacingadd,
52                         BoringLayout.Metrics metrics, boolean includepad,
53                         TextUtils.TruncateAt ellipsize, int ellipsizedWidth) {
54         return new BoringLayout(source, paint, outerwidth, align,
55                                 spacingmult, spacingadd, metrics,
56                                 includepad, ellipsize, ellipsizedWidth);
57     }
58
59     /**
60      * Returns a BoringLayout for the specified text, potentially reusing
61      * this one if it is already suitable.  The caller must make sure that
62      * no one is still using this Layout.
63      */
64     public BoringLayout replaceOrMake(CharSequence source, TextPaint paint,
65                                       int outerwidth, Alignment align,
66                                       float spacingmult, float spacingadd,
67                                       BoringLayout.Metrics metrics,
68                                       boolean includepad) {
69         replaceWith(source, paint, outerwidth, align, spacingmult,
70                     spacingadd);
71
72         mEllipsizedWidth = outerwidth;
73         mEllipsizedStart = 0;
74         mEllipsizedCount = 0;
75
76         init(source, paint, outerwidth, align, spacingmult, spacingadd,
77              metrics, includepad, true);
78         return this;
79     }
80
81     /**
82      * Returns a BoringLayout for the specified text, potentially reusing
83      * this one if it is already suitable.  The caller must make sure that
84      * no one is still using this Layout.
85      */
86     public BoringLayout replaceOrMake(CharSequence source, TextPaint paint,
87                                       int outerwidth, Alignment align,
88                                       float spacingmult, float spacingadd,
89                                       BoringLayout.Metrics metrics,
90                                       boolean includepad,
91                                       TextUtils.TruncateAt ellipsize,
92                                       int ellipsizedWidth) {
93         boolean trust;
94
95         if (ellipsize == null || ellipsize == TextUtils.TruncateAt.MARQUEE) {
96             replaceWith(source, paint, outerwidth, align, spacingmult,
97                         spacingadd);
98
99             mEllipsizedWidth = outerwidth;
100             mEllipsizedStart = 0;
101             mEllipsizedCount = 0;
102             trust = true;
103         } else {
104             replaceWith(TextUtils.ellipsize(source, paint, ellipsizedWidth,
105                                            ellipsize, true, this),
106                         paint, outerwidth, align, spacingmult,
107                         spacingadd);
108
109             mEllipsizedWidth = ellipsizedWidth;
110             trust = false;
111         }
112
113         init(getText(), paint, outerwidth, align, spacingmult, spacingadd,
114              metrics, includepad, trust);
115         return this;
116     }
117
118     public BoringLayout(CharSequence source,
119                         TextPaint paint, int outerwidth,
120                         Alignment align,
121                         float spacingmult, float spacingadd,
122                         BoringLayout.Metrics metrics, boolean includepad) {
123         super(source, paint, outerwidth, align, spacingmult, spacingadd);
124
125         mEllipsizedWidth = outerwidth;
126         mEllipsizedStart = 0;
127         mEllipsizedCount = 0;
128
129         init(source, paint, outerwidth, align, spacingmult, spacingadd,
130              metrics, includepad, true);
131     }
132
133     public BoringLayout(CharSequence source,
134                         TextPaint paint, int outerwidth,
135                         Alignment align,
136                         float spacingmult, float spacingadd,
137                         BoringLayout.Metrics metrics, boolean includepad,
138                         TextUtils.TruncateAt ellipsize, int ellipsizedWidth) {
139         /*
140          * It is silly to have to call super() and then replaceWith(),
141          * but we can't use "this" for the callback until the call to
142          * super() finishes.
143          */
144         super(source, paint, outerwidth, align, spacingmult, spacingadd);
145
146         boolean trust;
147
148         if (ellipsize == null || ellipsize == TextUtils.TruncateAt.MARQUEE) {
149             mEllipsizedWidth = outerwidth;
150             mEllipsizedStart = 0;
151             mEllipsizedCount = 0;
152             trust = true;
153         } else {
154             replaceWith(TextUtils.ellipsize(source, paint, ellipsizedWidth,
155                                            ellipsize, true, this),
156                         paint, outerwidth, align, spacingmult,
157                         spacingadd);
158
159
160             mEllipsizedWidth = ellipsizedWidth;
161             trust = false;
162         }
163
164         init(getText(), paint, outerwidth, align, spacingmult, spacingadd,
165              metrics, includepad, trust);
166     }
167
168     /* package */ void init(CharSequence source,
169                             TextPaint paint, int outerwidth,
170                             Alignment align,
171                             float spacingmult, float spacingadd,
172                             BoringLayout.Metrics metrics, boolean includepad,
173                             boolean trustWidth) {
174         int spacing;
175
176         if (source instanceof String && align == Layout.Alignment.ALIGN_NORMAL) {
177             mDirect = source.toString();
178         } else {
179             mDirect = null;
180         }
181
182         mPaint = paint;
183
184         if (includepad) {
185             spacing = metrics.bottom - metrics.top;
186         } else {
187             spacing = metrics.descent - metrics.ascent;
188         }
189
190         mBottom = spacing;
191
192         if (trustWidth) {
193             mMax = metrics.width;
194         } else {
195             /*
196              * If we have ellipsized, we have to actually calculate the
197              * width because the width that was passed in was for the
198              * full text, not the ellipsized form.
199              */
200             TextLine line = TextLine.obtain();
201             line.set(paint, source, 0, source.length(), Layout.DIR_LEFT_TO_RIGHT,
202                     Layout.DIRS_ALL_LEFT_TO_RIGHT, false, null);
203             mMax = (int) Math.ceil(line.metrics(null));
204             TextLine.recycle(line);
205         }
206
207         if (includepad) {
208             mTopPadding = metrics.top - metrics.ascent;
209             mBottomPadding = metrics.bottom - metrics.descent;
210         }
211
212         mDesc = spacing + mBottomPadding + (includepad ? metrics.top : metrics.ascent);
213     }
214
215     /**
216      * Returns null if not boring; the width, ascent, and descent if boring.
217      */
218     public static Metrics isBoring(CharSequence text,
219                                    TextPaint paint) {
220         return isBoring(text, paint, TextDirectionHeuristics.FIRSTSTRONG_LTR, null);
221     }
222
223     /**
224      * Returns null if not boring; the width, ascent, and descent if boring.
225      * @hide
226      */
227     public static Metrics isBoring(CharSequence text,
228                                    TextPaint paint,
229                                    TextDirectionHeuristic textDir) {
230         return isBoring(text, paint, textDir, null);
231     }
232
233     /**
234      * Returns null if not boring; the width, ascent, and descent in the
235      * provided Metrics object (or a new one if the provided one was null)
236      * if boring.
237      */
238     public static Metrics isBoring(CharSequence text, TextPaint paint, Metrics metrics) {
239         return isBoring(text, paint, TextDirectionHeuristics.FIRSTSTRONG_LTR, metrics);
240     }
241
242     /**
243      * Returns null if not boring; the width, ascent, and descent in the
244      * provided Metrics object (or a new one if the provided one was null)
245      * if boring.
246      * @hide
247      */
248     public static Metrics isBoring(CharSequence text, TextPaint paint,
249             TextDirectionHeuristic textDir, Metrics metrics) {
250         char[] temp = TextUtils.obtain(500);
251         int length = text.length();
252         boolean boring = true;
253
254         outer:
255         for (int i = 0; i < length; i += 500) {
256             int j = i + 500;
257
258             if (j > length)
259                 j = length;
260
261             TextUtils.getChars(text, i, j, temp, 0);
262
263             int n = j - i;
264
265             for (int a = 0; a < n; a++) {
266                 char c = temp[a];
267
268                 if (c == '\n' || c == '\t' ||
269                         (c >= 0x0590 && c <= 0x08FF) ||  // RTL scripts
270                         c == 0x200F ||  // Bidi format character
271                         (c >= 0x202A && c <= 0x202E) ||  // Bidi format characters
272                         (c >= 0x2066 && c <= 0x2069) ||  // Bidi format characters
273                         (c >= 0xD800 && c <= 0xDFFF) ||  // surrogate pairs
274                         (c >= 0xFB1D && c <= 0xFDFF) ||  // Hebrew and Arabic presentation forms
275                         (c >= 0xFE70 && c <= 0xFEFE) // Arabic presentation forms
276                    ) {
277                     boring = false;
278                     break outer;
279                 }
280             }
281
282             if (textDir != null && textDir.isRtl(temp, 0, n)) {
283                boring = false;
284                break outer;
285             }
286         }
287
288         TextUtils.recycle(temp);
289
290         if (boring && text instanceof Spanned) {
291             Spanned sp = (Spanned) text;
292             Object[] styles = sp.getSpans(0, length, ParagraphStyle.class);
293             if (styles.length > 0) {
294                 boring = false;
295             }
296         }
297
298         if (boring) {
299             Metrics fm = metrics;
300             if (fm == null) {
301                 fm = new Metrics();
302             }
303
304             TextLine line = TextLine.obtain();
305             line.set(paint, text, 0, length, Layout.DIR_LEFT_TO_RIGHT,
306                     Layout.DIRS_ALL_LEFT_TO_RIGHT, false, null);
307             fm.width = (int) Math.ceil(line.metrics(fm));
308             TextLine.recycle(line);
309
310             return fm;
311         } else {
312             return null;
313         }
314     }
315
316     @Override
317     public int getHeight() {
318         return mBottom;
319     }
320
321     @Override
322     public int getLineCount() {
323         return 1;
324     }
325
326     @Override
327     public int getLineTop(int line) {
328         if (line == 0)
329             return 0;
330         else
331             return mBottom;
332     }
333
334     @Override
335     public int getLineDescent(int line) {
336         return mDesc;
337     }
338
339     @Override
340     public int getLineStart(int line) {
341         if (line == 0)
342             return 0;
343         else
344             return getText().length();
345     }
346
347     @Override
348     public int getParagraphDirection(int line) {
349         return DIR_LEFT_TO_RIGHT;
350     }
351
352     @Override
353     public boolean getLineContainsTab(int line) {
354         return false;
355     }
356
357     @Override
358     public float getLineMax(int line) {
359         return mMax;
360     }
361
362     @Override
363     public float getLineWidth(int line) {
364         return (line == 0 ? mMax : 0);
365     }
366
367     @Override
368     public final Directions getLineDirections(int line) {
369         return Layout.DIRS_ALL_LEFT_TO_RIGHT;
370     }
371
372     @Override
373     public int getTopPadding() {
374         return mTopPadding;
375     }
376
377     @Override
378     public int getBottomPadding() {
379         return mBottomPadding;
380     }
381
382     @Override
383     public int getEllipsisCount(int line) {
384         return mEllipsizedCount;
385     }
386
387     @Override
388     public int getEllipsisStart(int line) {
389         return mEllipsizedStart;
390     }
391
392     @Override
393     public int getEllipsizedWidth() {
394         return mEllipsizedWidth;
395     }
396
397     // Override draw so it will be faster.
398     @Override
399     public void draw(Canvas c, Path highlight, Paint highlightpaint,
400                      int cursorOffset) {
401         if (mDirect != null && highlight == null) {
402             c.drawText(mDirect, 0, mBottom - mDesc, mPaint);
403         } else {
404             super.draw(c, highlight, highlightpaint, cursorOffset);
405         }
406     }
407
408     /**
409      * Callback for the ellipsizer to report what region it ellipsized.
410      */
411     public void ellipsized(int start, int end) {
412         mEllipsizedStart = start;
413         mEllipsizedCount = end - start;
414     }
415
416     private static final char FIRST_RIGHT_TO_LEFT = '\u0590';
417
418     private String mDirect;
419     private Paint mPaint;
420
421     /* package */ int mBottom, mDesc;   // for Direct
422     private int mTopPadding, mBottomPadding;
423     private float mMax;
424     private int mEllipsizedWidth, mEllipsizedStart, mEllipsizedCount;
425
426     public static class Metrics extends Paint.FontMetricsInt {
427         public int width;
428
429         @Override public String toString() {
430             return super.toString() + " width=" + width;
431         }
432     }
433 }