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