Home | History | Annotate | Download | only in nav
      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
     56 namespace WebCore {
     57 
     58 class TextRunIterator {
     59 public:
     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 
     91 private:
     92     const TextRun* m_textRun;
     93     int m_offset;
     94 };
     95 
     96 // ReverseBidi is a trimmed-down version of GraphicsContext::drawBidiText()
     97 void 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 
    145 namespace 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 
    154 class CommonCheck : public SkBounder {
    155 public:
    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 
    356 protected:
    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;
    368 private:
    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
    377 class LineCheck : public CommonCheck {
    378 public:
    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 
    488 protected:
    489     int mX;
    490     int mY;
    491     SkIRect mLast;
    492     SkTDArray<SkIRect> mParagraphs;
    493     SkTDArray<SkIRect> mSelected;
    494     bool mInBetween;
    495 private:
    496     typedef CommonCheck INHERITED;
    497 };
    498 
    499 class SelectText::FirstCheck : public CommonCheck {
    500 public:
    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 
    644 protected:
    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;
    656 private:
    657     typedef CommonCheck INHERITED;
    658 };
    659 
    660 class SelectText::EdgeCheck : public SelectText::FirstCheck {
    661 public:
    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 
    725 protected:
    726     CommonCheck mLast;
    727     bool mLeft;
    728 private:
    729     typedef SelectText::FirstCheck INHERITED;
    730 };
    731 
    732 class FindFirst : public CommonCheck {
    733 public:
    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 
    755 protected:
    756     int mBestBase;
    757     SkIRect mBestBounds;
    758 private:
    759     typedef CommonCheck INHERITED;
    760 };
    761 
    762 class FindLast : public FindFirst {
    763 public:
    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 
    777 private:
    778     typedef FindFirst INHERITED;
    779 };
    780 
    781 static 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 
    788 class BuilderCheck : public CommonCheck {
    789 protected:
    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;
    952 private:
    953     typedef CommonCheck INHERITED;
    954 
    955 };
    956 
    957 class MultilineBuilder : public BuilderCheck {
    958 public:
    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 
   1029 protected:
   1030     SkRegion* mSelectRegion;
   1031 private:
   1032     typedef BuilderCheck INHERITED;
   1033 };
   1034 
   1035 static inline bool compareBounds(const SkIRect* first, const SkIRect* second)
   1036 {
   1037     return first->fTop < second->fTop;
   1038 }
   1039 
   1040 class TextExtractor : public BuilderCheck {
   1041 public:
   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 
   1178 protected:
   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;
   1186 private:
   1187     typedef BuilderCheck INHERITED;
   1188 };
   1189 
   1190 class TextCanvas : public ParseCanvas {
   1191 public:
   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;
   1253 private:
   1254     typedef ParseCanvas INHERITED;
   1255 };
   1256 
   1257 static 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 
   1279 static 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 
   1289 static 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 
   1299 static 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 
   1321 SelectText::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 
   1401 SelectText::~SelectText()
   1402 {
   1403     SkSafeUnref(m_picture);
   1404 }
   1405 
   1406 void 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 
   1422 static 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 
   1432 void 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 
   1463 static 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 
   1472 static 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 
   1481 void 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 
   1491 void 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 
   1575 void 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 
   1658 SkIRect 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 
   1672 SkIRect 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 
   1727 SkIRect 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 
   1733 SkIRect 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 
   1739 const 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 
   1756 void 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 
   1766 void 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 
   1779 bool 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 
   1786 bool 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 
   1792 bool 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 
   1798 bool 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 
   1809 void 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 
   1837 void 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 
   1853 IntPoint 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 
   1872 void 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 
   1881 int SelectText::selectionX() const
   1882 {
   1883     return (m_hitTopLeft ? m_selStart.fLeft : m_selEnd.fRight) + m_startOffset.fX;
   1884 }
   1885 
   1886 int 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 
   1892 void 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 
   1901 bool 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 
   1943 void 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 */
   1955 bool 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 
   2011 void 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