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