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
54 // Prevents destruction of the CXFA_ContentLayoutItem that owns |this|.
55 RetainPtr<CXFA_ContentLayoutItem> retain_layout(m_pLayoutItem.Get());
56
57 auto pNew = pdfium::MakeUnique<CFWL_ComboBox>(GetFWLApp());
58 CFWL_ComboBox* pComboBox = pNew.get();
59 SetNormalWidget(std::move(pNew));
60 pComboBox->SetAdapterIface(this);
61
62 CFWL_NoteDriver* pNoteDriver = pComboBox->GetOwnerApp()->GetNoteDriver();
63 pNoteDriver->RegisterEventTarget(pComboBox, pComboBox);
64 m_pOldDelegate = pComboBox->GetDelegate();
65 pComboBox->SetDelegate(this);
66
67 {
68 CFWL_Widget::ScopedUpdateLock update_lock(pComboBox);
69 for (const auto& label : m_pNode->GetChoiceListItems(false))
70 pComboBox->AddString(label);
71
72 std::vector<int32_t> iSelArray = m_pNode->GetSelectedItems();
73 if (iSelArray.empty())
74 pComboBox->SetEditText(m_pNode->GetValue(XFA_VALUEPICTURE_Raw));
75 else
76 pComboBox->SetCurSel(iSelArray.front());
77
78 UpdateWidgetProperty();
79 }
80
81 return CXFA_FFField::LoadWidget();
82 }
83
UpdateWidgetProperty()84 void CXFA_FFComboBox::UpdateWidgetProperty() {
85 auto* pComboBox = ToComboBox(GetNormalWidget());
86 if (!pComboBox)
87 return;
88
89 uint32_t dwExtendedStyle = 0;
90 uint32_t dwEditStyles = FWL_STYLEEXT_EDT_ReadOnly;
91 dwExtendedStyle |= UpdateUIProperty();
92 if (m_pNode->IsChoiceListAllowTextEntry()) {
93 dwEditStyles &= ~FWL_STYLEEXT_EDT_ReadOnly;
94 dwExtendedStyle |= FWL_STYLEEXT_CMB_DropDown;
95 }
96 if (!m_pNode->IsOpenAccess() || !GetDoc()->GetXFADoc()->IsInteractive()) {
97 dwEditStyles |= FWL_STYLEEXT_EDT_ReadOnly;
98 dwExtendedStyle |= FWL_STYLEEXT_CMB_ReadOnly;
99 }
100 dwExtendedStyle |= GetAlignment();
101 GetNormalWidget()->ModifyStylesEx(dwExtendedStyle, 0xFFFFFFFF);
102
103 if (!m_pNode->IsHorizontalScrollPolicyOff())
104 dwEditStyles |= FWL_STYLEEXT_EDT_AutoHScroll;
105
106 pComboBox->EditModifyStylesEx(dwEditStyles, 0xFFFFFFFF);
107 }
108
OnRButtonUp(uint32_t dwFlags,const CFX_PointF & point)109 bool CXFA_FFComboBox::OnRButtonUp(uint32_t dwFlags, const CFX_PointF& point) {
110 // Prevents destruction of the CXFA_ContentLayoutItem that owns |this|.
111 RetainPtr<CXFA_ContentLayoutItem> retainer(m_pLayoutItem.Get());
112
113 if (!CXFA_FFField::OnRButtonUp(dwFlags, point))
114 return false;
115
116 GetDoc()->GetDocEnvironment()->PopupMenu(this, point);
117 return true;
118 }
119
OnKillFocus(CXFA_FFWidget * pNewWidget)120 bool CXFA_FFComboBox::OnKillFocus(CXFA_FFWidget* pNewWidget) {
121 // Prevents destruction of the CXFA_ContentLayoutItem that owns |this|.
122 RetainPtr<CXFA_ContentLayoutItem> retainer(m_pLayoutItem.Get());
123
124 ObservedPtr<CXFA_FFWidget> pNewWatched(pNewWidget);
125 if (!ProcessCommittedData())
126 UpdateFWLData();
127
128 return pNewWatched && CXFA_FFField::OnKillFocus(pNewWatched.Get());
129 }
130
OpenDropDownList()131 void CXFA_FFComboBox::OpenDropDownList() {
132 ToComboBox(GetNormalWidget())->OpenDropDownList(true);
133 }
134
CommitData()135 bool CXFA_FFComboBox::CommitData() {
136 return m_pNode->SetValue(XFA_VALUEPICTURE_Raw, m_wsNewValue);
137 }
138
IsDataChanged()139 bool CXFA_FFComboBox::IsDataChanged() {
140 WideString wsText = GetCurrentText();
141 if (m_pNode->GetValue(XFA_VALUEPICTURE_Raw) == wsText)
142 return false;
143
144 m_wsNewValue = std::move(wsText);
145 return true;
146 }
147
FWLEventSelChange(CXFA_EventParam * pParam)148 void CXFA_FFComboBox::FWLEventSelChange(CXFA_EventParam* pParam) {
149 pParam->m_eType = XFA_EVENT_Change;
150 pParam->m_pTarget = m_pNode.Get();
151 pParam->m_wsPrevText = ToComboBox(GetNormalWidget())->GetEditText();
152 m_pNode->ProcessEvent(GetDocView(), XFA_AttributeValue::Change, pParam);
153 }
154
GetCurrentText() const155 WideString CXFA_FFComboBox::GetCurrentText() const {
156 auto* pFWLcombobox = ToComboBox(GetNormalWidget());
157 WideString wsText = pFWLcombobox->GetEditText();
158 int32_t iCursel = pFWLcombobox->GetCurSel();
159 if (iCursel >= 0) {
160 WideString wsSel = pFWLcombobox->GetTextByIndex(iCursel);
161 if (wsSel == wsText)
162 wsText = m_pNode->GetChoiceListItem(iCursel, true).value_or(L"");
163 }
164 return wsText;
165 }
166
GetAlignment()167 uint32_t CXFA_FFComboBox::GetAlignment() {
168 CXFA_Para* para = m_pNode->GetParaIfExists();
169 if (!para)
170 return 0;
171
172 uint32_t dwExtendedStyle = 0;
173 switch (para->GetHorizontalAlign()) {
174 case XFA_AttributeValue::Center:
175 dwExtendedStyle |=
176 FWL_STYLEEXT_CMB_EditHCenter | FWL_STYLEEXT_CMB_ListItemCenterAlign;
177 break;
178 case XFA_AttributeValue::Justify:
179 dwExtendedStyle |= FWL_STYLEEXT_CMB_EditJustified;
180 break;
181 case XFA_AttributeValue::JustifyAll:
182 break;
183 case XFA_AttributeValue::Radix:
184 break;
185 case XFA_AttributeValue::Right:
186 break;
187 default:
188 dwExtendedStyle |=
189 FWL_STYLEEXT_CMB_EditHNear | FWL_STYLEEXT_CMB_ListItemLeftAlign;
190 break;
191 }
192
193 switch (para->GetVerticalAlign()) {
194 case XFA_AttributeValue::Middle:
195 dwExtendedStyle |= FWL_STYLEEXT_CMB_EditVCenter;
196 break;
197 case XFA_AttributeValue::Bottom:
198 dwExtendedStyle |= FWL_STYLEEXT_CMB_EditVFar;
199 break;
200 default:
201 dwExtendedStyle |= FWL_STYLEEXT_CMB_EditVNear;
202 break;
203 }
204 return dwExtendedStyle;
205 }
206
UpdateFWLData()207 bool CXFA_FFComboBox::UpdateFWLData() {
208 auto* pComboBox = ToComboBox(GetNormalWidget());
209 if (!pComboBox)
210 return false;
211
212 // Prevents destruction of the CXFA_ContentLayoutItem that owns |this|.
213 RetainPtr<CXFA_ContentLayoutItem> retainer(m_pLayoutItem.Get());
214 std::vector<int32_t> iSelArray = m_pNode->GetSelectedItems();
215 if (!iSelArray.empty()) {
216 pComboBox->SetCurSel(iSelArray.front());
217 } else {
218 pComboBox->SetCurSel(-1);
219 pComboBox->SetEditText(m_pNode->GetValue(XFA_VALUEPICTURE_Raw));
220 }
221 pComboBox->Update();
222 return true;
223 }
224
CanUndo()225 bool CXFA_FFComboBox::CanUndo() {
226 return m_pNode->IsChoiceListAllowTextEntry() &&
227 ToComboBox(GetNormalWidget())->EditCanUndo();
228 }
229
CanRedo()230 bool CXFA_FFComboBox::CanRedo() {
231 return m_pNode->IsChoiceListAllowTextEntry() &&
232 ToComboBox(GetNormalWidget())->EditCanRedo();
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
Undo()252 bool CXFA_FFComboBox::Undo() {
253 // Prevents destruction of the CXFA_ContentLayoutItem that owns |this|.
254 RetainPtr<CXFA_ContentLayoutItem> retain_layout(m_pLayoutItem.Get());
255
256 return m_pNode->IsChoiceListAllowTextEntry() &&
257 ToComboBox(GetNormalWidget())->EditUndo();
258 }
259
Redo()260 bool CXFA_FFComboBox::Redo() {
261 // Prevents destruction of the CXFA_ContentLayoutItem that owns |this|.
262 RetainPtr<CXFA_ContentLayoutItem> retain_layout(m_pLayoutItem.Get());
263
264 return m_pNode->IsChoiceListAllowTextEntry() &&
265 ToComboBox(GetNormalWidget())->EditRedo();
266 }
267
Copy()268 Optional<WideString> CXFA_FFComboBox::Copy() {
269 // Prevents destruction of the CXFA_ContentLayoutItem that owns |this|.
270 RetainPtr<CXFA_ContentLayoutItem> retain_layout(m_pLayoutItem.Get());
271
272 return ToComboBox(GetNormalWidget())->EditCopy();
273 }
274
Cut()275 Optional<WideString> CXFA_FFComboBox::Cut() {
276 // Prevents destruction of the CXFA_ContentLayoutItem that owns |this|.
277 RetainPtr<CXFA_ContentLayoutItem> retain_layout(m_pLayoutItem.Get());
278
279 if (!m_pNode->IsChoiceListAllowTextEntry())
280 return {};
281
282 return ToComboBox(GetNormalWidget())->EditCut();
283 }
284
Paste(const WideString & wsPaste)285 bool CXFA_FFComboBox::Paste(const WideString& wsPaste) {
286 // Prevents destruction of the CXFA_ContentLayoutItem that owns |this|.
287 RetainPtr<CXFA_ContentLayoutItem> retain_layout(m_pLayoutItem.Get());
288
289 return m_pNode->IsChoiceListAllowTextEntry() &&
290 ToComboBox(GetNormalWidget())->EditPaste(wsPaste);
291 }
292
SelectAll()293 void CXFA_FFComboBox::SelectAll() {
294 // Prevents destruction of the CXFA_ContentLayoutItem that owns |this|.
295 RetainPtr<CXFA_ContentLayoutItem> retain_layout(m_pLayoutItem.Get());
296
297 ToComboBox(GetNormalWidget())->EditSelectAll();
298 }
299
Delete()300 void CXFA_FFComboBox::Delete() {
301 // Prevents destruction of the CXFA_ContentLayoutItem that owns |this|.
302 RetainPtr<CXFA_ContentLayoutItem> retain_layout(m_pLayoutItem.Get());
303
304 ToComboBox(GetNormalWidget())->EditDelete();
305 }
306
DeSelect()307 void CXFA_FFComboBox::DeSelect() {
308 // Prevents destruction of the CXFA_ContentLayoutItem that owns |this|.
309 RetainPtr<CXFA_ContentLayoutItem> retain_layout(m_pLayoutItem.Get());
310
311 ToComboBox(GetNormalWidget())->EditDeSelect();
312 }
313
GetText()314 WideString CXFA_FFComboBox::GetText() {
315 return GetCurrentText();
316 }
317
GetFormFieldType()318 FormFieldType CXFA_FFComboBox::GetFormFieldType() {
319 return FormFieldType::kXFA_ComboBox;
320 }
321
SetItemState(int32_t nIndex,bool bSelected)322 void CXFA_FFComboBox::SetItemState(int32_t nIndex, bool bSelected) {
323 ToComboBox(GetNormalWidget())->SetCurSel(bSelected ? nIndex : -1);
324 GetNormalWidget()->Update();
325 InvalidateRect();
326 }
327
InsertItem(const WideString & wsLabel,int32_t nIndex)328 void CXFA_FFComboBox::InsertItem(const WideString& wsLabel, int32_t nIndex) {
329 ToComboBox(GetNormalWidget())->AddString(wsLabel);
330 GetNormalWidget()->Update();
331 InvalidateRect();
332 }
333
DeleteItem(int32_t nIndex)334 void CXFA_FFComboBox::DeleteItem(int32_t nIndex) {
335 if (nIndex < 0)
336 ToComboBox(GetNormalWidget())->RemoveAll();
337 else
338 ToComboBox(GetNormalWidget())->RemoveAt(nIndex);
339
340 GetNormalWidget()->Update();
341 InvalidateRect();
342 }
343
OnTextChanged(CFWL_Widget * pWidget,const WideString & wsChanged)344 void CXFA_FFComboBox::OnTextChanged(CFWL_Widget* pWidget,
345 const WideString& wsChanged) {
346 // Prevents destruction of the CXFA_ContentLayoutItem that owns |this|.
347 RetainPtr<CXFA_ContentLayoutItem> retainer(m_pLayoutItem.Get());
348
349 CXFA_EventParam eParam;
350 eParam.m_wsPrevText = m_pNode->GetValue(XFA_VALUEPICTURE_Raw);
351 eParam.m_wsChange = wsChanged;
352 FWLEventSelChange(&eParam);
353 }
354
OnSelectChanged(CFWL_Widget * pWidget,bool bLButtonUp)355 void CXFA_FFComboBox::OnSelectChanged(CFWL_Widget* pWidget, bool bLButtonUp) {
356 // Prevents destruction of the CXFA_ContentLayoutItem that owns |this|.
357 RetainPtr<CXFA_ContentLayoutItem> retainer(m_pLayoutItem.Get());
358
359 CXFA_EventParam eParam;
360 eParam.m_wsPrevText = m_pNode->GetValue(XFA_VALUEPICTURE_Raw);
361 FWLEventSelChange(&eParam);
362 if (m_pNode->IsChoiceListCommitOnSelect() && bLButtonUp)
363 m_pDocView->SetFocusNode(nullptr);
364 }
365
OnPreOpen(CFWL_Widget * pWidget)366 void CXFA_FFComboBox::OnPreOpen(CFWL_Widget* pWidget) {
367 // Prevents destruction of the CXFA_ContentLayoutItem that owns |this|.
368 RetainPtr<CXFA_ContentLayoutItem> retainer(m_pLayoutItem.Get());
369
370 CXFA_EventParam eParam;
371 eParam.m_eType = XFA_EVENT_PreOpen;
372 eParam.m_pTarget = m_pNode.Get();
373 m_pNode->ProcessEvent(GetDocView(), XFA_AttributeValue::PreOpen, &eParam);
374 }
375
OnPostOpen(CFWL_Widget * pWidget)376 void CXFA_FFComboBox::OnPostOpen(CFWL_Widget* pWidget) {
377 // Prevents destruction of the CXFA_ContentLayoutItem that owns |this|.
378 RetainPtr<CXFA_ContentLayoutItem> retainer(m_pLayoutItem.Get());
379
380 CXFA_EventParam eParam;
381 eParam.m_eType = XFA_EVENT_PostOpen;
382 eParam.m_pTarget = m_pNode.Get();
383 m_pNode->ProcessEvent(GetDocView(), XFA_AttributeValue::PostOpen, &eParam);
384 }
385
OnProcessMessage(CFWL_Message * pMessage)386 void CXFA_FFComboBox::OnProcessMessage(CFWL_Message* pMessage) {
387 // Prevents destruction of the CXFA_ContentLayoutItem that owns |this|.
388 RetainPtr<CXFA_ContentLayoutItem> retainer(m_pLayoutItem.Get());
389
390 m_pOldDelegate->OnProcessMessage(pMessage);
391 }
392
OnProcessEvent(CFWL_Event * pEvent)393 void CXFA_FFComboBox::OnProcessEvent(CFWL_Event* pEvent) {
394 // Prevents destruction of the CXFA_ContentLayoutItem that owns |this|.
395 RetainPtr<CXFA_ContentLayoutItem> retain_layout(m_pLayoutItem.Get());
396
397 CXFA_FFField::OnProcessEvent(pEvent);
398 switch (pEvent->GetType()) {
399 case CFWL_Event::Type::SelectChanged: {
400 auto* postEvent = static_cast<CFWL_EventSelectChanged*>(pEvent);
401 OnSelectChanged(GetNormalWidget(), postEvent->bLButtonUp);
402 break;
403 }
404 case CFWL_Event::Type::EditChanged: {
405 WideString wsChanged;
406 OnTextChanged(GetNormalWidget(), wsChanged);
407 break;
408 }
409 case CFWL_Event::Type::PreDropDown: {
410 OnPreOpen(GetNormalWidget());
411 break;
412 }
413 case CFWL_Event::Type::PostDropDown: {
414 OnPostOpen(GetNormalWidget());
415 break;
416 }
417 default:
418 break;
419 }
420 m_pOldDelegate->OnProcessEvent(pEvent);
421 }
422
OnDrawWidget(CXFA_Graphics * pGraphics,const CFX_Matrix & matrix)423 void CXFA_FFComboBox::OnDrawWidget(CXFA_Graphics* pGraphics,
424 const CFX_Matrix& matrix) {
425 // Prevents destruction of the CXFA_ContentLayoutItem that owns |this|.
426 RetainPtr<CXFA_ContentLayoutItem> retainer(m_pLayoutItem.Get());
427
428 m_pOldDelegate->OnDrawWidget(pGraphics, matrix);
429 }
430