summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDan Sinclair <dsinclair@chromium.org>2017-09-20 11:17:52 -0400
committerChromium commit bot <commit-bot@chromium.org>2017-09-20 16:10:03 +0000
commitd56fd77ef0b2e2a14ceb127283ac0e7cf7ca090b (patch)
tree65728013e4f43b8ce2728cbbf8038b8707e08739
parentbc48185643b3983fd9620ba2f987ada1478fbdc2 (diff)
downloadpdfium-d56fd77ef0b2e2a14ceb127283ac0e7cf7ca090b.tar.xz
Implement CFDE_TextEditEngine::GetIndex* methods.
This CL adds unittests and implementations for the text edit engine methods to get various indexes based on cursor position. The |RebuildPieces| method was fixed to correctly keep track of character position when dealing with BIDI characters. Change-Id: Ie3c5ee5d63bfd00f6f0cdcb1c6fcfe6e05bba50e Reviewed-on: https://pdfium-review.googlesource.com/14430 Commit-Queue: dsinclair <dsinclair@chromium.org> Reviewed-by: Ryan Harrison <rharrison@chromium.org>
-rw-r--r--xfa/fde/cfde_texteditengine.cpp140
-rw-r--r--xfa/fde/cfde_texteditengine.h16
-rw-r--r--xfa/fde/cfde_texteditengine_unittest.cpp123
3 files changed, 268 insertions, 11 deletions
diff --git a/xfa/fde/cfde_texteditengine.cpp b/xfa/fde/cfde_texteditengine.cpp
index c619a984d2..143bac49f9 100644
--- a/xfa/fde/cfde_texteditengine.cpp
+++ b/xfa/fde/cfde_texteditengine.cpp
@@ -394,10 +394,145 @@ void CFDE_TextEditEngine::ClearOperationRecords() {
next_operation_index_to_insert_ = 0;
}
+size_t CFDE_TextEditEngine::GetIndexBefore(size_t pos) {
+ int32_t bidi_level;
+ CFX_RectF rect;
+ // Possible |Layout| triggered by |GetCharacterInfo|.
+ std::tie(bidi_level, rect) = GetCharacterInfo(pos);
+ return FX_IsOdd(bidi_level) ? GetIndexRight(pos) : GetIndexLeft(pos);
+}
+
+size_t CFDE_TextEditEngine::GetIndexLeft(size_t pos) const {
+ if (pos == 0)
+ return 0;
+ --pos;
+
+ wchar_t ch = GetChar(pos);
+ while (pos != 0) {
+ // We want to be on the location just before the \r or \n
+ ch = GetChar(pos - 1);
+ if (ch != '\r' && ch != '\n')
+ break;
+
+ --pos;
+ }
+ return pos;
+}
+
+size_t CFDE_TextEditEngine::GetIndexRight(size_t pos) const {
+ if (pos >= text_length_)
+ return text_length_;
+ ++pos;
+
+ wchar_t ch = GetChar(pos);
+ // We want to be on the location after the \r\n.
+ while (pos < text_length_ && (ch == '\r' || ch == '\n')) {
+ ++pos;
+ ch = GetChar(pos);
+ }
+
+ return pos;
+}
+
+size_t CFDE_TextEditEngine::GetIndexUp(size_t pos) const {
+ size_t line_start = GetIndexAtStartOfLine(pos);
+ if (line_start == 0)
+ return pos;
+
+ // Determine how far along the line we were.
+ size_t dist = pos - line_start;
+
+ // Move to the end of the preceding line.
+ wchar_t ch;
+ do {
+ --line_start;
+ ch = GetChar(line_start);
+ } while (line_start != 0 && (ch == '\r' || ch == '\n'));
+
+ if (line_start == 0)
+ return dist;
+
+ // Get the start of the line prior to the current line.
+ size_t prior_start = GetIndexAtStartOfLine(line_start);
+
+ // Prior line is shorter then next line, and we're past the end of that line
+ // return the end of line.
+ if (prior_start + dist > line_start)
+ return GetIndexAtEndOfLine(line_start);
+
+ return prior_start + dist;
+}
+
+size_t CFDE_TextEditEngine::GetIndexDown(size_t pos) const {
+ size_t line_end = GetIndexAtEndOfLine(pos);
+ if (line_end == text_length_)
+ return pos;
+
+ wchar_t ch;
+ do {
+ ++line_end;
+ ch = GetChar(line_end);
+ } while (line_end < text_length_ && (ch == '\r' || ch == '\n'));
+
+ if (line_end == text_length_)
+ return line_end;
+
+ // Determine how far along the line we are.
+ size_t dist = pos - GetIndexAtStartOfLine(pos);
+
+ // Check if next line is shorter then current line. If so, return end
+ // of next line.
+ size_t next_line_end = GetIndexAtEndOfLine(line_end);
+ if (line_end + dist > next_line_end)
+ return next_line_end;
+
+ return line_end + dist;
+}
+
+size_t CFDE_TextEditEngine::GetIndexAtStartOfLine(size_t pos) const {
+ if (pos == 0)
+ return 0;
+
+ wchar_t ch = GetChar(pos);
+ // What to do.
+ if (ch == '\r' || ch == '\n')
+ return pos;
+
+ do {
+ // We want to be on the location just after the \r\n
+ ch = GetChar(pos - 1);
+ if (ch == '\r' || ch == '\n')
+ break;
+
+ --pos;
+ } while (pos > 0);
+
+ return pos;
+}
+
+size_t CFDE_TextEditEngine::GetIndexAtEndOfLine(size_t pos) const {
+ if (pos >= text_length_)
+ return text_length_;
+
+ wchar_t ch = GetChar(pos);
+ // Not quite sure which way to go here?
+ if (ch == '\r' || ch == '\n')
+ return pos;
+
+ // We want to be on the location of the first \r or \n.
+ do {
+ ++pos;
+ ch = GetChar(pos);
+ } while (pos < text_length_ && (ch != '\r' && ch != '\n'));
+
+ return pos;
+}
+
void CFDE_TextEditEngine::LimitHorizontalScroll(bool val) {
ClearOperationRecords();
limit_horizontal_area_ = val;
}
+
void CFDE_TextEditEngine::LimitVerticalScroll(bool val) {
ClearOperationRecords();
limit_vertical_area_ = val;
@@ -838,6 +973,8 @@ void CFDE_TextEditEngine::RebuildPieces() {
bool initialized_bounding_box = false;
contents_bounding_box_ = CFX_RectF();
+ size_t current_piece_start = 0;
+ float current_line_start = 0;
auto iter = pdfium::MakeUnique<CFDE_TextEditEngine::Iterator>(this);
while (!iter->IsEOF(false)) {
@@ -850,9 +987,6 @@ void CFDE_TextEditEngine::RebuildPieces() {
if (CFX_BreakTypeNoneOrPiece(break_status))
continue;
-
- size_t current_piece_start = 0;
- float current_line_start = 0;
int32_t piece_count = text_break_.CountBreakPieces();
for (int32_t i = 0; i < piece_count; ++i) {
const CFX_BreakPiece* piece = text_break_.GetBreakPieceUnstable(i);
diff --git a/xfa/fde/cfde_texteditengine.h b/xfa/fde/cfde_texteditengine.h
index e26487aaf6..dbb12f3953 100644
--- a/xfa/fde/cfde_texteditengine.h
+++ b/xfa/fde/cfde_texteditengine.h
@@ -129,14 +129,14 @@ class CFDE_TextEditEngine {
bool Undo();
void ClearOperationRecords();
- // TODO(dsinclair): Implement ....
- size_t GetIndexBefore(size_t pos) { return 0; }
- size_t GetIndexLeft(size_t pos) { return 0; }
- size_t GetIndexRight(size_t pos) { return 0; }
- size_t GetIndexUp(size_t pos) { return 0; }
- size_t GetIndexDown(size_t pos) { return 0; }
- size_t GetIndexAtStartOfLine(size_t pos) { return 0; }
- size_t GetIndexAtEndOfLine(size_t pos) { return 0; }
+ // This is not const it can trigger a |Layout|.
+ size_t GetIndexBefore(size_t pos);
+ size_t GetIndexLeft(size_t pos) const;
+ size_t GetIndexRight(size_t pos) const;
+ size_t GetIndexUp(size_t pos) const;
+ size_t GetIndexDown(size_t pos) const;
+ size_t GetIndexAtStartOfLine(size_t pos) const;
+ size_t GetIndexAtEndOfLine(size_t pos) const;
void SelectAll();
void SetSelection(size_t start_idx, size_t count);
diff --git a/xfa/fde/cfde_texteditengine_unittest.cpp b/xfa/fde/cfde_texteditengine_unittest.cpp
index 51940f21a8..b0a27f331c 100644
--- a/xfa/fde/cfde_texteditengine_unittest.cpp
+++ b/xfa/fde/cfde_texteditengine_unittest.cpp
@@ -571,3 +571,126 @@ TEST_F(CFDE_TextEditEngineTest, BoundsForWordAt) {
}
}
}
+
+TEST_F(CFDE_TextEditEngineTest, CursorMovement) {
+ engine()->Clear();
+ engine()->Insert(0, L"Hello");
+
+ EXPECT_EQ(0U, engine()->GetIndexLeft(0));
+ EXPECT_EQ(5U, engine()->GetIndexRight(5));
+ EXPECT_EQ(2U, engine()->GetIndexUp(2));
+ EXPECT_EQ(2U, engine()->GetIndexDown(2));
+ EXPECT_EQ(1U, engine()->GetIndexLeft(2));
+ EXPECT_EQ(1U, engine()->GetIndexBefore(2));
+ EXPECT_EQ(3U, engine()->GetIndexRight(2));
+ EXPECT_EQ(0U, engine()->GetIndexAtStartOfLine(2));
+ EXPECT_EQ(5U, engine()->GetIndexAtEndOfLine(2));
+
+ engine()->Clear();
+ engine()->Insert(0, L"The book is \"مدخل إلى C++\"");
+ EXPECT_EQ(2U, engine()->GetIndexBefore(3)); // Before is to left.
+ EXPECT_EQ(16U, engine()->GetIndexBefore(15)); // Before is to right.
+ EXPECT_EQ(22U, engine()->GetIndexBefore(23)); // Before is to left.
+
+ engine()->Clear();
+ engine()->Insert(0, L"Hello\r\nWorld\r\nTest");
+ // Move to end of Hello from start of World.
+ engine()->SetSelection(engine()->GetIndexBefore(7U), 7);
+ EXPECT_STREQ(L"\r\nWorld", engine()->GetSelectedText().c_str());
+
+ // Second letter in Hello from second letter in World.
+ engine()->SetSelection(engine()->GetIndexUp(8U), 2);
+ EXPECT_STREQ(L"el", engine()->GetSelectedText().c_str());
+
+ // Second letter in World from second letter in Test.
+ engine()->SetSelection(engine()->GetIndexUp(15U), 2);
+ EXPECT_STREQ(L"or", engine()->GetSelectedText().c_str());
+
+ // Second letter in World from second letter in Hello.
+ engine()->SetSelection(engine()->GetIndexDown(1U), 2);
+ EXPECT_STREQ(L"or", engine()->GetSelectedText().c_str());
+
+ // Second letter in Test from second letter in World.
+ engine()->SetSelection(engine()->GetIndexDown(8U), 2);
+ EXPECT_STREQ(L"es", engine()->GetSelectedText().c_str());
+
+ size_t start_idx = engine()->GetIndexAtStartOfLine(8U);
+ size_t end_idx = engine()->GetIndexAtEndOfLine(8U);
+ engine()->SetSelection(start_idx, end_idx - start_idx);
+ EXPECT_STREQ(L"World", engine()->GetSelectedText().c_str());
+
+ // Move past \r\n to before W.
+ engine()->SetSelection(engine()->GetIndexRight(5U), 5);
+ EXPECT_STREQ(L"World", engine()->GetSelectedText().c_str());
+
+ engine()->Clear();
+ engine()->Insert(0, L"Short\nAnd a very long line");
+ engine()->SetSelection(engine()->GetIndexUp(14U), 11);
+ EXPECT_STREQ(L"\nAnd a very", engine()->GetSelectedText().c_str());
+
+ engine()->Clear();
+ engine()->Insert(0, L"A Very long line\nShort");
+ EXPECT_EQ(engine()->GetLength(), engine()->GetIndexDown(8U));
+
+ engine()->Clear();
+ engine()->Insert(0, L"Hello\rWorld\rTest");
+ // Move to end of Hello from start of World.
+ engine()->SetSelection(engine()->GetIndexBefore(6U), 6);
+ EXPECT_STREQ(L"\rWorld", engine()->GetSelectedText().c_str());
+
+ // Second letter in Hello from second letter in World.
+ engine()->SetSelection(engine()->GetIndexUp(7U), 2);
+ EXPECT_STREQ(L"el", engine()->GetSelectedText().c_str());
+
+ // Second letter in World from second letter in Test.
+ engine()->SetSelection(engine()->GetIndexUp(13U), 2);
+ EXPECT_STREQ(L"or", engine()->GetSelectedText().c_str());
+
+ // Second letter in World from second letter in Hello.
+ engine()->SetSelection(engine()->GetIndexDown(1U), 2);
+ EXPECT_STREQ(L"or", engine()->GetSelectedText().c_str());
+
+ // Second letter in Test from second letter in World.
+ engine()->SetSelection(engine()->GetIndexDown(7U), 2);
+ EXPECT_STREQ(L"es", engine()->GetSelectedText().c_str());
+
+ start_idx = engine()->GetIndexAtStartOfLine(7U);
+ end_idx = engine()->GetIndexAtEndOfLine(7U);
+ engine()->SetSelection(start_idx, end_idx - start_idx);
+ EXPECT_STREQ(L"World", engine()->GetSelectedText().c_str());
+
+ // Move past \r to before W.
+ engine()->SetSelection(engine()->GetIndexRight(5U), 5);
+ EXPECT_STREQ(L"World", engine()->GetSelectedText().c_str());
+
+ engine()->Clear();
+ engine()->Insert(0, L"Hello\nWorld\nTest");
+ // Move to end of Hello from start of World.
+ engine()->SetSelection(engine()->GetIndexBefore(6U), 6);
+ EXPECT_STREQ(L"\nWorld", engine()->GetSelectedText().c_str());
+
+ // Second letter in Hello from second letter in World.
+ engine()->SetSelection(engine()->GetIndexUp(7U), 2);
+ EXPECT_STREQ(L"el", engine()->GetSelectedText().c_str());
+
+ // Second letter in World from second letter in Test.
+ engine()->SetSelection(engine()->GetIndexUp(13U), 2);
+ EXPECT_STREQ(L"or", engine()->GetSelectedText().c_str());
+
+ // Second letter in World from second letter in Hello.
+ engine()->SetSelection(engine()->GetIndexDown(1U), 2);
+ EXPECT_STREQ(L"or", engine()->GetSelectedText().c_str());
+
+ // Second letter in Test from second letter in World.
+ engine()->SetSelection(engine()->GetIndexDown(7U), 2);
+ EXPECT_STREQ(L"es", engine()->GetSelectedText().c_str());
+
+ start_idx = engine()->GetIndexAtStartOfLine(7U);
+ end_idx = engine()->GetIndexAtEndOfLine(7U);
+ engine()->SetSelection(start_idx, end_idx - start_idx);
+ EXPECT_STREQ(L"World", engine()->GetSelectedText().c_str());
+
+ // Move past \r to before W.
+ engine()->SetSelection(engine()->GetIndexRight(5U), 5);
+ EXPECT_STREQ(L"World", engine()->GetSelectedText().c_str());
+}