SelectText.cpp revision 62740ff35119cc54e82b6af582a01dd34b9e027e
1/*
2 * Copyright 2008, The Android Open Source Project
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 *  * Redistributions of source code must retain the above copyright
8 *    notice, this list of conditions and the following disclaimer.
9 *  * Redistributions in binary form must reproduce the above copyright
10 *    notice, this list of conditions and the following disclaimer in the
11 *    documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#define LOG_TAG "webviewglue"
27
28#include "CachedPrefix.h"
29#include "BidiResolver.h"
30#include "BidiRunList.h"
31#include "CachedRoot.h"
32#include "LayerAndroid.h"
33#include "ParseCanvas.h"
34#include "SelectText.h"
35#include "SkBitmap.h"
36#include "SkBounder.h"
37#include "SkGradientShader.h"
38#include "SkMatrix.h"
39#include "SkPicture.h"
40#include "SkPixelXorXfermode.h"
41#include "SkPoint.h"
42#include "SkRect.h"
43#include "SkRegion.h"
44#include "SkUtils.h"
45#include "TextRun.h"
46
47#ifdef DEBUG_NAV_UI
48#include <wtf/text/CString.h>
49#endif
50
51#define VERBOSE_LOGGING 0
52// #define EXTRA_NOISY_LOGGING 1
53#define DEBUG_TOUCH_HANDLES 0
54
55// TextRunIterator has been copied verbatim from GraphicsContext.cpp
56namespace WebCore {
57
58class TextRunIterator {
59public:
60    TextRunIterator()
61        : m_textRun(0)
62        , m_offset(0)
63    {
64    }
65
66    TextRunIterator(const TextRun* textRun, unsigned offset)
67        : m_textRun(textRun)
68        , m_offset(offset)
69    {
70    }
71
72    TextRunIterator(const TextRunIterator& other)
73        : m_textRun(other.m_textRun)
74        , m_offset(other.m_offset)
75    {
76    }
77
78    unsigned offset() const { return m_offset; }
79    void increment() { m_offset++; }
80    bool atEnd() const { return !m_textRun || m_offset >= m_textRun->length(); }
81    UChar current() const { return (*m_textRun)[m_offset]; }
82    WTF::Unicode::Direction direction() const { return atEnd() ? WTF::Unicode::OtherNeutral : WTF::Unicode::direction(current()); }
83
84    bool operator==(const TextRunIterator& other)
85    {
86        return m_offset == other.m_offset && m_textRun == other.m_textRun;
87    }
88
89    bool operator!=(const TextRunIterator& other) { return !operator==(other); }
90
91private:
92    const TextRun* m_textRun;
93    int m_offset;
94};
95
96// ReverseBidi is a trimmed-down version of GraphicsContext::drawBidiText()
97void ReverseBidi(UChar* chars, int len) {
98    using namespace WTF::Unicode;
99    WTF::Vector<UChar> result;
100    result.reserveCapacity(len);
101    TextRun run(chars, len);
102    BidiResolver<TextRunIterator, BidiCharacterRun> bidiResolver;
103    BidiRunList<BidiCharacterRun>& bidiRuns = bidiResolver.runs();
104    bidiResolver.setStatus(BidiStatus(LeftToRight, LeftToRight, LeftToRight,
105        BidiContext::create(0, LeftToRight, false)));
106    bidiResolver.setPosition(TextRunIterator(&run, 0));
107    bidiResolver.createBidiRunsForLine(TextRunIterator(&run, len));
108    if (!bidiRuns.runCount())
109        return;
110    BidiCharacterRun* bidiRun = bidiRuns.firstRun();
111    while (bidiRun) {
112        int bidiStart = bidiRun->start();
113        int bidiStop = bidiRun->stop();
114        int size = result.size();
115        int bidiCount = bidiStop - bidiStart;
116        result.append(chars + bidiStart, bidiCount);
117        if (bidiRun->level() % 2) {
118            UChar* start = &result[size];
119            UChar* end = start + bidiCount;
120            // reverse the order of any RTL substrings
121            while (start < end) {
122                UChar temp = *start;
123                *start++ = *--end;
124                *end = temp;
125            }
126            start = &result[size];
127            end = start + bidiCount - 1;
128            // if the RTL substring had a surrogate pair, restore its order
129            while (start < end) {
130                UChar trail = *start++;
131                if (!U16_IS_SURROGATE(trail))
132                    continue;
133                start[-1] = *start; // lead
134                *start++ = trail;
135            }
136        }
137        bidiRun = bidiRun->next();
138    }
139    bidiRuns.deleteRuns();
140    memcpy(chars, &result[0], len * sizeof(UChar));
141}
142
143}
144
145namespace android {
146
147#define HYPHEN_MINUS 0x2D // ASCII hyphen
148#define SOLIDUS 0x2F // ASCII slash
149#define REVERSE_SOLIDUS 0x5C // ASCII backslash
150#define HYPHEN 0x2010 // unicode hyphen, first in range of dashes
151#define HORZ_BAR 0x2015 // unicode horizontal bar, last in range of dashes
152#define TOUCH_SLOP 10 // additional distance from character rect when hit
153
154class CommonCheck : public SkBounder {
155public:
156    CommonCheck(const SkIRect& area)
157        : mArea(area)
158        , mLastUni(0)
159    {
160        mLastGlyph.fGlyphID = static_cast<uint16_t>(-1);
161        mLastCandidate.fGlyphID = static_cast<uint16_t>(-1);
162        mMatrix.reset();
163        reset();
164    }
165
166    /* called only while the picture is parsed */
167    int base() {
168        if (mBase == INT_MAX) {
169            SkPoint result;
170            mMatrix.mapXY(0, mY, &result);
171            mBase = SkScalarFloor(result.fY);
172        }
173        return mBase;
174    }
175
176    /* called only while the picture is parsed */
177     int bottom() {
178        if (mBottom == INT_MAX) {
179            SkPoint result;
180            SkPaint::FontMetrics metrics;
181            mPaint.getFontMetrics(&metrics);
182            mMatrix.mapXY(0, metrics.fDescent + mY, &result);
183            mBottom = SkScalarCeil(result.fY);
184        }
185        return mBottom;
186    }
187
188#if DEBUG_NAV_UI
189    // make current (possibily uncomputed) value visible for debugging
190    int bottomDebug() const
191    {
192        return mBottom;
193    }
194#endif
195
196    bool addNewLine(const SkBounder::GlyphRec& rec)
197    {
198        SkFixed lineSpacing = SkFixedAbs(mLastGlyph.fLSB.fY - rec.fLSB.fY);
199        SkFixed lineHeight = SkIntToFixed(bottom() - top());
200        return lineSpacing >= lineHeight + (lineHeight >> 1); // 1.5
201    }
202
203    bool addSpace(const SkBounder::GlyphRec& rec)
204    {
205        bool newBaseLine = mLastGlyph.fLSB.fY != rec.fLSB.fY;
206        if (((mLastUni >= HYPHEN && mLastUni <= HORZ_BAR)
207            || mLastUni == HYPHEN_MINUS || mLastUni == SOLIDUS
208            || mLastUni == REVERSE_SOLIDUS) && newBaseLine)
209        {
210            return false;
211        }
212        return isSpace(rec);
213    }
214
215    void finishGlyph()
216    {
217        mLastGlyph = mLastCandidate;
218        mLastUni = mLastUniCandidate;
219        mLastPaint = mLastPaintCandidate;
220    }
221
222    const SkIRect& getArea() const {
223        return mArea;
224    }
225
226    /* called only while the picture is parsed */
227    SkUnichar getUniChar(const SkBounder::GlyphRec& rec)
228    {
229        SkUnichar unichar;
230        SkPaint::TextEncoding save = mPaint.getTextEncoding();
231        mPaint.setTextEncoding(SkPaint::kUTF16_TextEncoding);
232        mPaint.glyphsToUnichars(&rec.fGlyphID, 1, &unichar);
233        mPaint.setTextEncoding(save);
234        return unichar;
235    }
236
237    bool isSpace(const SkBounder::GlyphRec& rec)
238    {
239        if (mLastGlyph.fGlyphID == static_cast<uint16_t>(-1))
240            return true;
241        DBG_NAV_LOGD("mLastGlyph=((%g, %g),(%g, %g), %d)"
242            " rec=((%g, %g),(%g, %g), %d) mLastUni=0x%04x '%c'",
243            SkFixedToScalar(mLastGlyph.fLSB.fX),
244            SkFixedToScalar(mLastGlyph.fLSB.fY),
245            SkFixedToScalar(mLastGlyph.fRSB.fX),
246            SkFixedToScalar(mLastGlyph.fRSB.fY), mLastGlyph.fGlyphID,
247            SkFixedToScalar(rec.fLSB.fX), SkFixedToScalar(rec.fLSB.fY),
248            SkFixedToScalar(rec.fRSB.fX), SkFixedToScalar(rec.fRSB.fY),
249            rec.fGlyphID,
250            mLastUni, mLastUni && mLastUni < 0x7f ? mLastUni : '?');
251        bool newBaseLine = mLastGlyph.fLSB.fY != rec.fLSB.fY;
252        if (newBaseLine)
253            return true;
254        SkFixed gapOne = mLastGlyph.fLSB.fX - rec.fRSB.fX;
255        SkFixed gapTwo = rec.fLSB.fX - mLastGlyph.fRSB.fX;
256        if (gapOne < 0 && gapTwo < 0)
257            return false; // overlaps
258        const SkBounder::GlyphRec& first = mLastGlyph.fLSB.fX < rec.fLSB.fX
259            ? mLastGlyph : rec;
260        const SkBounder::GlyphRec& second = mLastGlyph.fLSB.fX < rec.fLSB.fX
261            ? rec : mLastGlyph;
262        uint16_t firstGlyph = first.fGlyphID;
263        SkScalar firstWidth = mLastPaint.measureText(&firstGlyph, sizeof(firstGlyph));
264        SkFixed ceilWidth = SkIntToFixed(SkScalarCeil(firstWidth));
265        SkFixed posNoSpace = first.fLSB.fX + ceilWidth;
266        SkFixed ceilSpace = SkIntToFixed(SkFixedCeil(minSpaceWidth(mLastPaint)));
267        SkFixed posWithSpace = posNoSpace + ceilSpace;
268        SkFixed diffNoSpace = SkFixedAbs(second.fLSB.fX - posNoSpace);
269        SkFixed diffWithSpace = SkFixedAbs(second.fLSB.fX - posWithSpace);
270        DBG_NAV_LOGD("second=%g width=%g (%g) noSpace=%g (%g) withSpace=%g (%g)"
271            " fontSize=%g",
272            SkFixedToScalar(second.fLSB.fX),
273            firstWidth, SkFixedToScalar(ceilWidth),
274            SkFixedToScalar(posNoSpace), SkFixedToScalar(diffNoSpace),
275            SkFixedToScalar(posWithSpace), SkFixedToScalar(diffWithSpace),
276            mLastPaint.getTextSize());
277        return diffWithSpace <= diffNoSpace;
278    }
279
280    SkFixed minSpaceWidth(SkPaint& paint)
281    {
282        if (mMinSpaceWidth == SK_FixedMax) {
283            SkPaint::TextEncoding save = paint.getTextEncoding();
284            paint.setTextEncoding(SkPaint::kUTF8_TextEncoding);
285            SkScalar width = paint.measureText(" ", 1);
286            mMinSpaceWidth = SkScalarToFixed(width * mMatrix.getScaleX());
287            paint.setTextEncoding(save);
288            DBG_NAV_LOGV("width=%g matrix sx/sy=(%g, %g) tx/ty=(%g, %g)"
289                " mMinSpaceWidth=%g", width,
290                mMatrix.getScaleX(), mMatrix.getScaleY(),
291                mMatrix.getTranslateX(), mMatrix.getTranslateY(),
292                SkFixedToScalar(mMinSpaceWidth));
293        }
294        return mMinSpaceWidth;
295    }
296
297    void recordGlyph(const SkBounder::GlyphRec& rec)
298    {
299        mLastCandidate = rec;
300        mLastUniCandidate = getUniChar(rec);
301        mLastPaintCandidate = mPaint;
302    }
303
304    void reset()
305    {
306        mMinSpaceWidth = SK_FixedMax; // mark as uninitialized
307        mBase = mBottom = mTop = INT_MAX; // mark as uninitialized
308    }
309
310    void set(CommonCheck& check)
311    {
312        mLastGlyph = check.mLastGlyph;
313        mLastUni = check.mLastUni;
314        mMatrix = check.mMatrix;
315        mLastPaint = check.mLastPaint;
316        reset();
317    }
318
319    void setGlyph(CommonCheck& check)
320    {
321        mLastGlyph = check.mLastGlyph;
322        mLastUni = check.mLastUni;
323        mLastPaint = check.mLastPaint;
324    }
325
326    void setUp(const SkPaint& paint, const SkMatrix& matrix, SkScalar y,
327            const void* text)
328    {
329        mMatrix = matrix;
330        mPaint = paint;
331        mText = static_cast<const uint16_t*>(text);
332        mY = y;
333        reset();
334    }
335
336    /* called only while the picture is parsed */
337    int top() {
338        if (mTop == INT_MAX) {
339            SkPoint result;
340            SkPaint::FontMetrics metrics;
341            mPaint.getFontMetrics(&metrics);
342            mMatrix.mapXY(0, metrics.fAscent + mY, &result);
343            mTop = SkScalarFloor(result.fY);
344        }
345        return mTop;
346    }
347
348#if DEBUG_NAV_UI
349    // make current (possibily uncomputed) value visible for debugging
350    int topDebug() const
351    {
352        return mTop;
353    }
354#endif
355
356protected:
357    SkIRect mArea;
358    SkBounder::GlyphRec mLastCandidate;
359    SkBounder::GlyphRec mLastGlyph;
360    SkPaint mLastPaint; // available after picture has been parsed
361    SkPaint mLastPaintCandidate; // associated with candidate glyph
362    SkUnichar mLastUni;
363    SkUnichar mLastUniCandidate;
364    SkMatrix mMatrix;
365    SkPaint mPaint; // only set up while the picture is parsed
366    const uint16_t* mText;
367    SkScalar mY;
368private:
369    int mBase;
370    int mBottom;
371    SkFixed mMinSpaceWidth;
372    int mTop;
373    friend class EdgeCheck;
374};
375
376// generate the limit area for the new selection
377class LineCheck : public CommonCheck {
378public:
379    LineCheck(int x, int y, const SkIRect& area)
380        : INHERITED(area)
381        , mX(x)
382        , mY(y)
383        , mInBetween(false)
384    {
385        mLast.setEmpty();
386    }
387
388    void finish(const SkRegion& selectedRgn)
389    {
390        if (!mParagraphs.count() && mLast.isEmpty())
391            return;
392        processLine();
393        bool above = false;
394        bool below = false;
395        bool selected = false;
396        SkRegion localRgn(selectedRgn);
397        localRgn.translate(-mArea.fLeft, -mArea.fTop, &localRgn);
398        DBG_NAV_LOGD("localRgn=(%d,%d,%d,%d)",
399            localRgn.getBounds().fLeft, localRgn.getBounds().fTop,
400            localRgn.getBounds().fRight, localRgn.getBounds().fBottom);
401        for (int index = 0; index < mParagraphs.count(); index++) {
402            const SkIRect& rect = mParagraphs[index];
403            bool localSelected = localRgn.intersects(rect);
404            DBG_NAV_LOGD("[%d] rect=(%d,%d,%d,%d)", index, rect.fLeft, rect.fTop,
405                rect.fRight, rect.fBottom);
406            if (localSelected) {
407                DBG_NAV_LOGD("[%d] localSelected=true", index);
408                *mSelected.append() = rect;
409            }
410            if (rect.fRight <= mX || rect.fLeft >= mX)
411                continue;
412            if (mY > rect.fBottom) {
413                below = true;
414                selected |= localSelected;
415                DBG_NAV_LOGD("[%d] below=true localSelected=%s", index,
416                    localSelected ? "true" : "false");
417           }
418            if (mY < rect.fTop) {
419                above = true;
420                selected |= localSelected;
421                DBG_NAV_LOGD("[%d] above=true localSelected=%s", index,
422                    localSelected ? "true" : "false");
423            }
424        }
425        DBG_NAV_LOGD("mX=%d mY=%d above=%s below=%s selected=%s",
426            mX, mY, above ? "true" : "false", below ? "true" : "false",
427            selected ? "true" : "false");
428        mInBetween = above && below && selected;
429    }
430
431    bool inBetween() const
432    {
433        return mInBetween;
434    }
435
436    bool inColumn(const SkIRect& test) const
437    {
438        for (int index = 0; index < mSelected.count(); index++) {
439            const SkIRect& rect = mSelected[index];
440            if (rect.fRight > test.fLeft && rect.fLeft < test.fRight)
441                return true;
442        }
443        return false;
444    }
445
446    bool inColumn(int x, int y) const
447    {
448        for (int index = 0; index < mSelected.count(); index++) {
449            const SkIRect& rect = mSelected[index];
450            if (rect.contains(x, y))
451                return true;
452        }
453        return false;
454    }
455
456    virtual bool onIRect(const SkIRect& rect)
457    {
458        SkIRect bounds;
459        bounds.set(rect.fLeft, top(), rect.fRight, bottom());
460        // assume that characters must be consecutive to describe spaces
461        // (i.e., don't join rects drawn at different times)
462        if (bounds.fTop != mLast.fTop || bounds.fBottom != mLast.fBottom
463            || bounds.fLeft > mLast.fRight + minSpaceWidth(mPaint)
464            || bounds.fLeft < mLast.fLeft) {
465            processLine();
466            mLast = bounds;
467        } else
468            mLast.join(bounds);
469        return false;
470    }
471
472    void processLine()
473    {
474        // assume line spacing of 1.5
475        int lineHeight = bottom() - top();
476        mLast.inset(0, -lineHeight >> 1);
477        // collect arrays of rectangles making up glyphs below or above this one
478        for (int index = 0; index < mParagraphs.count(); index++) {
479            SkIRect& rect = mParagraphs[index];
480            if (SkIRect::Intersects(rect, mLast)) {
481                rect.join(mLast);
482                return;
483            }
484        }
485        *mParagraphs.append() = mLast;
486    }
487
488protected:
489    int mX;
490    int mY;
491    SkIRect mLast;
492    SkTDArray<SkIRect> mParagraphs;
493    SkTDArray<SkIRect> mSelected;
494    bool mInBetween;
495private:
496    typedef CommonCheck INHERITED;
497};
498
499class SelectText::FirstCheck : public CommonCheck {
500public:
501    FirstCheck(int x, int y, const SkIRect& area)
502        : INHERITED(area)
503        , mLineCheck(0)
504        , mFocusX(x - area.fLeft)
505        , mFocusY(y - area.fTop)
506        , mBestInColumn(false)
507        , mRecordGlyph(false)
508    {
509        reset();
510    }
511
512    const SkIRect& adjustedBounds(int* base)
513    {
514        *base = mBestBase + mArea.fTop;
515        mBestBounds.offset(mArea.fLeft, mArea.fTop);
516        DBG_NAV_LOGD("FirstCheck mBestBounds:(%d, %d, %d, %d) mTop=%d mBottom=%d",
517            mBestBounds.fLeft, mBestBounds.fTop, mBestBounds.fRight,
518            mBestBounds.fBottom, topDebug(), bottomDebug());
519        return mBestBounds;
520    }
521
522    int focusX() const { return mFocusX; }
523    int focusY() const { return mFocusY; }
524
525    virtual bool onIRectGlyph(const SkIRect& rect,
526        const SkBounder::GlyphRec& rec)
527    {
528        /* compute distance from rectangle center.
529         * centerX = (rect.L + rect.R) / 2
530         * multiply centerX and comparison x by 2 to retain better precision
531         */
532        SkIRect testBounds = {rect.fLeft, top(), rect.fRight, bottom()};
533        // dx and dy are the distances from the tested edge
534        // The edge distance is paramount if the test point is far away
535        int dx = std::max(0, std::max(testBounds.fLeft - mFocusX,
536            mFocusX - testBounds.fRight));
537        int dy = std::max(0, std::max(testBounds.fTop - mFocusY,
538            mFocusY - testBounds.fBottom));
539        bool testInColumn = false;
540        bool inBetween = false;
541        bool inFocus = false;
542        if (mLineCheck) {
543            testInColumn = mLineCheck->inColumn(testBounds);
544            inBetween = mLineCheck->inBetween();
545            inFocus = mLineCheck->inColumn(mFocusX, mFocusY);
546        }
547#ifdef EXTRA_NOISY_LOGGING
548        if (dy < 10) {
549            SkUnichar ch = getUniChar(rec);
550            DBG_NAV_LOGD("FC dx/y=%d,%d mDx/y=%d,%d test=%d,%d,%d,%d"
551                " best=%d,%d,%d,%d bestIn=%s tween=%s testIn=%s focus=%s ch=%c",
552                dx, dy, mDx, mDy,
553                testBounds.fLeft, testBounds.fTop, testBounds.fRight,
554                testBounds.fBottom, mBestBounds.fLeft, mBestBounds.fTop,
555                mBestBounds.fRight, mBestBounds.fBottom,
556                mBestInColumn ? "true" : "false", inBetween ? "true" : "false",
557                testInColumn ? "true" : "false", inFocus ? "true" : "false",
558                ch < 0x7f ? ch : '?');
559        }
560#endif
561        if ((mBestInColumn || inBetween) && !testInColumn) {
562#ifdef EXTRA_NOISY_LOGGING
563            if (dy < 10) DBG_NAV_LOG("FirstCheck reject column");
564#endif
565            return false;
566        }
567        bool ignoreColumn = mBestInColumn == testInColumn || !inFocus;
568        if (ignoreColumn && dy > 0 && (mDy < dy
569            || (mDy == dy && dx > 0 && mDx <= dx))) {
570#ifdef EXTRA_NOISY_LOGGING
571            if (dy < 10) DBG_NAV_LOG("FirstCheck reject edge");
572#endif
573            return false;
574        }
575        // cx and cy are the distances from the tested center
576        // The center distance is used when the test point is over the text
577        int cx = std::abs(((testBounds.fLeft + testBounds.fRight) >> 1)
578                - mFocusX);
579        int cy = std::abs(((testBounds.fTop + testBounds.fBottom) >> 1)
580                - mFocusY);
581        if (ignoreColumn && dy == 0 && mDy == 0) {
582            if (mCy < cy) {
583#ifdef EXTRA_NOISY_LOGGING
584                DBG_NAV_LOGD("FirstCheck reject cy=%d mCy=%d", cy, mCy);
585#endif
586                return false;
587            }
588            if (mCy == cy) {
589                if (dx == 0 && mDx == 0) {
590                    if (mCx < cx) {
591#ifdef EXTRA_NOISY_LOGGING
592                        DBG_NAV_LOGD("FirstCheck reject cx=%d mCx=%d", cx, mCx);
593#endif
594                        return false;
595                    }
596                } else if (dx > 0 && mDx <= dx) {
597#ifdef EXTRA_NOISY_LOGGING
598                    DBG_NAV_LOGD("FirstCheck reject dx=%d mDx=%d", dx, mDx);
599#endif
600                    return false;
601                }
602            }
603        }
604#ifdef EXTRA_NOISY_LOGGING
605        if (dy < 10) {
606            DBG_NAV_LOGD("FirstCheck cx/y=(%d,%d)", cx, cy);
607        }
608#endif
609        mBestBase = base();
610        mBestBounds = testBounds;
611        mBestInColumn = testInColumn;
612#ifndef EXTRA_NOISY_LOGGING
613        if (dy < 10 && dx < 10)
614#endif
615        {
616#if DEBUG_NAV_UI
617            SkUnichar ch = getUniChar(rec);
618#endif
619            DBG_NAV_LOGD("FirstCheck dx/y=(%d,%d) mFocus=(%d,%d)"
620                " mBestBounds={%d,%d,r=%d,b=%d} inColumn=%s ch=%c",
621                dx, dy, mFocusX, mFocusY,
622                mBestBounds.fLeft, mBestBounds.fTop,
623                mBestBounds.fRight, mBestBounds.fBottom,
624                mBestInColumn ? "true" : "false", ch < 0x7f ? ch : '?');
625        }
626        mCx = cx;
627        mCy = cy;
628        mDx = dx;
629        mDy = dy;
630        if (mRecordGlyph)
631            recordGlyph(rec);
632        return false;
633    }
634
635    void reset()
636    {
637        mBestBounds.setEmpty();
638        mDx = mDy = mCx = mCy = INT_MAX;
639    }
640
641    void setLines(const LineCheck* lineCheck) { mLineCheck = lineCheck; }
642    void setRecordGlyph() { mRecordGlyph = true; }
643
644protected:
645    const LineCheck* mLineCheck;
646    int mBestBase;
647    SkIRect mBestBounds;
648    int mCx;
649    int mCy;
650    int mDx;
651    int mDy;
652    int mFocusX;
653    int mFocusY;
654    bool mBestInColumn;
655    bool mRecordGlyph;
656private:
657    typedef CommonCheck INHERITED;
658};
659
660class SelectText::EdgeCheck : public SelectText::FirstCheck {
661public:
662    EdgeCheck(int x, int y, const SkIRect& area, CommonCheck& last, bool left)
663        : INHERITED(x, y, area)
664        , mLast(area)
665        , mLeft(left)
666    {
667        mLast.set(last); // CommonCheck::set()
668        setGlyph(last);
669    }
670
671    bool adjacent()
672    {
673        return !mLast.isSpace(mLastGlyph);
674    }
675
676    const SkIRect& bestBounds(int* base)
677    {
678        *base = mBestBase;
679        return mBestBounds;
680    }
681
682    virtual bool onIRectGlyph(const SkIRect& rect,
683        const SkBounder::GlyphRec& rec)
684    {
685        int dx = mLeft ? mFocusX - rect.fRight : rect.fLeft - mFocusX;
686        int dy = ((top() + bottom()) >> 1) - mFocusY;
687        dx = abs(dx);
688        dy = abs(dy);
689        if (mLeft ? mFocusX <= rect.fLeft : mFocusX >= rect.fRight) {
690            if (dx <= 10 && dy <= 10) {
691                DBG_NAV_LOGD("EdgeCheck fLeft=%d fRight=%d mFocusX=%d dx=%d dy=%d",
692                    rect.fLeft, rect.fRight, mFocusX, dx, dy);
693            }
694            return false;
695        }
696        if (mDy > dy || (mDy == dy && mDx > dx)) {
697            if (rec.fLSB == mLastGlyph.fLSB && rec.fRSB == mLastGlyph.fRSB) {
698                DBG_NAV_LOGD("dup rec.fLSB.fX=%g rec.fRSB.fX=%g",
699                SkFixedToScalar(rec.fLSB.fX), SkFixedToScalar(rec.fRSB.fX));
700                return false;
701            }
702            recordGlyph(rec);
703            mDx = dx;
704            mDy = dy;
705            mBestBase = base();
706            mBestBounds.set(rect.fLeft, top(), rect.fRight, bottom());
707            if (dx <= 10 && dy <= 10) {
708                DBG_NAV_LOGD("EdgeCheck mBestBounds={%d,%d,r=%d,b=%d} dx/y=(%d, %d)",
709                    mBestBounds.fLeft, mBestBounds.fTop,
710                    mBestBounds.fRight, mBestBounds.fBottom, dx, dy);
711            }
712        }
713        return false;
714    }
715
716    void shiftStart(SkIRect bounds)
717    {
718        DBG_NAV_LOGD("EdgeCheck mFocusX=%d mLeft=%s bounds.fLeft=%d bounds.fRight=%d",
719            mFocusX, mLeft ? "true" : "false", bounds.fLeft, bounds.fRight);
720        reset();
721        mFocusX = mLeft ? bounds.fLeft : bounds.fRight;
722        mLast.set(*this); // CommonCheck::set()
723    }
724
725protected:
726    CommonCheck mLast;
727    bool mLeft;
728private:
729    typedef SelectText::FirstCheck INHERITED;
730};
731
732class FindFirst : public CommonCheck {
733public:
734    FindFirst(const SkIRect& area)
735        : INHERITED(area)
736    {
737        mBestBounds.set(area.width(), area.height(), area.width(), area.height());
738    }
739
740    const SkIRect& bestBounds(int* base)
741    {
742        *base = mBestBase;
743        return mBestBounds;
744    }
745
746    virtual bool onIRect(const SkIRect& rect)
747    {
748        if (mBestBounds.isEmpty()) {
749            mBestBase = base();
750            mBestBounds.set(rect.fLeft, top(), rect.fRight, bottom());
751        }
752        return false;
753    }
754
755protected:
756    int mBestBase;
757    SkIRect mBestBounds;
758private:
759    typedef CommonCheck INHERITED;
760};
761
762class FindLast : public FindFirst {
763public:
764    FindLast(const SkIRect& area)
765        : INHERITED(area)
766    {
767        mBestBounds.setEmpty();
768    }
769
770    virtual bool onIRect(const SkIRect& rect)
771    {
772        mBestBase = base();
773        mBestBounds.set(rect.fLeft, top(), rect.fRight, bottom());
774        return false;
775    }
776
777private:
778    typedef FindFirst INHERITED;
779};
780
781static bool baseLinesAgree(const SkIRect& rectA, int baseA,
782    const SkIRect& rectB, int baseB)
783{
784    return (rectA.fTop < baseB && rectA.fBottom >= baseB)
785        || (rectB.fTop < baseA && rectB.fBottom >= baseA);
786}
787
788class BuilderCheck : public CommonCheck {
789protected:
790    enum IntersectionType {
791        NO_INTERSECTION, // debugging printf expects this to equal zero
792        LAST_INTERSECTION, // debugging printf expects this to equal one
793        WAIT_FOR_INTERSECTION
794    };
795
796    BuilderCheck(const SkIRect& start, int startBase, const SkIRect& end,
797        int endBase, const SkIRect& area)
798        : INHERITED(area)
799        , mCapture(false)
800        , mEnd(end)
801        , mEndBase(endBase)
802        , mStart(start)
803        , mStartBase(startBase)
804    {
805        mEnd.offset(-area.fLeft, -area.fTop);
806        mEndBase -= area.fTop;
807        mEndExtra.setEmpty();
808        mLast.setEmpty();
809        mLastBase = INT_MAX;
810        mSelectRect.setEmpty();
811        mStart.offset(-area.fLeft, -area.fTop);
812        mStartBase -= area.fTop;
813        mStartExtra.setEmpty();
814        DBG_NAV_LOGD(" mStart=(%d,%d,r=%d,b=%d) mStartBase=%d"
815            " mEnd=(%d,%d,r=%d,b=%d) mEndBase=%d",
816            mStart.fLeft, mStart.fTop, mStart.fRight, mStart.fBottom, mStartBase,
817            mEnd.fLeft, mEnd.fTop, mEnd.fRight, mEnd.fBottom, mEndBase);
818    }
819
820    int checkFlipRect(const SkIRect& full, int fullBase) {
821        mCollectFull = false;
822        // is the text to collect between the selection top and bottom?
823        if (fullBase < mStart.fTop || fullBase > mEnd.fBottom) {
824            if (VERBOSE_LOGGING && !mLast.isEmpty()) DBG_NAV_LOGD("%s 1"
825                " full=(%d,%d,r=%d,b=%d) fullBase=%d"
826                " mLast=(%d,%d,r=%d,b=%d) mLastBase=%d",
827                mLastIntersects ? "LAST_INTERSECTION" : "NO_INTERSECTION",
828                full.fLeft, full.fTop, full.fRight, full.fBottom, fullBase,
829                mLast.fLeft, mLast.fTop, mLast.fRight, mLast.fBottom, mLastBase);
830            return mLastIntersects;
831        }
832        // is the text to the left of the selection start?
833        if (baseLinesAgree(mStart, mStartBase, full, fullBase)
834            && full.fLeft < mStart.fLeft) {
835            if (VERBOSE_LOGGING) DBG_NAV_LOGD("%s 2"
836                " full=(%d,%d,r=%d,b=%d) fullBase=%d"
837                " mLast=(%d,%d,r=%d,b=%d) mLastBase=%d"
838                " mStart=(%d,%d,r=%d,b=%d) mStartBase=%d",
839                mLastIntersects ? "LAST_INTERSECTION" : "NO_INTERSECTION",
840                full.fLeft, full.fTop, full.fRight, full.fBottom, fullBase,
841                mLast.fLeft, mLast.fTop, mLast.fRight, mLast.fBottom, mLastBase,
842                mStart.fLeft, mStart.fTop, mStart.fRight, mStart.fBottom, mStartBase);
843            mStartExtra.join(full);
844            return mLastIntersects;
845        }
846        // is the text to the right of the selection end?
847        if (baseLinesAgree(mEnd, mEndBase, full, fullBase)
848            && full.fRight > mEnd.fRight) {
849            if (VERBOSE_LOGGING) DBG_NAV_LOGD("%s 3"
850                " full=(%d,%d,r=%d,b=%d) fullBase=%d"
851                " mLast=(%d,%d,r=%d,b=%d) mLastBase=%d"
852                " mEnd=(%d,%d,r=%d,b=%d) mEndBase=%d",
853                mLastIntersects ? "LAST_INTERSECTION" : "NO_INTERSECTION",
854                full.fLeft, full.fTop, full.fRight, full.fBottom, fullBase,
855                mLast.fLeft, mLast.fTop, mLast.fRight, mLast.fBottom, mLastBase,
856                mEnd.fLeft, mEnd.fTop, mEnd.fRight, mEnd.fBottom, mEndBase);
857            mEndExtra.join(full);
858            return mLastIntersects;
859        }
860        int spaceGap = SkFixedRound(minSpaceWidth(mPaint) * 3);
861        // should text to the left of the start be added to the selection bounds?
862        if (!mStartExtra.isEmpty()) {
863            if (VERBOSE_LOGGING) DBG_NAV_LOGD("mSelectRect=(%d,%d,r=%d,b=%d)"
864                " mStartExtra=(%d,%d,r=%d,b=%d)",
865                mSelectRect.fLeft, mSelectRect.fTop, mSelectRect.fRight, mSelectRect.fBottom,
866                mStartExtra.fLeft, mStartExtra.fTop, mStartExtra.fRight, mStartExtra.fBottom);
867            if (mStartExtra.fRight + spaceGap >= mStart.fLeft)
868                mSelectRect.join(mStartExtra);
869            mStartExtra.setEmpty();
870        }
871        // should text to the right of the end be added to the selection bounds?
872        if (!mEndExtra.isEmpty()) {
873            if (VERBOSE_LOGGING) DBG_NAV_LOGD("mSelectRect=(%d,%d,r=%d,b=%d)"
874                " mEndExtra=(%d,%d,r=%d,b=%d)",
875                mSelectRect.fLeft, mSelectRect.fTop, mSelectRect.fRight, mSelectRect.fBottom,
876                mEndExtra.fLeft, mEndExtra.fTop, mEndExtra.fRight, mEndExtra.fBottom);
877            if (mEndExtra.fLeft - spaceGap <= mEnd.fRight)
878                mSelectRect.join(mEndExtra);
879            mEndExtra.setEmpty();
880        }
881        bool sameBaseLine = baseLinesAgree(mLast, mLastBase, full, fullBase);
882        bool adjacent = (full.fLeft - mLast.fRight) < spaceGap;
883        // is this the first, or are there more characters on the same line?
884        if (mLast.isEmpty() || (sameBaseLine && adjacent)) {
885            if (VERBOSE_LOGGING) DBG_NAV_LOGD("WAIT_FOR_INTERSECTION"
886                " full=(%d,%d,r=%d,b=%d) fullBase=%d"
887                " mLast=(%d,%d,r=%d,b=%d) mLastBase=%d"
888                " mSelectRect=(%d,%d,r=%d,b=%d)",
889                full.fLeft, full.fTop, full.fRight, full.fBottom, fullBase,
890                mLast.fLeft, mLast.fTop, mLast.fRight, mLast.fBottom, mLastBase,
891                mSelectRect.fLeft, mSelectRect.fTop, mSelectRect.fRight, mSelectRect.fBottom);
892            mLast.join(full);
893            mLastIntersects = SkIRect::Intersects(mLast, mSelectRect);
894            return WAIT_FOR_INTERSECTION;
895        }
896        if (VERBOSE_LOGGING) DBG_NAV_LOGD("%s 4"
897            " mLast=(%d,%d,r=%d,b=%d) mLastBase=%d"
898            " full=(%d,%d,r=%d,b=%d) fullBase=%d"
899            " mSelectRect=(%d,%d,r=%d,b=%d)"
900            " mStartExtra=(%d,%d,r=%d,b=%d)"
901            " mEndExtra=(%d,%d,r=%d,b=%d)",
902            mLastIntersects ? "LAST_INTERSECTION" : "NO_INTERSECTION",
903            mLast.fLeft, mLast.fTop, mLast.fRight, mLast.fBottom, mLastBase,
904            full.fLeft, full.fTop, full.fRight, full.fBottom, fullBase,
905            mSelectRect.fLeft, mSelectRect.fTop, mSelectRect.fRight, mSelectRect.fBottom,
906            mStartExtra.fLeft, mStartExtra.fTop, mStartExtra.fRight, mStartExtra.fBottom,
907            mEndExtra.fLeft, mEndExtra.fTop, mEndExtra.fRight, mEndExtra.fBottom);
908        // after the caller determines what to do with the last collection,
909        // start the collection over with full and fullBase.
910        mCollectFull = true;
911        return mLastIntersects;
912    }
913
914    bool resetLast(const SkIRect& full, int fullBase)
915    {
916        if (mCollectFull) {
917            mLast = full;
918            mLastBase = fullBase;
919            mLastIntersects = SkIRect::Intersects(mLast, mSelectRect);
920        } else {
921            mLast.setEmpty();
922            mLastBase = INT_MAX;
923            mLastIntersects = false;
924        }
925        return mCollectFull;
926    }
927
928    void setFlippedState()
929    {
930        mSelectRect = mStart;
931        mSelectRect.join(mEnd);
932        DBG_NAV_LOGD("mSelectRect=(%d,%d,r=%d,b=%d)",
933            mSelectRect.fLeft, mSelectRect.fTop, mSelectRect.fRight, mSelectRect.fBottom);
934        mLast.setEmpty();
935        mLastBase = INT_MAX;
936        mLastIntersects = NO_INTERSECTION;
937    }
938
939    bool mCapture;
940    bool mCollectFull;
941    SkIRect mEnd;
942    int mEndBase;
943    SkIRect mEndExtra;
944    bool mFlipped;
945    SkIRect mLast;
946    int mLastBase;
947    int mLastIntersects;
948    SkIRect mSelectRect;
949    SkIRect mStart;
950    SkIRect mStartExtra;
951    int mStartBase;
952private:
953    typedef CommonCheck INHERITED;
954
955};
956
957class MultilineBuilder : public BuilderCheck {
958public:
959    MultilineBuilder(const SkIRect& start, int startBase, const SkIRect& end,
960            int endBase, const SkIRect& area, SkRegion* region)
961        : INHERITED(start, startBase, end, endBase, area)
962        , mSelectRegion(region)
963    {
964        mFlipped = false;
965    }
966
967    void addLastToRegion() {
968        if (VERBOSE_LOGGING) DBG_NAV_LOGD(" mLast=(%d,%d,r=%d,b=%d)",
969            mLast.fLeft, mLast.fTop, mLast.fRight, mLast.fBottom);
970        mSelectRegion->op(mLast, SkRegion::kUnion_Op);
971    }
972
973    void finish() {
974        if (!mFlipped || !mLastIntersects)
975            return;
976        addLastToRegion();
977    }
978
979    // return true if capture end was not found after capture begin
980    bool flipped() {
981        DBG_NAV_LOGD("flipped=%s", mCapture ? "true" : "false");
982        if (!mCapture)
983            return false;
984        mFlipped = true;
985        setFlippedState();
986        mSelectRegion->setEmpty();
987        return true;
988    }
989
990    virtual bool onIRect(const SkIRect& rect) {
991        SkIRect full;
992        full.set(rect.fLeft, top(), rect.fRight, bottom());
993        int fullBase = base();
994        if (mFlipped) {
995            int intersectType = checkFlipRect(full, fullBase);
996            if (intersectType == LAST_INTERSECTION)
997                addLastToRegion();
998            if (intersectType != WAIT_FOR_INTERSECTION)
999                resetLast(full, fullBase);
1000            return false;
1001        }
1002        if (full == mStart) {
1003            if (VERBOSE_LOGGING) DBG_NAV_LOGD("full == mStart full=(%d,%d,r=%d,b=%d)",
1004                full.fLeft, full.fTop, full.fRight, full.fBottom);
1005            mCapture = true;
1006        }
1007        if (mCapture) {
1008            bool sameLines = baseLinesAgree(mLast, mLastBase, full, fullBase);
1009            if (sameLines)
1010                mLast.join(full);
1011            if (!sameLines || full == mEnd) {
1012                if (VERBOSE_LOGGING) DBG_NAV_LOGD("finish mLast=(%d,%d,r=%d,b=%d)",
1013                    mLast.fLeft, mLast.fTop, mLast.fRight, mLast.fBottom);
1014                addLastToRegion();
1015                mLast = full;
1016                mLastBase = fullBase;
1017            }
1018        }
1019        if (full == mEnd) {
1020            if (VERBOSE_LOGGING) DBG_NAV_LOGD("full == mEnd full=(%d,%d,r=%d,b=%d)",
1021                full.fLeft, full.fTop, full.fRight, full.fBottom);
1022            mCapture = false;
1023            if (full == mStart)
1024                addLastToRegion();
1025        }
1026        return false;
1027    }
1028
1029protected:
1030    SkRegion* mSelectRegion;
1031private:
1032    typedef BuilderCheck INHERITED;
1033};
1034
1035static inline bool compareBounds(const SkIRect* first, const SkIRect* second)
1036{
1037    return first->fTop < second->fTop;
1038}
1039
1040class TextExtractor : public BuilderCheck {
1041public:
1042    TextExtractor(const SkIRect& start, int startBase, const SkIRect& end,
1043        int endBase, const SkIRect& area, bool flipped)
1044        : INHERITED(start, startBase, end, endBase, area)
1045        , mSelectStartIndex(-1)
1046        , mSkipFirstSpace(true) // don't start with a space
1047    {
1048        mFlipped = flipped;
1049        if (flipped)
1050            setFlippedState();
1051    }
1052
1053    void addCharacter(const SkBounder::GlyphRec& rec)
1054    {
1055        if (mSelectStartIndex < 0)
1056            mSelectStartIndex = mSelectText.count();
1057        if (!mSkipFirstSpace) {
1058            if (addNewLine(rec)) {
1059                DBG_NAV_LOG("write new line");
1060                *mSelectText.append() = '\n';
1061                *mSelectText.append() = '\n';
1062            } else if (addSpace(rec)) {
1063                DBG_NAV_LOG("write space");
1064                *mSelectText.append() = ' ';
1065            }
1066        } else
1067            mSkipFirstSpace = false;
1068        recordGlyph(rec);
1069        finishGlyph();
1070        if (VERBOSE_LOGGING) DBG_NAV_LOGD("glyphID=%d uni=%d '%c'", rec.fGlyphID,
1071            mLastUni, mLastUni && mLastUni < 0x7f ? mLastUni : '?');
1072        if (mLastUni) {
1073            uint16_t chars[2];
1074            size_t count = SkUTF16_FromUnichar(mLastUni, chars);
1075            *mSelectText.append() = chars[0];
1076            if (count == 2)
1077                *mSelectText.append() = chars[1];
1078        }
1079    }
1080
1081    void addLast()
1082    {
1083        *mSelectBounds.append() = mLast;
1084        *mSelectStart.append() = mSelectStartIndex;
1085        *mSelectEnd.append() = mSelectText.count();
1086    }
1087
1088    /* Text characters are collected before it's been determined that the
1089       characters are part of the selection. The bounds describe valid parts
1090       of the selection, but the bounds are out of order.
1091
1092       This sorts the characters by sorting the bounds, then copying the
1093       characters that were captured.
1094     */
1095    void finish()
1096    {
1097        if (mLastIntersects)
1098            addLast();
1099        Vector<SkIRect*> sortedBounds;
1100        SkTDArray<uint16_t> temp;
1101        int index;
1102        DBG_NAV_LOGD("mSelectBounds.count=%d text=%d", mSelectBounds.count(),
1103            mSelectText.count());
1104        for (index = 0; index < mSelectBounds.count(); index++)
1105            sortedBounds.append(&mSelectBounds[index]);
1106        std::sort(sortedBounds.begin(), sortedBounds.end(), compareBounds);
1107        int lastEnd = -1;
1108        for (index = 0; index < mSelectBounds.count(); index++) {
1109            int order = sortedBounds[index] - &mSelectBounds[0];
1110            int start = mSelectStart[order];
1111            int end = mSelectEnd[order];
1112            DBG_NAV_LOGD("order=%d start=%d end=%d top=%d", order, start, end,
1113                mSelectBounds[order].fTop);
1114            int count = temp.count();
1115            if (count > 0 && temp[count - 1] != '\n' && start != lastEnd) {
1116                // always separate paragraphs when original text is out of order
1117                DBG_NAV_LOG("write new line");
1118                *temp.append() = '\n';
1119                *temp.append() = '\n';
1120            }
1121            temp.append(end - start, &mSelectText[start]);
1122            lastEnd = end;
1123        }
1124        mSelectText.swap(temp);
1125    }
1126
1127    virtual bool onIRectGlyph(const SkIRect& rect,
1128        const SkBounder::GlyphRec& rec)
1129    {
1130        SkIRect full;
1131        full.set(rect.fLeft, top(), rect.fRight, bottom());
1132        int fullBase = base();
1133        if (mFlipped) {
1134            int intersectType = checkFlipRect(full, fullBase);
1135            if (WAIT_FOR_INTERSECTION == intersectType)
1136                addCharacter(rec); // may not be copied
1137            else {
1138                if (LAST_INTERSECTION == intersectType)
1139                    addLast();
1140                else
1141                    mSkipFirstSpace = true;
1142                mSelectStartIndex = -1;
1143                if (resetLast(full, fullBase))
1144                    addCharacter(rec); // may not be copied
1145            }
1146            return false;
1147        }
1148        if (full == mStart)
1149            mCapture = true;
1150        if (mCapture)
1151            addCharacter(rec);
1152        else
1153            mSkipFirstSpace = true;
1154        if (full == mEnd)
1155            mCapture = false;
1156        return false;
1157    }
1158
1159    WTF::String text() {
1160        if (mFlipped)
1161            finish();
1162        // the text has been copied in visual order. Reverse as needed if
1163        // result contains right-to-left characters.
1164        const uint16_t* start = mSelectText.begin();
1165        const uint16_t* end = mSelectText.end();
1166        while (start < end) {
1167            SkUnichar ch = SkUTF16_NextUnichar(&start);
1168            WTF::Unicode::Direction charDirection = WTF::Unicode::direction(ch);
1169            if (WTF::Unicode::RightToLeftArabic == charDirection
1170                    || WTF::Unicode::RightToLeft == charDirection) {
1171                WebCore::ReverseBidi(mSelectText.begin(), mSelectText.count());
1172                break;
1173            }
1174        }
1175        return WTF::String(mSelectText.begin(), mSelectText.count());
1176    }
1177
1178protected:
1179    SkIRect mEmpty;
1180    SkTDArray<SkIRect> mSelectBounds;
1181    SkTDArray<int> mSelectEnd;
1182    SkTDArray<int> mSelectStart;
1183    int mSelectStartIndex;
1184    SkTDArray<uint16_t> mSelectText;
1185    bool mSkipFirstSpace;
1186private:
1187    typedef BuilderCheck INHERITED;
1188};
1189
1190class TextCanvas : public ParseCanvas {
1191public:
1192
1193    TextCanvas(CommonCheck* bounder)
1194            : mBounder(*bounder) {
1195        setBounder(bounder);
1196        SkBitmap bitmap;
1197        const SkIRect& area = bounder->getArea();
1198        bitmap.setConfig(SkBitmap::kARGB_8888_Config, area.width(),
1199            area.height());
1200        setBitmapDevice(bitmap);
1201        translate(SkIntToScalar(-area.fLeft), SkIntToScalar(-area.fTop));
1202#ifdef DEBUG_NAV_UI
1203        const SkIRect& clip = getTotalClip().getBounds();
1204        const SkMatrix& matrix = getTotalMatrix();
1205        DBG_NAV_LOGD("bitmap=(%d,%d) clip=(%d,%d,%d,%d) matrix=(%g,%g)",
1206            bitmap.width(), bitmap.height(), clip.fLeft, clip.fTop,
1207            clip.fRight, clip.fBottom, matrix.getTranslateX(), matrix.getTranslateY());
1208#endif
1209    }
1210
1211    virtual void drawPaint(const SkPaint& paint) {
1212    }
1213
1214    virtual void drawPoints(PointMode mode, size_t count, const SkPoint pts[],
1215                            const SkPaint& paint) {
1216    }
1217
1218    virtual void drawRect(const SkRect& rect, const SkPaint& paint) {
1219    }
1220
1221    virtual void drawPath(const SkPath& path, const SkPaint& paint) {
1222    }
1223
1224    virtual void commonDrawBitmap(const SkBitmap& bitmap, const SkIRect* rect,
1225                              const SkMatrix& matrix, const SkPaint& paint) {
1226    }
1227
1228    virtual void drawSprite(const SkBitmap& bitmap, int left, int top,
1229                            const SkPaint* paint = NULL) {
1230    }
1231
1232    virtual void drawText(const void* text, size_t byteLength, SkScalar x,
1233                          SkScalar y, const SkPaint& paint) {
1234        mBounder.setUp(paint, getTotalMatrix(), y, text);
1235        INHERITED::drawText(text, byteLength, x, y, paint);
1236    }
1237
1238    virtual void drawPosTextH(const void* text, size_t byteLength,
1239                              const SkScalar xpos[], SkScalar constY,
1240                              const SkPaint& paint) {
1241        mBounder.setUp(paint, getTotalMatrix(), constY, text);
1242        INHERITED::drawPosTextH(text, byteLength, xpos, constY, paint);
1243    }
1244
1245    virtual void drawVertices(VertexMode vmode, int vertexCount,
1246                              const SkPoint vertices[], const SkPoint texs[],
1247                              const SkColor colors[], SkXfermode* xmode,
1248                              const uint16_t indices[], int indexCount,
1249                              const SkPaint& paint) {
1250    }
1251
1252    CommonCheck& mBounder;
1253private:
1254    typedef ParseCanvas INHERITED;
1255};
1256
1257static bool buildSelection(const SkPicture& picture, const SkIRect& area,
1258        const SkIRect& selStart, int startBase,
1259        const SkIRect& selEnd, int endBase, SkRegion* region)
1260{
1261    DBG_NAV_LOGD("area=(%d, %d, %d, %d) selStart=(%d, %d, %d, %d)"
1262        " selEnd=(%d, %d, %d, %d)",
1263        area.fLeft, area.fTop, area.fRight, area.fBottom,
1264        selStart.fLeft, selStart.fTop, selStart.fRight, selStart.fBottom,
1265        selEnd.fLeft, selEnd.fTop, selEnd.fRight, selEnd.fBottom);
1266    MultilineBuilder builder(selStart, startBase, selEnd, endBase, area, region);
1267    TextCanvas checker(&builder);
1268    checker.drawPicture(const_cast<SkPicture&>(picture));
1269    bool flipped = builder.flipped();
1270    if (flipped) {
1271        TextCanvas checker(&builder);
1272        checker.drawPicture(const_cast<SkPicture&>(picture));
1273    }
1274    builder.finish();
1275    region->translate(area.fLeft, area.fTop);
1276    return flipped;
1277}
1278
1279static SkIRect findFirst(const SkPicture& picture, int* base)
1280{
1281    SkIRect area;
1282    area.set(0, 0, picture.width(), picture.height());
1283    FindFirst finder(area);
1284    TextCanvas checker(&finder);
1285    checker.drawPicture(const_cast<SkPicture&>(picture));
1286    return finder.bestBounds(base);
1287}
1288
1289static SkIRect findLast(const SkPicture& picture, int* base)
1290{
1291    SkIRect area;
1292    area.set(0, 0, picture.width(), picture.height());
1293    FindLast finder(area);
1294    TextCanvas checker(&finder);
1295    checker.drawPicture(const_cast<SkPicture&>(picture));
1296    return finder.bestBounds(base);
1297}
1298
1299static WTF::String text(const SkPicture& picture, const SkIRect& area,
1300        const SkIRect& start, int startBase, const SkIRect& end,
1301        int endBase, bool flipped)
1302{
1303    TextExtractor extractor(start, startBase, end, endBase, area, flipped);
1304    TextCanvas checker(&extractor);
1305    checker.drawPicture(const_cast<SkPicture&>(picture));
1306    return extractor.text();
1307}
1308
1309#define CONTROL_NOTCH 16
1310// TODO: Now that java is the one actually drawing these, get the real values
1311// from the drawable itself
1312#define CONTROL_HEIGHT 47
1313#define CONTROL_WIDTH 26
1314#define STROKE_WIDTH 1.0f
1315#define STROKE_OUTSET 3.5f
1316#define STROKE_I_OUTSET 4 // (int) ceil(STROKE_OUTSET)
1317#define STROKE_COLOR 0x66000000
1318#define OUTER_COLOR 0x33000000
1319#define INNER_COLOR 0xe6aae300
1320
1321SelectText::SelectText()
1322    : m_controlWidth(CONTROL_WIDTH)
1323    , m_controlHeight(CONTROL_HEIGHT)
1324{
1325    m_picture = 0;
1326    reset();
1327    SkPaint paint;
1328    SkRect oval;
1329
1330    SkPath startOuterPath;
1331    oval.set(-CONTROL_WIDTH - STROKE_OUTSET, CONTROL_NOTCH - STROKE_OUTSET,
1332        -CONTROL_WIDTH + STROKE_OUTSET, CONTROL_NOTCH + STROKE_OUTSET);
1333    startOuterPath.arcTo(oval, 180, 45, true);
1334    oval.set(-STROKE_OUTSET, -STROKE_OUTSET,  STROKE_OUTSET, STROKE_OUTSET);
1335    startOuterPath.arcTo(oval, 180 + 45, 135, false);
1336    oval.set(-STROKE_OUTSET, CONTROL_HEIGHT - STROKE_OUTSET,
1337        STROKE_OUTSET, CONTROL_HEIGHT + STROKE_OUTSET);
1338    startOuterPath.arcTo(oval, 0, 90, false);
1339    oval.set(-CONTROL_WIDTH - STROKE_OUTSET, CONTROL_HEIGHT - STROKE_OUTSET,
1340        -CONTROL_WIDTH + STROKE_OUTSET, CONTROL_HEIGHT + STROKE_OUTSET);
1341    startOuterPath.arcTo(oval, 90, 90, false);
1342    startOuterPath.close();
1343    SkPath startInnerPath;
1344    startInnerPath.moveTo(-CONTROL_WIDTH, CONTROL_NOTCH);
1345    startInnerPath.lineTo(-CONTROL_WIDTH, CONTROL_HEIGHT);
1346    startInnerPath.lineTo(0, CONTROL_HEIGHT);
1347    startInnerPath.lineTo(0, 0);
1348    startInnerPath.close();
1349    startOuterPath.addPath(startInnerPath, 0, 0);
1350
1351    SkCanvas* canvas = m_startControl.beginRecording(
1352        CONTROL_WIDTH + STROKE_OUTSET * 2,
1353        CONTROL_HEIGHT + STROKE_OUTSET * 2);
1354    paint.setAntiAlias(true);
1355    paint.setColor(INNER_COLOR);
1356    paint.setStyle(SkPaint::kFill_Style);
1357    canvas->drawPath(startInnerPath, paint);
1358    paint.setColor(OUTER_COLOR);
1359    canvas->drawPath(startOuterPath, paint);
1360    paint.setStyle(SkPaint::kStroke_Style);
1361    paint.setColor(STROKE_COLOR);
1362    paint.setStrokeWidth(STROKE_WIDTH);
1363    canvas->drawPath(startInnerPath, paint);
1364    m_startControl.endRecording();
1365
1366    SkPath endOuterPath;
1367    oval.set(-STROKE_OUTSET, -STROKE_OUTSET,  STROKE_OUTSET, STROKE_OUTSET);
1368    endOuterPath.arcTo(oval, 180, 135, true);
1369    oval.set(CONTROL_WIDTH - STROKE_OUTSET, CONTROL_NOTCH - STROKE_OUTSET,
1370        CONTROL_WIDTH + STROKE_OUTSET, CONTROL_NOTCH + STROKE_OUTSET);
1371    endOuterPath.arcTo(oval, 360 - 45, 45, false);
1372    oval.set(CONTROL_WIDTH - STROKE_OUTSET, CONTROL_HEIGHT - STROKE_OUTSET,
1373        CONTROL_WIDTH + STROKE_OUTSET, CONTROL_HEIGHT + STROKE_OUTSET);
1374    endOuterPath.arcTo(oval, 0, 90, false);
1375    oval.set(-STROKE_OUTSET, CONTROL_HEIGHT - STROKE_OUTSET,
1376        STROKE_OUTSET, CONTROL_HEIGHT + STROKE_OUTSET);
1377    endOuterPath.arcTo(oval, 90, 90, false);
1378    startOuterPath.close();
1379    SkPath endInnerPath;
1380    endInnerPath.moveTo(0, 0);
1381    endInnerPath.lineTo(0, CONTROL_HEIGHT);
1382    endInnerPath.lineTo(CONTROL_WIDTH, CONTROL_HEIGHT);
1383    endInnerPath.lineTo(CONTROL_WIDTH, CONTROL_NOTCH);
1384    endInnerPath.close();
1385    endOuterPath.addPath(endInnerPath, 0, 0);
1386
1387    canvas = m_endControl.beginRecording(CONTROL_WIDTH + STROKE_OUTSET * 2,
1388        CONTROL_HEIGHT + STROKE_OUTSET * 2);
1389    paint.setColor(INNER_COLOR);
1390    paint.setStyle(SkPaint::kFill_Style);
1391    canvas->drawPath(endInnerPath, paint);
1392    paint.setColor(OUTER_COLOR);
1393    canvas->drawPath(endOuterPath, paint);
1394    paint.setStyle(SkPaint::kStroke_Style);
1395    paint.setColor(STROKE_COLOR);
1396    paint.setStrokeWidth(STROKE_WIDTH);
1397    canvas->drawPath(endInnerPath, paint);
1398    m_endControl.endRecording();
1399}
1400
1401SelectText::~SelectText()
1402{
1403    SkSafeUnref(m_picture);
1404}
1405
1406void SelectText::draw(SkCanvas* canvas, LayerAndroid* layer, IntRect* inval)
1407{
1408    if (m_layerId != layer->uniqueId())
1409        return;
1410    // reset m_picture to match m_layerId
1411    SkSafeUnref(m_picture);
1412    m_picture = layer->picture();
1413    SkSafeRef(m_picture);
1414    DBG_NAV_LOGD("m_extendSelection=%d m_drawPointer=%d layer [%d]",
1415        m_extendSelection, m_drawPointer, layer->uniqueId());
1416    if (m_extendSelection)
1417        drawSelectionRegion(canvas, inval);
1418    if (m_drawPointer)
1419        drawSelectionPointer(canvas, inval);
1420}
1421
1422static void addInval(IntRect* inval, const SkCanvas* canvas,
1423        const SkRect& bounds) {
1424    const SkMatrix& matrix = canvas->getTotalMatrix();
1425    SkRect transformed;
1426    matrix.mapRect(&transformed, bounds);
1427    SkIRect iTrans;
1428    transformed.round(&iTrans);
1429    inval->unite(iTrans);
1430}
1431
1432void SelectText::drawSelectionPointer(SkCanvas* canvas, IntRect* inval)
1433{
1434    SkPath path;
1435    if (m_extendSelection)
1436        getSelectionCaret(&path);
1437    else
1438        getSelectionArrow(&path);
1439    SkPixelXorXfermode xorMode(SK_ColorWHITE);
1440    SkPaint paint;
1441    paint.setAntiAlias(true);
1442    paint.setStyle(SkPaint::kStroke_Style);
1443    paint.setColor(SK_ColorBLACK);
1444    if (m_extendSelection)
1445        paint.setXfermode(&xorMode);
1446    else
1447        paint.setStrokeWidth(SK_Scalar1 * 2);
1448    int sc = canvas->save();
1449    canvas->scale(m_inverseScale, m_inverseScale);
1450    canvas->translate(m_selectX, m_selectY);
1451    canvas->drawPath(path, paint);
1452    if (!m_extendSelection) {
1453        paint.setStyle(SkPaint::kFill_Style);
1454        paint.setColor(SK_ColorWHITE);
1455        canvas->drawPath(path, paint);
1456    }
1457    SkRect bounds = path.getBounds();
1458    bounds.inset(-SK_Scalar1 * 2, -SK_Scalar1 * 2); // stroke width
1459    addInval(inval, canvas, bounds);
1460    canvas->restoreToCount(sc);
1461}
1462
1463static void addStart(SkRegion* diff, const SkIRect& rect)
1464{
1465    SkIRect bounds;
1466    bounds.set(rect.fLeft - CONTROL_WIDTH - STROKE_I_OUTSET,
1467        rect.fBottom - STROKE_I_OUTSET, rect.fLeft + STROKE_I_OUTSET,
1468        rect.fBottom + CONTROL_HEIGHT + STROKE_I_OUTSET);
1469    diff->op(bounds, SkRegion::kUnion_Op);
1470}
1471
1472static void addEnd(SkRegion* diff, const SkIRect& rect)
1473{
1474    SkIRect bounds;
1475    bounds.set(rect.fRight - STROKE_I_OUTSET, rect.fBottom - STROKE_I_OUTSET,
1476        rect.fRight + CONTROL_WIDTH + STROKE_I_OUTSET,
1477        rect.fBottom + CONTROL_HEIGHT + STROKE_I_OUTSET);
1478    diff->op(bounds, SkRegion::kUnion_Op);
1479}
1480
1481void SelectText::getSelectionRegion(const IntRect& vis, SkRegion *region)
1482{
1483    SkIRect ivisBounds = vis;
1484    ivisBounds.join(m_selStart);
1485    ivisBounds.join(m_selEnd);
1486    region->setEmpty();
1487    buildSelection(*m_picture, ivisBounds, m_selStart, m_startBase,
1488        m_selEnd, m_endBase, region);
1489}
1490
1491void SelectText::drawSelectionRegion(SkCanvas* canvas, IntRect* inval)
1492{
1493    if (!m_picture)
1494        return;
1495    SkIRect ivisBounds = m_visibleRect;
1496    ivisBounds.join(m_selStart);
1497    ivisBounds.join(m_selEnd);
1498    DBG_NAV_LOGD("m_selStart=(%d,%d,r=%d,b=%d) m_selEnd=(%d,%d,r=%d,b=%d)"
1499        " ivisBounds=(%d,%d,r=%d,b=%d)",
1500        m_selStart.fLeft, m_selStart.fTop, m_selStart.fRight, m_selStart.fBottom,
1501        m_selEnd.fLeft, m_selEnd.fTop, m_selEnd.fRight, m_selEnd.fBottom,
1502        ivisBounds.fLeft, ivisBounds.fTop, ivisBounds.fRight, ivisBounds.fBottom);
1503    if (m_lastSelRegion != m_selRegion)
1504        m_lastSelRegion.set(m_selRegion);
1505    SkRegion diff(m_lastSelRegion);
1506    m_selRegion.setEmpty();
1507    m_flipped = buildSelection(*m_picture, ivisBounds, m_selStart, m_startBase,
1508        m_selEnd, m_endBase, &m_selRegion);
1509    SkPath path;
1510    m_selRegion.getBoundaryPath(&path);
1511    path.setFillType(SkPath::kEvenOdd_FillType);
1512
1513    SkPaint paint;
1514    paint.setAntiAlias(true);
1515    paint.setColor(SkColorSetARGB(0x80, 0x83, 0xCC, 0x39));
1516    canvas->drawPath(path, paint);
1517    // experiment to draw touchable controls that resize the selection
1518    float scale = m_controlHeight / (float)CONTROL_HEIGHT;
1519    canvas->save();
1520    canvas->translate(m_selStart.fLeft, m_selStart.fBottom);
1521    canvas->scale(scale, scale);
1522    canvas->drawPicture(m_startControl);
1523    canvas->restore();
1524    canvas->save();
1525    canvas->translate(m_selEnd.fRight, m_selEnd.fBottom);
1526    canvas->scale(scale, scale);
1527    canvas->drawPicture(m_endControl);
1528    canvas->restore();
1529
1530#if DEBUG_TOUCH_HANDLES
1531    SkRect touchHandleRect;
1532    paint.setColor(SkColorSetARGB(0xA0, 0xFF, 0x00, 0x00));
1533    touchHandleRect.set(0, m_selStart.fBottom, m_selStart.fLeft, 0);
1534    touchHandleRect.fBottom = touchHandleRect.fTop + m_controlHeight;
1535    touchHandleRect.fLeft = touchHandleRect.fRight - m_controlWidth;
1536    canvas->drawRect(touchHandleRect, paint);
1537    touchHandleRect.set(m_selEnd.fRight, m_selEnd.fBottom, 0, 0);
1538    touchHandleRect.fBottom = touchHandleRect.fTop + m_controlHeight;
1539    touchHandleRect.fRight = touchHandleRect.fLeft + m_controlWidth;
1540    canvas->drawRect(touchHandleRect, paint);
1541#endif
1542
1543    SkIRect a = diff.getBounds();
1544    SkIRect b = m_selRegion.getBounds();
1545    diff.op(m_selRegion, SkRegion::kXOR_Op);
1546    SkIRect c = diff.getBounds();
1547    DBG_NAV_LOGD("old=(%d,%d,r=%d,b=%d) new=(%d,%d,r=%d,b=%d) diff=(%d,%d,r=%d,b=%d)",
1548        a.fLeft, a.fTop, a.fRight, a.fBottom, b.fLeft, b.fTop, b.fRight, b.fBottom,
1549        c.fLeft, c.fTop, c.fRight, c.fBottom);
1550    DBG_NAV_LOGD("lastStart=(%d,%d,r=%d,b=%d) m_lastEnd=(%d,%d,r=%d,b=%d)",
1551        m_lastStart.fLeft, m_lastStart.fTop, m_lastStart.fRight, m_lastStart.fBottom,
1552        m_lastEnd.fLeft, m_lastEnd.fTop, m_lastEnd.fRight, m_lastEnd.fBottom);
1553    if (!m_lastDrawnStart.isEmpty())
1554        addStart(&diff, m_lastDrawnStart);
1555    if (m_lastStart != m_selStart) {
1556        m_lastDrawnStart = m_lastStart;
1557        m_lastStart = m_selStart;
1558    }
1559    addStart(&diff, m_selStart);
1560    if (!m_lastDrawnEnd.isEmpty())
1561        addEnd(&diff, m_lastDrawnEnd);
1562    if (m_lastEnd != m_selEnd) {
1563        m_lastDrawnEnd = m_lastEnd;
1564        m_lastEnd = m_selEnd;
1565    }
1566    addEnd(&diff, m_selEnd);
1567    SkIRect iBounds = diff.getBounds();
1568    DBG_NAV_LOGD("diff=(%d,%d,r=%d,b=%d)",
1569        iBounds.fLeft, iBounds.fTop, iBounds.fRight, iBounds.fBottom);
1570    SkRect bounds;
1571    bounds.set(iBounds);
1572    addInval(inval, canvas, bounds);
1573}
1574
1575void SelectText::extendSelection(const IntRect& vis, int x, int y)
1576{
1577    if (!m_picture)
1578        return;
1579    setVisibleRect(vis);
1580    SkIRect clipRect = m_visibleRect;
1581    int base;
1582    DBG_NAV_LOGD("extend x/y=%d,%d m_startOffset=%d,%d", x, y,
1583        m_startOffset.fX, m_startOffset.fY);
1584    x -= m_startOffset.fX;
1585    y -= m_startOffset.fY;
1586    if (m_startSelection) {
1587        if (!clipRect.contains(x, y)
1588                || !clipRect.contains(m_original.fX, m_original.fY)) {
1589            clipRect.set(m_original.fX, m_original.fY, x, y);
1590            clipRect.sort();
1591            clipRect.inset(-m_visibleRect.width(), -m_visibleRect.height());
1592        }
1593        FirstCheck center(m_original.fX, m_original.fY, clipRect);
1594        m_selStart = m_selEnd = findClosest(center, *m_picture, &base);
1595        if (m_selStart.isEmpty())
1596            return;
1597        DBG_NAV_LOGD("selStart clip=(%d,%d,%d,%d) m_original=%d,%d"
1598            " m_selStart=(%d,%d,%d,%d)", clipRect.fLeft, clipRect.fTop,
1599            clipRect.fRight, clipRect.fBottom, m_original.fX, m_original.fY,
1600            m_selStart.fLeft, m_selStart.fTop, m_selStart.fRight, m_selStart.fBottom);
1601        m_startBase = m_endBase = base;
1602        m_startSelection = false;
1603        m_extendSelection = true;
1604        m_original.fX = m_original.fY = 0;
1605    }
1606    DBG_NAV_LOGD("extend x/y=%d,%d m_original=%d,%d", x, y,
1607        m_original.fX, m_original.fY);
1608    x -= m_original.fX;
1609    y -= m_original.fY;
1610    if (!clipRect.contains(x, y) || !clipRect.contains(m_selStart)) {
1611        clipRect.set(m_selStart.fLeft, m_selStart.fTop, x, y);
1612        clipRect.sort();
1613        clipRect.inset(-m_visibleRect.width(), -m_visibleRect.height());
1614    }
1615    DBG_NAV_LOGD("extend clip=(%d,%d,%d,%d) x/y=%d,%d wordSel=%s outsideWord=%s",
1616        clipRect.fLeft, clipRect.fTop, clipRect.fRight, clipRect.fBottom, x, y,
1617        m_wordSelection ? "true" : "false", m_outsideWord ? "true" : "false");
1618    FirstCheck extension(x, y, clipRect);
1619    SkIRect found = findClosest(extension, *m_picture, &base);
1620    if (m_wordSelection) {
1621        SkIRect wordBounds = m_wordBounds;
1622        if (!m_outsideWord)
1623            wordBounds.inset(-TOUCH_SLOP, -TOUCH_SLOP);
1624        DBG_NAV_LOGD("x=%d y=%d wordBounds=(%d,%d,r=%d,b=%d)"
1625            " found=(%d,%d,r=%d,b=%d)", x, y, wordBounds.fLeft, wordBounds.fTop,
1626            wordBounds.fRight, wordBounds.fBottom, found.fLeft, found.fTop,
1627            found.fRight, found.fBottom);
1628        if (wordBounds.contains(x, y)) {
1629            DBG_NAV_LOG("wordBounds.contains=true");
1630            m_outsideWord = false;
1631            return;
1632        }
1633        m_outsideWord = true;
1634        if (found.fBottom <= wordBounds.fTop)
1635            m_hitTopLeft = true;
1636        else if (found.fTop >= wordBounds.fBottom)
1637            m_hitTopLeft = false;
1638        else
1639            m_hitTopLeft = (found.fLeft + found.fRight)
1640                < (wordBounds.fLeft + wordBounds.fRight);
1641    }
1642    DBG_NAV_LOGD("x=%d y=%d m_startSelection=%s %s=(%d, %d, %d, %d)"
1643        " m_extendSelection=%s",
1644        x, y, m_startSelection ? "true" : "false",
1645        m_hitTopLeft ? "m_selStart" : "m_selEnd",
1646        found.fLeft, found.fTop, found.fRight, found.fBottom,
1647        m_extendSelection ? "true" : "false");
1648    if (m_hitTopLeft) {
1649        m_startBase = base;
1650        m_selStart = found;
1651    } else {
1652        m_endBase = base;
1653        m_selEnd = found;
1654    }
1655    swapAsNeeded();
1656}
1657
1658SkIRect SelectText::findClosest(FirstCheck& check, const SkPicture& picture,
1659        int* base)
1660{
1661    LineCheck lineCheck(check.focusX(), check.focusY(), check.getArea());
1662    TextCanvas lineChecker(&lineCheck);
1663    lineChecker.drawPicture(const_cast<SkPicture&>(picture));
1664    lineCheck.finish(m_selRegion);
1665    check.setLines(&lineCheck);
1666    TextCanvas checker(&check);
1667    checker.drawPicture(const_cast<SkPicture&>(picture));
1668    check.finishGlyph();
1669    return check.adjustedBounds(base);
1670}
1671
1672SkIRect SelectText::findEdge(const SkPicture& picture, const SkIRect& area,
1673        int x, int y, bool left, int* base)
1674{
1675    SkIRect result;
1676    result.setEmpty();
1677    FirstCheck center(x, y, area);
1678    center.setRecordGlyph();
1679    int closestBase;
1680    SkIRect closest = findClosest(center, picture, &closestBase);
1681    SkIRect sloppy = closest;
1682    sloppy.inset(-TOUCH_SLOP, -TOUCH_SLOP);
1683    if (!sloppy.contains(x, y)) {
1684        DBG_NAV_LOGD("sloppy=(%d, %d, %d, %d) area=(%d, %d, %d, %d) x/y=%d,%d",
1685            sloppy.fLeft, sloppy.fTop, sloppy.fRight, sloppy.fBottom,
1686            area.fLeft, area.fTop, area.fRight, area.fBottom, x, y);
1687        return result;
1688    }
1689    EdgeCheck edge(x, y, area, center, left);
1690    do { // detect left or right until there's a gap
1691        DBG_NAV_LOGD("edge=%p picture=%p area=%d,%d,%d,%d",
1692            &edge, &picture, area.fLeft, area.fTop, area.fRight, area.fBottom);
1693        TextCanvas checker(&edge);
1694        checker.drawPicture(const_cast<SkPicture&>(picture));
1695        edge.finishGlyph();
1696        if (!edge.adjacent()) {
1697            if (result.isEmpty()) {
1698                *base = closestBase;
1699                DBG_NAV_LOGD("closest=%d,%d,%d,%d", closest.fLeft,
1700                    closest.fTop, closest.fRight, closest.fBottom);
1701                return closest;
1702            }
1703            DBG_NAV_LOG("adjacent break");
1704            break;
1705        }
1706        int nextBase;
1707        const SkIRect& next = edge.bestBounds(&nextBase);
1708        if (next.isEmpty()) {
1709            DBG_NAV_LOG("empty");
1710            break;
1711        }
1712        if (result == next) {
1713            DBG_NAV_LOG("result == next");
1714            break;
1715        }
1716        *base = nextBase;
1717        result = next;
1718        edge.shiftStart(result);
1719    } while (true);
1720    if (!result.isEmpty()) {
1721        *base += area.fTop;
1722        result.offset(area.fLeft, area.fTop);
1723    }
1724    return result;
1725}
1726
1727SkIRect SelectText::findLeft(const SkPicture& picture, const SkIRect& area,
1728        int x, int y, int* base)
1729{
1730    return findEdge(picture, area, x, y, true, base);
1731}
1732
1733SkIRect SelectText::findRight(const SkPicture& picture, const SkIRect& area,
1734        int x, int y, int* base)
1735{
1736    return findEdge(picture, area, x, y, false, base);
1737}
1738
1739const String SelectText::getSelection()
1740{
1741    if (!m_picture)
1742        return String();
1743    SkIRect clipRect;
1744    clipRect.set(0, 0, m_picture->width(), m_picture->height());
1745    String result = text(*m_picture, clipRect, m_selStart, m_startBase,
1746        m_selEnd, m_endBase, m_flipped);
1747    DBG_NAV_LOGD("clip=(%d,%d,%d,%d)"
1748        " m_selStart=(%d, %d, %d, %d) m_selEnd=(%d, %d, %d, %d)",
1749        clipRect.fLeft, clipRect.fTop, clipRect.fRight, clipRect.fBottom,
1750        m_selStart.fLeft, m_selStart.fTop, m_selStart.fRight, m_selStart.fBottom,
1751        m_selEnd.fLeft, m_selEnd.fTop, m_selEnd.fRight, m_selEnd.fBottom);
1752    DBG_NAV_LOGD("text=%s", result.latin1().data()); // uses CString
1753    return result;
1754}
1755
1756void SelectText::getSelectionArrow(SkPath* path)
1757{
1758    const int arrow[] = {
1759        0, 14, 3, 11, 5, 15, 9, 15, 7, 11, 11, 11
1760    };
1761    for (unsigned index = 0; index < sizeof(arrow)/sizeof(arrow[0]); index += 2)
1762        path->lineTo(arrow[index], arrow[index + 1]);
1763    path->close();
1764}
1765
1766void SelectText::getSelectionCaret(SkPath* path)
1767{
1768    SkScalar height = m_selStart.fBottom - m_selStart.fTop;
1769    SkScalar dist = height / 4;
1770    path->moveTo(0, -height / 2);
1771    path->rLineTo(0, height);
1772    path->rLineTo(-dist, dist);
1773    path->rMoveTo(0, -0.5f);
1774    path->rLineTo(dist * 2, 0);
1775    path->rMoveTo(0, 0.5f);
1776    path->rLineTo(-dist, -dist);
1777}
1778
1779bool SelectText::hitCorner(int cx, int cy, int x, int y) const
1780{
1781    SkIRect test;
1782    test.set(cx, cy, cx + m_controlWidth, cy + m_controlHeight);
1783    return test.contains(x, y);
1784}
1785
1786bool SelectText::hitStartHandle(int x, int y) const
1787{
1788    int left = m_selStart.fLeft - m_controlWidth;
1789    return hitCorner(left, m_selStart.fBottom, x, y);
1790}
1791
1792bool SelectText::hitEndHandle(int x, int y) const
1793{
1794    int left = m_selEnd.fRight;
1795    return hitCorner(left, m_selEnd.fBottom, x, y);
1796}
1797
1798bool SelectText::hitSelection(int x, int y) const
1799{
1800    x -= m_startOffset.fX;
1801    y -= m_startOffset.fY;
1802    if (hitStartHandle(x, y))
1803        return true;
1804    if (hitEndHandle(x, y))
1805        return true;
1806    return m_selRegion.contains(x, y);
1807}
1808
1809void SelectText::moveSelection(const IntRect& vis, int x, int y)
1810{
1811    if (!m_picture)
1812        return;
1813    x -= m_startOffset.fX;
1814    y -= m_startOffset.fY;
1815    setVisibleRect(vis);
1816    SkIRect clipRect = m_visibleRect;
1817    clipRect.join(m_selStart);
1818    clipRect.join(m_selEnd);
1819    FirstCheck center(x, y, clipRect);
1820    int base;
1821    SkIRect found = findClosest(center, *m_picture, &base);
1822    if (m_hitTopLeft || !m_extendSelection) {
1823        m_startBase = base;
1824        m_selStart = found;
1825    }
1826    if (!m_hitTopLeft || !m_extendSelection) {
1827        m_endBase = base;
1828        m_selEnd = found;
1829    }
1830    swapAsNeeded();
1831    DBG_NAV_LOGD("x=%d y=%d extendSelection=%s m_selStart=(%d, %d, %d, %d)"
1832        " m_selEnd=(%d, %d, %d, %d)", x, y, m_extendSelection ? "true" : "false",
1833        m_selStart.fLeft, m_selStart.fTop, m_selStart.fRight, m_selStart.fBottom,
1834        m_selEnd.fLeft, m_selEnd.fTop, m_selEnd.fRight, m_selEnd.fBottom);
1835}
1836
1837void SelectText::reset()
1838{
1839    DBG_NAV_LOG("m_extendSelection=false");
1840    m_selStart.setEmpty();
1841    m_lastStart.setEmpty();
1842    m_lastDrawnStart.setEmpty();
1843    m_selEnd.setEmpty();
1844    m_lastEnd.setEmpty();
1845    m_lastDrawnEnd.setEmpty();
1846    m_extendSelection = false;
1847    m_startSelection = false;
1848    SkSafeUnref(m_picture);
1849    m_picture = 0;
1850    m_layerId = 0;
1851}
1852
1853IntPoint SelectText::selectableText(const CachedRoot* root)
1854{
1855    int x = 0;
1856    int y = 0;
1857    SkPicture* picture = root->pictureAt(&x, &y, &m_layerId);
1858    if (!picture) {
1859        DBG_NAV_LOG("picture==0");
1860        return IntPoint(0, 0);
1861    }
1862    int width = picture->width();
1863    int height = picture->height();
1864    IntRect vis(0, 0, width, height);
1865    FirstCheck center(width >> 1, height >> 1, vis);
1866    int base;
1867    const SkIRect& closest = findClosest(center, *picture, &base);
1868    return IntPoint((closest.fLeft + closest.fRight) >> 1,
1869        (closest.fTop + closest.fBottom) >> 1);
1870}
1871
1872void SelectText::selectAll()
1873{
1874    if (!m_picture)
1875        return;
1876    m_selStart = findFirst(*m_picture, &m_startBase);
1877    m_selEnd = findLast(*m_picture, &m_endBase);
1878    m_extendSelection = true;
1879}
1880
1881int SelectText::selectionX() const
1882{
1883    return (m_hitTopLeft ? m_selStart.fLeft : m_selEnd.fRight) + m_startOffset.fX;
1884}
1885
1886int SelectText::selectionY() const
1887{
1888    const SkIRect& rect = m_hitTopLeft ? m_selStart : m_selEnd;
1889    return ((rect.fTop + rect.fBottom) >> 1) + m_startOffset.fY;
1890}
1891
1892void SelectText::setVisibleRect(const IntRect& vis)
1893{
1894    DBG_NAV_LOGD("vis=(%d,%d,w=%d,h=%d) offset=(%d,%d)",
1895        vis.x(), vis.y(), vis.width(), vis.height(), m_startOffset.fX,
1896        m_startOffset.fY);
1897    m_visibleRect = vis;
1898    m_visibleRect.offset(-m_startOffset.fX, -m_startOffset.fY);
1899}
1900
1901bool SelectText::startSelection(const CachedRoot* root, const IntRect& vis,
1902    int x, int y)
1903{
1904    m_wordSelection = false;
1905    m_startOffset.set(x, y);
1906    DBG_NAV_LOGD("x/y=(%d,%d)", x, y);
1907    SkSafeUnref(m_picture);
1908    m_picture = root->pictureAt(&x, &y, &m_layerId);
1909    DBG_NAV_LOGD("m_picture=%p m_layerId=%d x/y=(%d,%d)", m_picture, m_layerId,
1910        x, y);
1911    if (!m_picture) {
1912        DBG_NAV_LOG("picture==0");
1913        return false;
1914    }
1915    m_picture->ref();
1916    m_startOffset.fX -= x;
1917    m_startOffset.fY -= y;
1918    m_original.fX = x;
1919    m_original.fY = y;
1920    setVisibleRect(vis);
1921    if (m_selStart.isEmpty()) {
1922        DBG_NAV_LOGD("empty start picture=(%d,%d) x=%d y=%d",
1923             m_picture->width(), m_picture->height(), x, y);
1924        m_startSelection = true;
1925        return true;
1926    }
1927    m_hitTopLeft = hitStartHandle(x, y);
1928    bool hitBottomRight = hitEndHandle(x, y);
1929    DBG_NAV_LOGD("picture=(%d,%d) left=%d top=%d right=%d bottom=%d x=%d y=%d",
1930        m_picture->width(), m_picture->height(),left, top, right, bottom, x, y);
1931    if (m_hitTopLeft) {
1932        DBG_NAV_LOG("hit top left");
1933        m_original.fX -= m_selStart.fLeft;
1934        m_original.fY -= (m_selStart.fTop + m_selStart.fBottom) >> 1;
1935    } else if (hitBottomRight) {
1936        DBG_NAV_LOG("hit bottom right");
1937        m_original.fX -= m_selEnd.fRight;
1938        m_original.fY -= (m_selEnd.fTop + m_selEnd.fBottom) >> 1;
1939    }
1940    return m_hitTopLeft || hitBottomRight;
1941}
1942
1943void SelectText::updateHandleScale(float handleScale)
1944{
1945    m_controlHeight = CONTROL_HEIGHT * handleScale;
1946    m_controlWidth = CONTROL_WIDTH * handleScale;
1947}
1948
1949/* selects the word at (x, y)
1950* a word is normally delimited by spaces
1951* a string of digits (even with inside spaces) is a word (for phone numbers)
1952* FIXME: digit find isn't implemented yet
1953* returns true if a word was selected
1954*/
1955bool SelectText::wordSelection(const CachedRoot* root, const IntRect& vis,
1956    int x, int y)
1957{
1958    IntRect tapArea = IntRect(x - TOUCH_SLOP, y - TOUCH_SLOP, TOUCH_SLOP * 2,
1959        TOUCH_SLOP * 2);
1960    if (!startSelection(root, tapArea, x, y))
1961        return false;
1962    extendSelection(tapArea, x, y);
1963    if (m_selStart.isEmpty())
1964        return false;
1965    setDrawPointer(false);
1966    setVisibleRect(vis);
1967    SkIRect ivisBounds = m_visibleRect;
1968    ivisBounds.join(m_selStart);
1969    ivisBounds.join(m_selEnd);
1970    DBG_NAV_LOGD("m_selStart=(%d,%d,r=%d,b=%d) m_selEnd=(%d,%d,r=%d,b=%d)"
1971        " ivisBounds=(%d,%d,r=%d,b=%d)",
1972        m_selStart.fLeft, m_selStart.fTop, m_selStart.fRight, m_selStart.fBottom,
1973        m_selEnd.fLeft, m_selEnd.fTop, m_selEnd.fRight, m_selEnd.fBottom,
1974        ivisBounds.fLeft, ivisBounds.fTop, ivisBounds.fRight, ivisBounds.fBottom);
1975    m_selRegion.setEmpty();
1976    buildSelection(*m_picture, ivisBounds, m_selStart, m_startBase,
1977        m_selEnd, m_endBase, &m_selRegion);
1978    x = m_selStart.fLeft;
1979    y = (m_selStart.fTop + m_selStart.fBottom) >> 1;
1980    SkIRect clipRect = m_visibleRect;
1981    clipRect.fLeft -= m_visibleRect.width() >> 1;
1982    clipRect.fLeft = std::max(clipRect.fLeft, 0);
1983    int base;
1984    SkIRect left = findLeft(*m_picture, clipRect, x, y, &base);
1985    if (!left.isEmpty()) {
1986        m_startBase = base;
1987        m_selStart = left;
1988    }
1989    x = m_selEnd.fRight;
1990    y = (m_selEnd.fTop + m_selEnd.fBottom) >> 1;
1991    clipRect = m_visibleRect;
1992    clipRect.fRight += m_visibleRect.width() >> 1;
1993    SkIRect right = findRight(*m_picture, clipRect, x, y, &base);
1994    if (!right.isEmpty()) {
1995        m_endBase = base;
1996        m_selEnd = right;
1997    }
1998    DBG_NAV_LOGD("m_selStart=(%d, %d, %d, %d) m_selEnd=(%d, %d, %d, %d)",
1999        m_selStart.fLeft, m_selStart.fTop, m_selStart.fRight, m_selStart.fBottom,
2000        m_selEnd.fLeft, m_selEnd.fTop, m_selEnd.fRight, m_selEnd.fBottom);
2001    if (!left.isEmpty() || !right.isEmpty()) {
2002        m_wordBounds = m_selStart;
2003        m_wordBounds.join(m_selEnd);
2004        m_extendSelection = m_wordSelection = true;
2005        m_outsideWord = false;
2006        return true;
2007    }
2008    return false;
2009}
2010
2011void SelectText::swapAsNeeded()
2012{
2013    if (m_selStart.fTop >= (m_selEnd.fTop + m_selEnd.fBottom) >> 1
2014            || (m_selEnd.fTop < (m_selStart.fTop + m_selStart.fBottom) >> 1
2015            && m_selStart.fRight > m_selEnd.fLeft))
2016    {
2017        SkTSwap(m_startBase, m_endBase);
2018        SkTSwap(m_selStart, m_selEnd);
2019        m_hitTopLeft ^= true;
2020        DBG_NAV_LOGD("m_hitTopLeft=%s", m_hitTopLeft ? "true" : "false");
2021    }
2022}
2023
2024}
2025