summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--WebKit/android/nav/SelectText.cpp905
-rw-r--r--WebKit/android/nav/SelectText.h50
-rw-r--r--WebKit/android/nav/WebView.cpp155
3 files changed, 937 insertions, 173 deletions
diff --git a/WebKit/android/nav/SelectText.cpp b/WebKit/android/nav/SelectText.cpp
index 29d47e5..011349e 100644
--- a/WebKit/android/nav/SelectText.cpp
+++ b/WebKit/android/nav/SelectText.cpp
@@ -23,7 +23,7 @@
* 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"
@@ -46,6 +46,9 @@
#include <wtf/text/CString.h>
#endif
+#define VERBOSE_LOGGING 0
+// #define EXTRA_NOISY_LOGGING 1
+
// TextRunIterator has been copied verbatim from GraphicsContext.cpp
namespace WebCore {
@@ -137,19 +140,56 @@ void ReverseBidi(UChar* chars, int len) {
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 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;
@@ -169,7 +209,147 @@ 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);
+ return lineSpacing >= SkIntToFixed((bottom() - top()) * 2);
+ }
+
+ bool addSpace(const SkBounder::GlyphRec& rec)
+ {
+ bool newBaseLine = mLastGlyph.fLSB.fY != rec.fLSB.fY;
+ if (((mLastUni >= HYPHEN && mLastUni <= HORZ_BAR)
+ || mLastUni == HYPHEN_MINUS) && newBaseLine)
+ {
+ return false;
+ }
+ return isSpace(rec);
+ }
+
+ void finish()
+ {
+ 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/2 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 / 2;
+ }
+
+ 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;
@@ -180,70 +360,235 @@ 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)
+ {
+ 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;
+ mBestBounds.fBottom, topDebug(), bottomDebug());
+ return mBestBounds;
}
-
- void offsetBounds(int dx, int dy) {
- mBestBounds.offset(dx, dy);
- }
-
- 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;
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:
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()
+ {
+ 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;
+ 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()
+ {
+ return mBestBounds;
+ }
+
+ virtual bool onIRect(const SkIRect& rect)
+ {
+ if (top() > mBestBounds.fTop)
+ return false;
+ if (top() < mBestBounds.fTop || rect.fLeft < mBestBounds.fLeft)
+ mBestBounds.set(rect.fLeft, top(), rect.fRight, bottom());
+ return false;
+ }
+
+protected:
+ SkIRect mBestBounds;
+private:
+ typedef CommonCheck INHERITED;
+};
+
+class FindLast : public FindFirst {
+public:
+ FindLast(int width, int height)
+ : INHERITED(width, height)
+ {
+ mBestBounds.setEmpty();
+ }
+
+ virtual bool onIRect(const SkIRect& rect)
+ {
+ if (bottom() < mBestBounds.fBottom)
+ return false;
+ if (bottom() > mBestBounds.fBottom || rect.fRight > mBestBounds.fRight)
+ mBestBounds.set(rect.fLeft, top(), rect.fRight, bottom());
+ return false;
+ }
+
+private:
+ typedef FindFirst INHERITED;
};
class MultilineBuilder : public CommonCheck {
public:
- MultilineBuilder(const SkIRect& start, const SkIRect& end, int dx, int dy,
- SkRegion* region)
- : mStart(start), mEnd(end), mSelectRegion(region), mCapture(false) {
+ MultilineBuilder(const SkIRect& start, const SkIRect& end,
+ const SkIRect& area, SkRegion* region)
+ : INHERITED(area.width(),area.height())
+ , mStart(start)
+ , mEnd(end)
+ , mSelectRegion(region)
+ , mCapture(false)
+ {
mLast.setEmpty();
mLastBase = INT_MAX;
- mStart.offset(-dx, -dy);
- mEnd.offset(-dx, -dy);
+ mStart.offset(-area.fLeft, -area.fTop);
+ mEnd.offset(-area.fLeft, -area.fTop);
}
virtual bool onIRect(const SkIRect& rect) {
@@ -266,7 +611,7 @@ public:
full.fRight = mLast.fLeft;
}
mSelectRegion->op(full, SkRegion::kUnion_Op);
- DBG_NAV_LOGD("MultilineBuilder full=(%d,%d,r=%d,b=%d)",
+ if (VERBOSE_LOGGING) DBG_NAV_LOGD("MultilineBuilder full=(%d,%d,r=%d,b=%d)",
full.fLeft, full.fTop, full.fRight, full.fBottom);
mLast = full;
mLastBase = base();
@@ -282,25 +627,17 @@ protected:
int mLastBase;
SkRegion* mSelectRegion;
bool mCapture;
+private:
+ typedef CommonCheck 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
-
class TextExtractor : public CommonCheck {
public:
- TextExtractor(const SkRegion& region) : mSelectRegion(region),
- mSkipFirstSpace(true) { // don't start with a space
- }
-
- 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);
+ TextExtractor(const SkRegion& region, const SkIRect& area)
+ : INHERITED(area.width(), area.height())
+ , mSelectRegion(region)
+ , mSkipFirstSpace(true) // don't start with a space
+ {
}
virtual bool onIRectGlyph(const SkIRect& rect,
@@ -309,34 +646,23 @@ 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() = ' ';
+ 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;
- 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);
+ recordGlyph(rec);
+ finish();
+ DBG_NAV_LOGD("TextExtractor glyphID=%d uni=%d '%c'"
+ " append full=(%d,%d,r=%d,b=%d)", rec.fGlyphID,
+ mLastUni, mLastUni && mLastUni < 0x7f ? mLastUni : '?',
+ full.fLeft, full.fTop, full.fRight, full.fBottom);
if (mLastUni) {
uint16_t chars[2];
size_t count = SkUTF16_FromUnichar(mLastUni, chars);
@@ -344,8 +670,6 @@ public:
if (count == 2)
*mSelectText.append() = chars[1];
}
- mLast = full;
- mLastGlyph = rec;
} else {
mSkipFirstSpace = true;
DBG_NAV_LOGD("TextExtractor [%02x] skip full=(%d,%d,r=%d,b=%d)",
@@ -374,10 +698,6 @@ public:
protected:
const SkRegion& mSelectRegion;
SkTDArray<uint16_t> mSelectText;
- SkIRect mLast;
- SkBounder::GlyphRec mLastGlyph;
- SkUnichar mLastUni;
- SkFixed mMinSpaceWidth;
bool mSkipFirstSpace;
private:
typedef CommonCheck INHERITED;
@@ -386,7 +706,7 @@ private:
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;
@@ -444,49 +764,193 @@ public:
CommonCheck& mBounder;
};
-void CopyPaste::buildSelection(const SkPicture& picture, const SkIRect& area,
- const SkIRect& selStart, const SkIRect& selEnd, SkRegion* region) {
+static void buildSelection(const SkPicture& picture, const SkIRect& area,
+ const SkIRect& selStart, const SkIRect& selEnd, 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, selEnd, area, region);
+ TextCanvas checker(&builder, area);
checker.drawPicture(const_cast<SkPicture&>(picture));
region->translate(area.fLeft, area.fTop);
}
-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)
+{
+ 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.finish();
+ return _check.adjustedBounds(area);
+}
+
+static SkIRect findEdge(const SkPicture& picture, const SkIRect& area,
+ int x, int y, bool left)
+{
+ SkIRect result;
+ result.setEmpty();
+ FirstCheck center(x, y, area);
+ center.setRecordGlyph();
+ SkIRect closest = findClosest(center, picture, area);
+ 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.finish();
+ if (!edge.adjacent()) {
+ DBG_NAV_LOG("adjacent break");
+ break;
+ }
+ const SkIRect& next = edge.bestBounds();
+ if (next.isEmpty()) {
+ DBG_NAV_LOG("empty");
+ break;
+ }
+ if (result == next) {
+ DBG_NAV_LOG("result == next");
+ break;
+ }
+ result = next;
+ edge.shiftStart(result);
+ } while (true);
+ if (!result.isEmpty())
+ result.offset(area.fLeft, area.fTop);
+ return result;
+}
+
+static SkIRect findFirst(const SkPicture& picture)
+{
+ 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();
+}
+
+static SkIRect findLast(const SkPicture& picture)
+{
+ 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));
- _check.offsetBounds(area.fLeft, area.fTop);
- return _check.bestBounds();
+ return finder.bestBounds();
+}
+
+static SkIRect findLeft(const SkPicture& picture, const SkIRect& area,
+ int x, int y)
+{
+ return findEdge(picture, area, x, y, true);
+}
+
+static SkIRect findRight(const SkPicture& picture, const SkIRect& area,
+ int x, int y)
+{
+ return findEdge(picture, area, x, y, false);
}
-WebCore::String CopyPaste::text(const SkPicture& picture, const SkIRect& area,
- const SkRegion& region) {
+static WebCore::String 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);
+ TextExtractor extractor(copy, area);
+ 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);
@@ -510,7 +974,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);
@@ -528,19 +992,81 @@ 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);
+ buildSelection(*m_picture, ivisBounds, m_selStart, m_selEnd, &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;
+ 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);
+ 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);
+ 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_selStart = found;
+ else
+ m_selEnd = found;
+ swapAsNeeded();
}
const String SelectText::getSelection()
{
- String result = CopyPaste::text(*m_picture, m_visibleRect, m_selRegion);
+ SkIRect clipRect = m_selRegion.getBounds();
+ DBG_NAV_LOGD("clip=(%d,%d,%d,%d)", clipRect.fLeft,
+ clipRect.fTop, clipRect.fRight, clipRect.fBottom);
+ String result = text(*m_picture, clipRect, m_selRegion);
DBG_NAV_LOGD("text=%s", result.latin1().data()); // uses CString
return result;
}
@@ -551,35 +1077,162 @@ 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
{
- if (!extendSelection)
+ 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)
+{
+ 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);
+ SkIRect found = findClosest(center, *picture, clipRect);
+ if (m_hitTopLeft || !m_extendSelection)
+ m_selStart = found;
+ if (!m_hitTopLeft || !m_extendSelection)
+ 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_selEnd = findLast(*picture);
+ 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;
+ SkIRect left = findLeft(*picture, clipRect, x, y);
+ if (!left.isEmpty())
+ 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);
+ if (!right.isEmpty())
+ 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.fBottom
+ || (m_selStart.fBottom > m_selEnd.fTop
+ && m_selStart.fRight > m_selEnd.fLeft))
+ {
+ 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 2e17a74..00b9ca9 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,45 +39,46 @@ 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;
+ 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_hitTopLeft;
+ bool m_startSelection;
};
}
diff --git a/WebKit/android/nav/WebView.cpp b/WebKit/android/nav/WebView.cpp
index b3d42c5..9dbefd9 100644
--- a/WebKit/android/nav/WebView.cpp
+++ b/WebKit/android/nav/WebView.cpp
@@ -947,7 +947,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)
@@ -958,26 +958,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);
@@ -1834,11 +1889,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)
@@ -1849,15 +1932,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
@@ -1966,6 +2059,8 @@ 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",
@@ -2012,6 +2107,8 @@ static JNINativeMethod gJavaWebViewMethods[] = {
(void*) nativeHasFocusNode },
{ "nativeHideCursor", "()V",
(void*) nativeHideCursor },
+ { "nativeHitSelection", "(II)Z",
+ (void*) nativeHitSelection },
{ "nativeImageURI", "(II)Ljava/lang/String;",
(void*) nativeImageURI },
{ "nativeInstrumentReport", "()V",
@@ -2024,14 +2121,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",
@@ -2042,16 +2149,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 },
};