1 // Copyright 2014 PDFium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
6 
7 #include "fpdfsdk/formfiller/cffl_combobox.h"
8 
9 #include <utility>
10 
11 #include "constants/form_flags.h"
12 #include "core/fpdfdoc/cba_fontmap.h"
13 #include "fpdfsdk/cpdfsdk_formfillenvironment.h"
14 #include "fpdfsdk/cpdfsdk_widget.h"
15 #include "fpdfsdk/formfiller/cffl_interactiveformfiller.h"
16 #include "fpdfsdk/pwl/cpwl_combo_box.h"
17 #include "third_party/base/ptr_util.h"
18 
CFFL_ComboBox(CPDFSDK_FormFillEnvironment * pApp,CPDFSDK_Widget * pWidget)19 CFFL_ComboBox::CFFL_ComboBox(CPDFSDK_FormFillEnvironment* pApp,
20                              CPDFSDK_Widget* pWidget)
21     : CFFL_TextObject(pApp, pWidget) {
22 }
23 
~CFFL_ComboBox()24 CFFL_ComboBox::~CFFL_ComboBox() {
25   for (const auto& it : m_Maps)
26     it.second->InvalidateFocusHandler(this);
27 
28   // See comment in cffl_formfiller.h.
29   // The font map should be stored somewhere more appropriate so it will live
30   // until the PWL_Edit is done with it. pdfium:566
31   DestroyWindows();
32 }
33 
GetCreateParam()34 CPWL_Wnd::CreateParams CFFL_ComboBox::GetCreateParam() {
35   CPWL_Wnd::CreateParams cp = CFFL_TextObject::GetCreateParam();
36   if (m_pWidget->GetFieldFlags() & pdfium::form_flags::kChoiceEdit)
37     cp.dwFlags |= PCBS_ALLOWCUSTOMTEXT;
38 
39   cp.pFontMap = MaybeCreateFontMap();
40   cp.pFocusHandler = this;
41   return cp;
42 }
43 
NewPWLWindow(const CPWL_Wnd::CreateParams & cp,std::unique_ptr<IPWL_SystemHandler::PerWindowData> pAttachedData)44 std::unique_ptr<CPWL_Wnd> CFFL_ComboBox::NewPWLWindow(
45     const CPWL_Wnd::CreateParams& cp,
46     std::unique_ptr<IPWL_SystemHandler::PerWindowData> pAttachedData) {
47   auto pWnd = pdfium::MakeUnique<CPWL_ComboBox>(cp, std::move(pAttachedData));
48   pWnd->AttachFFLData(this);
49   pWnd->Realize();
50 
51   CFFL_InteractiveFormFiller* pFormFiller =
52       m_pFormFillEnv->GetInteractiveFormFiller();
53   pWnd->SetFillerNotify(pFormFiller);
54 
55   int32_t nCurSel = m_pWidget->GetSelectedIndex(0);
56   WideString swText;
57   if (nCurSel < 0)
58     swText = m_pWidget->GetValue();
59   else
60     swText = m_pWidget->GetOptionLabel(nCurSel);
61 
62   for (int32_t i = 0, sz = m_pWidget->CountOptions(); i < sz; i++)
63     pWnd->AddString(m_pWidget->GetOptionLabel(i));
64 
65   pWnd->SetSelect(nCurSel);
66   pWnd->SetText(swText);
67   return std::move(pWnd);
68 }
69 
OnChar(CPDFSDK_Annot * pAnnot,uint32_t nChar,uint32_t nFlags)70 bool CFFL_ComboBox::OnChar(CPDFSDK_Annot* pAnnot,
71                            uint32_t nChar,
72                            uint32_t nFlags) {
73   return CFFL_TextObject::OnChar(pAnnot, nChar, nFlags);
74 }
75 
IsDataChanged(CPDFSDK_PageView * pPageView)76 bool CFFL_ComboBox::IsDataChanged(CPDFSDK_PageView* pPageView) {
77   auto* pWnd = GetComboBox(pPageView, false);
78   if (!pWnd)
79     return false;
80 
81   int32_t nCurSel = pWnd->GetSelect();
82   if (!(m_pWidget->GetFieldFlags() & pdfium::form_flags::kChoiceEdit))
83     return nCurSel != m_pWidget->GetSelectedIndex(0);
84 
85   if (nCurSel >= 0)
86     return nCurSel != m_pWidget->GetSelectedIndex(0);
87 
88   return pWnd->GetText() != m_pWidget->GetValue();
89 }
90 
SaveData(CPDFSDK_PageView * pPageView)91 void CFFL_ComboBox::SaveData(CPDFSDK_PageView* pPageView) {
92   CPWL_ComboBox* pWnd = GetComboBox(pPageView, false);
93   if (!pWnd)
94     return;
95 
96   WideString swText = pWnd->GetText();
97   int32_t nCurSel = pWnd->GetSelect();
98   bool bSetValue = false;
99   if (m_pWidget->GetFieldFlags() & pdfium::form_flags::kChoiceEdit)
100     bSetValue = (nCurSel < 0) || (swText != m_pWidget->GetOptionLabel(nCurSel));
101 
102   if (bSetValue) {
103     m_pWidget->SetValue(swText, NotificationOption::kDoNotNotify);
104   } else {
105     m_pWidget->GetSelectedIndex(0);
106     m_pWidget->SetOptionSelection(nCurSel, true,
107                                   NotificationOption::kDoNotNotify);
108   }
109   ObservedPtr<CPDFSDK_Widget> observed_widget(m_pWidget.Get());
110   ObservedPtr<CFFL_ComboBox> observed_this(this);
111   m_pWidget->ResetFieldAppearance();
112   if (!observed_widget)
113     return;
114 
115   m_pWidget->UpdateField();
116   if (!observed_widget || !observed_this)
117     return;
118 
119   SetChangeMark();
120   m_pWidget->GetPDFPage();
121 }
122 
GetActionData(CPDFSDK_PageView * pPageView,CPDF_AAction::AActionType type,CPDFSDK_FieldAction & fa)123 void CFFL_ComboBox::GetActionData(CPDFSDK_PageView* pPageView,
124                                   CPDF_AAction::AActionType type,
125                                   CPDFSDK_FieldAction& fa) {
126   switch (type) {
127     case CPDF_AAction::kKeyStroke:
128       if (CPWL_ComboBox* pComboBox = GetComboBox(pPageView, false)) {
129         if (CPWL_Edit* pEdit = pComboBox->GetEdit()) {
130           fa.bFieldFull = pEdit->IsTextFull();
131           int nSelStart = 0;
132           int nSelEnd = 0;
133           pEdit->GetSelection(nSelStart, nSelEnd);
134           fa.nSelEnd = nSelEnd;
135           fa.nSelStart = nSelStart;
136           fa.sValue = pEdit->GetText();
137           fa.sChangeEx = GetSelectExportText();
138 
139           if (fa.bFieldFull) {
140             fa.sChange.clear();
141             fa.sChangeEx.clear();
142           }
143         }
144       }
145       break;
146     case CPDF_AAction::kValidate:
147       if (CPWL_ComboBox* pComboBox = GetComboBox(pPageView, false)) {
148         if (CPWL_Edit* pEdit = pComboBox->GetEdit()) {
149           fa.sValue = pEdit->GetText();
150         }
151       }
152       break;
153     case CPDF_AAction::kLoseFocus:
154     case CPDF_AAction::kGetFocus:
155       fa.sValue = m_pWidget->GetValue();
156       break;
157     default:
158       break;
159   }
160 }
161 
SetActionData(CPDFSDK_PageView * pPageView,CPDF_AAction::AActionType type,const CPDFSDK_FieldAction & fa)162 void CFFL_ComboBox::SetActionData(CPDFSDK_PageView* pPageView,
163                                   CPDF_AAction::AActionType type,
164                                   const CPDFSDK_FieldAction& fa) {
165   switch (type) {
166     case CPDF_AAction::kKeyStroke:
167       if (CPWL_ComboBox* pComboBox = GetComboBox(pPageView, false)) {
168         if (CPWL_Edit* pEdit = pComboBox->GetEdit()) {
169           pEdit->SetSelection(fa.nSelStart, fa.nSelEnd);
170           pEdit->ReplaceSelection(fa.sChange);
171         }
172       }
173       break;
174     default:
175       break;
176   }
177 }
178 
SaveState(CPDFSDK_PageView * pPageView)179 void CFFL_ComboBox::SaveState(CPDFSDK_PageView* pPageView) {
180   CPWL_ComboBox* pComboBox = GetComboBox(pPageView, false);
181   if (!pComboBox)
182     return;
183 
184   m_State.nIndex = pComboBox->GetSelect();
185 
186   CPWL_Edit* pEdit = pComboBox->GetEdit();
187   if (!pEdit)
188     return;
189 
190   pEdit->GetSelection(m_State.nStart, m_State.nEnd);
191   m_State.sValue = pEdit->GetText();
192 }
193 
RestoreState(CPDFSDK_PageView * pPageView)194 void CFFL_ComboBox::RestoreState(CPDFSDK_PageView* pPageView) {
195   CPWL_ComboBox* pComboBox = GetComboBox(pPageView, true);
196   if (!pComboBox)
197     return;
198 
199   if (m_State.nIndex >= 0) {
200     pComboBox->SetSelect(m_State.nIndex);
201     return;
202   }
203 
204   CPWL_Edit* pEdit = pComboBox->GetEdit();
205   if (!pEdit)
206     return;
207 
208   pEdit->SetText(m_State.sValue);
209   pEdit->SetSelection(m_State.nStart, m_State.nEnd);
210 }
211 
SetIndexSelected(int index,bool selected)212 bool CFFL_ComboBox::SetIndexSelected(int index, bool selected) {
213   if (!IsValid() || !selected)
214     return false;
215 
216   if (index < 0 || index >= m_pWidget->CountOptions())
217     return false;
218 
219   CPWL_ComboBox* pWnd = GetComboBox(GetCurPageView(true), false);
220   if (!pWnd)
221     return false;
222 
223   pWnd->SetSelect(index);
224   return true;
225 }
226 
IsIndexSelected(int index)227 bool CFFL_ComboBox::IsIndexSelected(int index) {
228   if (!IsValid())
229     return false;
230 
231   if (index < 0 || index >= m_pWidget->CountOptions())
232     return false;
233 
234   CPWL_ComboBox* pWnd = GetComboBox(GetCurPageView(true), false);
235   return pWnd && index == pWnd->GetSelect();
236 }
237 
238 #ifdef PDF_ENABLE_XFA
IsFieldFull(CPDFSDK_PageView * pPageView)239 bool CFFL_ComboBox::IsFieldFull(CPDFSDK_PageView* pPageView) {
240   CPWL_ComboBox* pComboBox = GetComboBox(pPageView, false);
241   if (!pComboBox)
242     return false;
243 
244   CPWL_Edit* pEdit = pComboBox->GetEdit();
245   return pEdit && pEdit->IsTextFull();
246 }
247 #endif  // PDF_ENABLE_XFA
248 
OnSetFocus(CPWL_Edit * pEdit)249 void CFFL_ComboBox::OnSetFocus(CPWL_Edit* pEdit) {
250   pEdit->SetCharSet(FX_CHARSET_ChineseSimplified);
251   pEdit->SetReadyToInput();
252 
253   WideString wsText = pEdit->GetText();
254   int nCharacters = wsText.GetLength();
255   ByteString bsUTFText = wsText.ToUTF16LE();
256   auto* pBuffer = reinterpret_cast<const unsigned short*>(bsUTFText.c_str());
257   m_pFormFillEnv->OnSetFieldInputFocus(pBuffer, nCharacters, true);
258 }
259 
GetSelectExportText()260 WideString CFFL_ComboBox::GetSelectExportText() {
261   WideString swRet;
262 
263   CPWL_ComboBox* pComboBox = GetComboBox(GetCurPageView(true), false);
264   int nExport = pComboBox ? pComboBox->GetSelect() : -1;
265 
266   if (nExport >= 0) {
267     if (CPDF_FormField* pFormField = m_pWidget->GetFormField()) {
268       swRet = pFormField->GetOptionValue(nExport);
269       if (swRet.IsEmpty())
270         swRet = pFormField->GetOptionLabel(nExport);
271     }
272   }
273 
274   return swRet;
275 }
276 
GetComboBox(CPDFSDK_PageView * pPageView,bool bNew)277 CPWL_ComboBox* CFFL_ComboBox::GetComboBox(CPDFSDK_PageView* pPageView,
278                                           bool bNew) {
279   return static_cast<CPWL_ComboBox*>(GetPWLWindow(pPageView, bNew));
280 }
281