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