From 6af5369477ec05554ef9e73ae6762860095f09e9 Mon Sep 17 00:00:00 2001 From: Dan Sinclair Date: Tue, 29 May 2018 19:42:39 +0000 Subject: [xfa] Propagate the xfa change data for text to JS and back. This CL adds the necessary plumbing to propagate the change information for a text widget from FWL out to JS and handle the returned value as necessary. Bug: pdfium:1066 Change-Id: I78fd81761b90294f1836e9f09dba12ed238963cc Reviewed-on: https://pdfium-review.googlesource.com/33070 Reviewed-by: Ryan Harrison Commit-Queue: dsinclair --- BUILD.gn | 4 +- fxjs/cfxjse_engine.h | 7 ++- fxjs/cfxjse_formcalc_context_embeddertest.cpp | 91 +++++++++++++++------------ xfa/fde/cfde_texteditengine.cpp | 85 ++++++++++++++++++++----- xfa/fde/cfde_texteditengine.h | 16 +++-- xfa/fde/cfde_texteditengine_unittest.cpp | 3 +- xfa/fwl/README.md | 2 +- xfa/fwl/cfwl_barcode.cpp | 7 ++- xfa/fwl/cfwl_barcode.h | 5 +- xfa/fwl/cfwl_combobox.cpp | 3 +- xfa/fwl/cfwl_edit.cpp | 29 ++++++--- xfa/fwl/cfwl_edit.h | 7 ++- xfa/fwl/cfwl_event.h | 2 +- xfa/fwl/cfwl_eventtextchanged.cpp | 12 ---- xfa/fwl/cfwl_eventtextchanged.h | 20 ------ xfa/fwl/cfwl_eventtextwillchange.cpp | 12 ++++ xfa/fwl/cfwl_eventtextwillchange.h | 24 +++++++ xfa/fxfa/cxfa_eventparam.cpp | 2 +- xfa/fxfa/cxfa_fftextedit.cpp | 48 +++++++------- xfa/fxfa/cxfa_fftextedit.h | 5 +- xfa/fxfa/parser/cxfa_node.cpp | 3 +- 21 files changed, 239 insertions(+), 148 deletions(-) delete mode 100644 xfa/fwl/cfwl_eventtextchanged.cpp delete mode 100644 xfa/fwl/cfwl_eventtextchanged.h create mode 100644 xfa/fwl/cfwl_eventtextwillchange.cpp create mode 100644 xfa/fwl/cfwl_eventtextwillchange.h diff --git a/BUILD.gn b/BUILD.gn index e9f21e0311..5bba3fe352 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -1908,8 +1908,8 @@ if (pdf_enable_xfa) { "xfa/fwl/cfwl_eventselectchanged.h", "xfa/fwl/cfwl_eventtarget.cpp", "xfa/fwl/cfwl_eventtarget.h", - "xfa/fwl/cfwl_eventtextchanged.cpp", - "xfa/fwl/cfwl_eventtextchanged.h", + "xfa/fwl/cfwl_eventtextwillchange.cpp", + "xfa/fwl/cfwl_eventtextwillchange.h", "xfa/fwl/cfwl_eventvalidate.cpp", "xfa/fwl/cfwl_eventvalidate.h", "xfa/fwl/cfwl_form.cpp", diff --git a/fxjs/cfxjse_engine.h b/fxjs/cfxjse_engine.h index ca5f3ee098..33723ad8a9 100644 --- a/fxjs/cfxjse_engine.h +++ b/fxjs/cfxjse_engine.h @@ -11,6 +11,7 @@ #include #include +#include "core/fxcrt/unowned_ptr.h" #include "fxjs/cfx_v8.h" #include "fxjs/cfxjse_formcalc_context.h" #include "v8/include/v8.h" @@ -54,8 +55,8 @@ class CFXJSE_Engine : public CFX_V8 { CFXJSE_Engine(CXFA_Document* pDocument, CFXJS_Engine* fxjs_engine); ~CFXJSE_Engine() override; - void SetEventParam(CXFA_EventParam param) { m_eventParam = param; } - CXFA_EventParam* GetEventParam() { return &m_eventParam; } + void SetEventParam(CXFA_EventParam* param) { m_eventParam = param; } + CXFA_EventParam* GetEventParam() { return m_eventParam.Get(); } bool RunScript(CXFA_Script::Type eScriptType, const WideStringView& wsScript, CFXJSE_Value* pRetValue, @@ -113,7 +114,7 @@ class CFXJSE_Engine : public CFX_V8 { std::map> m_mapObjectToValue; std::map> m_mapVariableToContext; - CXFA_EventParam m_eventParam; + UnownedPtr m_eventParam; std::vector m_upObjectArray; // CacheList holds the List items so we can clean them up when we're done. std::vector> m_CacheList; diff --git a/fxjs/cfxjse_formcalc_context_embeddertest.cpp b/fxjs/cfxjse_formcalc_context_embeddertest.cpp index ed0c5bccc7..a227ac558d 100644 --- a/fxjs/cfxjse_formcalc_context_embeddertest.cpp +++ b/fxjs/cfxjse_formcalc_context_embeddertest.cpp @@ -1465,7 +1465,7 @@ TEST_F(CFXJSE_FormCalcContextEmbedderTest, GetXFAEventChange) { params.m_wsChange = L"changed"; CFXJSE_Engine* context = GetScriptContext(); - context->SetEventParam(params); + context->SetEventParam(¶ms); const char test[] = {"xfa.event.change"}; EXPECT_TRUE(Execute(test)); @@ -1473,6 +1473,7 @@ TEST_F(CFXJSE_FormCalcContextEmbedderTest, GetXFAEventChange) { CFXJSE_Value* value = GetValue(); EXPECT_TRUE(value->IsString()); EXPECT_STREQ("changed", value->ToString().c_str()); + context->SetEventParam(nullptr); } TEST_F(CFXJSE_FormCalcContextEmbedderTest, SetXFAEventChange) { @@ -1480,11 +1481,12 @@ TEST_F(CFXJSE_FormCalcContextEmbedderTest, SetXFAEventChange) { CXFA_EventParam params; CFXJSE_Engine* context = GetScriptContext(); - context->SetEventParam(params); + context->SetEventParam(¶ms); const char test[] = {"xfa.event.change = \"changed\""}; EXPECT_TRUE(Execute(test)); - EXPECT_EQ(L"changed", context->GetEventParam()->m_wsChange); + EXPECT_EQ(L"changed", params.m_wsChange); + context->SetEventParam(nullptr); } TEST_F(CFXJSE_FormCalcContextEmbedderTest, SetXFAEventFullTextFails) { @@ -1494,11 +1496,12 @@ TEST_F(CFXJSE_FormCalcContextEmbedderTest, SetXFAEventFullTextFails) { params.m_wsFullText = L"Original Full Text"; CFXJSE_Engine* context = GetScriptContext(); - context->SetEventParam(params); + context->SetEventParam(¶ms); const char test[] = {"xfa.event.fullText = \"Changed Full Text\""}; EXPECT_TRUE(Execute(test)); - EXPECT_EQ(L"Original Full Text", context->GetEventParam()->m_wsFullText); + EXPECT_EQ(L"Original Full Text", params.m_wsFullText); + context->SetEventParam(nullptr); } TEST_F(CFXJSE_FormCalcContextEmbedderTest, EventChangeSelection) { @@ -1510,49 +1513,51 @@ TEST_F(CFXJSE_FormCalcContextEmbedderTest, EventChangeSelection) { params.m_iSelEnd = 3; CFXJSE_Engine* context = GetScriptContext(); - context->SetEventParam(params); + context->SetEventParam(¶ms); // Moving end to start works fine. EXPECT_TRUE(Execute("xfa.event.selEnd = \"1\"")); - EXPECT_EQ(1, context->GetEventParam()->m_iSelStart); - EXPECT_EQ(1, context->GetEventParam()->m_iSelEnd); + EXPECT_EQ(1, params.m_iSelStart); + EXPECT_EQ(1, params.m_iSelEnd); // Moving end before end, forces start to move in response. EXPECT_TRUE(Execute("xfa.event.selEnd = \"0\"")); - EXPECT_EQ(0, context->GetEventParam()->m_iSelStart); - EXPECT_EQ(0, context->GetEventParam()->m_iSelEnd); + EXPECT_EQ(0, params.m_iSelStart); + EXPECT_EQ(0, params.m_iSelEnd); // Negatives not allowed EXPECT_TRUE(Execute("xfa.event.selEnd = \"-1\"")); - EXPECT_EQ(0, context->GetEventParam()->m_iSelStart); - EXPECT_EQ(0, context->GetEventParam()->m_iSelEnd); + EXPECT_EQ(0, params.m_iSelStart); + EXPECT_EQ(0, params.m_iSelEnd); // Negatives not allowed EXPECT_TRUE(Execute("xfa.event.selStart = \"-1\"")); - EXPECT_EQ(0, context->GetEventParam()->m_iSelStart); - EXPECT_EQ(0, context->GetEventParam()->m_iSelEnd); + EXPECT_EQ(0, params.m_iSelStart); + EXPECT_EQ(0, params.m_iSelEnd); - context->GetEventParam()->m_iSelEnd = 1; + params.m_iSelEnd = 1; // Moving start to end works fine. EXPECT_TRUE(Execute("xfa.event.selStart = \"1\"")); - EXPECT_EQ(1, context->GetEventParam()->m_iSelStart); - EXPECT_EQ(1, context->GetEventParam()->m_iSelEnd); + EXPECT_EQ(1, params.m_iSelStart); + EXPECT_EQ(1, params.m_iSelEnd); // Moving start after end moves end. EXPECT_TRUE(Execute("xfa.event.selStart = \"2\"")); - EXPECT_EQ(2, context->GetEventParam()->m_iSelStart); - EXPECT_EQ(2, context->GetEventParam()->m_iSelEnd); + EXPECT_EQ(2, params.m_iSelStart); + EXPECT_EQ(2, params.m_iSelEnd); // Setting End past end of string clamps to string length; EXPECT_TRUE(Execute("xfa.event.selEnd = \"20\"")); - EXPECT_EQ(2, context->GetEventParam()->m_iSelStart); - EXPECT_EQ(4, context->GetEventParam()->m_iSelEnd); + EXPECT_EQ(2, params.m_iSelStart); + EXPECT_EQ(4, params.m_iSelEnd); // Setting Start past end of string clamps to string length; EXPECT_TRUE(Execute("xfa.event.selStart = \"20\"")); - EXPECT_EQ(4, context->GetEventParam()->m_iSelStart); - EXPECT_EQ(4, context->GetEventParam()->m_iSelEnd); + EXPECT_EQ(4, params.m_iSelStart); + EXPECT_EQ(4, params.m_iSelEnd); + + context->SetEventParam(nullptr); } TEST_F(CFXJSE_FormCalcContextEmbedderTest, XFAEventCancelAction) { @@ -1562,7 +1567,7 @@ TEST_F(CFXJSE_FormCalcContextEmbedderTest, XFAEventCancelAction) { params.m_bCancelAction = false; CFXJSE_Engine* context = GetScriptContext(); - context->SetEventParam(params); + context->SetEventParam(¶ms); EXPECT_TRUE(Execute("xfa.event.cancelAction")); @@ -1571,7 +1576,9 @@ TEST_F(CFXJSE_FormCalcContextEmbedderTest, XFAEventCancelAction) { EXPECT_FALSE(value->ToBoolean()); EXPECT_TRUE(Execute("xfa.event.cancelAction = \"true\"")); - EXPECT_TRUE(context->GetEventParam()->m_bCancelAction); + EXPECT_TRUE(params.m_bCancelAction); + + context->SetEventParam(nullptr); } TEST_F(CFXJSE_FormCalcContextEmbedderTest, ComplexTextChangeEvent) { @@ -1584,29 +1591,31 @@ TEST_F(CFXJSE_FormCalcContextEmbedderTest, ComplexTextChangeEvent) { params.m_iSelEnd = 3; CFXJSE_Engine* context = GetScriptContext(); - context->SetEventParam(params); + context->SetEventParam(¶ms); - EXPECT_EQ(L"abcd", context->GetEventParam()->m_wsPrevText); - EXPECT_EQ(L"agd", context->GetEventParam()->GetNewText()); - EXPECT_EQ(L"g", context->GetEventParam()->m_wsChange); - EXPECT_EQ(1, context->GetEventParam()->m_iSelStart); - EXPECT_EQ(3, context->GetEventParam()->m_iSelEnd); + EXPECT_EQ(L"abcd", params.m_wsPrevText); + EXPECT_EQ(L"agd", params.GetNewText()); + EXPECT_EQ(L"g", params.m_wsChange); + EXPECT_EQ(1, params.m_iSelStart); + EXPECT_EQ(3, params.m_iSelEnd); const char change_event[] = {"xfa.event.change = \"xyz\""}; EXPECT_TRUE(Execute(change_event)); - EXPECT_EQ(L"abcd", context->GetEventParam()->m_wsPrevText); - EXPECT_EQ(L"xyz", context->GetEventParam()->m_wsChange); - EXPECT_EQ(L"axyzd", context->GetEventParam()->GetNewText()); - EXPECT_EQ(1, context->GetEventParam()->m_iSelStart); - EXPECT_EQ(3, context->GetEventParam()->m_iSelEnd); + EXPECT_EQ(L"abcd", params.m_wsPrevText); + EXPECT_EQ(L"xyz", params.m_wsChange); + EXPECT_EQ(L"axyzd", params.GetNewText()); + EXPECT_EQ(1, params.m_iSelStart); + EXPECT_EQ(3, params.m_iSelEnd); const char sel_event[] = {"xfa.event.selEnd = \"1\""}; EXPECT_TRUE(Execute(sel_event)); - EXPECT_EQ(L"abcd", context->GetEventParam()->m_wsPrevText); - EXPECT_EQ(L"xyz", context->GetEventParam()->m_wsChange); - EXPECT_EQ(L"axyzbcd", context->GetEventParam()->GetNewText()); - EXPECT_EQ(1, context->GetEventParam()->m_iSelStart); - EXPECT_EQ(1, context->GetEventParam()->m_iSelEnd); + EXPECT_EQ(L"abcd", params.m_wsPrevText); + EXPECT_EQ(L"xyz", params.m_wsChange); + EXPECT_EQ(L"axyzbcd", params.GetNewText()); + EXPECT_EQ(1, params.m_iSelStart); + EXPECT_EQ(1, params.m_iSelEnd); + + context->SetEventParam(nullptr); } diff --git a/xfa/fde/cfde_texteditengine.cpp b/xfa/fde/cfde_texteditengine.cpp index 3363baa0cd..cfe844e656 100644 --- a/xfa/fde/cfde_texteditengine.cpp +++ b/xfa/fde/cfde_texteditengine.cpp @@ -262,11 +262,35 @@ size_t CFDE_TextEditEngine::CountCharsExceedingSize(const WideString& text, } void CFDE_TextEditEngine::Insert(size_t idx, - const WideString& text, + const WideString& request_text, RecordOperation add_operation) { + WideString text = request_text; + if (text.GetLength() == 0) + return; if (idx > text_length_) idx = text_length_; + TextChange change; + change.selection_start = idx; + change.selection_end = idx; + change.text = text; + change.previous_text = GetText(); + change.cancelled = false; + + if (delegate_ && (add_operation != RecordOperation::kSkipRecord && + add_operation != RecordOperation::kSkipNotify)) { + delegate_->OnTextWillChange(&change); + if (change.cancelled) + return; + + text = change.text; + idx = change.selection_start; + + // JS extended the selection, so delete it before we insert. + if (change.selection_end != change.selection_start) + DeleteSelectedText(RecordOperation::kSkipRecord); + } + size_t length = text.GetLength(); if (length == 0) return; @@ -276,11 +300,11 @@ void CFDE_TextEditEngine::Insert(size_t idx, bool exceeded_limit = false; // Currently we allow inserting a number of characters over the text limit if - // the text edit is already empty. This allows supporting text fields which - // do formatting. Otherwise, if you enter 123456789 for an SSN into a field + // we're skipping notify. This means we're setting the formatted text into the + // engine. Otherwise, if you enter 123456789 for an SSN into a field // with a 9 character limit and we reformat to 123-45-6789 we'll truncate // the 89 when inserting into the text edit. See https://crbug.com/pdfium/1089 - if (has_character_limit_ && text_length_ > 0 && + if (has_character_limit_ && add_operation != RecordOperation::kSkipNotify && text_length_ + length > character_limit_) { exceeded_limit = true; length = character_limit_ - text_length_; @@ -348,7 +372,7 @@ void CFDE_TextEditEngine::Insert(size_t idx, if (exceeded_limit) delegate_->NotifyTextFull(); - delegate_->OnTextChanged(previous_text); + delegate_->OnTextChanged(); } } @@ -811,6 +835,23 @@ WideString CFDE_TextEditEngine::Delete(size_t start_idx, if (start_idx >= text_length_) return L""; + TextChange change; + change.text = L""; + change.cancelled = false; + if (delegate_ && (add_operation != RecordOperation::kSkipRecord && + add_operation != RecordOperation::kSkipNotify)) { + change.previous_text = GetText(); + change.selection_start = start_idx; + change.selection_end = start_idx + length; + + delegate_->OnTextWillChange(&change); + if (change.cancelled) + return L""; + + start_idx = change.selection_start; + length = change.selection_end - change.selection_start; + } + length = std::min(length, text_length_ - start_idx); AdjustGap(start_idx + length, 0); @@ -831,15 +872,37 @@ WideString CFDE_TextEditEngine::Delete(size_t start_idx, is_dirty_ = true; ClearSelection(); + // The JS requested the insertion of text instead of just a deletion. + if (change.text != L"") + Insert(start_idx, change.text, RecordOperation::kSkipRecord); + if (delegate_) - delegate_->OnTextChanged(previous_text); + delegate_->OnTextChanged(); return ret; } -void CFDE_TextEditEngine::ReplaceSelectedText(const WideString& rep) { - size_t start_idx = selection_.start_idx; +void CFDE_TextEditEngine::ReplaceSelectedText(const WideString& requested_rep) { + WideString rep = requested_rep; + + if (delegate_) { + TextChange change; + change.selection_start = selection_.start_idx; + change.selection_end = selection_.start_idx + selection_.count; + change.text = rep; + change.previous_text = GetText(); + change.cancelled = false; + + delegate_->OnTextWillChange(&change); + if (change.cancelled) + return; + + rep = change.text; + selection_.start_idx = change.selection_start; + selection_.count = change.selection_end - change.selection_start; + } + size_t start_idx = selection_.start_idx; WideString txt = DeleteSelectedText(RecordOperation::kSkipRecord); Insert(gap_position_, rep, RecordOperation::kSkipRecord); @@ -1079,13 +1142,7 @@ void CFDE_TextEditEngine::RebuildPieces() { if (IsAlignedRight() && bounds_smaller) { delta = available_width_ - contents_bounding_box_.width; } else if (IsAlignedCenter() && bounds_smaller) { - // TODO(dsinclair): Old code used CombText here and set the space to - // something unrelated to the available width .... Figure out if this is - // needed and what it should do. - // if (is_comb_text_) { - // } else { delta = (available_width_ - contents_bounding_box_.width) / 2.0f; - // } } if (delta != 0.0) { diff --git a/xfa/fde/cfde_texteditengine.h b/xfa/fde/cfde_texteditengine.h index 58f77edf31..868be8866a 100644 --- a/xfa/fde/cfde_texteditengine.h +++ b/xfa/fde/cfde_texteditengine.h @@ -61,21 +61,27 @@ class CFDE_TextEditEngine { virtual void Undo() const = 0; }; + struct TextChange { + WideString text; + WideString previous_text; + size_t selection_start; + size_t selection_end; + bool cancelled; + }; + class Delegate { public: virtual ~Delegate() = default; virtual void NotifyTextFull() = 0; virtual void OnCaretChanged() = 0; - virtual void OnTextChanged(const WideString& prevText) = 0; + virtual void OnTextWillChange(TextChange* change) = 0; + virtual void OnTextChanged() = 0; virtual void OnSelChanged() = 0; virtual bool OnValidate(const WideString& wsText) = 0; virtual void SetScrollOffset(float fScrollOffset) = 0; }; - enum class RecordOperation { - kInsertRecord, - kSkipRecord, - }; + enum class RecordOperation { kInsertRecord, kSkipRecord, kSkipNotify }; CFDE_TextEditEngine(); ~CFDE_TextEditEngine(); diff --git a/xfa/fde/cfde_texteditengine_unittest.cpp b/xfa/fde/cfde_texteditengine_unittest.cpp index 123d16ce4b..c5efe529e6 100644 --- a/xfa/fde/cfde_texteditengine_unittest.cpp +++ b/xfa/fde/cfde_texteditengine_unittest.cpp @@ -21,7 +21,8 @@ class CFDE_TextEditEngineTest : public testing::Test { void NotifyTextFull() override { text_is_full = true; } void OnCaretChanged() override {} - void OnTextChanged(const WideString& prevText) override {} + void OnTextWillChange(CFDE_TextEditEngine::TextChange* change) override {} + void OnTextChanged() override {} void OnSelChanged() override {} bool OnValidate(const WideString& wsText) override { return !fail_validation; diff --git a/xfa/fwl/README.md b/xfa/fwl/README.md index 95a0f4c92a..6460ff1fae 100644 --- a/xfa/fwl/README.md +++ b/xfa/fwl/README.md @@ -46,7 +46,7 @@ and CXFA classes. * CFWL_EventMouse * CFWL_EventScroll * CFWL_EventSelectChanged - * CFWL_EventTextChanged + * CFWL_EventTextWillChange * CFWL_EventValidate The widgets use IFWL_ThemeProvider for rendering everything, calling diff --git a/xfa/fwl/cfwl_barcode.cpp b/xfa/fwl/cfwl_barcode.cpp index 74ac7dd9c2..649dea0c8e 100644 --- a/xfa/fwl/cfwl_barcode.cpp +++ b/xfa/fwl/cfwl_barcode.cpp @@ -67,10 +67,11 @@ void CFWL_Barcode::SetType(BC_TYPE type) { m_dwStatus = XFA_BCS_NeedUpdate; } -void CFWL_Barcode::SetText(const WideString& wsText) { +void CFWL_Barcode::SetText(const WideString& wsText, + CFDE_TextEditEngine::RecordOperation op) { m_pBarcodeEngine.reset(); m_dwStatus = XFA_BCS_NeedUpdate; - CFWL_Edit::SetText(wsText); + CFWL_Edit::SetText(wsText, op); } bool CFWL_Barcode::IsProtectedType() const { @@ -86,7 +87,7 @@ bool CFWL_Barcode::IsProtectedType() const { } void CFWL_Barcode::OnProcessEvent(CFWL_Event* pEvent) { - if (pEvent->GetType() == CFWL_Event::Type::TextChanged) { + if (pEvent->GetType() == CFWL_Event::Type::TextWillChange) { m_pBarcodeEngine.reset(); m_dwStatus = XFA_BCS_NeedUpdate; } diff --git a/xfa/fwl/cfwl_barcode.h b/xfa/fwl/cfwl_barcode.h index 2fc79608b3..58484dfba1 100644 --- a/xfa/fwl/cfwl_barcode.h +++ b/xfa/fwl/cfwl_barcode.h @@ -49,7 +49,10 @@ class CFWL_Barcode : public CFWL_Edit { void OnProcessEvent(CFWL_Event* pEvent) override; // CFWL_Edit - void SetText(const WideString& wsText) override; + void SetText( + const WideString& wsText, + CFDE_TextEditEngine::RecordOperation op = + CFDE_TextEditEngine::RecordOperation::kInsertRecord) override; void SetType(BC_TYPE type); bool IsProtectedType() const; diff --git a/xfa/fwl/cfwl_combobox.cpp b/xfa/fwl/cfwl_combobox.cpp index 8980dc312c..9cd2119aaf 100644 --- a/xfa/fwl/cfwl_combobox.cpp +++ b/xfa/fwl/cfwl_combobox.cpp @@ -16,7 +16,6 @@ #include "xfa/fwl/cfwl_app.h" #include "xfa/fwl/cfwl_event.h" #include "xfa/fwl/cfwl_eventselectchanged.h" -#include "xfa/fwl/cfwl_eventtextchanged.h" #include "xfa/fwl/cfwl_listbox.h" #include "xfa/fwl/cfwl_messagekey.h" #include "xfa/fwl/cfwl_messagekillfocus.h" @@ -505,7 +504,7 @@ void CFWL_ComboBox::OnProcessEvent(CFWL_Event* pEvent) { pScrollEv.m_iScrollCode = pScrollEvent->m_iScrollCode; pScrollEv.m_fPos = pScrollEvent->m_fPos; DispatchEvent(&pScrollEv); - } else if (type == CFWL_Event::Type::TextChanged) { + } else if (type == CFWL_Event::Type::TextWillChange) { CFWL_Event pTemp(CFWL_Event::Type::EditChanged, this); DispatchEvent(&pTemp); } diff --git a/xfa/fwl/cfwl_edit.cpp b/xfa/fwl/cfwl_edit.cpp index 7bdb818192..7c4f0e1607 100644 --- a/xfa/fwl/cfwl_edit.cpp +++ b/xfa/fwl/cfwl_edit.cpp @@ -19,7 +19,7 @@ #include "xfa/fwl/cfwl_app.h" #include "xfa/fwl/cfwl_caret.h" #include "xfa/fwl/cfwl_event.h" -#include "xfa/fwl/cfwl_eventtextchanged.h" +#include "xfa/fwl/cfwl_eventtextwillchange.h" #include "xfa/fwl/cfwl_eventvalidate.h" #include "xfa/fwl/cfwl_messagekey.h" #include "xfa/fwl/cfwl_messagemouse.h" @@ -171,9 +171,10 @@ void CFWL_Edit::SetThemeProvider(IFWL_ThemeProvider* pThemeProvider) { m_pProperties->m_pThemeProvider = pThemeProvider; } -void CFWL_Edit::SetText(const WideString& wsText) { +void CFWL_Edit::SetText(const WideString& wsText, + CFDE_TextEditEngine::RecordOperation op) { m_EdtEngine.Clear(); - m_EdtEngine.Insert(0, wsText); + m_EdtEngine.Insert(0, wsText, op); } int32_t CFWL_Edit::GetTextLength() const { @@ -297,14 +298,26 @@ void CFWL_Edit::OnCaretChanged() { } } -void CFWL_Edit::OnTextChanged(const WideString& prevText) { - if (m_pProperties->m_dwStyleExes & FWL_STYLEEXT_EDT_VAlignMask) - UpdateVAlignment(); +void CFWL_Edit::OnTextWillChange(CFDE_TextEditEngine::TextChange* change) { + CFWL_EventTextWillChange event(this); + event.previous_text = change->previous_text; + event.change_text = change->text; + event.selection_start = change->selection_start; + event.selection_end = change->selection_end; + event.cancelled = false; - CFWL_EventTextChanged event(this); - event.wsPrevText = prevText; DispatchEvent(&event); + change->text = event.change_text; + change->selection_start = event.selection_start; + change->selection_end = event.selection_end; + change->cancelled = event.cancelled; +} + +void CFWL_Edit::OnTextChanged() { + if (m_pProperties->m_dwStyleExes & FWL_STYLEEXT_EDT_VAlignMask) + UpdateVAlignment(); + LayoutScrollBar(); RepaintRect(GetClientRect()); } diff --git a/xfa/fwl/cfwl_edit.h b/xfa/fwl/cfwl_edit.h index 894315de82..634ac225ad 100644 --- a/xfa/fwl/cfwl_edit.h +++ b/xfa/fwl/cfwl_edit.h @@ -65,7 +65,9 @@ class CFWL_Edit : public CFWL_Widget, public CFDE_TextEditEngine::Delegate { void OnDrawWidget(CXFA_Graphics* pGraphics, const CFX_Matrix& matrix) override; - virtual void SetText(const WideString& wsText); + virtual void SetText(const WideString& wsText, + CFDE_TextEditEngine::RecordOperation op = + CFDE_TextEditEngine::RecordOperation::kInsertRecord); int32_t GetTextLength() const; WideString GetText() const; @@ -93,7 +95,8 @@ class CFWL_Edit : public CFWL_Widget, public CFDE_TextEditEngine::Delegate { // CFDE_TextEditEngine::Delegate void NotifyTextFull() override; void OnCaretChanged() override; - void OnTextChanged(const WideString& prevText) override; + void OnTextWillChange(CFDE_TextEditEngine::TextChange* change) override; + void OnTextChanged() override; void OnSelChanged() override; bool OnValidate(const WideString& wsText) override; void SetScrollOffset(float fScrollOffset) override; diff --git a/xfa/fwl/cfwl_event.h b/xfa/fwl/cfwl_event.h index 787f8c2640..8546447209 100644 --- a/xfa/fwl/cfwl_event.h +++ b/xfa/fwl/cfwl_event.h @@ -28,7 +28,7 @@ class CFWL_Event { PreDropDown, Scroll, SelectChanged, - TextChanged, + TextWillChange, TextFull, Validate }; diff --git a/xfa/fwl/cfwl_eventtextchanged.cpp b/xfa/fwl/cfwl_eventtextchanged.cpp deleted file mode 100644 index 439d99d927..0000000000 --- a/xfa/fwl/cfwl_eventtextchanged.cpp +++ /dev/null @@ -1,12 +0,0 @@ -// Copyright 2016 PDFium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com - -#include "xfa/fwl/cfwl_eventtextchanged.h" - -CFWL_EventTextChanged::CFWL_EventTextChanged(CFWL_Widget* pSrcTarget) - : CFWL_Event(CFWL_Event::Type::TextChanged, pSrcTarget) {} - -CFWL_EventTextChanged::~CFWL_EventTextChanged() {} diff --git a/xfa/fwl/cfwl_eventtextchanged.h b/xfa/fwl/cfwl_eventtextchanged.h deleted file mode 100644 index 4494f08075..0000000000 --- a/xfa/fwl/cfwl_eventtextchanged.h +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright 2016 PDFium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com - -#ifndef XFA_FWL_CFWL_EVENTTEXTCHANGED_H_ -#define XFA_FWL_CFWL_EVENTTEXTCHANGED_H_ - -#include "xfa/fwl/cfwl_event.h" - -class CFWL_EventTextChanged : public CFWL_Event { - public: - explicit CFWL_EventTextChanged(CFWL_Widget* pSrcTarget); - ~CFWL_EventTextChanged() override; - - WideString wsPrevText; -}; - -#endif // XFA_FWL_CFWL_EVENTTEXTCHANGED_H_ diff --git a/xfa/fwl/cfwl_eventtextwillchange.cpp b/xfa/fwl/cfwl_eventtextwillchange.cpp new file mode 100644 index 0000000000..22b1100f59 --- /dev/null +++ b/xfa/fwl/cfwl_eventtextwillchange.cpp @@ -0,0 +1,12 @@ +// Copyright 2016 PDFium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com + +#include "xfa/fwl/cfwl_eventtextwillchange.h" + +CFWL_EventTextWillChange::CFWL_EventTextWillChange(CFWL_Widget* pSrcTarget) + : CFWL_Event(CFWL_Event::Type::TextWillChange, pSrcTarget) {} + +CFWL_EventTextWillChange::~CFWL_EventTextWillChange() = default; diff --git a/xfa/fwl/cfwl_eventtextwillchange.h b/xfa/fwl/cfwl_eventtextwillchange.h new file mode 100644 index 0000000000..4b2781f0d1 --- /dev/null +++ b/xfa/fwl/cfwl_eventtextwillchange.h @@ -0,0 +1,24 @@ +// Copyright 2016 PDFium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com + +#ifndef XFA_FWL_CFWL_EVENTTEXTWILLCHANGE_H_ +#define XFA_FWL_CFWL_EVENTTEXTWILLCHANGE_H_ + +#include "xfa/fwl/cfwl_event.h" + +class CFWL_EventTextWillChange : public CFWL_Event { + public: + explicit CFWL_EventTextWillChange(CFWL_Widget* pSrcTarget); + ~CFWL_EventTextWillChange() override; + + WideString change_text; + WideString previous_text; + bool cancelled = false; + size_t selection_start = 0; + size_t selection_end = 0; +}; + +#endif // XFA_FWL_CFWL_EVENTTEXTWILLCHANGE_H_ diff --git a/xfa/fxfa/cxfa_eventparam.cpp b/xfa/fxfa/cxfa_eventparam.cpp index 4e3cae9d1d..4746a6ad86 100644 --- a/xfa/fxfa/cxfa_eventparam.cpp +++ b/xfa/fxfa/cxfa_eventparam.cpp @@ -21,7 +21,7 @@ CXFA_EventParam::CXFA_EventParam() m_bShift(false), m_bIsFormReady(false) {} -CXFA_EventParam::~CXFA_EventParam() {} +CXFA_EventParam::~CXFA_EventParam() = default; CXFA_EventParam::CXFA_EventParam(const CXFA_EventParam& other) = default; diff --git a/xfa/fxfa/cxfa_fftextedit.cpp b/xfa/fxfa/cxfa_fftextedit.cpp index 02787d579b..a8080d13d5 100644 --- a/xfa/fxfa/cxfa_fftextedit.cpp +++ b/xfa/fxfa/cxfa_fftextedit.cpp @@ -11,7 +11,7 @@ #include "xfa/fwl/cfwl_datetimepicker.h" #include "xfa/fwl/cfwl_edit.h" #include "xfa/fwl/cfwl_eventtarget.h" -#include "xfa/fwl/cfwl_eventtextchanged.h" +#include "xfa/fwl/cfwl_eventtextwillchange.h" #include "xfa/fwl/cfwl_messagekillfocus.h" #include "xfa/fwl/cfwl_messagesetfocus.h" #include "xfa/fwl/cfwl_notedriver.h" @@ -287,7 +287,7 @@ bool CXFA_FFTextEdit::UpdateFWLData() { WideString wsText = m_pNode->GetValue(eType); WideString wsOldText = pEdit->GetText(); if (wsText != wsOldText || (eType == XFA_VALUEPICTURE_Edit && bUpdate)) { - pEdit->SetText(wsText); + pEdit->SetText(wsText, CFDE_TextEditEngine::RecordOperation::kSkipNotify); bUpdate = true; } if (bUpdate) @@ -296,28 +296,26 @@ bool CXFA_FFTextEdit::UpdateFWLData() { return true; } -void CXFA_FFTextEdit::OnTextChanged(CFWL_Widget* pWidget, - const WideString& wsChanged, - const WideString& wsPrevText) { +void CXFA_FFTextEdit::OnTextWillChange(CFWL_Widget* pWidget, + CFWL_EventTextWillChange* event) { m_dwStatus |= XFA_WidgetStatus_TextEditValueChanged; + CXFA_EventParam eParam; eParam.m_eType = XFA_EVENT_Change; - eParam.m_wsChange = wsChanged; + eParam.m_wsChange = event->change_text; eParam.m_pTarget = m_pNode.Get(); - eParam.m_wsPrevText = wsPrevText; - if (m_pNode->GetFFWidgetType() == XFA_FFWidgetType::kDateTimeEdit) { - auto* pDateTime = static_cast(m_pNormalWidget.get()); - if (pDateTime->HasSelection()) { - size_t count; - std::tie(eParam.m_iSelStart, count) = pDateTime->GetSelection(); - eParam.m_iSelEnd = eParam.m_iSelStart + count; - } - } else { - CFWL_Edit* pEdit = ToEdit(m_pNormalWidget.get()); - if (pEdit->HasSelection()) - std::tie(eParam.m_iSelStart, eParam.m_iSelEnd) = pEdit->GetSelection(); - } + eParam.m_wsPrevText = event->previous_text; + eParam.m_iSelStart = static_cast(event->selection_start); + eParam.m_iSelEnd = static_cast(event->selection_end); + m_pNode->ProcessEvent(GetDocView(), XFA_AttributeEnum::Change, &eParam); + + // Copy the data back out of the EventParam and into the TextChanged event so + // it can propagate back to the calling widget. + event->cancelled = eParam.m_bCancelAction; + event->change_text = eParam.m_wsChange; + event->selection_start = static_cast(eParam.m_iSelStart); + event->selection_end = static_cast(eParam.m_iSelEnd); } void CXFA_FFTextEdit::OnTextFull(CFWL_Widget* pWidget) { @@ -334,17 +332,13 @@ void CXFA_FFTextEdit::OnProcessMessage(CFWL_Message* pMessage) { void CXFA_FFTextEdit::OnProcessEvent(CFWL_Event* pEvent) { CXFA_FFField::OnProcessEvent(pEvent); switch (pEvent->GetType()) { - case CFWL_Event::Type::TextChanged: { - CFWL_EventTextChanged* event = - static_cast(pEvent); - WideString wsChange; - OnTextChanged(m_pNormalWidget.get(), wsChange, event->wsPrevText); + case CFWL_Event::Type::TextWillChange: + OnTextWillChange(m_pNormalWidget.get(), + static_cast(pEvent)); break; - } - case CFWL_Event::Type::TextFull: { + case CFWL_Event::Type::TextFull: OnTextFull(m_pNormalWidget.get()); break; - } default: break; } diff --git a/xfa/fxfa/cxfa_fftextedit.h b/xfa/fxfa/cxfa_fftextedit.h index 7c19444ae6..9b61373df9 100644 --- a/xfa/fxfa/cxfa_fftextedit.h +++ b/xfa/fxfa/cxfa_fftextedit.h @@ -12,6 +12,7 @@ #include "xfa/fxfa/cxfa_fffield.h" class CFWL_Event; +class CFWL_EventTextWillChange; class CFWL_Widget; class CFX_Matrix; class CXFA_FFWidget; @@ -38,9 +39,7 @@ class CXFA_FFTextEdit : public CXFA_FFField { void OnDrawWidget(CXFA_Graphics* pGraphics, const CFX_Matrix& matrix) override; - void OnTextChanged(CFWL_Widget* pWidget, - const WideString& wsChanged, - const WideString& wsPrevText); + void OnTextWillChange(CFWL_Widget* pWidget, CFWL_EventTextWillChange* change); void OnTextFull(CFWL_Widget* pWidget); // CXFA_FFWidget diff --git a/xfa/fxfa/parser/cxfa_node.cpp b/xfa/fxfa/parser/cxfa_node.cpp index 66c4fb99f4..6672e7fb29 100644 --- a/xfa/fxfa/parser/cxfa_node.cpp +++ b/xfa/fxfa/parser/cxfa_node.cpp @@ -2290,7 +2290,7 @@ std::pair CXFA_Node::ExecuteBoolScript( CXFA_FFDoc* pDoc = docView->GetDoc(); CFXJSE_Engine* pContext = pDoc->GetXFADoc()->GetScriptContext(); - pContext->SetEventParam(*pEventParam); + pContext->SetEventParam(pEventParam); pContext->SetRunAtType(script->GetRunAt()); std::vector refNodes; @@ -2344,6 +2344,7 @@ std::pair CXFA_Node::ExecuteBoolScript( } } pContext->SetNodesOfRunScript(nullptr); + pContext->SetEventParam(nullptr); return {iRet, pTmpRetValue->IsBoolean() ? pTmpRetValue->ToBoolean() : false}; } -- cgit v1.2.3