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