| // 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_listbox.h" | 
 |  | 
 | #include <utility> | 
 |  | 
 | #include "constants/form_flags.h" | 
 | #include "core/fpdfdoc/cpdf_bafontmap.h" | 
 | #include "fpdfsdk/cpdfsdk_widget.h" | 
 | #include "fpdfsdk/formfiller/cffl_interactiveformfiller.h" | 
 | #include "fpdfsdk/formfiller/cffl_perwindowdata.h" | 
 | #include "fpdfsdk/pwl/cpwl_list_box.h" | 
 | #include "third_party/base/containers/contains.h" | 
 |  | 
 | CFFL_ListBox::CFFL_ListBox(CFFL_InteractiveFormFiller* pFormFiller, | 
 |                            CPDFSDK_Widget* pWidget) | 
 |     : CFFL_TextObject(pFormFiller, pWidget) {} | 
 |  | 
 | CFFL_ListBox::~CFFL_ListBox() = default; | 
 |  | 
 | CPWL_Wnd::CreateParams CFFL_ListBox::GetCreateParam() { | 
 |   CPWL_Wnd::CreateParams cp = CFFL_TextObject::GetCreateParam(); | 
 |   uint32_t dwFieldFlag = m_pWidget->GetFieldFlags(); | 
 |   if (dwFieldFlag & pdfium::form_flags::kChoiceMultiSelect) | 
 |     cp.dwFlags |= PLBS_MULTIPLESEL; | 
 |  | 
 |   cp.dwFlags |= PWS_VSCROLL; | 
 |  | 
 |   if (cp.dwFlags & PWS_AUTOFONTSIZE) { | 
 |     constexpr float kDefaultListBoxFontSize = 12.0f; | 
 |     cp.fFontSize = kDefaultListBoxFontSize; | 
 |   } | 
 |  | 
 |   cp.pFontMap = GetOrCreateFontMap(); | 
 |   return cp; | 
 | } | 
 |  | 
 | std::unique_ptr<CPWL_Wnd> CFFL_ListBox::NewPWLWindow( | 
 |     const CPWL_Wnd::CreateParams& cp, | 
 |     std::unique_ptr<IPWL_FillerNotify::PerWindowData> pAttachedData) { | 
 |   static_cast<CFFL_PerWindowData*>(pAttachedData.get())->SetFormField(this); | 
 |   auto pWnd = std::make_unique<CPWL_ListBox>(cp, std::move(pAttachedData)); | 
 |   pWnd->Realize(); | 
 |  | 
 |   for (int32_t i = 0, sz = m_pWidget->CountOptions(); i < sz; i++) | 
 |     pWnd->AddString(m_pWidget->GetOptionLabel(i)); | 
 |  | 
 |   if (pWnd->HasFlag(PLBS_MULTIPLESEL)) { | 
 |     m_OriginSelections.clear(); | 
 |  | 
 |     bool bSetCaret = false; | 
 |     for (int32_t i = 0, sz = m_pWidget->CountOptions(); i < sz; i++) { | 
 |       if (m_pWidget->IsOptionSelected(i)) { | 
 |         if (!bSetCaret) { | 
 |           pWnd->SetCaret(i); | 
 |           bSetCaret = true; | 
 |         } | 
 |         pWnd->Select(i); | 
 |         m_OriginSelections.insert(i); | 
 |       } | 
 |     } | 
 |   } else { | 
 |     for (int i = 0, sz = m_pWidget->CountOptions(); i < sz; i++) { | 
 |       if (m_pWidget->IsOptionSelected(i)) { | 
 |         pWnd->Select(i); | 
 |         break; | 
 |       } | 
 |     } | 
 |   } | 
 |  | 
 |   pWnd->SetTopVisibleIndex(m_pWidget->GetTopVisibleIndex()); | 
 |   return std::move(pWnd); | 
 | } | 
 |  | 
 | bool CFFL_ListBox::OnChar(CPDFSDK_Widget* pWidget, | 
 |                           uint32_t nChar, | 
 |                           Mask<FWL_EVENTFLAG> nFlags) { | 
 |   return CFFL_TextObject::OnChar(pWidget, nChar, nFlags); | 
 | } | 
 |  | 
 | bool CFFL_ListBox::IsDataChanged(const CPDFSDK_PageView* pPageView) { | 
 |   CPWL_ListBox* pListBox = GetPWLListBox(pPageView); | 
 |   if (!pListBox) | 
 |     return false; | 
 |  | 
 |   if (m_pWidget->GetFieldFlags() & pdfium::form_flags::kChoiceMultiSelect) { | 
 |     size_t nSelCount = 0; | 
 |     for (int32_t i = 0, sz = pListBox->GetCount(); i < sz; ++i) { | 
 |       if (pListBox->IsItemSelected(i)) { | 
 |         if (!pdfium::Contains(m_OriginSelections, i)) | 
 |           return true; | 
 |  | 
 |         ++nSelCount; | 
 |       } | 
 |     } | 
 |  | 
 |     return nSelCount != m_OriginSelections.size(); | 
 |   } | 
 |   return pListBox->GetCurSel() != m_pWidget->GetSelectedIndex(0); | 
 | } | 
 |  | 
 | void CFFL_ListBox::SaveData(const CPDFSDK_PageView* pPageView) { | 
 |   CPWL_ListBox* pListBox = GetPWLListBox(pPageView); | 
 |   if (!pListBox) | 
 |     return; | 
 |  | 
 |   int32_t nNewTopIndex = pListBox->GetTopVisibleIndex(); | 
 |   m_pWidget->ClearSelection(); | 
 |   if (m_pWidget->GetFieldFlags() & pdfium::form_flags::kChoiceMultiSelect) { | 
 |     for (int32_t i = 0, sz = pListBox->GetCount(); i < sz; i++) { | 
 |       if (pListBox->IsItemSelected(i)) | 
 |         m_pWidget->SetOptionSelection(i); | 
 |     } | 
 |   } else { | 
 |     m_pWidget->SetOptionSelection(pListBox->GetCurSel()); | 
 |   } | 
 |   ObservedPtr<CPDFSDK_Widget> observed_widget(m_pWidget.Get()); | 
 |   ObservedPtr<CFFL_ListBox> observed_this(this); | 
 |   m_pWidget->SetTopVisibleIndex(nNewTopIndex); | 
 |   if (!observed_widget) | 
 |     return; | 
 |  | 
 |   m_pWidget->ResetFieldAppearance(); | 
 |   if (!observed_widget) | 
 |     return; | 
 |  | 
 |   m_pWidget->UpdateField(); | 
 |   if (!observed_widget || !observed_this) | 
 |     return; | 
 |  | 
 |   SetChangeMark(); | 
 | } | 
 |  | 
 | void CFFL_ListBox::GetActionData(const CPDFSDK_PageView* pPageView, | 
 |                                  CPDF_AAction::AActionType type, | 
 |                                  CFFL_FieldAction& fa) { | 
 |   switch (type) { | 
 |     case CPDF_AAction::kValidate: | 
 |       if (m_pWidget->GetFieldFlags() & pdfium::form_flags::kChoiceMultiSelect) { | 
 |         fa.sValue.clear(); | 
 |       } else { | 
 |         CPWL_ListBox* pListBox = GetPWLListBox(pPageView); | 
 |         if (pListBox) { | 
 |           int32_t nCurSel = pListBox->GetCurSel(); | 
 |           if (nCurSel >= 0) | 
 |             fa.sValue = m_pWidget->GetOptionLabel(nCurSel); | 
 |         } | 
 |       } | 
 |       break; | 
 |     case CPDF_AAction::kLoseFocus: | 
 |     case CPDF_AAction::kGetFocus: | 
 |       if (m_pWidget->GetFieldFlags() & pdfium::form_flags::kChoiceMultiSelect) { | 
 |         fa.sValue.clear(); | 
 |       } else { | 
 |         int32_t nCurSel = m_pWidget->GetSelectedIndex(0); | 
 |         if (nCurSel >= 0) | 
 |           fa.sValue = m_pWidget->GetOptionLabel(nCurSel); | 
 |       } | 
 |       break; | 
 |     default: | 
 |       break; | 
 |   } | 
 | } | 
 |  | 
 | void CFFL_ListBox::SavePWLWindowState(const CPDFSDK_PageView* pPageView) { | 
 |   CPWL_ListBox* pListBox = GetPWLListBox(pPageView); | 
 |   if (!pListBox) | 
 |     return; | 
 |  | 
 |   for (int32_t i = 0, sz = pListBox->GetCount(); i < sz; i++) { | 
 |     if (pListBox->IsItemSelected(i)) | 
 |       m_State.push_back(i); | 
 |   } | 
 | } | 
 |  | 
 | void CFFL_ListBox::RecreatePWLWindowFromSavedState( | 
 |     const CPDFSDK_PageView* pPageView) { | 
 |   CPWL_ListBox* pListBox = CreateOrUpdatePWLListBox(pPageView); | 
 |   if (!pListBox) | 
 |     return; | 
 |  | 
 |   for (const auto& item : m_State) | 
 |     pListBox->Select(item); | 
 | } | 
 |  | 
 | bool CFFL_ListBox::SetIndexSelected(int index, bool selected) { | 
 |   if (!IsValid()) | 
 |     return false; | 
 |  | 
 |   if (index < 0 || index >= m_pWidget->CountOptions()) | 
 |     return false; | 
 |  | 
 |   CPWL_ListBox* pListBox = GetPWLListBox(GetCurPageView()); | 
 |   if (!pListBox) | 
 |     return false; | 
 |  | 
 |   if (selected) { | 
 |     pListBox->Select(index); | 
 |     pListBox->SetCaret(index); | 
 |   } else { | 
 |     pListBox->Deselect(index); | 
 |     pListBox->SetCaret(index); | 
 |   } | 
 |  | 
 |   return true; | 
 | } | 
 |  | 
 | bool CFFL_ListBox::IsIndexSelected(int index) { | 
 |   if (!IsValid()) | 
 |     return false; | 
 |  | 
 |   if (index < 0 || index >= m_pWidget->CountOptions()) | 
 |     return false; | 
 |  | 
 |   CPWL_ListBox* pListBox = GetPWLListBox(GetCurPageView()); | 
 |   return pListBox && pListBox->IsItemSelected(index); | 
 | } | 
 |  | 
 | CPWL_ListBox* CFFL_ListBox::GetPWLListBox( | 
 |     const CPDFSDK_PageView* pPageView) const { | 
 |   return static_cast<CPWL_ListBox*>(GetPWLWindow(pPageView)); | 
 | } | 
 |  | 
 | CPWL_ListBox* CFFL_ListBox::CreateOrUpdatePWLListBox( | 
 |     const CPDFSDK_PageView* pPageView) { | 
 |   return static_cast<CPWL_ListBox*>(CreateOrUpdatePWLWindow(pPageView)); | 
 | } |