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