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 "xfa/fxfa/cxfa_ffcombobox.h"
8 
9 #include <utility>
10 #include <vector>
11 
12 #include "third_party/base/ptr_util.h"
13 #include "xfa/fwl/cfwl_combobox.h"
14 #include "xfa/fwl/cfwl_eventselectchanged.h"
15 #include "xfa/fwl/cfwl_notedriver.h"
16 #include "xfa/fxfa/cxfa_eventparam.h"
17 #include "xfa/fxfa/cxfa_ffdocview.h"
18 #include "xfa/fxfa/parser/cxfa_para.h"
19 
20 namespace {
21 
ToComboBox(CFWL_Widget * widget)22 CFWL_ComboBox* ToComboBox(CFWL_Widget* widget) {
23   return static_cast<CFWL_ComboBox*>(widget);
24 }
25 
ToComboBox(const CFWL_Widget * widget)26 const CFWL_ComboBox* ToComboBox(const CFWL_Widget* widget) {
27   return static_cast<const CFWL_ComboBox*>(widget);
28 }
29 
30 }  // namespace
31 
CXFA_FFComboBox(CXFA_Node * pNode)32 CXFA_FFComboBox::CXFA_FFComboBox(CXFA_Node* pNode) : CXFA_FFDropDown(pNode) {}
33 
34 CXFA_FFComboBox::~CXFA_FFComboBox() = default;
35 
AsComboBox()36 CXFA_FFComboBox* CXFA_FFComboBox::AsComboBox() {
37   return this;
38 }
39 
GetBBox(FocusOption focus)40 CFX_RectF CXFA_FFComboBox::GetBBox(FocusOption focus) {
41   if (focus == kDrawFocus)
42     return CFX_RectF();
43   return CXFA_FFWidget::GetBBox(kDoNotDrawFocus);
44 }
45 
PtInActiveRect(const CFX_PointF & point)46 bool CXFA_FFComboBox::PtInActiveRect(const CFX_PointF& point) {
47   auto* pComboBox = ToComboBox(GetNormalWidget());
48   return pComboBox && pComboBox->GetBBox().Contains(point);
49 }
50 
LoadWidget()51 bool CXFA_FFComboBox::LoadWidget() {
52   ASSERT(!IsLoaded());
53   auto pNew = pdfium::MakeUnique<CFWL_ComboBox>(GetFWLApp());
54   CFWL_ComboBox* pComboBox = pNew.get();
55   SetNormalWidget(std::move(pNew));
56   pComboBox->SetAdapterIface(this);
57 
58   CFWL_NoteDriver* pNoteDriver = pComboBox->GetOwnerApp()->GetNoteDriver();
59   pNoteDriver->RegisterEventTarget(pComboBox, pComboBox);
60   m_pOldDelegate = pComboBox->GetDelegate();
61   pComboBox->SetDelegate(this);
62 
63   {
64     CFWL_Widget::ScopedUpdateLock update_lock(pComboBox);
65     for (const auto& label : m_pNode->GetChoiceListItems(false))
66       pComboBox->AddString(label);
67 
68     std::vector<int32_t> iSelArray = m_pNode->GetSelectedItems();
69     if (iSelArray.empty())
70       pComboBox->SetEditText(m_pNode->GetValue(XFA_VALUEPICTURE_Raw));
71     else
72       pComboBox->SetCurSel(iSelArray.front());
73 
74     UpdateWidgetProperty();
75   }
76 
77   return CXFA_FFField::LoadWidget();
78 }
79 
UpdateWidgetProperty()80 void CXFA_FFComboBox::UpdateWidgetProperty() {
81   auto* pComboBox = ToComboBox(GetNormalWidget());
82   if (!pComboBox)
83     return;
84 
85   uint32_t dwExtendedStyle = 0;
86   uint32_t dwEditStyles = FWL_STYLEEXT_EDT_ReadOnly;
87   dwExtendedStyle |= UpdateUIProperty();
88   if (m_pNode->IsChoiceListAllowTextEntry()) {
89     dwEditStyles &= ~FWL_STYLEEXT_EDT_ReadOnly;
90     dwExtendedStyle |= FWL_STYLEEXT_CMB_DropDown;
91   }
92   if (!m_pNode->IsOpenAccess() || !GetDoc()->GetXFADoc()->IsInteractive()) {
93     dwEditStyles |= FWL_STYLEEXT_EDT_ReadOnly;
94     dwExtendedStyle |= FWL_STYLEEXT_CMB_ReadOnly;
95   }
96   dwExtendedStyle |= GetAlignment();
97   GetNormalWidget()->ModifyStylesEx(dwExtendedStyle, 0xFFFFFFFF);
98 
99   if (!m_pNode->IsHorizontalScrollPolicyOff())
100     dwEditStyles |= FWL_STYLEEXT_EDT_AutoHScroll;
101 
102   pComboBox->EditModifyStylesEx(dwEditStyles, 0xFFFFFFFF);
103 }
104 
OnRButtonUp(uint32_t dwFlags,const CFX_PointF & point)105 bool CXFA_FFComboBox::OnRButtonUp(uint32_t dwFlags, const CFX_PointF& point) {
106   if (!CXFA_FFField::OnRButtonUp(dwFlags, point))
107     return false;
108 
109   GetDoc()->GetDocEnvironment()->PopupMenu(this, point);
110   return true;
111 }
112 
OnKillFocus(CXFA_FFWidget * pNewWidget)113 bool CXFA_FFComboBox::OnKillFocus(CXFA_FFWidget* pNewWidget) {
114   ObservedPtr<CXFA_FFWidget> pWatched(this);
115   ObservedPtr<CXFA_FFWidget> pNewWatched(pNewWidget);
116   if (!ProcessCommittedData())
117     UpdateFWLData();
118 
119   return pWatched && pNewWatched &&
120          CXFA_FFField::OnKillFocus(pNewWatched.Get());
121 }
122 
OpenDropDownList()123 void CXFA_FFComboBox::OpenDropDownList() {
124   ToComboBox(GetNormalWidget())->OpenDropDownList(true);
125 }
126 
CommitData()127 bool CXFA_FFComboBox::CommitData() {
128   return m_pNode->SetValue(XFA_VALUEPICTURE_Raw, m_wsNewValue);
129 }
130 
IsDataChanged()131 bool CXFA_FFComboBox::IsDataChanged() {
132   WideString wsText = GetCurrentText();
133   if (m_pNode->GetValue(XFA_VALUEPICTURE_Raw) == wsText)
134     return false;
135 
136   m_wsNewValue = std::move(wsText);
137   return true;
138 }
139 
FWLEventSelChange(CXFA_EventParam * pParam)140 void CXFA_FFComboBox::FWLEventSelChange(CXFA_EventParam* pParam) {
141   pParam->m_eType = XFA_EVENT_Change;
142   pParam->m_pTarget = m_pNode.Get();
143   pParam->m_wsPrevText = ToComboBox(GetNormalWidget())->GetEditText();
144   m_pNode->ProcessEvent(GetDocView(), XFA_AttributeValue::Change, pParam);
145 }
146 
GetCurrentText() const147 WideString CXFA_FFComboBox::GetCurrentText() const {
148   auto* pFWLcombobox = ToComboBox(GetNormalWidget());
149   WideString wsText = pFWLcombobox->GetEditText();
150   int32_t iCursel = pFWLcombobox->GetCurSel();
151   if (iCursel >= 0) {
152     WideString wsSel = pFWLcombobox->GetTextByIndex(iCursel);
153     if (wsSel == wsText)
154       wsText = m_pNode->GetChoiceListItem(iCursel, true).value_or(L"");
155   }
156   return wsText;
157 }
158 
GetAlignment()159 uint32_t CXFA_FFComboBox::GetAlignment() {
160   CXFA_Para* para = m_pNode->GetParaIfExists();
161   if (!para)
162     return 0;
163 
164   uint32_t dwExtendedStyle = 0;
165   switch (para->GetHorizontalAlign()) {
166     case XFA_AttributeValue::Center:
167       dwExtendedStyle |=
168           FWL_STYLEEXT_CMB_EditHCenter | FWL_STYLEEXT_CMB_ListItemCenterAlign;
169       break;
170     case XFA_AttributeValue::Justify:
171       dwExtendedStyle |= FWL_STYLEEXT_CMB_EditJustified;
172       break;
173     case XFA_AttributeValue::JustifyAll:
174       break;
175     case XFA_AttributeValue::Radix:
176       break;
177     case XFA_AttributeValue::Right:
178       break;
179     default:
180       dwExtendedStyle |=
181           FWL_STYLEEXT_CMB_EditHNear | FWL_STYLEEXT_CMB_ListItemLeftAlign;
182       break;
183   }
184 
185   switch (para->GetVerticalAlign()) {
186     case XFA_AttributeValue::Middle:
187       dwExtendedStyle |= FWL_STYLEEXT_CMB_EditVCenter;
188       break;
189     case XFA_AttributeValue::Bottom:
190       dwExtendedStyle |= FWL_STYLEEXT_CMB_EditVFar;
191       break;
192     default:
193       dwExtendedStyle |= FWL_STYLEEXT_CMB_EditVNear;
194       break;
195   }
196   return dwExtendedStyle;
197 }
198 
UpdateFWLData()199 bool CXFA_FFComboBox::UpdateFWLData() {
200   auto* pComboBox = ToComboBox(GetNormalWidget());
201   if (!pComboBox)
202     return false;
203 
204   std::vector<int32_t> iSelArray = m_pNode->GetSelectedItems();
205   if (!iSelArray.empty()) {
206     pComboBox->SetCurSel(iSelArray.front());
207   } else {
208     pComboBox->SetCurSel(-1);
209     pComboBox->SetEditText(m_pNode->GetValue(XFA_VALUEPICTURE_Raw));
210   }
211   pComboBox->Update();
212   return true;
213 }
214 
CanUndo()215 bool CXFA_FFComboBox::CanUndo() {
216   return m_pNode->IsChoiceListAllowTextEntry() &&
217          ToComboBox(GetNormalWidget())->EditCanUndo();
218 }
219 
CanRedo()220 bool CXFA_FFComboBox::CanRedo() {
221   return m_pNode->IsChoiceListAllowTextEntry() &&
222          ToComboBox(GetNormalWidget())->EditCanRedo();
223 }
224 
Undo()225 bool CXFA_FFComboBox::Undo() {
226   return m_pNode->IsChoiceListAllowTextEntry() &&
227          ToComboBox(GetNormalWidget())->EditUndo();
228 }
229 
Redo()230 bool CXFA_FFComboBox::Redo() {
231   return m_pNode->IsChoiceListAllowTextEntry() &&
232          ToComboBox(GetNormalWidget())->EditRedo();
233 }
234 
CanCopy()235 bool CXFA_FFComboBox::CanCopy() {
236   return ToComboBox(GetNormalWidget())->EditCanCopy();
237 }
238 
CanCut()239 bool CXFA_FFComboBox::CanCut() {
240   return m_pNode->IsOpenAccess() && m_pNode->IsChoiceListAllowTextEntry() &&
241          ToComboBox(GetNormalWidget())->EditCanCut();
242 }
243 
CanPaste()244 bool CXFA_FFComboBox::CanPaste() {
245   return m_pNode->IsChoiceListAllowTextEntry() && m_pNode->IsOpenAccess();
246 }
247 
CanSelectAll()248 bool CXFA_FFComboBox::CanSelectAll() {
249   return ToComboBox(GetNormalWidget())->EditCanSelectAll();
250 }
251 
Copy()252 Optional<WideString> CXFA_FFComboBox::Copy() {
253   return ToComboBox(GetNormalWidget())->EditCopy();
254 }
255 
Cut()256 Optional<WideString> CXFA_FFComboBox::Cut() {
257   if (!m_pNode->IsChoiceListAllowTextEntry())
258     return {};
259 
260   return ToComboBox(GetNormalWidget())->EditCut();
261 }
262 
Paste(const WideString & wsPaste)263 bool CXFA_FFComboBox::Paste(const WideString& wsPaste) {
264   return m_pNode->IsChoiceListAllowTextEntry() &&
265          ToComboBox(GetNormalWidget())->EditPaste(wsPaste);
266 }
267 
SelectAll()268 void CXFA_FFComboBox::SelectAll() {
269   ToComboBox(GetNormalWidget())->EditSelectAll();
270 }
271 
Delete()272 void CXFA_FFComboBox::Delete() {
273   ToComboBox(GetNormalWidget())->EditDelete();
274 }
275 
DeSelect()276 void CXFA_FFComboBox::DeSelect() {
277   ToComboBox(GetNormalWidget())->EditDeSelect();
278 }
279 
GetText()280 WideString CXFA_FFComboBox::GetText() {
281   return GetCurrentText();
282 }
283 
GetFormFieldType()284 FormFieldType CXFA_FFComboBox::GetFormFieldType() {
285   return FormFieldType::kXFA_ComboBox;
286 }
287 
SetItemState(int32_t nIndex,bool bSelected)288 void CXFA_FFComboBox::SetItemState(int32_t nIndex, bool bSelected) {
289   ToComboBox(GetNormalWidget())->SetCurSel(bSelected ? nIndex : -1);
290   GetNormalWidget()->Update();
291   InvalidateRect();
292 }
293 
InsertItem(const WideString & wsLabel,int32_t nIndex)294 void CXFA_FFComboBox::InsertItem(const WideString& wsLabel, int32_t nIndex) {
295   ToComboBox(GetNormalWidget())->AddString(wsLabel);
296   GetNormalWidget()->Update();
297   InvalidateRect();
298 }
299 
DeleteItem(int32_t nIndex)300 void CXFA_FFComboBox::DeleteItem(int32_t nIndex) {
301   if (nIndex < 0)
302     ToComboBox(GetNormalWidget())->RemoveAll();
303   else
304     ToComboBox(GetNormalWidget())->RemoveAt(nIndex);
305 
306   GetNormalWidget()->Update();
307   InvalidateRect();
308 }
309 
OnTextChanged(CFWL_Widget * pWidget,const WideString & wsChanged)310 void CXFA_FFComboBox::OnTextChanged(CFWL_Widget* pWidget,
311                                     const WideString& wsChanged) {
312   CXFA_EventParam eParam;
313   eParam.m_wsPrevText = m_pNode->GetValue(XFA_VALUEPICTURE_Raw);
314   eParam.m_wsChange = wsChanged;
315   FWLEventSelChange(&eParam);
316 }
317 
OnSelectChanged(CFWL_Widget * pWidget,bool bLButtonUp)318 void CXFA_FFComboBox::OnSelectChanged(CFWL_Widget* pWidget, bool bLButtonUp) {
319   ObservedPtr<CXFA_FFComboBox> watched(this);
320   CXFA_EventParam eParam;
321   eParam.m_wsPrevText = m_pNode->GetValue(XFA_VALUEPICTURE_Raw);
322   FWLEventSelChange(&eParam);
323   if (!watched)
324     return;
325 
326   if (m_pNode->IsChoiceListCommitOnSelect() && bLButtonUp)
327     m_pDocView->SetFocusNode(nullptr);
328 }
329 
OnPreOpen(CFWL_Widget * pWidget)330 void CXFA_FFComboBox::OnPreOpen(CFWL_Widget* pWidget) {
331   CXFA_EventParam eParam;
332   eParam.m_eType = XFA_EVENT_PreOpen;
333   eParam.m_pTarget = m_pNode.Get();
334   m_pNode->ProcessEvent(GetDocView(), XFA_AttributeValue::PreOpen, &eParam);
335 }
336 
OnPostOpen(CFWL_Widget * pWidget)337 void CXFA_FFComboBox::OnPostOpen(CFWL_Widget* pWidget) {
338   CXFA_EventParam eParam;
339   eParam.m_eType = XFA_EVENT_PostOpen;
340   eParam.m_pTarget = m_pNode.Get();
341   m_pNode->ProcessEvent(GetDocView(), XFA_AttributeValue::PostOpen, &eParam);
342 }
343 
OnProcessMessage(CFWL_Message * pMessage)344 void CXFA_FFComboBox::OnProcessMessage(CFWL_Message* pMessage) {
345   m_pOldDelegate->OnProcessMessage(pMessage);
346 }
347 
OnProcessEvent(CFWL_Event * pEvent)348 void CXFA_FFComboBox::OnProcessEvent(CFWL_Event* pEvent) {
349   ObservedPtr<CXFA_FFComboBox> watched(this);
350   CXFA_FFField::OnProcessEvent(pEvent);
351   switch (pEvent->GetType()) {
352     case CFWL_Event::Type::SelectChanged: {
353       auto* postEvent = static_cast<CFWL_EventSelectChanged*>(pEvent);
354       OnSelectChanged(GetNormalWidget(), postEvent->bLButtonUp);
355       break;
356     }
357     case CFWL_Event::Type::EditChanged: {
358       WideString wsChanged;
359       OnTextChanged(GetNormalWidget(), wsChanged);
360       break;
361     }
362     case CFWL_Event::Type::PreDropDown: {
363       OnPreOpen(GetNormalWidget());
364       break;
365     }
366     case CFWL_Event::Type::PostDropDown: {
367       OnPostOpen(GetNormalWidget());
368       break;
369     }
370     default:
371       break;
372   }
373   if (watched)
374     m_pOldDelegate->OnProcessEvent(pEvent);
375 }
376 
OnDrawWidget(CXFA_Graphics * pGraphics,const CFX_Matrix & matrix)377 void CXFA_FFComboBox::OnDrawWidget(CXFA_Graphics* pGraphics,
378                                    const CFX_Matrix& matrix) {
379   m_pOldDelegate->OnDrawWidget(pGraphics, matrix);
380 }
381