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 61 namespace WebCore { 62 63 class TextRunIterator { 64 public: 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 96 private: 97 const TextRun* m_textRun; 98 int m_offset; 99 }; 100 101 // ReverseBidi is a trimmed-down version of GraphicsContext::drawBidiText() 102 void 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 150 namespace 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 159 class CommonCheck : public SkBounder { 160 public: 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 361 protected: 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; 373 private: 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 382 class LineCheck : public CommonCheck { 383 public: 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 493 protected: 494 int mX; 495 int mY; 496 SkIRect mLast; 497 SkTDArray<SkIRect> mParagraphs; 498 SkTDArray<SkIRect> mSelected; 499 bool mInBetween; 500 private: 501 typedef CommonCheck INHERITED; 502 }; 503 504 class SelectText::FirstCheck : public CommonCheck { 505 public: 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 649 protected: 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; 661 private: 662 typedef CommonCheck INHERITED; 663 }; 664 665 class SelectText::EdgeCheck : public SelectText::FirstCheck { 666 public: 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 730 protected: 731 CommonCheck mLast; 732 bool mLeft; 733 private: 734 typedef SelectText::FirstCheck INHERITED; 735 }; 736 737 class FindFirst : public CommonCheck { 738 public: 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 760 protected: 761 int mBestBase; 762 SkIRect mBestBounds; 763 private: 764 typedef CommonCheck INHERITED; 765 }; 766 767 class FindLast : public FindFirst { 768 public: 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 782 private: 783 typedef FindFirst INHERITED; 784 }; 785 786 static 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 793 class BuilderCheck : public CommonCheck { 794 protected: 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; 957 private: 958 typedef CommonCheck INHERITED; 959 960 }; 961 962 class MultilineBuilder : public BuilderCheck { 963 public: 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 1034 protected: 1035 SkRegion* mSelectRegion; 1036 private: 1037 typedef BuilderCheck INHERITED; 1038 }; 1039 1040 static inline bool compareBounds(const SkIRect* first, const SkIRect* second) 1041 { 1042 return first->fTop < second->fTop; 1043 } 1044 1045 class TextExtractor : public BuilderCheck { 1046 public: 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 1183 protected: 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; 1191 private: 1192 typedef BuilderCheck INHERITED; 1193 }; 1194 1195 class TextCanvas : public ParseCanvas { 1196 public: 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; 1258 private: 1259 typedef ParseCanvas INHERITED; 1260 }; 1261 1262 static 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 1284 static 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 1294 static 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 1304 static 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 1327 SelectText::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 1408 SelectText::~SelectText() 1409 { 1410 SkSafeUnref(m_picture); 1411 } 1412 1413 void 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 1429 static 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 1439 void 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 1470 static 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 1479 static 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 1488 void 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 1507 void 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 1595 void 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 1678 SkIRect 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 1692 SkIRect 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 1747 SkIRect 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 1753 SkIRect 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 1759 const 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 1776 void 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 1786 void 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 1799 bool 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 1809 bool 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 1815 bool 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 1821 bool 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 1832 void SelectText::getSelectionHandles(int* handles, LayerAndroid* root) 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 if (root && m_layerId) { 1839 Layer* layer = root->findById(m_layerId); 1840 while (layer) { 1841 const SkPoint& pos = layer->getPosition(); 1842 handles[0] += pos.fX; 1843 handles[2] += pos.fX; 1844 handles[1] += pos.fY; 1845 handles[3] += pos.fY; 1846 layer = layer->getParent(); 1847 } 1848 } 1849 } 1850 1851 void SelectText::moveSelection(const IntRect& vis, int x, int y) 1852 { 1853 if (!m_picture) 1854 return; 1855 x -= m_startOffset.fX; 1856 y -= m_startOffset.fY; 1857 setVisibleRect(vis); 1858 SkIRect clipRect = m_visibleRect; 1859 clipRect.join(m_selStart); 1860 clipRect.join(m_selEnd); 1861 FirstCheck center(x, y, clipRect); 1862 int base; 1863 SkIRect found = findClosest(center, *m_picture, &base); 1864 if (m_hitTopLeft || !m_extendSelection) { 1865 m_startBase = base; 1866 m_selStart = found; 1867 } 1868 if (!m_hitTopLeft || !m_extendSelection) { 1869 m_endBase = base; 1870 m_selEnd = found; 1871 } 1872 swapAsNeeded(); 1873 DBG_NAV_LOGD("x=%d y=%d extendSelection=%s m_selStart=(%d, %d, %d, %d)" 1874 " m_selEnd=(%d, %d, %d, %d)", x, y, m_extendSelection ? "true" : "false", 1875 m_selStart.fLeft, m_selStart.fTop, m_selStart.fRight, m_selStart.fBottom, 1876 m_selEnd.fLeft, m_selEnd.fTop, m_selEnd.fRight, m_selEnd.fBottom); 1877 } 1878 1879 void SelectText::reset() 1880 { 1881 DBG_NAV_LOG("m_extendSelection=false"); 1882 m_selStart.setEmpty(); 1883 m_lastStart.setEmpty(); 1884 m_lastDrawnStart.setEmpty(); 1885 m_selEnd.setEmpty(); 1886 m_lastEnd.setEmpty(); 1887 m_lastDrawnEnd.setEmpty(); 1888 m_extendSelection = false; 1889 m_startSelection = false; 1890 SkSafeUnref(m_picture); 1891 m_picture = 0; 1892 m_layerId = 0; 1893 } 1894 1895 IntPoint SelectText::selectableText(const CachedRoot* root) 1896 { 1897 int x = 0; 1898 int y = 0; 1899 SkPicture* picture = root->pictureAt(&x, &y, &m_layerId); 1900 if (!picture) { 1901 DBG_NAV_LOG("picture==0"); 1902 return IntPoint(0, 0); 1903 } 1904 int width = picture->width(); 1905 int height = picture->height(); 1906 IntRect vis(0, 0, width, height); 1907 FirstCheck center(width >> 1, height >> 1, vis); 1908 int base; 1909 const SkIRect& closest = findClosest(center, *picture, &base); 1910 return IntPoint((closest.fLeft + closest.fRight) >> 1, 1911 (closest.fTop + closest.fBottom) >> 1); 1912 } 1913 1914 void SelectText::selectAll() 1915 { 1916 if (!m_picture) 1917 return; 1918 m_selStart = findFirst(*m_picture, &m_startBase); 1919 m_selEnd = findLast(*m_picture, &m_endBase); 1920 m_extendSelection = true; 1921 } 1922 1923 int SelectText::selectionX() const 1924 { 1925 return (m_hitTopLeft ? m_selStart.fLeft : m_selEnd.fRight) + m_startOffset.fX; 1926 } 1927 1928 int SelectText::selectionY() const 1929 { 1930 const SkIRect& rect = m_hitTopLeft ? m_selStart : m_selEnd; 1931 return ((rect.fTop + rect.fBottom) >> 1) + m_startOffset.fY; 1932 } 1933 1934 void SelectText::setVisibleRect(const IntRect& vis) 1935 { 1936 DBG_NAV_LOGD("vis=(%d,%d,w=%d,h=%d) offset=(%d,%d)", 1937 vis.x(), vis.y(), vis.width(), vis.height(), m_startOffset.fX, 1938 m_startOffset.fY); 1939 m_visibleRect = vis; 1940 m_visibleRect.offset(-m_startOffset.fX, -m_startOffset.fY); 1941 } 1942 1943 bool SelectText::startSelection(const CachedRoot* root, const IntRect& vis, 1944 int x, int y) 1945 { 1946 m_wordSelection = false; 1947 m_startOffset.set(x, y); 1948 DBG_NAV_LOGD("x/y=(%d,%d)", x, y); 1949 SkSafeUnref(m_picture); 1950 m_picture = root->pictureAt(&x, &y, &m_layerId); 1951 DBG_NAV_LOGD("m_picture=%p m_layerId=%d x/y=(%d,%d)", m_picture, m_layerId, 1952 x, y); 1953 if (!m_picture) { 1954 DBG_NAV_LOG("picture==0"); 1955 return false; 1956 } 1957 m_picture->ref(); 1958 m_startOffset.fX -= x; 1959 m_startOffset.fY -= y; 1960 m_original.fX = x; 1961 m_original.fY = y; 1962 setVisibleRect(vis); 1963 if (m_selStart.isEmpty()) { 1964 DBG_NAV_LOGD("empty start picture=(%d,%d) x=%d y=%d", 1965 m_picture->width(), m_picture->height(), x, y); 1966 m_startSelection = true; 1967 return true; 1968 } 1969 m_hitTopLeft = hitStartHandle(x, y); 1970 bool hitBottomRight = hitEndHandle(x, y); 1971 DBG_NAV_LOGD("picture=(%d,%d) left=%d top=%d right=%d bottom=%d x=%d y=%d", 1972 m_picture->width(), m_picture->height(),left, top, right, bottom, x, y); 1973 if (m_hitTopLeft) { 1974 DBG_NAV_LOG("hit top left"); 1975 m_original.fX -= m_selStart.fLeft; 1976 m_original.fY -= (m_selStart.fTop + m_selStart.fBottom) >> 1; 1977 } else if (hitBottomRight) { 1978 DBG_NAV_LOG("hit bottom right"); 1979 m_original.fX -= m_selEnd.fRight; 1980 m_original.fY -= (m_selEnd.fTop + m_selEnd.fBottom) >> 1; 1981 } 1982 return m_hitTopLeft || hitBottomRight; 1983 } 1984 1985 void SelectText::updateHandleScale(float handleScale) 1986 { 1987 m_controlHeight = CONTROL_HEIGHT * handleScale; 1988 m_controlWidth = CONTROL_WIDTH * handleScale; 1989 m_controlSlop = CONTROL_SLOP * handleScale; 1990 } 1991 1992 /* selects the word at (x, y) 1993 * a word is normally delimited by spaces 1994 * a string of digits (even with inside spaces) is a word (for phone numbers) 1995 * FIXME: digit find isn't implemented yet 1996 * returns true if a word was selected 1997 */ 1998 bool SelectText::wordSelection(const CachedRoot* root, const IntRect& vis, 1999 int x, int y) 2000 { 2001 IntRect tapArea = IntRect(x - TOUCH_SLOP, y - TOUCH_SLOP, TOUCH_SLOP * 2, 2002 TOUCH_SLOP * 2); 2003 if (!startSelection(root, tapArea, x, y)) 2004 return false; 2005 extendSelection(tapArea, x, y); 2006 if (m_selStart.isEmpty()) 2007 return false; 2008 setDrawPointer(false); 2009 setVisibleRect(vis); 2010 SkIRect ivisBounds = m_visibleRect; 2011 ivisBounds.join(m_selStart); 2012 ivisBounds.join(m_selEnd); 2013 DBG_NAV_LOGD("m_selStart=(%d,%d,r=%d,b=%d) m_selEnd=(%d,%d,r=%d,b=%d)" 2014 " ivisBounds=(%d,%d,r=%d,b=%d)", 2015 m_selStart.fLeft, m_selStart.fTop, m_selStart.fRight, m_selStart.fBottom, 2016 m_selEnd.fLeft, m_selEnd.fTop, m_selEnd.fRight, m_selEnd.fBottom, 2017 ivisBounds.fLeft, ivisBounds.fTop, ivisBounds.fRight, ivisBounds.fBottom); 2018 m_selRegion.setEmpty(); 2019 buildSelection(*m_picture, ivisBounds, m_selStart, m_startBase, 2020 m_selEnd, m_endBase, &m_selRegion); 2021 x = m_selStart.fLeft; 2022 y = (m_selStart.fTop + m_selStart.fBottom) >> 1; 2023 SkIRect clipRect = m_visibleRect; 2024 clipRect.fLeft -= m_visibleRect.width() >> 1; 2025 clipRect.fLeft = std::max(clipRect.fLeft, 0); 2026 int base; 2027 SkIRect left = findLeft(*m_picture, clipRect, x, y, &base); 2028 if (!left.isEmpty()) { 2029 m_startBase = base; 2030 m_selStart = left; 2031 } 2032 x = m_selEnd.fRight; 2033 y = (m_selEnd.fTop + m_selEnd.fBottom) >> 1; 2034 clipRect = m_visibleRect; 2035 clipRect.fRight += m_visibleRect.width() >> 1; 2036 SkIRect right = findRight(*m_picture, clipRect, x, y, &base); 2037 if (!right.isEmpty()) { 2038 m_endBase = base; 2039 m_selEnd = right; 2040 } 2041 DBG_NAV_LOGD("m_selStart=(%d, %d, %d, %d) m_selEnd=(%d, %d, %d, %d)", 2042 m_selStart.fLeft, m_selStart.fTop, m_selStart.fRight, m_selStart.fBottom, 2043 m_selEnd.fLeft, m_selEnd.fTop, m_selEnd.fRight, m_selEnd.fBottom); 2044 if (!left.isEmpty() || !right.isEmpty()) { 2045 m_wordBounds = m_selStart; 2046 m_wordBounds.join(m_selEnd); 2047 m_extendSelection = m_wordSelection = true; 2048 m_outsideWord = false; 2049 return true; 2050 } 2051 return false; 2052 } 2053 2054 void SelectText::swapAsNeeded() 2055 { 2056 if (m_selStart.fTop >= (m_selEnd.fTop + m_selEnd.fBottom) >> 1 2057 || (m_selEnd.fTop < (m_selStart.fTop + m_selStart.fBottom) >> 1 2058 && m_selStart.fRight > m_selEnd.fLeft)) 2059 { 2060 SkTSwap(m_startBase, m_endBase); 2061 SkTSwap(m_selStart, m_selEnd); 2062 m_hitTopLeft ^= true; 2063 DBG_NAV_LOGD("m_hitTopLeft=%s", m_hitTopLeft ? "true" : "false"); 2064 } 2065 } 2066 2067 } 2068