1 // Copyright 2017 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_fftextedit.h" 8 9 #include <utility> 10 11 #include "xfa/fwl/cfwl_datetimepicker.h" 12 #include "xfa/fwl/cfwl_edit.h" 13 #include "xfa/fwl/cfwl_eventcheckword.h" 14 #include "xfa/fwl/cfwl_eventtarget.h" 15 #include "xfa/fwl/cfwl_eventtextchanged.h" 16 #include "xfa/fwl/cfwl_messagekillfocus.h" 17 #include "xfa/fwl/cfwl_messagesetfocus.h" 18 #include "xfa/fwl/cfwl_notedriver.h" 19 #include "xfa/fxfa/cxfa_eventparam.h" 20 #include "xfa/fxfa/cxfa_ffapp.h" 21 #include "xfa/fxfa/cxfa_ffdoc.h" 22 #include "xfa/fxfa/parser/cxfa_node.h" 23 #include "xfa/fxfa/parser/cxfa_para.h" 24 25 namespace { 26 27 CFWL_Edit* ToEdit(CFWL_Widget* widget) { 28 return static_cast<CFWL_Edit*>(widget); 29 } 30 31 } // namespace 32 33 CXFA_FFTextEdit::CXFA_FFTextEdit(CXFA_Node* pNode) 34 : CXFA_FFField(pNode), m_pOldDelegate(nullptr) {} 35 36 CXFA_FFTextEdit::~CXFA_FFTextEdit() { 37 if (m_pNormalWidget) { 38 CFWL_NoteDriver* pNoteDriver = 39 m_pNormalWidget->GetOwnerApp()->GetNoteDriver(); 40 pNoteDriver->UnregisterEventTarget(m_pNormalWidget.get()); 41 } 42 } 43 44 bool CXFA_FFTextEdit::LoadWidget() { 45 auto pNewWidget = pdfium::MakeUnique<CFWL_Edit>( 46 GetFWLApp(), pdfium::MakeUnique<CFWL_WidgetProperties>(), nullptr); 47 CFWL_Edit* pFWLEdit = pNewWidget.get(); 48 m_pNormalWidget = std::move(pNewWidget); 49 m_pNormalWidget->SetLayoutItem(this); 50 51 CFWL_NoteDriver* pNoteDriver = 52 m_pNormalWidget->GetOwnerApp()->GetNoteDriver(); 53 pNoteDriver->RegisterEventTarget(m_pNormalWidget.get(), 54 m_pNormalWidget.get()); 55 m_pOldDelegate = m_pNormalWidget->GetDelegate(); 56 m_pNormalWidget->SetDelegate(this); 57 m_pNormalWidget->LockUpdate(); 58 UpdateWidgetProperty(); 59 60 pFWLEdit->SetText( 61 m_pNode->GetWidgetAcc()->GetValue(XFA_VALUEPICTURE_Display)); 62 m_pNormalWidget->UnlockUpdate(); 63 return CXFA_FFField::LoadWidget(); 64 } 65 66 void CXFA_FFTextEdit::UpdateWidgetProperty() { 67 CFWL_Edit* pWidget = static_cast<CFWL_Edit*>(m_pNormalWidget.get()); 68 if (!pWidget) 69 return; 70 71 uint32_t dwStyle = 0; 72 uint32_t dwExtendedStyle = 73 FWL_STYLEEXT_EDT_ShowScrollbarFocus | FWL_STYLEEXT_EDT_OuterScrollbar; 74 dwExtendedStyle |= UpdateUIProperty(); 75 if (m_pNode->GetWidgetAcc()->IsMultiLine()) { 76 dwExtendedStyle |= FWL_STYLEEXT_EDT_MultiLine | FWL_STYLEEXT_EDT_WantReturn; 77 if (!m_pNode->GetWidgetAcc()->IsVerticalScrollPolicyOff()) { 78 dwStyle |= FWL_WGTSTYLE_VScroll; 79 dwExtendedStyle |= FWL_STYLEEXT_EDT_AutoVScroll; 80 } 81 } else if (!m_pNode->GetWidgetAcc()->IsHorizontalScrollPolicyOff()) { 82 dwExtendedStyle |= FWL_STYLEEXT_EDT_AutoHScroll; 83 } 84 if (!m_pNode->IsOpenAccess() || !GetDoc()->GetXFADoc()->IsInteractive()) { 85 dwExtendedStyle |= FWL_STYLEEXT_EDT_ReadOnly; 86 dwExtendedStyle |= FWL_STYLEEXT_EDT_MultiLine; 87 } 88 89 XFA_Element eType; 90 int32_t iMaxChars; 91 std::tie(eType, iMaxChars) = m_pNode->GetWidgetAcc()->GetMaxChars(); 92 if (eType == XFA_Element::ExData) 93 iMaxChars = 0; 94 95 Optional<int32_t> numCells = m_pNode->GetWidgetAcc()->GetNumberOfCells(); 96 if (!numCells) { 97 pWidget->SetLimit(iMaxChars); 98 } else if (*numCells == 0) { 99 dwExtendedStyle |= FWL_STYLEEXT_EDT_CombText; 100 pWidget->SetLimit(iMaxChars > 0 ? iMaxChars : 1); 101 } else { 102 dwExtendedStyle |= FWL_STYLEEXT_EDT_CombText; 103 pWidget->SetLimit(*numCells); 104 } 105 106 dwExtendedStyle |= GetAlignment(); 107 m_pNormalWidget->ModifyStyles(dwStyle, 0xFFFFFFFF); 108 m_pNormalWidget->ModifyStylesEx(dwExtendedStyle, 0xFFFFFFFF); 109 } 110 111 bool CXFA_FFTextEdit::OnLButtonDown(uint32_t dwFlags, const CFX_PointF& point) { 112 if (!PtInActiveRect(point)) 113 return false; 114 if (!IsFocused()) { 115 m_dwStatus |= XFA_WidgetStatus_Focused; 116 UpdateFWLData(); 117 AddInvalidateRect(); 118 } 119 120 SetButtonDown(true); 121 CFWL_MessageMouse ms(nullptr, m_pNormalWidget.get()); 122 ms.m_dwCmd = FWL_MouseCommand::LeftButtonDown; 123 ms.m_dwFlags = dwFlags; 124 ms.m_pos = FWLToClient(point); 125 TranslateFWLMessage(&ms); 126 return true; 127 } 128 129 bool CXFA_FFTextEdit::OnRButtonDown(uint32_t dwFlags, const CFX_PointF& point) { 130 if (!m_pNode->IsOpenAccess()) 131 return false; 132 if (!PtInActiveRect(point)) 133 return false; 134 if (!IsFocused()) { 135 m_dwStatus |= XFA_WidgetStatus_Focused; 136 UpdateFWLData(); 137 AddInvalidateRect(); 138 } 139 140 SetButtonDown(true); 141 CFWL_MessageMouse ms(nullptr, nullptr); 142 ms.m_dwCmd = FWL_MouseCommand::RightButtonDown; 143 ms.m_dwFlags = dwFlags; 144 ms.m_pos = FWLToClient(point); 145 TranslateFWLMessage(&ms); 146 return true; 147 } 148 149 bool CXFA_FFTextEdit::OnRButtonUp(uint32_t dwFlags, const CFX_PointF& point) { 150 if (!CXFA_FFField::OnRButtonUp(dwFlags, point)) 151 return false; 152 153 GetDoc()->GetDocEnvironment()->PopupMenu(this, point); 154 return true; 155 } 156 157 bool CXFA_FFTextEdit::OnSetFocus(CXFA_FFWidget* pOldWidget) { 158 m_dwStatus &= ~XFA_WidgetStatus_TextEditValueChanged; 159 if (!IsFocused()) { 160 m_dwStatus |= XFA_WidgetStatus_Focused; 161 UpdateFWLData(); 162 AddInvalidateRect(); 163 } 164 CXFA_FFWidget::OnSetFocus(pOldWidget); 165 CFWL_MessageSetFocus ms(nullptr, m_pNormalWidget.get()); 166 TranslateFWLMessage(&ms); 167 return true; 168 } 169 170 bool CXFA_FFTextEdit::OnKillFocus(CXFA_FFWidget* pNewWidget) { 171 CFWL_MessageKillFocus ms(nullptr, m_pNormalWidget.get()); 172 TranslateFWLMessage(&ms); 173 m_dwStatus &= ~XFA_WidgetStatus_Focused; 174 175 SetEditScrollOffset(); 176 ProcessCommittedData(); 177 UpdateFWLData(); 178 AddInvalidateRect(); 179 CXFA_FFWidget::OnKillFocus(pNewWidget); 180 181 m_dwStatus &= ~XFA_WidgetStatus_TextEditValueChanged; 182 return true; 183 } 184 185 bool CXFA_FFTextEdit::CommitData() { 186 WideString wsText = static_cast<CFWL_Edit*>(m_pNormalWidget.get())->GetText(); 187 if (m_pNode->GetWidgetAcc()->SetValue(XFA_VALUEPICTURE_Edit, wsText)) { 188 m_pNode->GetWidgetAcc()->UpdateUIDisplay(GetDoc()->GetDocView(), this); 189 return true; 190 } 191 ValidateNumberField(wsText); 192 return false; 193 } 194 195 void CXFA_FFTextEdit::ValidateNumberField(const WideString& wsText) { 196 CXFA_WidgetAcc* pAcc = GetNode()->GetWidgetAcc(); 197 if (!pAcc || pAcc->GetUIType() != XFA_Element::NumericEdit) 198 return; 199 200 IXFA_AppProvider* pAppProvider = GetApp()->GetAppProvider(); 201 if (!pAppProvider) 202 return; 203 204 WideString wsSomField = pAcc->GetNode()->GetSOMExpression(); 205 pAppProvider->MsgBox(WideString::Format(L"%ls can not contain %ls", 206 wsText.c_str(), wsSomField.c_str()), 207 pAppProvider->GetAppTitle(), XFA_MBICON_Error, 208 XFA_MB_OK); 209 } 210 211 bool CXFA_FFTextEdit::IsDataChanged() { 212 return (m_dwStatus & XFA_WidgetStatus_TextEditValueChanged) != 0; 213 } 214 215 uint32_t CXFA_FFTextEdit::GetAlignment() { 216 CXFA_Para* para = m_pNode->GetParaIfExists(); 217 if (!para) 218 return 0; 219 220 uint32_t dwExtendedStyle = 0; 221 switch (para->GetHorizontalAlign()) { 222 case XFA_AttributeEnum::Center: 223 dwExtendedStyle |= FWL_STYLEEXT_EDT_HCenter; 224 break; 225 case XFA_AttributeEnum::Justify: 226 dwExtendedStyle |= FWL_STYLEEXT_EDT_Justified; 227 break; 228 case XFA_AttributeEnum::JustifyAll: 229 case XFA_AttributeEnum::Radix: 230 break; 231 case XFA_AttributeEnum::Right: 232 dwExtendedStyle |= FWL_STYLEEXT_EDT_HFar; 233 break; 234 default: 235 dwExtendedStyle |= FWL_STYLEEXT_EDT_HNear; 236 break; 237 } 238 239 switch (para->GetVerticalAlign()) { 240 case XFA_AttributeEnum::Middle: 241 dwExtendedStyle |= FWL_STYLEEXT_EDT_VCenter; 242 break; 243 case XFA_AttributeEnum::Bottom: 244 dwExtendedStyle |= FWL_STYLEEXT_EDT_VFar; 245 break; 246 default: 247 dwExtendedStyle |= FWL_STYLEEXT_EDT_VNear; 248 break; 249 } 250 return dwExtendedStyle; 251 } 252 253 bool CXFA_FFTextEdit::UpdateFWLData() { 254 if (!m_pNormalWidget) 255 return false; 256 257 CFWL_Edit* pEdit = static_cast<CFWL_Edit*>(m_pNormalWidget.get()); 258 XFA_VALUEPICTURE eType = XFA_VALUEPICTURE_Display; 259 if (IsFocused()) 260 eType = XFA_VALUEPICTURE_Edit; 261 262 bool bUpdate = false; 263 if (m_pNode->GetWidgetAcc()->GetUIType() == XFA_Element::TextEdit && 264 !m_pNode->GetWidgetAcc()->GetNumberOfCells()) { 265 XFA_Element elementType; 266 int32_t iMaxChars; 267 std::tie(elementType, iMaxChars) = m_pNode->GetWidgetAcc()->GetMaxChars(); 268 if (elementType == XFA_Element::ExData) 269 iMaxChars = eType == XFA_VALUEPICTURE_Edit ? iMaxChars : 0; 270 if (pEdit->GetLimit() != iMaxChars) { 271 pEdit->SetLimit(iMaxChars); 272 bUpdate = true; 273 } 274 } else if (m_pNode->GetWidgetAcc()->GetUIType() == XFA_Element::Barcode) { 275 int32_t nDataLen = 0; 276 if (eType == XFA_VALUEPICTURE_Edit) 277 nDataLen = m_pNode->GetBarcodeAttribute_DataLength().value_or(0); 278 279 pEdit->SetLimit(nDataLen); 280 bUpdate = true; 281 } 282 283 WideString wsText = m_pNode->GetWidgetAcc()->GetValue(eType); 284 WideString wsOldText = pEdit->GetText(); 285 if (wsText != wsOldText || (eType == XFA_VALUEPICTURE_Edit && bUpdate)) { 286 pEdit->SetText(wsText); 287 bUpdate = true; 288 } 289 if (bUpdate) 290 m_pNormalWidget->Update(); 291 292 return true; 293 } 294 295 void CXFA_FFTextEdit::OnTextChanged(CFWL_Widget* pWidget, 296 const WideString& wsChanged, 297 const WideString& wsPrevText) { 298 m_dwStatus |= XFA_WidgetStatus_TextEditValueChanged; 299 CXFA_EventParam eParam; 300 eParam.m_eType = XFA_EVENT_Change; 301 eParam.m_wsChange = wsChanged; 302 eParam.m_pTarget = m_pNode->GetWidgetAcc(); 303 eParam.m_wsPrevText = wsPrevText; 304 CFWL_Edit* pEdit = static_cast<CFWL_Edit*>(m_pNormalWidget.get()); 305 if (m_pNode->GetWidgetAcc()->GetUIType() == XFA_Element::DateTimeEdit) { 306 CFWL_DateTimePicker* pDateTime = (CFWL_DateTimePicker*)pEdit; 307 eParam.m_wsNewText = pDateTime->GetEditText(); 308 if (pDateTime->HasSelection()) { 309 size_t count; 310 std::tie(eParam.m_iSelStart, count) = pDateTime->GetSelection(); 311 eParam.m_iSelEnd = eParam.m_iSelStart + count; 312 } 313 } else { 314 eParam.m_wsNewText = pEdit->GetText(); 315 if (pEdit->HasSelection()) 316 std::tie(eParam.m_iSelStart, eParam.m_iSelEnd) = pEdit->GetSelection(); 317 } 318 m_pNode->ProcessEvent(GetDocView(), XFA_AttributeEnum::Change, &eParam); 319 } 320 321 void CXFA_FFTextEdit::OnTextFull(CFWL_Widget* pWidget) { 322 CXFA_EventParam eParam; 323 eParam.m_eType = XFA_EVENT_Full; 324 eParam.m_pTarget = m_pNode->GetWidgetAcc(); 325 m_pNode->ProcessEvent(GetDocView(), XFA_AttributeEnum::Full, &eParam); 326 } 327 328 bool CXFA_FFTextEdit::CheckWord(const ByteStringView& sWord) { 329 return sWord.IsEmpty() || 330 m_pNode->GetWidgetAcc()->GetUIType() != XFA_Element::TextEdit; 331 } 332 333 void CXFA_FFTextEdit::OnProcessMessage(CFWL_Message* pMessage) { 334 m_pOldDelegate->OnProcessMessage(pMessage); 335 } 336 337 void CXFA_FFTextEdit::OnProcessEvent(CFWL_Event* pEvent) { 338 CXFA_FFField::OnProcessEvent(pEvent); 339 switch (pEvent->GetType()) { 340 case CFWL_Event::Type::TextChanged: { 341 CFWL_EventTextChanged* event = 342 static_cast<CFWL_EventTextChanged*>(pEvent); 343 WideString wsChange; 344 OnTextChanged(m_pNormalWidget.get(), wsChange, event->wsPrevText); 345 break; 346 } 347 case CFWL_Event::Type::TextFull: { 348 OnTextFull(m_pNormalWidget.get()); 349 break; 350 } 351 case CFWL_Event::Type::CheckWord: { 352 WideString wstr(L"FWL_EVENT_DTP_SelectChanged"); 353 CFWL_EventCheckWord* event = static_cast<CFWL_EventCheckWord*>(pEvent); 354 event->bCheckWord = CheckWord(event->bsWord.AsStringView()); 355 break; 356 } 357 default: 358 break; 359 } 360 m_pOldDelegate->OnProcessEvent(pEvent); 361 } 362 363 void CXFA_FFTextEdit::OnDrawWidget(CXFA_Graphics* pGraphics, 364 const CFX_Matrix& matrix) { 365 m_pOldDelegate->OnDrawWidget(pGraphics, matrix); 366 } 367 368 bool CXFA_FFTextEdit::CanUndo() { 369 return ToEdit(m_pNormalWidget.get())->CanUndo(); 370 } 371 372 bool CXFA_FFTextEdit::CanRedo() { 373 return ToEdit(m_pNormalWidget.get())->CanRedo(); 374 } 375 376 bool CXFA_FFTextEdit::Undo() { 377 return ToEdit(m_pNormalWidget.get())->Undo(); 378 } 379 380 bool CXFA_FFTextEdit::Redo() { 381 return ToEdit(m_pNormalWidget.get())->Redo(); 382 } 383 384 bool CXFA_FFTextEdit::CanCopy() { 385 return ToEdit(m_pNormalWidget.get())->HasSelection(); 386 } 387 388 bool CXFA_FFTextEdit::CanCut() { 389 if (ToEdit(m_pNormalWidget.get())->GetStylesEx() & FWL_STYLEEXT_EDT_ReadOnly) 390 return false; 391 return ToEdit(m_pNormalWidget.get())->HasSelection(); 392 } 393 394 bool CXFA_FFTextEdit::CanPaste() { 395 return !(ToEdit(m_pNormalWidget.get())->GetStylesEx() & 396 FWL_STYLEEXT_EDT_ReadOnly); 397 } 398 399 bool CXFA_FFTextEdit::CanSelectAll() { 400 return ToEdit(m_pNormalWidget.get())->GetTextLength() > 0; 401 } 402 403 Optional<WideString> CXFA_FFTextEdit::Copy() { 404 return ToEdit(m_pNormalWidget.get())->Copy(); 405 } 406 407 Optional<WideString> CXFA_FFTextEdit::Cut() { 408 return ToEdit(m_pNormalWidget.get())->Cut(); 409 } 410 411 bool CXFA_FFTextEdit::Paste(const WideString& wsPaste) { 412 return ToEdit(m_pNormalWidget.get())->Paste(wsPaste); 413 } 414 415 void CXFA_FFTextEdit::SelectAll() { 416 ToEdit(m_pNormalWidget.get())->SelectAll(); 417 } 418 419 void CXFA_FFTextEdit::Delete() { 420 ToEdit(m_pNormalWidget.get())->ClearText(); 421 } 422 423 void CXFA_FFTextEdit::DeSelect() { 424 ToEdit(m_pNormalWidget.get())->ClearSelection(); 425 } 426 427 FormFieldType CXFA_FFTextEdit::GetFormFieldType() { 428 return FormFieldType::kXFA_TextField; 429 } 430