summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorCary Clark <cary@android.com>2010-08-20 09:28:25 -0700
committerAndroid Git Automerger <android-git-automerger@android.com>2010-08-20 09:28:25 -0700
commit0ee151789837b0429171a2eeeedc93ce2f8d8d10 (patch)
tree1f142c2779dc07c8cde064e2ce94f3391613d172
parent3c54ece08a0ea9e5dc5d5788a4c49c8dc94b501f (diff)
parente4be1fd5f14137de249ee66c000f9dadfb2862d7 (diff)
downloadexternal_webkit-0ee151789837b0429171a2eeeedc93ce2f8d8d10.zip
external_webkit-0ee151789837b0429171a2eeeedc93ce2f8d8d10.tar.gz
external_webkit-0ee151789837b0429171a2eeeedc93ce2f8d8d10.tar.bz2
am e4be1fd5: DO NOT MERGE Refactor find and select dialogs
Merge commit 'e4be1fd5f14137de249ee66c000f9dadfb2862d7' into gingerbread-plus-aosp * commit 'e4be1fd5f14137de249ee66c000f9dadfb2862d7': DO NOT MERGE Refactor find and select dialogs
-rw-r--r--WebKit/android/nav/FindCanvas.h1
-rw-r--r--WebKit/android/nav/SelectText.cpp1421
-rw-r--r--WebKit/android/nav/SelectText.h59
-rw-r--r--WebKit/android/nav/WebView.cpp169
4 files changed, 1430 insertions, 220 deletions
diff --git a/WebKit/android/nav/FindCanvas.h b/WebKit/android/nav/FindCanvas.h
index b9dbeea..34929ec 100644
--- a/WebKit/android/nav/FindCanvas.h
+++ b/WebKit/android/nav/FindCanvas.h
@@ -220,6 +220,7 @@ public:
virtual ~FindOnPage() { delete m_matches; }
void clearCurrentLocation() { m_hasCurrentLocation = false; }
IntRect currentMatchBounds() const;
+ int currentMatchIndex() const { return m_findIndex; }
bool currentMatchIsInLayer() const;
virtual void draw(SkCanvas* , LayerAndroid* );
void findNext(bool forward);
diff --git a/WebKit/android/nav/SelectText.cpp b/WebKit/android/nav/SelectText.cpp
index e471307..7665972 100644
--- a/WebKit/android/nav/SelectText.cpp
+++ b/WebKit/android/nav/SelectText.cpp
@@ -23,9 +23,10 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#define LOG_TAG "webcoreglue"
+#define LOG_TAG "webviewglue"
#include "CachedPrefix.h"
+#include "BidiResolver.h"
#include "CachedRoot.h"
#include "LayerAndroid.h"
#include "SelectText.h"
@@ -39,26 +40,158 @@
#include "SkRect.h"
#include "SkRegion.h"
#include "SkUtils.h"
+#include "TextRun.h"
#ifdef DEBUG_NAV_UI
#include "CString.h"
#endif
+#define VERBOSE_LOGGING 0
+// #define EXTRA_NOISY_LOGGING 1
+
+// TextRunIterator has been copied verbatim from GraphicsContext.cpp
+namespace WebCore {
+
+class TextRunIterator {
+public:
+ TextRunIterator()
+ : m_textRun(0)
+ , m_offset(0)
+ {
+ }
+
+ TextRunIterator(const TextRun* textRun, unsigned offset)
+ : m_textRun(textRun)
+ , m_offset(offset)
+ {
+ }
+
+ TextRunIterator(const TextRunIterator& other)
+ : m_textRun(other.m_textRun)
+ , m_offset(other.m_offset)
+ {
+ }
+
+ unsigned offset() const { return m_offset; }
+ void increment() { m_offset++; }
+ bool atEnd() const { return !m_textRun || m_offset >= m_textRun->length(); }
+ UChar current() const { return (*m_textRun)[m_offset]; }
+ WTF::Unicode::Direction direction() const { return atEnd() ? WTF::Unicode::OtherNeutral : WTF::Unicode::direction(current()); }
+
+ bool operator==(const TextRunIterator& other)
+ {
+ return m_offset == other.m_offset && m_textRun == other.m_textRun;
+ }
+
+ bool operator!=(const TextRunIterator& other) { return !operator==(other); }
+
+private:
+ const TextRun* m_textRun;
+ int m_offset;
+};
+
+// ReverseBidi is a trimmed-down version of GraphicsContext::drawBidiText()
+void ReverseBidi(UChar* chars, int len) {
+ using namespace WTF::Unicode;
+ WTF::Vector<UChar> result;
+ result.reserveCapacity(len);
+ TextRun run(chars, len);
+ BidiResolver<TextRunIterator, BidiCharacterRun> bidiResolver;
+ bidiResolver.setStatus(BidiStatus(LeftToRight, LeftToRight, LeftToRight,
+ BidiContext::create(0, LeftToRight, false)));
+ bidiResolver.setPosition(TextRunIterator(&run, 0));
+ bidiResolver.createBidiRunsForLine(TextRunIterator(&run, len));
+ if (!bidiResolver.runCount())
+ return;
+ BidiCharacterRun* bidiRun = bidiResolver.firstRun();
+ while (bidiRun) {
+ int bidiStart = bidiRun->start();
+ int bidiStop = bidiRun->stop();
+ int size = result.size();
+ int bidiCount = bidiStop - bidiStart;
+ result.append(chars + bidiStart, bidiCount);
+ if (bidiRun->level() % 2) {
+ UChar* start = &result[size];
+ UChar* end = start + bidiCount;
+ // reverse the order of any RTL substrings
+ while (start < end) {
+ UChar temp = *start;
+ *start++ = *--end;
+ *end = temp;
+ }
+ start = &result[size];
+ end = start + bidiCount - 1;
+ // if the RTL substring had a surrogate pair, restore its order
+ while (start < end) {
+ UChar trail = *start++;
+ if (!U16_IS_SURROGATE(trail))
+ continue;
+ start[-1] = *start; // lead
+ *start++ = trail;
+ }
+ }
+ bidiRun = bidiRun->next();
+ }
+ bidiResolver.deleteRuns();
+ memcpy(chars, &result[0], len * sizeof(UChar));
+}
+
+}
+
namespace android {
+/* SpaceBounds and SpaceCanvas are used to measure the left and right side
+ * bearings of two consecutive glyphs to help determine if the glyphs were
+ * originally laid out with a space character between the glyphs.
+ */
+class SpaceBounds : public SkBounder {
+public:
+ virtual bool onIRectGlyph(const SkIRect& , const SkBounder::GlyphRec& rec)
+ {
+ mFirstGlyph = mLastGlyph;
+ mLastGlyph = rec;
+ return false;
+ }
+
+ SkBounder::GlyphRec mFirstGlyph;
+ SkBounder::GlyphRec mLastGlyph;
+};
+
+class SpaceCanvas : public SkCanvas {
+public:
+ SpaceCanvas(const SkIRect& area)
+ {
+ setBounder(&mBounder);
+ SkBitmap bitmap;
+ bitmap.setConfig(SkBitmap::kARGB_8888_Config, area.width(),
+ area.height());
+ setBitmapDevice(bitmap);
+ translate(SkIntToScalar(-area.fLeft), SkIntToScalar(-area.fTop));
+ }
+
+ SpaceBounds mBounder;
+};
+
+#define HYPHEN_MINUS 0x2D // ASCII hyphen
+#define SOLIDUS 0x2F // ASCII slash
+#define REVERSE_SOLIDUS 0x5C // ASCII backslash
+#define HYPHEN 0x2010 // unicode hyphen, first in range of dashes
+#define HORZ_BAR 0x2015 // unicode horizontal bar, last in range of dashes
+#define TOUCH_SLOP 10 // additional distance from character rect when hit
+
class CommonCheck : public SkBounder {
public:
- CommonCheck() : mMatrix(NULL), mPaint(NULL) {}
-
- virtual void setUp(const SkPaint& paint, const SkMatrix& matrix, SkScalar y,
- const void* text) {
- mMatrix = &matrix;
- mPaint = &paint;
- mText = static_cast<const uint16_t*>(text);
- mY = y;
- mBase = mBottom = mTop = INT_MAX;
+ CommonCheck(int width, int height)
+ : mHeight(height)
+ , mLastUni(0)
+ , mMatrix(0)
+ , mPaint(0)
+ , mWidth(width)
+ {
+ mLastGlyph.fGlyphID = static_cast<uint16_t>(-1);
+ reset();
}
-
+
int base() {
if (mBase == INT_MAX) {
SkPoint result;
@@ -78,7 +211,149 @@ public:
}
return mBottom;
}
-
+
+#if DEBUG_NAV_UI
+ // make current (possibily uncomputed) value visible for debugging
+ int bottomDebug() const
+ {
+ return mBottom;
+ }
+#endif
+
+ bool addNewLine(const SkBounder::GlyphRec& rec)
+ {
+ SkFixed lineSpacing = SkFixedAbs(mLastGlyph.fLSB.fY - rec.fLSB.fY);
+ SkFixed lineHeight = SkIntToFixed(bottom() - top());
+ return lineSpacing >= lineHeight + (lineHeight >> 1); // 1.5
+ }
+
+ bool addSpace(const SkBounder::GlyphRec& rec)
+ {
+ bool newBaseLine = mLastGlyph.fLSB.fY != rec.fLSB.fY;
+ if (((mLastUni >= HYPHEN && mLastUni <= HORZ_BAR)
+ || mLastUni == HYPHEN_MINUS || mLastUni == SOLIDUS
+ || mLastUni == REVERSE_SOLIDUS) && newBaseLine)
+ {
+ return false;
+ }
+ return isSpace(rec);
+ }
+
+ void finishGlyph()
+ {
+ mLastGlyph = mLastCandidate;
+ mLastUni = mLastUniCandidate;
+ }
+
+ SkUnichar getUniChar(const SkBounder::GlyphRec& rec)
+ {
+ SkUnichar unichar;
+ SkPaint utfPaint = *mPaint;
+ utfPaint.setTextEncoding(SkPaint::kUTF16_TextEncoding);
+ utfPaint.glyphsToUnichars(&rec.fGlyphID, 1, &unichar);
+ return unichar;
+ }
+
+ bool isSpace(const SkBounder::GlyphRec& rec)
+ {
+ DBG_NAV_LOGD("mLastGlyph=((%g, %g),(%g, %g), %d)"
+ " rec=((%g, %g),(%g, %g), %d)"
+ " mMinSpaceWidth=%g mLastUni=0x%04x '%c'",
+ SkFixedToScalar(mLastGlyph.fLSB.fX),
+ SkFixedToScalar(mLastGlyph.fLSB.fY),
+ SkFixedToScalar(mLastGlyph.fRSB.fX),
+ SkFixedToScalar(mLastGlyph.fRSB.fY), mLastGlyph.fGlyphID,
+ SkFixedToScalar(rec.fLSB.fX), SkFixedToScalar(rec.fLSB.fY),
+ SkFixedToScalar(rec.fRSB.fX), SkFixedToScalar(rec.fRSB.fY),
+ rec.fGlyphID,
+ SkFixedToScalar(mMinSpaceWidth),
+ mLastUni, mLastUni && mLastUni < 0x7f ? mLastUni : '?');
+ bool newBaseLine = mLastGlyph.fLSB.fY != rec.fLSB.fY;
+ if (newBaseLine)
+ return true;
+ SkFixed gapOne = mLastGlyph.fLSB.fX - rec.fRSB.fX;
+ SkFixed gapTwo = rec.fLSB.fX - mLastGlyph.fRSB.fX;
+ if (gapOne < 0 && gapTwo < 0)
+ return false; // overlaps
+ uint16_t test[2];
+ test[0] = mLastGlyph.fGlyphID;
+ test[1] = rec.fGlyphID;
+ SkIRect area;
+ area.set(0, 0, mWidth, mHeight);
+ SpaceCanvas spaceChecker(area);
+ spaceChecker.drawText(test, sizeof(test),
+ SkFixedToScalar(mLastGlyph.fLSB.fX),
+ SkFixedToScalar(mLastGlyph.fLSB.fY), *mPaint);
+ const SkBounder::GlyphRec& g1 = spaceChecker.mBounder.mFirstGlyph;
+ const SkBounder::GlyphRec& g2 = spaceChecker.mBounder.mLastGlyph;
+ DBG_NAV_LOGD("g1=(%g, %g, %g, %g) g2=(%g, %g, %g, %g)",
+ SkFixedToScalar(g1.fLSB.fX), SkFixedToScalar(g1.fLSB.fY),
+ SkFixedToScalar(g1.fRSB.fX), SkFixedToScalar(g1.fRSB.fY),
+ SkFixedToScalar(g2.fLSB.fX), SkFixedToScalar(g2.fLSB.fY),
+ SkFixedToScalar(g2.fRSB.fX), SkFixedToScalar(g2.fRSB.fY));
+ gapOne = SkFixedAbs(gapOne);
+ gapTwo = SkFixedAbs(gapTwo);
+ SkFixed gap = gapOne < gapTwo ? gapOne : gapTwo;
+ SkFixed overlap = g2.fLSB.fX - g1.fRSB.fX;
+ if (overlap < 0)
+ gap -= overlap;
+ DBG_NAV_LOGD("gap=%g overlap=%g gapOne=%g gapTwo=%g minSpaceWidth()=%g",
+ SkFixedToScalar(gap), SkFixedToScalar(overlap),
+ SkFixedToScalar(gapOne), SkFixedToScalar(gapTwo),
+ SkFixedToScalar(minSpaceWidth()));
+ // FIXME: the -1/8 below takes care of slop beween the computed gap
+ // and the actual space width -- it's a rounding error from
+ // moving from fixed to float and back and could be much smaller.
+ return gap >= minSpaceWidth() - SK_Fixed1 / 8;
+ }
+
+ SkFixed minSpaceWidth()
+ {
+ if (mMinSpaceWidth == SK_FixedMax) {
+ SkPaint charPaint = *mPaint;
+ charPaint.setTextEncoding(SkPaint::kUTF8_TextEncoding);
+ SkScalar width = charPaint.measureText(" ", 1);
+ mMinSpaceWidth = SkScalarToFixed(width * mMatrix->getScaleX());
+ DBG_NAV_LOGD("width=%g matrix sx/sy=(%g, %g) tx/ty=(%g, %g)"
+ " mMinSpaceWidth=%g", width,
+ mMatrix->getScaleX(), mMatrix->getScaleY(),
+ mMatrix->getTranslateX(), mMatrix->getTranslateY(),
+ SkFixedToScalar(mMinSpaceWidth));
+ }
+ return mMinSpaceWidth;
+ }
+
+ void recordGlyph(const SkBounder::GlyphRec& rec)
+ {
+ mLastCandidate = rec;
+ mLastUniCandidate = getUniChar(rec);
+ }
+
+ void reset()
+ {
+ mMinSpaceWidth = SK_FixedMax; // mark as uninitialized
+ mBase = mBottom = mTop = INT_MAX; // mark as uninitialized
+ }
+
+ void set(CommonCheck& check)
+ {
+ mLastGlyph = check.mLastGlyph;
+ mLastUni = check.mLastUni;
+ mMatrix = check.mMatrix;
+ mPaint = check.mPaint;
+ reset();
+ }
+
+ void setUp(const SkPaint& paint, const SkMatrix& matrix, SkScalar y,
+ const void* text)
+ {
+ mMatrix = &matrix;
+ mPaint = &paint;
+ mText = static_cast<const uint16_t*>(text);
+ mY = y;
+ reset();
+ }
+
int top() {
if (mTop == INT_MAX) {
SkPoint result;
@@ -89,127 +364,568 @@ public:
}
return mTop;
}
-
-protected:
+
+#if DEBUG_NAV_UI
+ // make current (possibily uncomputed) value visible for debugging
+ int topDebug() const
+ {
+ return mTop;
+ }
+#endif
+
+protected:
+ int mHeight;
+ SkBounder::GlyphRec mLastCandidate;
+ SkBounder::GlyphRec mLastGlyph;
+ SkUnichar mLastUni;
+ SkUnichar mLastUniCandidate;
const SkMatrix* mMatrix;
const SkPaint* mPaint;
const uint16_t* mText;
+ int mWidth;
SkScalar mY;
+private:
int mBase;
int mBottom;
+ SkFixed mMinSpaceWidth;
int mTop;
+ friend class EdgeCheck;
};
class FirstCheck : public CommonCheck {
public:
- FirstCheck(int x, int y)
- : mDistance(INT_MAX), mFocusX(x), mFocusY(y) {
- mBestBounds.setEmpty();
+ FirstCheck(int x, int y, const SkIRect& area)
+ : INHERITED(area.width(), area.height())
+ , mFocusX(x - area.fLeft)
+ , mFocusY(y - area.fTop)
+ , mRecordGlyph(false)
+ {
+ reset();
}
- const SkIRect& bestBounds() {
- DBG_NAV_LOGD("mBestBounds:(%d, %d, %d, %d) mTop=%d mBottom=%d",
+ const SkIRect& adjustedBounds(const SkIRect& area, int* base)
+ {
+ *base = mBestBase + area.fTop;
+ mBestBounds.offset(area.fLeft, area.fTop);
+ DBG_NAV_LOGD("FirstCheck mBestBounds:(%d, %d, %d, %d) mTop=%d mBottom=%d",
mBestBounds.fLeft, mBestBounds.fTop, mBestBounds.fRight,
- mBestBounds.fBottom, mTop, mBottom);
- return mBestBounds;
- }
-
- void offsetBounds(int dx, int dy) {
- mBestBounds.offset(dx, dy);
+ mBestBounds.fBottom, topDebug(), bottomDebug());
+ return mBestBounds;
}
-
- virtual bool onIRect(const SkIRect& rect) {
- int dx = ((rect.fLeft + rect.fRight) >> 1) - mFocusX;
- int dy = ((top() + bottom()) >> 1) - mFocusY;
+
+ virtual bool onIRectGlyph(const SkIRect& rect,
+ const SkBounder::GlyphRec& rec)
+ {
+ /* compute distance from rectangle center.
+ * centerX = (rect.L + rect.R) / 2
+ * multiply centerX and comparison x by 2 to retain better precision
+ */
+ int dx = rect.fLeft + rect.fRight - (mFocusX << 1);
+ int dy = top() + bottom() - (mFocusY << 1);
int distance = dx * dx + dy * dy;
#ifdef EXTRA_NOISY_LOGGING
if (distance < 500 || abs(distance - mDistance) < 500)
- DBG_NAV_LOGD("distance=%d mDistance=%d", distance, mDistance);
+ DBG_NAV_LOGD("FirstCheck distance=%d mDistance=%d", distance, mDistance);
#endif
if (mDistance > distance) {
- mDistance = distance;
+ mBestBase = base();
mBestBounds.set(rect.fLeft, top(), rect.fRight, bottom());
-#ifdef EXTRA_NOISY_LOGGING
- DBG_NAV_LOGD("mBestBounds={%d,%d,r=%d,b=%d}",
- mBestBounds.fLeft, mBestBounds.fTop,
- mBestBounds.fRight, mBestBounds.fBottom);
-#endif
+ if (distance < 100) {
+ DBG_NAV_LOGD("FirstCheck mBestBounds={%d,%d,r=%d,b=%d} distance=%d",
+ mBestBounds.fLeft, mBestBounds.fTop,
+ mBestBounds.fRight, mBestBounds.fBottom, distance >> 2);
+ }
+ mDistance = distance;
+ if (mRecordGlyph)
+ recordGlyph(rec);
}
return false;
}
+
+ void reset()
+ {
+ mBestBounds.setEmpty();
+ mDistance = INT_MAX;
+ }
+
+ void setRecordGlyph()
+ {
+ mRecordGlyph = true;
+ }
+
protected:
+ int mBestBase;
SkIRect mBestBounds;
int mDistance;
int mFocusX;
int mFocusY;
+ bool mRecordGlyph;
+private:
+ typedef CommonCheck INHERITED;
+};
+
+class EdgeCheck : public FirstCheck {
+public:
+ EdgeCheck(int x, int y, const SkIRect& area, CommonCheck& last, bool left)
+ : INHERITED(x, y, area)
+ , mLast(area.width(), area.height())
+ , mLeft(left)
+ {
+ mLast.set(last);
+ mLastGlyph = last.mLastGlyph;
+ mLastUni = last.mLastUni;
+ }
+
+ bool adjacent()
+ {
+ return !mLast.isSpace(mLastGlyph);
+ }
+
+ const SkIRect& bestBounds(int* base)
+ {
+ *base = mBestBase;
+ return mBestBounds;
+ }
+
+ virtual bool onIRectGlyph(const SkIRect& rect,
+ const SkBounder::GlyphRec& rec)
+ {
+ int dx = mLeft ? mFocusX - rect.fRight : rect.fLeft - mFocusX;
+ int dy = ((top() + bottom()) >> 1) - mFocusY;
+ if (mLeft ? mFocusX <= rect.fLeft : mFocusX >= rect.fRight) {
+ if (abs(dx) <= 10 && abs(dy) <= 10) {
+ DBG_NAV_LOGD("EdgeCheck fLeft=%d fRight=%d mFocusX=%d dx=%d dy=%d",
+ rect.fLeft, rect.fRight, mFocusX, dx, dy);
+ }
+ return false;
+ }
+ int distance = dx * dx + dy * dy;
+ if (mDistance > distance) {
+ if (rec.fLSB == mLastGlyph.fLSB && rec.fRSB == mLastGlyph.fRSB) {
+ DBG_NAV_LOGD("dup rec.fLSB.fX=%g rec.fRSB.fX=%g",
+ SkFixedToScalar(rec.fLSB.fX), SkFixedToScalar(rec.fRSB.fX));
+ return false;
+ }
+ recordGlyph(rec);
+ mDistance = distance;
+ mBestBase = base();
+ mBestBounds.set(rect.fLeft, top(), rect.fRight, bottom());
+ if (distance <= 100) {
+ DBG_NAV_LOGD("EdgeCheck mBestBounds={%d,%d,r=%d,b=%d} distance=%d",
+ mBestBounds.fLeft, mBestBounds.fTop,
+ mBestBounds.fRight, mBestBounds.fBottom, distance);
+ }
+ }
+ return false;
+ }
+
+ void shiftStart(SkIRect bounds)
+ {
+ DBG_NAV_LOGD("EdgeCheck mFocusX=%d mLeft=%s bounds.fLeft=%d bounds.fRight=%d",
+ mFocusX, mLeft ? "true" : "false", bounds.fLeft, bounds.fRight);
+ reset();
+ mFocusX = mLeft ? bounds.fLeft : bounds.fRight;
+ mLast.set(*this);
+ }
+
+protected:
+ CommonCheck mLast;
+ bool mLeft;
+private:
+ typedef FirstCheck INHERITED;
+};
+
+class FindFirst : public CommonCheck {
+public:
+ FindFirst(int width, int height)
+ : INHERITED(width, height)
+ {
+ mBestBounds.set(width, height, width, height);
+ }
+
+ const SkIRect& bestBounds(int* base)
+ {
+ *base = mBestBase;
+ return mBestBounds;
+ }
+
+ virtual bool onIRect(const SkIRect& rect)
+ {
+ if (mBestBounds.isEmpty()) {
+ mBestBase = base();
+ mBestBounds.set(rect.fLeft, top(), rect.fRight, bottom());
+ }
+ return false;
+ }
+
+protected:
+ int mBestBase;
+ SkIRect mBestBounds;
+private:
+ typedef CommonCheck INHERITED;
};
-class MultilineBuilder : public CommonCheck {
+class FindLast : public FindFirst {
public:
- MultilineBuilder(const SkIRect& start, const SkIRect& end, int dx, int dy,
- SkRegion* region)
- : mStart(start), mEnd(end), mSelectRegion(region), mCapture(false) {
+ FindLast(int width, int height)
+ : INHERITED(width, height)
+ {
+ mBestBounds.setEmpty();
+ }
+
+ virtual bool onIRect(const SkIRect& rect)
+ {
+ mBestBase = base();
+ mBestBounds.set(rect.fLeft, top(), rect.fRight, bottom());
+ return false;
+ }
+
+private:
+ typedef FindFirst INHERITED;
+};
+
+static bool baseLinesAgree(const SkIRect& rectA, int baseA,
+ const SkIRect& rectB, int baseB)
+{
+ return (rectA.fTop < baseB && rectA.fBottom >= baseB)
+ || (rectB.fTop < baseA && rectB.fBottom >= baseA);
+}
+
+class BuilderCheck : public CommonCheck {
+protected:
+ enum IntersectionType {
+ NO_INTERSECTION, // debugging printf expects this to equal zero
+ LAST_INTERSECTION, // debugging printf expects this to equal one
+ WAIT_FOR_INTERSECTION
+ };
+
+ BuilderCheck(const SkIRect& start, int startBase, const SkIRect& end,
+ int endBase, const SkIRect& area)
+ : INHERITED(area.width(), area.height())
+ , mCapture(false)
+ , mEnd(end)
+ , mEndBase(endBase)
+ , mStart(start)
+ , mStartBase(startBase)
+ {
+ mEnd.offset(-area.fLeft, -area.fTop);
+ mEndBase -= area.fTop;
+ mEndExtra.setEmpty();
mLast.setEmpty();
mLastBase = INT_MAX;
- mStart.offset(-dx, -dy);
- mEnd.offset(-dx, -dy);
+ mSelectRect.setEmpty();
+ mStart.offset(-area.fLeft, -area.fTop);
+ mStartBase -= area.fTop;
+ mStartExtra.setEmpty();
+ DBG_NAV_LOGD(" mStart=(%d,%d,r=%d,b=%d) mStartBase=%d"
+ " mEnd=(%d,%d,r=%d,b=%d) mEndBase=%d",
+ mStart.fLeft, mStart.fTop, mStart.fRight, mStart.fBottom, mStartBase,
+ mEnd.fLeft, mEnd.fTop, mEnd.fRight, mEnd.fBottom, mEndBase);
}
- virtual bool onIRect(const SkIRect& rect) {
- bool captureLast = false;
- if ((rect.fLeft == mStart.fLeft && rect.fRight == mStart.fRight &&
- top() == mStart.fTop && bottom() == mStart.fBottom) ||
- (rect.fLeft == mEnd.fLeft && rect.fRight == mEnd.fRight &&
- top() == mEnd.fTop && bottom() == mEnd.fBottom)) {
- captureLast = mCapture;
- mCapture ^= true;
+ int checkFlipRect(const SkIRect& full, int fullBase) {
+ mCollectFull = false;
+ // is the text to collect between the selection top and bottom?
+ if (fullBase < mStart.fTop || fullBase > mEnd.fBottom) {
+ if (VERBOSE_LOGGING && !mLast.isEmpty()) DBG_NAV_LOGD("%s 1"
+ " full=(%d,%d,r=%d,b=%d) fullBase=%d"
+ " mLast=(%d,%d,r=%d,b=%d) mLastBase=%d",
+ mLastIntersects ? "LAST_INTERSECTION" : "NO_INTERSECTION",
+ full.fLeft, full.fTop, full.fRight, full.fBottom, fullBase,
+ mLast.fLeft, mLast.fTop, mLast.fRight, mLast.fBottom, mLastBase);
+ return mLastIntersects;
}
- if (mCapture || captureLast) {
- SkIRect full;
- full.set(rect.fLeft, top(), rect.fRight, bottom());
- if ((mLast.fTop < base() && mLast.fBottom >= base())
- || (mLastBase <= full.fBottom && mLastBase > full.fTop)) {
- if (full.fLeft > mLast.fRight)
- full.fLeft = mLast.fRight;
- else if (full.fRight < mLast.fLeft)
- full.fRight = mLast.fLeft;
- }
- mSelectRegion->op(full, SkRegion::kUnion_Op);
- DBG_NAV_LOGD("MultilineBuilder full=(%d,%d,r=%d,b=%d)",
- full.fLeft, full.fTop, full.fRight, full.fBottom);
+ // is the text to the left of the selection start?
+ if (baseLinesAgree(mStart, mStartBase, full, fullBase)
+ && full.fLeft < mStart.fLeft) {
+ if (VERBOSE_LOGGING) DBG_NAV_LOGD("%s 2"
+ " full=(%d,%d,r=%d,b=%d) fullBase=%d"
+ " mLast=(%d,%d,r=%d,b=%d) mLastBase=%d"
+ " mStart=(%d,%d,r=%d,b=%d) mStartBase=%d",
+ mLastIntersects ? "LAST_INTERSECTION" : "NO_INTERSECTION",
+ full.fLeft, full.fTop, full.fRight, full.fBottom, fullBase,
+ mLast.fLeft, mLast.fTop, mLast.fRight, mLast.fBottom, mLastBase,
+ mStart.fLeft, mStart.fTop, mStart.fRight, mStart.fBottom, mStartBase);
+ mStartExtra.join(full);
+ return mLastIntersects;
+ }
+ // is the text to the right of the selection end?
+ if (baseLinesAgree(mEnd, mEndBase, full, fullBase)
+ && full.fRight > mEnd.fRight) {
+ if (VERBOSE_LOGGING) DBG_NAV_LOGD("%s 3"
+ " full=(%d,%d,r=%d,b=%d) fullBase=%d"
+ " mLast=(%d,%d,r=%d,b=%d) mLastBase=%d"
+ " mEnd=(%d,%d,r=%d,b=%d) mEndBase=%d",
+ mLastIntersects ? "LAST_INTERSECTION" : "NO_INTERSECTION",
+ full.fLeft, full.fTop, full.fRight, full.fBottom, fullBase,
+ mLast.fLeft, mLast.fTop, mLast.fRight, mLast.fBottom, mLastBase,
+ mEnd.fLeft, mEnd.fTop, mEnd.fRight, mEnd.fBottom, mEndBase);
+ mEndExtra.join(full);
+ return mLastIntersects;
+ }
+ int spaceGap = SkFixedRound(minSpaceWidth() * 3);
+ // should text to the left of the start be added to the selection bounds?
+ if (!mStartExtra.isEmpty()) {
+ if (VERBOSE_LOGGING) DBG_NAV_LOGD("mSelectRect=(%d,%d,r=%d,b=%d)"
+ " mStartExtra=(%d,%d,r=%d,b=%d)",
+ mSelectRect.fLeft, mSelectRect.fTop, mSelectRect.fRight, mSelectRect.fBottom,
+ mStartExtra.fLeft, mStartExtra.fTop, mStartExtra.fRight, mStartExtra.fBottom);
+ if (mStartExtra.fRight + spaceGap >= mStart.fLeft)
+ mSelectRect.join(mStartExtra);
+ mStartExtra.setEmpty();
+ }
+ // should text to the right of the end be added to the selection bounds?
+ if (!mEndExtra.isEmpty()) {
+ if (VERBOSE_LOGGING) DBG_NAV_LOGD("mSelectRect=(%d,%d,r=%d,b=%d)"
+ " mEndExtra=(%d,%d,r=%d,b=%d)",
+ mSelectRect.fLeft, mSelectRect.fTop, mSelectRect.fRight, mSelectRect.fBottom,
+ mEndExtra.fLeft, mEndExtra.fTop, mEndExtra.fRight, mEndExtra.fBottom);
+ if (mEndExtra.fLeft - spaceGap <= mEnd.fRight)
+ mSelectRect.join(mEndExtra);
+ mEndExtra.setEmpty();
+ }
+ bool sameBaseLine = baseLinesAgree(mLast, mLastBase, full, fullBase);
+ bool adjacent = (full.fLeft - mLast.fRight) < spaceGap;
+ // is this the first, or are there more characters on the same line?
+ if (mLast.isEmpty() || (sameBaseLine && adjacent)) {
+ if (VERBOSE_LOGGING) DBG_NAV_LOGD("WAIT_FOR_INTERSECTION"
+ " full=(%d,%d,r=%d,b=%d) fullBase=%d"
+ " mLast=(%d,%d,r=%d,b=%d) mLastBase=%d"
+ " mSelectRect=(%d,%d,r=%d,b=%d)",
+ full.fLeft, full.fTop, full.fRight, full.fBottom, fullBase,
+ mLast.fLeft, mLast.fTop, mLast.fRight, mLast.fBottom, mLastBase,
+ mSelectRect.fLeft, mSelectRect.fTop, mSelectRect.fRight, mSelectRect.fBottom);
+ mLast.join(full);
+ mLastIntersects = SkIRect::Intersects(mLast, mSelectRect);
+ return WAIT_FOR_INTERSECTION;
+ }
+ if (VERBOSE_LOGGING) DBG_NAV_LOGD("%s 4"
+ " mLast=(%d,%d,r=%d,b=%d) mLastBase=%d"
+ " full=(%d,%d,r=%d,b=%d) fullBase=%d"
+ " mSelectRect=(%d,%d,r=%d,b=%d)"
+ " mStartExtra=(%d,%d,r=%d,b=%d)"
+ " mEndExtra=(%d,%d,r=%d,b=%d)",
+ mLastIntersects ? "LAST_INTERSECTION" : "NO_INTERSECTION",
+ mLast.fLeft, mLast.fTop, mLast.fRight, mLast.fBottom, mLastBase,
+ full.fLeft, full.fTop, full.fRight, full.fBottom, fullBase,
+ mSelectRect.fLeft, mSelectRect.fTop, mSelectRect.fRight, mSelectRect.fBottom,
+ mStartExtra.fLeft, mStartExtra.fTop, mStartExtra.fRight, mStartExtra.fBottom,
+ mEndExtra.fLeft, mEndExtra.fTop, mEndExtra.fRight, mEndExtra.fBottom);
+ // after the caller determines what to do with the last collection,
+ // start the collection over with full and fullBase.
+ mCollectFull = true;
+ return mLastIntersects;
+ }
+
+ bool resetLast(const SkIRect& full, int fullBase)
+ {
+ if (mCollectFull) {
mLast = full;
- mLastBase = base();
- if (mStart == mEnd)
- mCapture = false;
+ mLastBase = fullBase;
+ mLastIntersects = SkIRect::Intersects(mLast, mSelectRect);
+ } else {
+ mLast.setEmpty();
+ mLastBase = INT_MAX;
+ mLastIntersects = false;
}
- return false;
+ return mCollectFull;
}
-protected:
- SkIRect mStart;
+
+ void setFlippedState()
+ {
+ mSelectRect = mStart;
+ mSelectRect.join(mEnd);
+ DBG_NAV_LOGD("mSelectRect=(%d,%d,r=%d,b=%d)",
+ mSelectRect.fLeft, mSelectRect.fTop, mSelectRect.fRight, mSelectRect.fBottom);
+ mLast.setEmpty();
+ mLastBase = INT_MAX;
+ mLastIntersects = NO_INTERSECTION;
+ }
+
+ bool mCapture;
+ bool mCollectFull;
SkIRect mEnd;
+ int mEndBase;
+ SkIRect mEndExtra;
+ bool mFlipped;
SkIRect mLast;
int mLastBase;
+ int mLastIntersects;
+ SkIRect mSelectRect;
+ SkIRect mStart;
+ SkIRect mStartExtra;
+ int mStartBase;
+private:
+ typedef CommonCheck INHERITED;
+
+};
+
+class MultilineBuilder : public BuilderCheck {
+public:
+ MultilineBuilder(const SkIRect& start, int startBase, const SkIRect& end,
+ int endBase, const SkIRect& area, SkRegion* region)
+ : INHERITED(start, startBase, end, endBase, area)
+ , mSelectRegion(region)
+ {
+ mFlipped = false;
+ }
+
+ void addLastToRegion() {
+ if (VERBOSE_LOGGING) DBG_NAV_LOGD(" mLast=(%d,%d,r=%d,b=%d)",
+ mLast.fLeft, mLast.fTop, mLast.fRight, mLast.fBottom);
+ mSelectRegion->op(mLast, SkRegion::kUnion_Op);
+ }
+
+ void finish() {
+ if (!mFlipped || !mLastIntersects)
+ return;
+ addLastToRegion();
+ }
+
+ // return true if capture end was not found after capture begin
+ bool flipped() {
+ DBG_NAV_LOGD("flipped=%s", mCapture ? "true" : "false");
+ if (!mCapture)
+ return false;
+ mFlipped = true;
+ setFlippedState();
+ mSelectRegion->setEmpty();
+ return true;
+ }
+
+ virtual bool onIRect(const SkIRect& rect) {
+ SkIRect full;
+ full.set(rect.fLeft, top(), rect.fRight, bottom());
+ int fullBase = base();
+ if (mFlipped) {
+ int intersectType = checkFlipRect(full, fullBase);
+ if (intersectType == LAST_INTERSECTION)
+ addLastToRegion();
+ if (intersectType != WAIT_FOR_INTERSECTION)
+ resetLast(full, fullBase);
+ return false;
+ }
+ if (full == mStart) {
+ if (VERBOSE_LOGGING) DBG_NAV_LOGD("full == mStart full=(%d,%d,r=%d,b=%d)",
+ full.fLeft, full.fTop, full.fRight, full.fBottom);
+ mCapture = true;
+ }
+ if (mCapture) {
+ bool sameLines = baseLinesAgree(mLast, mLastBase, full, fullBase);
+ if (sameLines)
+ mLast.join(full);
+ if (!sameLines || full == mEnd) {
+ if (VERBOSE_LOGGING) DBG_NAV_LOGD("finish mLast=(%d,%d,r=%d,b=%d)",
+ mLast.fLeft, mLast.fTop, mLast.fRight, mLast.fBottom);
+ addLastToRegion();
+ mLast = full;
+ mLastBase = fullBase;
+ }
+ }
+ if (full == mEnd) {
+ if (VERBOSE_LOGGING) DBG_NAV_LOGD("full == mEnd full=(%d,%d,r=%d,b=%d)",
+ full.fLeft, full.fTop, full.fRight, full.fBottom);
+ mCapture = false;
+ }
+ return false;
+ }
+
+protected:
SkRegion* mSelectRegion;
- bool mCapture;
+private:
+ typedef BuilderCheck INHERITED;
};
-#define HYPHEN_MINUS 0x2D // ASCII hyphen
-#define HYPHEN 0x2010 // unicode hyphen, first in range of dashes
-#define HORZ_BAR 0x2015 // unicode horizontal bar, last in range of dashes
+static inline bool compareBounds(const SkIRect* first, const SkIRect* second)
+{
+ return first->fTop < second->fTop;
+}
-class TextExtractor : public CommonCheck {
+class TextExtractor : public BuilderCheck {
public:
- TextExtractor(const SkRegion& region) : mSelectRegion(region),
- mSkipFirstSpace(true) { // don't start with a space
+ TextExtractor(const SkIRect& start, int startBase, const SkIRect& end,
+ int endBase, const SkIRect& area, bool flipped)
+ : INHERITED(start, startBase, end, endBase, area)
+ , mSelectStartIndex(-1)
+ , mSkipFirstSpace(true) // don't start with a space
+ {
+ mFlipped = flipped;
+ if (flipped)
+ setFlippedState();
+ }
+
+ void addCharacter(const SkBounder::GlyphRec& rec)
+ {
+ if (mSelectStartIndex < 0)
+ mSelectStartIndex = mSelectText.count();
+ if (!mSkipFirstSpace) {
+ if (addNewLine(rec)) {
+ DBG_NAV_LOG("write new line");
+ *mSelectText.append() = '\n';
+ *mSelectText.append() = '\n';
+ } else if (addSpace(rec)) {
+ DBG_NAV_LOG("write space");
+ *mSelectText.append() = ' ';
+ }
+ } else
+ mSkipFirstSpace = false;
+ recordGlyph(rec);
+ finishGlyph();
+ if (VERBOSE_LOGGING) DBG_NAV_LOGD("glyphID=%d uni=%d '%c'", rec.fGlyphID,
+ mLastUni, mLastUni && mLastUni < 0x7f ? mLastUni : '?');
+ if (mLastUni) {
+ uint16_t chars[2];
+ size_t count = SkUTF16_FromUnichar(mLastUni, chars);
+ *mSelectText.append() = chars[0];
+ if (count == 2)
+ *mSelectText.append() = chars[1];
+ }
+ }
+
+ void addLast()
+ {
+ *mSelectBounds.append() = mLast;
+ *mSelectStart.append() = mSelectStartIndex;
+ *mSelectEnd.append() = mSelectText.count();
}
- virtual void setUp(const SkPaint& paint, const SkMatrix& matrix, SkScalar y,
- const void* text) {
- INHERITED::setUp(paint, matrix, y, text);
- SkPaint charPaint = paint;
- charPaint.setTextEncoding(SkPaint::kUTF8_TextEncoding);
- mMinSpaceWidth = std::max(0, SkScalarToFixed(
- charPaint.measureText(" ", 1)) - SK_Fixed1);
+ /* Text characters are collected before it's been determined that the
+ characters are part of the selection. The bounds describe valid parts
+ of the selection, but the bounds are out of order.
+
+ This sorts the characters by sorting the bounds, then copying the
+ characters that were captured.
+ */
+ void finish()
+ {
+ if (mLastIntersects)
+ addLast();
+ Vector<SkIRect*> sortedBounds;
+ SkTDArray<uint16_t> temp;
+ int index;
+ DBG_NAV_LOGD("mSelectBounds.count=%d text=%d", mSelectBounds.count(),
+ mSelectText.count());
+ for (index = 0; index < mSelectBounds.count(); index++)
+ sortedBounds.append(&mSelectBounds[index]);
+ std::sort(sortedBounds.begin(), sortedBounds.end(), compareBounds);
+ int lastEnd = -1;
+ for (index = 0; index < mSelectBounds.count(); index++) {
+ int order = sortedBounds[index] - &mSelectBounds[0];
+ int start = mSelectStart[order];
+ int end = mSelectEnd[order];
+ DBG_NAV_LOGD("order=%d start=%d end=%d top=%d", order, start, end,
+ mSelectBounds[order].fTop);
+ int count = temp.count();
+ if (count > 0 && temp[count - 1] != '\n' && start != lastEnd) {
+ // always separate paragraphs when original text is out of order
+ DBG_NAV_LOG("write new line");
+ *temp.append() = '\n';
+ *temp.append() = '\n';
+ }
+ temp.append(end - start, &mSelectText[start]);
+ lastEnd = end;
+ }
+ mSelectText.swap(temp);
}
virtual bool onIRectGlyph(const SkIRect& rect,
@@ -217,72 +933,68 @@ public:
{
SkIRect full;
full.set(rect.fLeft, top(), rect.fRight, bottom());
- if (mSelectRegion.contains(full)) {
- if (!mSkipFirstSpace && (mLastUni < HYPHEN || mLastUni > HORZ_BAR)
- && mLastUni != HYPHEN_MINUS
- && (mLastGlyph.fLSB.fY != rec.fLSB.fY // new baseline
- || mLastGlyph.fLSB.fX > rec.fLSB.fX // glyphs are LTR
- || mLastGlyph.fRSB.fX + mMinSpaceWidth < rec.fLSB.fX)) {
- DBG_NAV_LOGD("TextExtractor append space"
- " mLast=(%d,%d,r=%d,b=%d) mLastGlyph=((%g,%g),(%g,%g),%d)"
- " full=(%d,%d,r=%d,b=%d) rec=((%g,%g),(%g,%g),%d)"
- " mMinSpaceWidth=%g",
- mLast.fLeft, mLast.fTop, mLast.fRight, mLast.fBottom,
- SkFixedToScalar(mLastGlyph.fLSB.fX),
- SkFixedToScalar(mLastGlyph.fLSB.fY),
- SkFixedToScalar(mLastGlyph.fRSB.fX),
- SkFixedToScalar(mLastGlyph.fRSB.fY), mLastGlyph.fGlyphID,
- full.fLeft, full.fTop, full.fRight, full.fBottom,
- SkFixedToScalar(rec.fLSB.fX),
- SkFixedToScalar(rec.fLSB.fY),
- SkFixedToScalar(rec.fRSB.fX),
- SkFixedToScalar(rec.fRSB.fY), rec.fGlyphID,
- SkFixedToScalar(mMinSpaceWidth));
- *mSelectText.append() = ' ';
- } else
- mSkipFirstSpace = false;
- DBG_NAV_LOGD("TextExtractor [%02x] append full=(%d,%d,r=%d,b=%d)",
- rec.fGlyphID, full.fLeft, full.fTop, full.fRight, full.fBottom);
- SkPaint utfPaint = *mPaint;
- utfPaint.setTextEncoding(SkPaint::kUTF16_TextEncoding);
- utfPaint.glyphsToUnichars(&rec.fGlyphID, 1, &mLastUni);
- if (mLastUni) {
- uint16_t chars[2];
- size_t count = SkUTF16_FromUnichar(mLastUni, chars);
- *mSelectText.append() = chars[0];
- if (count == 2)
- *mSelectText.append() = chars[1];
+ int fullBase = base();
+ if (mFlipped) {
+ int intersectType = checkFlipRect(full, fullBase);
+ if (WAIT_FOR_INTERSECTION == intersectType)
+ addCharacter(rec); // may not be copied
+ else {
+ if (LAST_INTERSECTION == intersectType)
+ addLast();
+ else
+ mSkipFirstSpace = true;
+ mSelectStartIndex = -1;
+ if (resetLast(full, fullBase))
+ addCharacter(rec); // may not be copied
}
- mLast = full;
- mLastGlyph = rec;
- } else {
- mSkipFirstSpace = true;
- DBG_NAV_LOGD("TextExtractor [%02x] skip full=(%d,%d,r=%d,b=%d)",
- rec.fGlyphID, full.fLeft, full.fTop, full.fRight, full.fBottom);
+ return false;
}
+ if (full == mStart)
+ mCapture = true;
+ if (mCapture)
+ addCharacter(rec);
+ else
+ mSkipFirstSpace = true;
+ if (full == mEnd)
+ mCapture = false;
return false;
}
WebCore::String text() {
+ if (mFlipped)
+ finish();
+ // the text has been copied in visual order. Reverse as needed if
+ // result contains right-to-left characters.
+ const uint16_t* start = mSelectText.begin();
+ const uint16_t* end = mSelectText.end();
+ while (start < end) {
+ SkUnichar ch = SkUTF16_NextUnichar(&start);
+ WTF::Unicode::Direction charDirection = WTF::Unicode::direction(ch);
+ if (WTF::Unicode::RightToLeftArabic == charDirection
+ || WTF::Unicode::RightToLeft == charDirection) {
+ WebCore::ReverseBidi(mSelectText.begin(), mSelectText.count());
+ break;
+ }
+ }
return WebCore::String(mSelectText.begin(), mSelectText.count());
}
protected:
- const SkRegion& mSelectRegion;
+ SkIRect mEmpty;
+ SkTDArray<SkIRect> mSelectBounds;
+ SkTDArray<int> mSelectEnd;
+ SkTDArray<int> mSelectStart;
+ int mSelectStartIndex;
SkTDArray<uint16_t> mSelectText;
- SkIRect mLast;
- SkBounder::GlyphRec mLastGlyph;
- SkUnichar mLastUni;
- SkFixed mMinSpaceWidth;
bool mSkipFirstSpace;
private:
- typedef CommonCheck INHERITED;
+ typedef BuilderCheck INHERITED;
};
class TextCanvas : public SkCanvas {
public:
- TextCanvas(CommonCheck* bounder, const SkPicture& picture, const SkIRect& area)
+ TextCanvas(CommonCheck* bounder, const SkIRect& area)
: mBounder(*bounder) {
setBounder(bounder);
SkBitmap bitmap;
@@ -340,49 +1052,201 @@ public:
CommonCheck& mBounder;
};
-void CopyPaste::buildSelection(const SkPicture& picture, const SkIRect& area,
- const SkIRect& selStart, const SkIRect& selEnd, SkRegion* region) {
+static bool buildSelection(const SkPicture& picture, const SkIRect& area,
+ const SkIRect& selStart, int startBase,
+ const SkIRect& selEnd, int endBase, SkRegion* region)
+{
DBG_NAV_LOGD("area=(%d, %d, %d, %d) selStart=(%d, %d, %d, %d)"
- " selEnd=(%d, %d, %d, %d)",
+ " selEnd=(%d, %d, %d, %d)",
area.fLeft, area.fTop, area.fRight, area.fBottom,
selStart.fLeft, selStart.fTop, selStart.fRight, selStart.fBottom,
selEnd.fLeft, selEnd.fTop, selEnd.fRight, selEnd.fBottom);
- MultilineBuilder builder(selStart, selEnd, area.fLeft, area.fTop, region);
- TextCanvas checker(&builder, picture, area);
+ MultilineBuilder builder(selStart, startBase, selEnd, endBase, area, region);
+ TextCanvas checker(&builder, area);
checker.drawPicture(const_cast<SkPicture&>(picture));
+ bool flipped = builder.flipped();
+ if (flipped) {
+ TextCanvas checker(&builder, area);
+ checker.drawPicture(const_cast<SkPicture&>(picture));
+ }
+ builder.finish();
region->translate(area.fLeft, area.fTop);
+ return flipped;
}
-SkIRect CopyPaste::findClosest(const SkPicture& picture, const SkIRect& area,
- int x, int y) {
- FirstCheck _check(x - area.fLeft, y - area.fTop);
- DBG_NAV_LOGD("area=(%d, %d, %d, %d) x=%d y=%d", area.fLeft, area.fTop,
- area.fRight, area.fBottom, x, y);
- TextCanvas checker(&_check, picture, area);
+static SkIRect findClosest(FirstCheck& _check, const SkPicture& picture,
+ const SkIRect& area, int* base)
+{
+ DBG_NAV_LOGD("area=(%d, %d, %d, %d)", area.fLeft, area.fTop,
+ area.fRight, area.fBottom);
+ TextCanvas checker(&_check, area);
checker.drawPicture(const_cast<SkPicture&>(picture));
- _check.offsetBounds(area.fLeft, area.fTop);
- return _check.bestBounds();
+ _check.finishGlyph();
+ return _check.adjustedBounds(area, base);
}
-WebCore::String CopyPaste::text(const SkPicture& picture, const SkIRect& area,
- const SkRegion& region) {
- SkRegion copy = region;
- copy.translate(-area.fLeft, -area.fTop);
- const SkIRect& bounds = copy.getBounds();
- DBG_NAV_LOGD("area=(%d, %d, %d, %d) region=(%d, %d, %d, %d)",
- area.fLeft, area.fTop, area.fRight, area.fBottom,
- bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom);
- TextExtractor extractor(copy);
- TextCanvas checker(&extractor, picture, area);
+static SkIRect findEdge(const SkPicture& picture, const SkIRect& area,
+ int x, int y, bool left, int* base)
+{
+ SkIRect result;
+ result.setEmpty();
+ FirstCheck center(x, y, area);
+ center.setRecordGlyph();
+ int closestBase;
+ SkIRect closest = findClosest(center, picture, area, &closestBase);
+ closest.inset(-TOUCH_SLOP, -TOUCH_SLOP);
+ if (!closest.contains(x, y)) {
+ DBG_NAV_LOGD("closest=(%d, %d, %d, %d) area=(%d, %d, %d, %d) x/y=%d,%d",
+ closest.fLeft, closest.fTop, closest.fRight, closest.fBottom,
+ area.fLeft, area.fTop, area.fRight, area.fBottom, x, y);
+ return result;
+ }
+ EdgeCheck edge(x, y, area, center, left);
+ do { // detect left or right until there's a gap
+ DBG_NAV_LOGD("edge=%p picture=%p area=%d,%d,%d,%d",
+ &edge, &picture, area.fLeft, area.fTop, area.fRight, area.fBottom);
+ TextCanvas checker(&edge, area);
+ checker.drawPicture(const_cast<SkPicture&>(picture));
+ edge.finishGlyph();
+ if (!edge.adjacent()) {
+ DBG_NAV_LOG("adjacent break");
+ break;
+ }
+ int nextBase;
+ const SkIRect& next = edge.bestBounds(&nextBase);
+ if (next.isEmpty()) {
+ DBG_NAV_LOG("empty");
+ break;
+ }
+ if (result == next) {
+ DBG_NAV_LOG("result == next");
+ break;
+ }
+ *base = nextBase;
+ result = next;
+ edge.shiftStart(result);
+ } while (true);
+ if (!result.isEmpty()) {
+ *base += area.fTop;
+ result.offset(area.fLeft, area.fTop);
+ }
+ return result;
+}
+
+static SkIRect findFirst(const SkPicture& picture, int* base)
+{
+ FindFirst finder(picture.width(), picture.height());
+ SkIRect area;
+ area.set(0, 0, picture.width(), picture.height());
+ TextCanvas checker(&finder, area);
+ checker.drawPicture(const_cast<SkPicture&>(picture));
+ return finder.bestBounds(base);
+}
+
+static SkIRect findLast(const SkPicture& picture, int* base)
+{
+ FindLast finder(picture.width(), picture.height());
+ SkIRect area;
+ area.set(0, 0, picture.width(), picture.height());
+ TextCanvas checker(&finder, area);
+ checker.drawPicture(const_cast<SkPicture&>(picture));
+ return finder.bestBounds(base);
+}
+
+static SkIRect findLeft(const SkPicture& picture, const SkIRect& area,
+ int x, int y, int* base)
+{
+ return findEdge(picture, area, x, y, true, base);
+}
+
+static SkIRect findRight(const SkPicture& picture, const SkIRect& area,
+ int x, int y, int* base)
+{
+ return findEdge(picture, area, x, y, false, base);
+}
+
+static WebCore::String text(const SkPicture& picture, const SkIRect& area,
+ const SkIRect& start, int startBase, const SkIRect& end,
+ int endBase, bool flipped)
+{
+ TextExtractor extractor(start, startBase, end, endBase, area, flipped);
+ TextCanvas checker(&extractor, area);
checker.drawPicture(const_cast<SkPicture&>(picture));
return extractor.text();
}
+#define CONTROL_OFFSET 3
+#define CONTROL_NOTCH 9
+#define CONTROL_HEIGHT 18
+#define CONTROL_WIDTH 12
+#define STROKE_WIDTH 0.4f
+#define SLOP 20
+
+SelectText::SelectText()
+{
+ reset();
+ SkScalar innerW = CONTROL_WIDTH - STROKE_WIDTH;
+ SkScalar innerH = CONTROL_HEIGHT - STROKE_WIDTH;
+ SkPaint paint;
+ paint.setAntiAlias(true);
+ paint.setStrokeWidth(STROKE_WIDTH);
+
+ SkPath startPath;
+ startPath.moveTo(-CONTROL_WIDTH, CONTROL_NOTCH);
+ startPath.lineTo(-CONTROL_WIDTH, CONTROL_HEIGHT);
+ startPath.lineTo(0, CONTROL_HEIGHT);
+ startPath.lineTo(0, CONTROL_OFFSET);
+ startPath.close();
+
+ SkCanvas* canvas = m_startControl.beginRecording(CONTROL_WIDTH, CONTROL_HEIGHT);
+ paint.setStyle(SkPaint::kFill_Style);
+ paint.setColor(0xD077A14B);
+ canvas->drawPath(startPath, paint);
+ paint.setStyle(SkPaint::kStroke_Style);
+ paint.setColor(0x40000000);
+ canvas->drawLine(-innerW, CONTROL_NOTCH, -innerW, innerH, paint);
+ canvas->drawLine(-innerW + STROKE_WIDTH, innerH, -STROKE_WIDTH, innerH, paint);
+ paint.setColor(0x40ffffff);
+ canvas->drawLine(0, CONTROL_OFFSET + STROKE_WIDTH,
+ -CONTROL_WIDTH, CONTROL_NOTCH + STROKE_WIDTH, paint);
+ canvas->drawLine(-STROKE_WIDTH, CONTROL_NOTCH + STROKE_WIDTH,
+ -STROKE_WIDTH, innerH, paint);
+ paint.setColor(0xffaaaaaa);
+ canvas->drawPath(startPath, paint);
+ m_startControl.endRecording();
+
+ SkPath endPath;
+ endPath.moveTo(0, CONTROL_OFFSET);
+ endPath.lineTo(0, CONTROL_HEIGHT);
+ endPath.lineTo(CONTROL_WIDTH, CONTROL_HEIGHT);
+ endPath.lineTo(CONTROL_WIDTH, CONTROL_NOTCH);
+ endPath.close();
+
+ canvas = m_endControl.beginRecording(CONTROL_WIDTH, CONTROL_HEIGHT);
+ paint.setStyle(SkPaint::kFill_Style);
+ paint.setColor(0xD077A14B);
+ canvas->drawPath(endPath, paint);
+ paint.setStyle(SkPaint::kStroke_Style);
+ paint.setColor(0x40000000);
+ canvas->drawLine(STROKE_WIDTH, CONTROL_OFFSET + STROKE_WIDTH,
+ STROKE_WIDTH, innerH, paint);
+ canvas->drawLine(STROKE_WIDTH + STROKE_WIDTH, innerH, innerW, innerH, paint);
+ paint.setColor(0x40ffffff);
+ canvas->drawLine(0, CONTROL_OFFSET + STROKE_WIDTH,
+ CONTROL_WIDTH, CONTROL_NOTCH + STROKE_WIDTH, paint);
+ canvas->drawLine(STROKE_WIDTH, CONTROL_NOTCH + STROKE_WIDTH,
+ STROKE_WIDTH, innerH, paint);
+ paint.setColor(0xffaaaaaa);
+ canvas->drawPath(endPath, paint);
+ m_endControl.endRecording();
+}
+
void SelectText::draw(SkCanvas* canvas, LayerAndroid* layer)
{
- if (layer->picture() != m_picture)
- return;
- if (m_drawRegion)
+ // FIXME: layer may not own the original selected picture
+ m_picture = layer->picture();
+ DBG_NAV_LOGD("m_extendSelection=%d m_drawPointer=%d", m_extendSelection, m_drawPointer);
+ if (m_extendSelection)
drawSelectionRegion(canvas);
if (m_drawPointer)
drawSelectionPointer(canvas);
@@ -406,7 +1270,7 @@ void SelectText::drawSelectionPointer(SkCanvas* canvas)
paint.setStrokeWidth(SK_Scalar1 * 2);
int sc = canvas->save();
canvas->scale(m_inverseScale, m_inverseScale);
- canvas->translate(SkIntToScalar(m_selectX), SkIntToScalar(m_selectY));
+ canvas->translate(m_selectX, m_selectY);
canvas->drawPath(path, paint);
if (!m_extendSelection) {
paint.setStyle(SkPaint::kFill_Style);
@@ -424,19 +1288,92 @@ void SelectText::drawSelectionRegion(SkCanvas* canvas)
return;
SkIRect ivisBounds;
visBounds.round(&ivisBounds);
- CopyPaste::buildSelection(*m_picture, ivisBounds, m_selStart, m_selEnd,
- &m_selRegion);
+ ivisBounds.join(m_selStart);
+ ivisBounds.join(m_selEnd);
+ DBG_NAV_LOGD("m_selStart=(%d, %d, %d, %d) m_selEnd=(%d, %d, %d, %d)",
+ m_selStart.fLeft, m_selStart.fTop, m_selStart.fRight, m_selStart.fBottom,
+ m_selEnd.fLeft, m_selEnd.fTop, m_selEnd.fRight, m_selEnd.fBottom);
+ m_flipped = buildSelection(*m_picture, ivisBounds, m_selStart, m_startBase,
+ m_selEnd, m_endBase, &m_selRegion);
SkPath path;
m_selRegion.getBoundaryPath(&path);
+ path.setFillType(SkPath::kEvenOdd_FillType);
+
SkPaint paint;
paint.setAntiAlias(true);
- paint.setColor(SkColorSetARGB(0x40, 255, 51, 204));
+ paint.setColor(SkColorSetARGB(0x80, 151, 200, 73));
canvas->drawPath(path, paint);
+ // experiment to draw touchable controls that resize the selection
+ canvas->save();
+ canvas->translate(m_selStart.fLeft, m_selStart.fBottom);
+ canvas->drawPicture(m_startControl);
+ canvas->restore();
+ canvas->save();
+ canvas->translate(m_selEnd.fRight, m_selEnd.fBottom);
+ canvas->drawPicture(m_endControl);
+ canvas->restore();
+}
+
+void SelectText::extendSelection(const SkPicture* picture, int x, int y)
+{
+ SkIRect clipRect = m_visibleRect;
+ int base;
+ if (m_startSelection) {
+ if (!clipRect.contains(x, y)
+ || !clipRect.contains(m_original.fX, m_original.fY)) {
+ clipRect.set(m_original.fX, m_original.fY, x, y);
+ clipRect.sort();
+ clipRect.inset(-m_visibleRect.width(), -m_visibleRect.height());
+ }
+ DBG_NAV_LOGD("selStart clip=(%d,%d,%d,%d)", clipRect.fLeft,
+ clipRect.fTop, clipRect.fRight, clipRect.fBottom);
+ m_picture = picture;
+ FirstCheck center(m_original.fX, m_original.fY, clipRect);
+ m_selStart = m_selEnd = findClosest(center, *picture, clipRect, &base);
+ m_startBase = m_endBase = base;
+ m_startSelection = false;
+ m_extendSelection = true;
+ m_original.fX = m_original.fY = 0;
+ } else if (picture != m_picture)
+ return;
+ x -= m_original.fX;
+ y -= m_original.fY;
+ if (!clipRect.contains(x, y) || !clipRect.contains(m_selStart)) {
+ clipRect.set(m_selStart.fLeft, m_selStart.fTop, x, y);
+ clipRect.sort();
+ clipRect.inset(-m_visibleRect.width(), -m_visibleRect.height());
+ }
+ DBG_NAV_LOGD("extend clip=(%d,%d,%d,%d)", clipRect.fLeft,
+ clipRect.fTop, clipRect.fRight, clipRect.fBottom);
+ FirstCheck extension(x, y, clipRect);
+ SkIRect found = findClosest(extension, *picture, clipRect, &base);
+ DBG_NAV_LOGD("pic=%p x=%d y=%d m_startSelection=%s %s=(%d, %d, %d, %d)"
+ " m_extendSelection=%s",
+ picture, x, y, m_startSelection ? "true" : "false",
+ m_hitTopLeft ? "m_selStart" : "m_selEnd",
+ found.fLeft, found.fTop, found.fRight, found.fBottom,
+ m_extendSelection ? "true" : "false");
+ if (m_hitTopLeft) {
+ m_startBase = base;
+ m_selStart = found;
+ } else {
+ m_endBase = base;
+ m_selEnd = found;
+ }
+ swapAsNeeded();
}
const String SelectText::getSelection()
{
- String result = CopyPaste::text(*m_picture, m_visibleRect, m_selRegion);
+ SkIRect clipRect;
+ clipRect.set(0, 0, m_picture->width(), m_picture->height());
+ String result = text(*m_picture, clipRect, m_selStart, m_startBase,
+ m_selEnd, m_endBase, m_flipped);
+ DBG_NAV_LOGD("clip=(%d,%d,%d,%d)"
+ " m_selStart=(%d, %d, %d, %d) m_selEnd=(%d, %d, %d, %d)",
+ clipRect.fLeft, clipRect.fTop, clipRect.fRight, clipRect.fBottom,
+ m_selStart.fLeft, m_selStart.fTop, m_selStart.fRight, m_selStart.fBottom,
+ m_selEnd.fLeft, m_selEnd.fTop, m_selEnd.fRight, m_selEnd.fBottom);
DBG_NAV_LOGD("text=%s", result.latin1().data()); // uses CString
return result;
}
@@ -447,35 +1384,173 @@ void SelectText::getSelectionArrow(SkPath* path)
0, 14, 3, 11, 5, 15, 9, 15, 7, 11, 11, 11
};
for (unsigned index = 0; index < sizeof(arrow)/sizeof(arrow[0]); index += 2)
- path->lineTo(SkIntToScalar(arrow[index]), SkIntToScalar(arrow[index + 1]));
+ path->lineTo(arrow[index], arrow[index + 1]);
path->close();
}
void SelectText::getSelectionCaret(SkPath* path)
{
- SkScalar height = SkIntToScalar(m_selStart.fBottom - m_selStart.fTop);
+ SkScalar height = m_selStart.fBottom - m_selStart.fTop;
SkScalar dist = height / 4;
path->moveTo(0, -height / 2);
path->rLineTo(0, height);
path->rLineTo(-dist, dist);
- path->rMoveTo(0, -SK_Scalar1/2);
+ path->rMoveTo(0, -0.5f);
path->rLineTo(dist * 2, 0);
- path->rMoveTo(0, SK_Scalar1/2);
+ path->rMoveTo(0, 0.5f);
path->rLineTo(-dist, -dist);
}
-void SelectText::moveSelection(const SkPicture* picture, int x, int y,
- bool extendSelection)
+bool SelectText::hitCorner(int cx, int cy, int x, int y) const
+{
+ SkIRect test;
+ test.set(cx, cy, cx, cy);
+ test.inset(-SLOP, -SLOP);
+ return test.contains(x, y);
+}
+
+bool SelectText::hitSelection(int x, int y) const
+{
+ int left = m_selStart.fLeft - CONTROL_WIDTH / 2;
+ int top = m_selStart.fBottom + CONTROL_HEIGHT / 2;
+ if (hitCorner(left, top, x, y))
+ return true;
+ int right = m_selEnd.fRight + CONTROL_WIDTH / 2;
+ int bottom = m_selEnd.fBottom + CONTROL_HEIGHT / 2;
+ if (hitCorner(right, bottom, x, y))
+ return true;
+ return m_selRegion.contains(x, y);
+}
+
+void SelectText::moveSelection(const SkPicture* picture, int x, int y)
{
- if (!extendSelection)
+ SkIRect clipRect = m_visibleRect;
+ clipRect.join(m_selStart);
+ clipRect.join(m_selEnd);
+ if (!m_extendSelection)
m_picture = picture;
- m_selEnd = CopyPaste::findClosest(*picture, m_visibleRect, x, y);
- if (!extendSelection)
- m_selStart = m_selEnd;
+ FirstCheck center(x, y, clipRect);
+ int base;
+ SkIRect found = findClosest(center, *picture, clipRect, &base);
+ if (m_hitTopLeft || !m_extendSelection) {
+ m_startBase = base;
+ m_selStart = found;
+ }
+ if (!m_hitTopLeft || !m_extendSelection) {
+ m_endBase = base;
+ m_selEnd = found;
+ }
+ swapAsNeeded();
DBG_NAV_LOGD("x=%d y=%d extendSelection=%s m_selStart=(%d, %d, %d, %d)"
- " m_selEnd=(%d, %d, %d, %d)", x, y, extendSelection ? "true" : "false",
+ " m_selEnd=(%d, %d, %d, %d)", x, y, m_extendSelection ? "true" : "false",
+ m_selStart.fLeft, m_selStart.fTop, m_selStart.fRight, m_selStart.fBottom,
+ m_selEnd.fLeft, m_selEnd.fTop, m_selEnd.fRight, m_selEnd.fBottom);
+}
+
+void SelectText::reset()
+{
+ DBG_NAV_LOG("m_extendSelection=false");
+ m_selStart.setEmpty();
+ m_selEnd.setEmpty();
+ m_extendSelection = false;
+ m_startSelection = false;
+}
+
+void SelectText::selectAll(const SkPicture* picture)
+{
+ m_selStart = findFirst(*picture, &m_startBase);
+ m_selEnd = findLast(*picture, &m_endBase);
+ m_extendSelection = true;
+}
+
+int SelectText::selectionX() const
+{
+ return m_hitTopLeft ? m_selStart.fLeft : m_selEnd.fRight;
+}
+
+int SelectText::selectionY() const
+{
+ const SkIRect& rect = m_hitTopLeft ? m_selStart : m_selEnd;
+ return (rect.fTop + rect.fBottom) >> 1;
+}
+
+bool SelectText::startSelection(int x, int y)
+{
+ m_original.fX = x;
+ m_original.fY = y;
+ if (m_selStart.isEmpty()) {
+ DBG_NAV_LOGD("empty start x=%d y=%d", x, y);
+ m_startSelection = true;
+ return true;
+ }
+ int left = m_selStart.fLeft - CONTROL_WIDTH / 2;
+ int top = m_selStart.fBottom + CONTROL_HEIGHT / 2;
+ m_hitTopLeft = hitCorner(left, top, x, y);
+ int right = m_selEnd.fRight + CONTROL_WIDTH / 2;
+ int bottom = m_selEnd.fBottom + CONTROL_HEIGHT / 2;
+ bool hitBottomRight = hitCorner(right, bottom, x, y);
+ DBG_NAV_LOGD("left=%d top=%d right=%d bottom=%d x=%d y=%d", left, top,
+ right, bottom, x, y);
+ if (m_hitTopLeft && (!hitBottomRight || y - top < bottom - y)) {
+ DBG_NAV_LOG("hit top left");
+ m_original.fX -= left;
+ m_original.fY -= (m_selStart.fTop + m_selStart.fBottom) >> 1;
+ } else if (hitBottomRight) {
+ DBG_NAV_LOG("hit bottom right");
+ m_original.fX -= right;
+ m_original.fY -= (m_selEnd.fTop + m_selEnd.fBottom) >> 1;
+ }
+ return m_hitTopLeft || hitBottomRight;
+}
+
+/* selects the word at (x, y)
+* a word is normally delimited by spaces
+* a string of digits (even with inside spaces) is a word (for phone numbers)
+* FIXME: digit find isn't implemented yet
+* returns true if a word was selected
+*/
+bool SelectText::wordSelection(const SkPicture* picture)
+{
+ int x = m_selStart.fLeft;
+ int y = (m_selStart.fTop + m_selStart.fBottom) >> 1;
+ SkIRect clipRect = m_visibleRect;
+ clipRect.fLeft -= m_visibleRect.width() >> 1;
+ int base;
+ SkIRect left = findLeft(*picture, clipRect, x, y, &base);
+ if (!left.isEmpty()) {
+ m_startBase = base;
+ m_selStart = left;
+ }
+ x = m_selEnd.fRight;
+ y = (m_selEnd.fTop + m_selEnd.fBottom) >> 1;
+ clipRect = m_visibleRect;
+ clipRect.fRight += m_visibleRect.width() >> 1;
+ SkIRect right = findRight(*picture, clipRect, x, y, &base);
+ if (!right.isEmpty()) {
+ m_endBase = base;
+ m_selEnd = right;
+ }
+ DBG_NAV_LOGD("m_selStart=(%d, %d, %d, %d) m_selEnd=(%d, %d, %d, %d)",
m_selStart.fLeft, m_selStart.fTop, m_selStart.fRight, m_selStart.fBottom,
m_selEnd.fLeft, m_selEnd.fTop, m_selEnd.fRight, m_selEnd.fBottom);
+ if (!left.isEmpty() || !right.isEmpty()) {
+ m_extendSelection = true;
+ return true;
+ }
+ return false;
+}
+
+void SelectText::swapAsNeeded()
+{
+ if (m_selStart.fTop >= (m_selEnd.fTop + m_selEnd.fBottom) >> 1
+ || (m_selEnd.fTop < (m_selStart.fTop + m_selStart.fBottom) >> 1
+ && m_selStart.fRight > m_selEnd.fLeft))
+ {
+ SkTSwap(m_startBase, m_endBase);
+ SkTSwap(m_selStart, m_selEnd);
+ m_hitTopLeft ^= true;
+ DBG_NAV_LOGD("m_hitTopLeft=%s", m_hitTopLeft ? "true" : "false");
+ }
}
}
diff --git a/WebKit/android/nav/SelectText.h b/WebKit/android/nav/SelectText.h
index 8174046..404e9e7 100644
--- a/WebKit/android/nav/SelectText.h
+++ b/WebKit/android/nav/SelectText.h
@@ -29,6 +29,7 @@
#include "DrawExtra.h"
#include "IntRect.h"
#include "PlatformString.h"
+#include "SkPath.h"
class SkPicture;
struct SkIRect;
@@ -38,47 +39,57 @@ namespace android {
class CachedRoot;
-class CopyPaste {
-public:
- static void buildSelection(const SkPicture& , const SkIRect& area,
- const SkIRect& selStart, const SkIRect& selEnd, SkRegion* region);
- static SkIRect findClosest(const SkPicture& , const SkIRect& area,
- int x, int y);
- static String text(const SkPicture& , const SkIRect& area,
- const SkRegion& );
-};
-
class SelectText : public DrawExtra {
public:
- SelectText() {
- m_selStart.setEmpty();
- m_selEnd.setEmpty();
- }
+ SelectText();
virtual void draw(SkCanvas* , LayerAndroid* );
+ void extendSelection(const SkPicture* , int x, int y);
const String getSelection();
- void moveSelection(const SkPicture* , int x, int y, bool extendSelection);
+ bool hitSelection(int x, int y) const;
+ void moveSelection(const SkPicture* , int x, int y);
+ void reset();
+ void selectAll(const SkPicture* );
+ int selectionX() const;
+ int selectionY() const;
void setDrawPointer(bool drawPointer) { m_drawPointer = drawPointer; }
- void setDrawRegion(bool drawRegion) { m_drawRegion = drawRegion; }
+ void setExtendSelection(bool extend) { m_extendSelection = extend; }
void setVisibleRect(const IntRect& rect) { m_visibleRect = rect; }
+ bool startSelection(int x, int y);
+ bool wordSelection(const SkPicture* picture);
+public:
+ float m_inverseScale; // inverse scale, x, y used for drawing select path
+ int m_selectX;
+ int m_selectY;
private:
- friend class WebView;
void drawSelectionPointer(SkCanvas* );
void drawSelectionRegion(SkCanvas* );
static void getSelectionArrow(SkPath* );
void getSelectionCaret(SkPath* );
+ bool hitCorner(int cx, int cy, int x, int y) const;
+ void swapAsNeeded();
+ SkIPoint m_original; // computed start of extend selection
SkIRect m_selStart;
SkIRect m_selEnd;
- SkIRect m_visibleRect;
- SkRegion m_selRegion;
+ int m_startBase;
+ int m_endBase;
+ SkIRect m_visibleRect; // constrains picture computations to visible area
+ SkRegion m_selRegion; // computed from sel start, end
+ SkPicture m_startControl;
+ SkPicture m_endControl;
const SkPicture* m_picture;
- float m_inverseScale;
- int m_selectX;
- int m_selectY;
- bool m_drawRegion;
bool m_drawPointer;
- bool m_extendSelection;
+ bool m_extendSelection; // false when trackball is moving pointer
+ bool m_flipped;
+ bool m_hitTopLeft;
+ bool m_startSelection;
};
}
+namespace WebCore {
+
+void ReverseBidi(UChar* chars, int len);
+
+}
+
#endif
diff --git a/WebKit/android/nav/WebView.cpp b/WebKit/android/nav/WebView.cpp
index 2ed6148..3dafaff 100644
--- a/WebKit/android/nav/WebView.cpp
+++ b/WebKit/android/nav/WebView.cpp
@@ -946,7 +946,7 @@ String getSelection()
return m_selectText.getSelection();
}
-void moveSelection(int x, int y, bool extendSelection)
+void moveSelection(int x, int y)
{
const CachedRoot* root = getFrameCache(DontAllowNewer);
if (!root)
@@ -957,26 +957,81 @@ void moveSelection(int x, int y, bool extendSelection)
IntRect visibleRect;
getVisibleRect(&visibleRect);
m_selectText.setVisibleRect(visibleRect);
- m_selectText.moveSelection(picture, x, y, extendSelection);
+ m_selectText.moveSelection(picture, x, y);
}
-void setSelectionPointer(bool set, float scale, int x, int y,
- bool extendSelection)
+void selectAll()
+{
+ const CachedRoot* root = getFrameCache(DontAllowNewer);
+ if (!root)
+ return;
+ SkPicture* picture = root->pictureAt(0, 0);
+ m_selectText.selectAll(picture);
+}
+
+int selectionX()
+{
+ return m_selectText.selectionX();
+}
+
+int selectionY()
+{
+ return m_selectText.selectionY();
+}
+
+void resetSelection()
+{
+ m_selectText.reset();
+}
+
+bool startSelection(int x, int y)
+{
+ return m_selectText.startSelection(x, y);
+}
+
+bool wordSelection(int x, int y)
+{
+ startSelection(x, y);
+ if (!extendSelection(x, y))
+ return false;
+ m_selectText.setDrawPointer(false);
+ SkPicture* picture = getFrameCache(DontAllowNewer)->pictureAt(x, y);
+ return m_selectText.wordSelection(picture);
+}
+
+bool extendSelection(int x, int y)
+{
+ const CachedRoot* root = getFrameCache(DontAllowNewer);
+ if (!root)
+ return false;
+ SkPicture* picture = root->pictureAt(x, y);
+ IntRect visibleRect;
+ getVisibleRect(&visibleRect);
+ m_selectText.setVisibleRect(visibleRect);
+ m_selectText.extendSelection(picture, x, y);
+ return true;
+}
+
+bool hitSelection(int x, int y)
+{
+ return m_selectText.hitSelection(x, y);
+}
+
+void setExtendSelection()
+{
+ m_selectText.setExtendSelection(true);
+}
+
+void setSelectionPointer(bool set, float scale, int x, int y)
{
m_selectText.setDrawPointer(set);
if (!set)
return;
- m_selectText.m_extendSelection = extendSelection;
m_selectText.m_inverseScale = scale;
m_selectText.m_selectX = x;
m_selectText.m_selectY = y;
}
-void setSelectionRegion(bool set)
-{
- m_selectText.setDrawRegion(set);
-}
-
void sendMoveFocus(WebCore::Frame* framePtr, WebCore::Node* nodePtr)
{
DBG_NAV_LOGD("framePtr=%p nodePtr=%p", framePtr, nodePtr);
@@ -1035,6 +1090,11 @@ void setMatches(WTF::Vector<MatchInfo>* matches)
viewInvalidate();
}
+int currentMatchIndex()
+{
+ return m_findOnPage.currentMatchIndex();
+}
+
bool scrollBy(int dx, int dy)
{
LOG_ASSERT(m_javaGlue.m_obj, "A java object was not associated with this native WebView!");
@@ -1735,6 +1795,13 @@ static void nativeFindNext(JNIEnv *env, jobject obj, bool forward)
view->findNext(forward);
}
+static int nativeFindIndex(JNIEnv *env, jobject obj)
+{
+ WebView* view = GET_NATIVE_VIEW(env, obj);
+ LOG_ASSERT(view, "view not set in nativeFindIndex");
+ return view->currentMatchIndex();
+}
+
static void nativeUpdateCachedTextfield(JNIEnv *env, jobject obj, jstring updatedText, jint generation)
{
WebView* view = GET_NATIVE_VIEW(env, obj);
@@ -1805,11 +1872,39 @@ static int nativeMoveGeneration(JNIEnv *env, jobject obj)
return view->moveGeneration();
}
-static void nativeMoveSelection(JNIEnv *env, jobject obj, int x, int y, bool ex)
+static void nativeMoveSelection(JNIEnv *env, jobject obj, int x, int y)
{
- WebView* view = GET_NATIVE_VIEW(env, obj);
- LOG_ASSERT(view, "view not set in %s", __FUNCTION__);
- view->moveSelection(x, y, ex);
+ GET_NATIVE_VIEW(env, obj)->moveSelection(x, y);
+}
+
+static void nativeResetSelection(JNIEnv *env, jobject obj)
+{
+ return GET_NATIVE_VIEW(env, obj)->resetSelection();
+}
+
+static void nativeSelectAll(JNIEnv* env, jobject obj)
+{
+ GET_NATIVE_VIEW(env, obj)->selectAll();
+}
+
+static void nativeSetExtendSelection(JNIEnv *env, jobject obj)
+{
+ GET_NATIVE_VIEW(env, obj)->setExtendSelection();
+}
+
+static jboolean nativeStartSelection(JNIEnv *env, jobject obj, int x, int y)
+{
+ return GET_NATIVE_VIEW(env, obj)->startSelection(x, y);
+}
+
+static jboolean nativeWordSelection(JNIEnv *env, jobject obj, int x, int y)
+{
+ return GET_NATIVE_VIEW(env, obj)->wordSelection(x, y);
+}
+
+static void nativeExtendSelection(JNIEnv *env, jobject obj, int x, int y)
+{
+ GET_NATIVE_VIEW(env, obj)->extendSelection(x, y);
}
static jobject nativeGetSelection(JNIEnv *env, jobject obj)
@@ -1820,15 +1915,25 @@ static jobject nativeGetSelection(JNIEnv *env, jobject obj)
return env->NewString((jchar*)selection.characters(), selection.length());
}
-static void nativeSetSelectionPointer(JNIEnv *env, jobject obj, jboolean set,
- jfloat scale, jint x, jint y, bool ex)
+static jboolean nativeHitSelection(JNIEnv *env, jobject obj, int x, int y)
+{
+ return GET_NATIVE_VIEW(env, obj)->hitSelection(x, y);
+}
+
+static jint nativeSelectionX(JNIEnv *env, jobject obj)
{
- GET_NATIVE_VIEW(env, obj)->setSelectionPointer(set, scale, x, y, ex);
+ return GET_NATIVE_VIEW(env, obj)->selectionX();
}
-static void nativeSetSelectionRegion(JNIEnv *env, jobject obj, jboolean set)
+static jint nativeSelectionY(JNIEnv *env, jobject obj)
+{
+ return GET_NATIVE_VIEW(env, obj)->selectionY();
+}
+
+static void nativeSetSelectionPointer(JNIEnv *env, jobject obj, jboolean set,
+ jfloat scale, jint x, jint y)
{
- GET_NATIVE_VIEW(env, obj)->setSelectionRegion(set);
+ GET_NATIVE_VIEW(env, obj)->setSelectionPointer(set, scale, x, y);
}
#ifdef ANDROID_DUMP_DISPLAY_TREE
@@ -1935,10 +2040,14 @@ static JNINativeMethod gJavaWebViewMethods[] = {
(void*) nativeDumpDisplayTree },
{ "nativeEvaluateLayersAnimations", "()Z",
(void*) nativeEvaluateLayersAnimations },
+ { "nativeExtendSelection", "(II)V",
+ (void*) nativeExtendSelection },
{ "nativeFindAll", "(Ljava/lang/String;Ljava/lang/String;)I",
(void*) nativeFindAll },
{ "nativeFindNext", "(Z)V",
(void*) nativeFindNext },
+ { "nativeFindIndex", "()I",
+ (void*) nativeFindIndex},
{ "nativeFocusCandidateFramePointer", "()I",
(void*) nativeFocusCandidateFramePointer },
{ "nativeFocusCandidateHasNextTextfield", "()Z",
@@ -1979,6 +2088,8 @@ static JNINativeMethod gJavaWebViewMethods[] = {
(void*) nativeHasFocusNode },
{ "nativeHideCursor", "()V",
(void*) nativeHideCursor },
+ { "nativeHitSelection", "(II)Z",
+ (void*) nativeHitSelection },
{ "nativeImageURI", "(II)Ljava/lang/String;",
(void*) nativeImageURI },
{ "nativeInstrumentReport", "()V",
@@ -1991,14 +2102,24 @@ static JNINativeMethod gJavaWebViewMethods[] = {
(void*) nativeMoveCursorToNextTextInput },
{ "nativeMoveGeneration", "()I",
(void*) nativeMoveGeneration },
- { "nativeMoveSelection", "(IIZ)V",
+ { "nativeMoveSelection", "(II)V",
(void*) nativeMoveSelection },
{ "nativePointInNavCache", "(III)Z",
(void*) nativePointInNavCache },
{ "nativeRecordButtons", "(ZZZ)V",
(void*) nativeRecordButtons },
+ { "nativeResetSelection", "()V",
+ (void*) nativeResetSelection },
+ { "nativeSelectAll", "()V",
+ (void*) nativeSelectAll },
{ "nativeSelectBestAt", "(Landroid/graphics/Rect;)V",
(void*) nativeSelectBestAt },
+ { "nativeSelectionX", "()I",
+ (void*) nativeSelectionX },
+ { "nativeSelectionY", "()I",
+ (void*) nativeSelectionY },
+ { "nativeSetExtendSelection", "()V",
+ (void*) nativeSetExtendSelection },
{ "nativeSetFindIsEmpty", "()V",
(void*) nativeSetFindIsEmpty },
{ "nativeSetFindIsUp", "(Z)V",
@@ -2009,16 +2130,18 @@ static JNINativeMethod gJavaWebViewMethods[] = {
(void*) nativeSetHeightCanMeasure },
{ "nativeSetRootLayer", "(I)V",
(void*) nativeSetRootLayer },
- { "nativeSetSelectionPointer", "(ZFIIZ)V",
+ { "nativeSetSelectionPointer", "(ZFII)V",
(void*) nativeSetSelectionPointer },
- { "nativeSetSelectionRegion", "(Z)V",
- (void*) nativeSetSelectionRegion },
+ { "nativeStartSelection", "(II)Z",
+ (void*) nativeStartSelection },
{ "nativeSubtractLayers", "(Landroid/graphics/Rect;)Landroid/graphics/Rect;",
(void*) nativeSubtractLayers },
{ "nativeTextGeneration", "()I",
(void*) nativeTextGeneration },
{ "nativeUpdateCachedTextfield", "(Ljava/lang/String;I)V",
(void*) nativeUpdateCachedTextfield },
+ { "nativeWordSelection", "(II)Z",
+ (void*) nativeWordSelection },
{ "nativeGetBlockLeftEdge", "(IIF)I",
(void*) nativeGetBlockLeftEdge },
};