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 "fpdfsdk/cpdfsdk_formfillenvironment.h"
10#include "fpdfsdk/cpdfsdk_widget.h"
11#include "fpdfsdk/formfiller/cba_fontmap.h"
12#include "fpdfsdk/formfiller/cffl_interactiveformfiller.h"
13#include "fpdfsdk/fsdk_common.h"
14#include "fpdfsdk/pwl/cpwl_combo_box.h"
15#include "third_party/base/ptr_util.h"
16
17CFFL_ComboBox::CFFL_ComboBox(CPDFSDK_FormFillEnvironment* pApp,
18                             CPDFSDK_Widget* pWidget)
19    : CFFL_TextObject(pApp, pWidget) {
20  m_State.nIndex = 0;
21  m_State.nStart = 0;
22  m_State.nEnd = 0;
23}
24
25CFFL_ComboBox::~CFFL_ComboBox() {
26  for (const auto& it : m_Maps)
27    it.second->InvalidateFocusHandler(this);
28
29  // See comment in cffl_formfiller.h.
30  // The font map should be stored somewhere more appropriate so it will live
31  // until the PWL_Edit is done with it. pdfium:566
32  DestroyWindows();
33}
34
35CPWL_Wnd::CreateParams CFFL_ComboBox::GetCreateParam() {
36  CPWL_Wnd::CreateParams cp = CFFL_TextObject::GetCreateParam();
37  if (m_pWidget->GetFieldFlags() & FIELDFLAG_EDIT)
38    cp.dwFlags |= PCBS_ALLOWCUSTOMTEXT;
39
40  cp.pFontMap = MaybeCreateFontMap();
41  cp.pFocusHandler = this;
42  return cp;
43}
44
45CPWL_Wnd* CFFL_ComboBox::NewPDFWindow(const CPWL_Wnd::CreateParams& cp) {
46  auto* pWnd = new CPWL_ComboBox();
47  pWnd->AttachFFLData(this);
48  pWnd->Create(cp);
49
50  CFFL_InteractiveFormFiller* pFormFiller =
51      m_pFormFillEnv->GetInteractiveFormFiller();
52  pWnd->SetFillerNotify(pFormFiller);
53
54  int32_t nCurSel = m_pWidget->GetSelectedIndex(0);
55  WideString swText;
56  if (nCurSel < 0)
57    swText = m_pWidget->GetValue();
58  else
59    swText = m_pWidget->GetOptionLabel(nCurSel);
60
61  for (int32_t i = 0, sz = m_pWidget->CountOptions(); i < sz; i++) {
62    pWnd->AddString(m_pWidget->GetOptionLabel(i));
63  }
64
65  pWnd->SetSelect(nCurSel);
66  pWnd->SetText(swText);
67  return pWnd;
68}
69
70bool CFFL_ComboBox::OnChar(CPDFSDK_Annot* pAnnot,
71                           uint32_t nChar,
72                           uint32_t nFlags) {
73  return CFFL_TextObject::OnChar(pAnnot, nChar, nFlags);
74}
75
76bool CFFL_ComboBox::IsDataChanged(CPDFSDK_PageView* pPageView) {
77  CPWL_ComboBox* pWnd = (CPWL_ComboBox*)GetPDFWindow(pPageView, false);
78  if (!pWnd)
79    return false;
80
81  int32_t nCurSel = pWnd->GetSelect();
82  if (!(m_pWidget->GetFieldFlags() & FIELDFLAG_EDIT))
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
91void CFFL_ComboBox::SaveData(CPDFSDK_PageView* pPageView) {
92  CPWL_ComboBox* pWnd =
93      static_cast<CPWL_ComboBox*>(GetPDFWindow(pPageView, false));
94  if (!pWnd)
95    return;
96
97  WideString swText = pWnd->GetText();
98  int32_t nCurSel = pWnd->GetSelect();
99
100  bool bSetValue = false;
101
102  if (m_pWidget->GetFieldFlags() & FIELDFLAG_EDIT)
103    bSetValue = (nCurSel < 0) || (swText != m_pWidget->GetOptionLabel(nCurSel));
104
105  if (bSetValue) {
106    m_pWidget->SetValue(swText, false);
107  } else {
108    m_pWidget->GetSelectedIndex(0);
109    m_pWidget->SetOptionSelection(nCurSel, true, false);
110  }
111  CPDFSDK_Widget::ObservedPtr observed_widget(m_pWidget.Get());
112  CFFL_ComboBox::ObservedPtr observed_this(this);
113
114  m_pWidget->ResetFieldAppearance(true);
115  if (!observed_widget)
116    return;
117  m_pWidget->UpdateField();
118  if (!observed_widget || !observed_this)
119    return;
120  SetChangeMark();
121  m_pWidget->GetPDFPage();
122}
123
124void CFFL_ComboBox::GetActionData(CPDFSDK_PageView* pPageView,
125                                  CPDF_AAction::AActionType type,
126                                  PDFSDK_FieldAction& fa) {
127  switch (type) {
128    case CPDF_AAction::KeyStroke:
129      if (CPWL_ComboBox* pComboBox =
130              static_cast<CPWL_ComboBox*>(GetPDFWindow(pPageView, false))) {
131        if (CPWL_Edit* pEdit = pComboBox->GetEdit()) {
132          fa.bFieldFull = pEdit->IsTextFull();
133          int nSelStart = 0;
134          int nSelEnd = 0;
135          pEdit->GetSelection(nSelStart, nSelEnd);
136          fa.nSelEnd = nSelEnd;
137          fa.nSelStart = nSelStart;
138          fa.sValue = pEdit->GetText();
139          fa.sChangeEx = GetSelectExportText();
140
141          if (fa.bFieldFull) {
142            fa.sChange = L"";
143            fa.sChangeEx = L"";
144          }
145        }
146      }
147      break;
148    case CPDF_AAction::Validate:
149      if (CPWL_ComboBox* pComboBox =
150              static_cast<CPWL_ComboBox*>(GetPDFWindow(pPageView, false))) {
151        if (CPWL_Edit* pEdit = pComboBox->GetEdit()) {
152          fa.sValue = pEdit->GetText();
153        }
154      }
155      break;
156    case CPDF_AAction::LoseFocus:
157    case CPDF_AAction::GetFocus:
158      fa.sValue = m_pWidget->GetValue();
159      break;
160    default:
161      break;
162  }
163}
164
165void CFFL_ComboBox::SetActionData(CPDFSDK_PageView* pPageView,
166                                  CPDF_AAction::AActionType type,
167                                  const PDFSDK_FieldAction& fa) {
168  switch (type) {
169    case CPDF_AAction::KeyStroke:
170      if (CPWL_ComboBox* pComboBox =
171              static_cast<CPWL_ComboBox*>(GetPDFWindow(pPageView, false))) {
172        if (CPWL_Edit* pEdit = pComboBox->GetEdit()) {
173          pEdit->SetSelection(fa.nSelStart, fa.nSelEnd);
174          pEdit->ReplaceSel(fa.sChange);
175        }
176      }
177      break;
178    default:
179      break;
180  }
181}
182
183bool CFFL_ComboBox::IsActionDataChanged(CPDF_AAction::AActionType type,
184                                        const PDFSDK_FieldAction& faOld,
185                                        const PDFSDK_FieldAction& faNew) {
186  switch (type) {
187    case CPDF_AAction::KeyStroke:
188      return (!faOld.bFieldFull && faOld.nSelEnd != faNew.nSelEnd) ||
189             faOld.nSelStart != faNew.nSelStart ||
190             faOld.sChange != faNew.sChange;
191    default:
192      break;
193  }
194
195  return false;
196}
197
198void CFFL_ComboBox::SaveState(CPDFSDK_PageView* pPageView) {
199  ASSERT(pPageView);
200
201  if (CPWL_ComboBox* pComboBox =
202          static_cast<CPWL_ComboBox*>(GetPDFWindow(pPageView, false))) {
203    m_State.nIndex = pComboBox->GetSelect();
204
205    if (CPWL_Edit* pEdit = pComboBox->GetEdit()) {
206      pEdit->GetSelection(m_State.nStart, m_State.nEnd);
207      m_State.sValue = pEdit->GetText();
208    }
209  }
210}
211
212void CFFL_ComboBox::RestoreState(CPDFSDK_PageView* pPageView) {
213  ASSERT(pPageView);
214
215  if (CPWL_ComboBox* pComboBox =
216          static_cast<CPWL_ComboBox*>(GetPDFWindow(pPageView, true))) {
217    if (m_State.nIndex >= 0) {
218      pComboBox->SetSelect(m_State.nIndex);
219    } else {
220      if (CPWL_Edit* pEdit = pComboBox->GetEdit()) {
221        pEdit->SetText(m_State.sValue);
222        pEdit->SetSelection(m_State.nStart, m_State.nEnd);
223      }
224    }
225  }
226}
227
228#ifdef PDF_ENABLE_XFA
229bool CFFL_ComboBox::IsFieldFull(CPDFSDK_PageView* pPageView) {
230  if (CPWL_ComboBox* pComboBox =
231          static_cast<CPWL_ComboBox*>(GetPDFWindow(pPageView, false))) {
232    if (CPWL_Edit* pEdit = pComboBox->GetEdit())
233      return pEdit->IsTextFull();
234  }
235  return false;
236}
237#endif  // PDF_ENABLE_XFA
238
239void CFFL_ComboBox::OnSetFocus(CPWL_Edit* pEdit) {
240  pEdit->SetCharSet(FX_CHARSET_ChineseSimplified);
241  pEdit->SetReadyToInput();
242
243  WideString wsText = pEdit->GetText();
244  int nCharacters = wsText.GetLength();
245  ByteString bsUTFText = wsText.UTF16LE_Encode();
246  auto* pBuffer = reinterpret_cast<const unsigned short*>(bsUTFText.c_str());
247  m_pFormFillEnv->OnSetFieldInputFocus(pBuffer, nCharacters, true);
248}
249
250WideString CFFL_ComboBox::GetSelectExportText() {
251  WideString swRet;
252
253  int nExport = -1;
254  CPDFSDK_PageView* pPageView = GetCurPageView(true);
255  if (CPWL_ComboBox* pComboBox =
256          (CPWL_ComboBox*)GetPDFWindow(pPageView, false)) {
257    nExport = pComboBox->GetSelect();
258  }
259
260  if (nExport >= 0) {
261    if (CPDF_FormField* pFormField = m_pWidget->GetFormField()) {
262      swRet = pFormField->GetOptionValue(nExport);
263      if (swRet.IsEmpty())
264        swRet = pFormField->GetOptionLabel(nExport);
265    }
266  }
267
268  return swRet;
269}
270