|  | // Copyright 2014 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 "fpdfsdk/formfiller/cffl_textfield.h" | 
|  |  | 
|  | #include "fpdfsdk/cpdfsdk_common.h" | 
|  | #include "fpdfsdk/cpdfsdk_formfillenvironment.h" | 
|  | #include "fpdfsdk/cpdfsdk_widget.h" | 
|  | #include "fpdfsdk/formfiller/cba_fontmap.h" | 
|  | #include "third_party/base/ptr_util.h" | 
|  |  | 
|  | CFFL_TextField::CFFL_TextField(CPDFSDK_FormFillEnvironment* pApp, | 
|  | CPDFSDK_Widget* pWidget) | 
|  | : CFFL_TextObject(pApp, pWidget) {} | 
|  |  | 
|  | CFFL_TextField::~CFFL_TextField() { | 
|  | for (const auto& it : m_Maps) | 
|  | it.second->InvalidateFocusHandler(this); | 
|  |  | 
|  | // See comment in cffl_formfiller.h. | 
|  | // The font map should be stored somewhere more appropriate so it will live | 
|  | // until the PWL_Edit is done with it. pdfium:566 | 
|  | DestroyWindows(); | 
|  | } | 
|  |  | 
|  | CPWL_Wnd::CreateParams CFFL_TextField::GetCreateParam() { | 
|  | CPWL_Wnd::CreateParams cp = CFFL_TextObject::GetCreateParam(); | 
|  | int nFlags = m_pWidget->GetFieldFlags(); | 
|  | if (nFlags & FIELDFLAG_PASSWORD) | 
|  | cp.dwFlags |= PES_PASSWORD; | 
|  |  | 
|  | if (nFlags & FIELDFLAG_MULTILINE) { | 
|  | cp.dwFlags |= PES_MULTILINE | PES_AUTORETURN | PES_TOP; | 
|  | if (!(nFlags & FIELDFLAG_DONOTSCROLL)) | 
|  | cp.dwFlags |= PWS_VSCROLL | PES_AUTOSCROLL; | 
|  | } else { | 
|  | cp.dwFlags |= PES_CENTER; | 
|  | if (!(nFlags & FIELDFLAG_DONOTSCROLL)) | 
|  | cp.dwFlags |= PES_AUTOSCROLL; | 
|  | } | 
|  |  | 
|  | if (nFlags & FIELDFLAG_COMB) | 
|  | cp.dwFlags |= PES_CHARARRAY; | 
|  |  | 
|  | if (nFlags & FIELDFLAG_RICHTEXT) | 
|  | cp.dwFlags |= PES_RICH; | 
|  |  | 
|  | cp.dwFlags |= PES_UNDO; | 
|  |  | 
|  | switch (m_pWidget->GetAlignment()) { | 
|  | default: | 
|  | case BF_ALIGN_LEFT: | 
|  | cp.dwFlags |= PES_LEFT; | 
|  | break; | 
|  | case BF_ALIGN_MIDDLE: | 
|  | cp.dwFlags |= PES_MIDDLE; | 
|  | break; | 
|  | case BF_ALIGN_RIGHT: | 
|  | cp.dwFlags |= PES_RIGHT; | 
|  | break; | 
|  | } | 
|  | cp.pFontMap = MaybeCreateFontMap(); | 
|  | cp.pFocusHandler = this; | 
|  | return cp; | 
|  | } | 
|  |  | 
|  | CPWL_Wnd* CFFL_TextField::NewPDFWindow(const CPWL_Wnd::CreateParams& cp) { | 
|  | auto* pWnd = new CPWL_Edit(); | 
|  | pWnd->AttachFFLData(this); | 
|  | pWnd->Create(cp); | 
|  | pWnd->SetFillerNotify(m_pFormFillEnv->GetInteractiveFormFiller()); | 
|  |  | 
|  | int32_t nMaxLen = m_pWidget->GetMaxLen(); | 
|  | WideString swValue = m_pWidget->GetValue(); | 
|  |  | 
|  | if (nMaxLen > 0) { | 
|  | if (pWnd->HasFlag(PES_CHARARRAY)) { | 
|  | pWnd->SetCharArray(nMaxLen); | 
|  | pWnd->SetAlignFormatVerticalCenter(); | 
|  | } else { | 
|  | pWnd->SetLimitChar(nMaxLen); | 
|  | } | 
|  | } | 
|  |  | 
|  | pWnd->SetText(swValue); | 
|  | return pWnd; | 
|  | } | 
|  |  | 
|  | bool CFFL_TextField::OnChar(CPDFSDK_Annot* pAnnot, | 
|  | uint32_t nChar, | 
|  | uint32_t nFlags) { | 
|  | switch (nChar) { | 
|  | case FWL_VKEY_Return: { | 
|  | if (m_pWidget->GetFieldFlags() & FIELDFLAG_MULTILINE) | 
|  | break; | 
|  |  | 
|  | CPDFSDK_PageView* pPageView = GetCurPageView(true); | 
|  | ASSERT(pPageView); | 
|  | m_bValid = !m_bValid; | 
|  | m_pFormFillEnv->Invalidate(pAnnot->GetPage(), | 
|  | pAnnot->GetRect().GetOuterRect()); | 
|  |  | 
|  | if (m_bValid) { | 
|  | if (CPWL_Wnd* pWnd = GetPDFWindow(pPageView, true)) | 
|  | pWnd->SetFocus(); | 
|  | break; | 
|  | } | 
|  |  | 
|  | if (!CommitData(pPageView, nFlags)) | 
|  | return false; | 
|  |  | 
|  | DestroyPDFWindow(pPageView); | 
|  | return true; | 
|  | } | 
|  | case FWL_VKEY_Escape: { | 
|  | CPDFSDK_PageView* pPageView = GetCurPageView(true); | 
|  | ASSERT(pPageView); | 
|  | EscapeFiller(pPageView, true); | 
|  | return true; | 
|  | } | 
|  | } | 
|  |  | 
|  | return CFFL_TextObject::OnChar(pAnnot, nChar, nFlags); | 
|  | } | 
|  |  | 
|  | bool CFFL_TextField::IsDataChanged(CPDFSDK_PageView* pPageView) { | 
|  | CPWL_Edit* pEdit = GetEdit(pPageView, false); | 
|  | return pEdit && pEdit->GetText() != m_pWidget->GetValue(); | 
|  | } | 
|  |  | 
|  | void CFFL_TextField::SaveData(CPDFSDK_PageView* pPageView) { | 
|  | CPWL_Edit* pWnd = GetEdit(pPageView, false); | 
|  | if (!pWnd) | 
|  | return; | 
|  |  | 
|  | WideString sOldValue = m_pWidget->GetValue(); | 
|  | WideString sNewValue = pWnd->GetText(); | 
|  | CPDFSDK_Widget::ObservedPtr observed_widget(m_pWidget.Get()); | 
|  | CFFL_TextField::ObservedPtr observed_this(this); | 
|  | m_pWidget->SetValue(sNewValue, NotificationOption::kDoNotNotify); | 
|  | if (!observed_widget) | 
|  | return; | 
|  |  | 
|  | m_pWidget->ResetFieldAppearance(true); | 
|  | if (!observed_widget) | 
|  | return; | 
|  |  | 
|  | m_pWidget->UpdateField(); | 
|  | if (!observed_widget || !observed_this) | 
|  | return; | 
|  |  | 
|  | SetChangeMark(); | 
|  | } | 
|  |  | 
|  | void CFFL_TextField::GetActionData(CPDFSDK_PageView* pPageView, | 
|  | CPDF_AAction::AActionType type, | 
|  | CPDFSDK_FieldAction& fa) { | 
|  | switch (type) { | 
|  | case CPDF_AAction::KeyStroke: | 
|  | if (CPWL_Edit* pWnd = GetEdit(pPageView, false)) { | 
|  | fa.bFieldFull = pWnd->IsTextFull(); | 
|  |  | 
|  | fa.sValue = pWnd->GetText(); | 
|  |  | 
|  | if (fa.bFieldFull) { | 
|  | fa.sChange = L""; | 
|  | fa.sChangeEx = L""; | 
|  | } | 
|  | } | 
|  | break; | 
|  | case CPDF_AAction::Validate: | 
|  | if (CPWL_Edit* pWnd = GetEdit(pPageView, false)) { | 
|  | fa.sValue = pWnd->GetText(); | 
|  | } | 
|  | break; | 
|  | case CPDF_AAction::LoseFocus: | 
|  | case CPDF_AAction::GetFocus: | 
|  | fa.sValue = m_pWidget->GetValue(); | 
|  | break; | 
|  | default: | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | void CFFL_TextField::SetActionData(CPDFSDK_PageView* pPageView, | 
|  | CPDF_AAction::AActionType type, | 
|  | const CPDFSDK_FieldAction& fa) { | 
|  | switch (type) { | 
|  | case CPDF_AAction::KeyStroke: | 
|  | if (CPWL_Edit* pEdit = GetEdit(pPageView, false)) { | 
|  | pEdit->SetFocus(); | 
|  | pEdit->SetSelection(fa.nSelStart, fa.nSelEnd); | 
|  | pEdit->ReplaceSel(fa.sChange); | 
|  | } | 
|  | break; | 
|  | default: | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | bool CFFL_TextField::IsActionDataChanged(CPDF_AAction::AActionType type, | 
|  | const CPDFSDK_FieldAction& faOld, | 
|  | const CPDFSDK_FieldAction& faNew) { | 
|  | switch (type) { | 
|  | case CPDF_AAction::KeyStroke: | 
|  | return (!faOld.bFieldFull && faOld.nSelEnd != faNew.nSelEnd) || | 
|  | faOld.nSelStart != faNew.nSelStart || | 
|  | faOld.sChange != faNew.sChange; | 
|  | default: | 
|  | break; | 
|  | } | 
|  |  | 
|  | return false; | 
|  | } | 
|  |  | 
|  | void CFFL_TextField::SaveState(CPDFSDK_PageView* pPageView) { | 
|  | ASSERT(pPageView); | 
|  |  | 
|  | CPWL_Edit* pWnd = GetEdit(pPageView, false); | 
|  | if (!pWnd) | 
|  | return; | 
|  |  | 
|  | pWnd->GetSelection(m_State.nStart, m_State.nEnd); | 
|  | m_State.sValue = pWnd->GetText(); | 
|  | } | 
|  |  | 
|  | void CFFL_TextField::RestoreState(CPDFSDK_PageView* pPageView) { | 
|  | ASSERT(pPageView); | 
|  |  | 
|  | CPWL_Edit* pWnd = GetEdit(pPageView, true); | 
|  | if (!pWnd) | 
|  | return; | 
|  |  | 
|  | pWnd->SetText(m_State.sValue); | 
|  | pWnd->SetSelection(m_State.nStart, m_State.nEnd); | 
|  | } | 
|  |  | 
|  | #ifdef PDF_ENABLE_XFA | 
|  | bool CFFL_TextField::IsFieldFull(CPDFSDK_PageView* pPageView) { | 
|  | CPWL_Edit* pWnd = GetEdit(pPageView, false); | 
|  | return pWnd && pWnd->IsTextFull(); | 
|  | } | 
|  | #endif  // PDF_ENABLE_XFA | 
|  |  | 
|  | void CFFL_TextField::OnSetFocus(CPWL_Edit* pEdit) { | 
|  | pEdit->SetCharSet(FX_CHARSET_ChineseSimplified); | 
|  | pEdit->SetReadyToInput(); | 
|  |  | 
|  | WideString wsText = pEdit->GetText(); | 
|  | int nCharacters = wsText.GetLength(); | 
|  | ByteString bsUTFText = wsText.UTF16LE_Encode(); | 
|  | auto* pBuffer = reinterpret_cast<const unsigned short*>(bsUTFText.c_str()); | 
|  | m_pFormFillEnv->OnSetFieldInputFocus(pBuffer, nCharacters, true); | 
|  | } | 
|  |  | 
|  | CPWL_Edit* CFFL_TextField::GetEdit(CPDFSDK_PageView* pPageView, bool bNew) { | 
|  | return static_cast<CPWL_Edit*>(GetPDFWindow(pPageView, bNew)); | 
|  | } |