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
ToEdit(CFWL_Widget * widget)27 CFWL_Edit* ToEdit(CFWL_Widget* widget) {
28 return static_cast<CFWL_Edit*>(widget);
29 }
30
31 } // namespace
32
CXFA_FFTextEdit(CXFA_Node * pNode)33 CXFA_FFTextEdit::CXFA_FFTextEdit(CXFA_Node* pNode)
34 : CXFA_FFField(pNode), m_pOldDelegate(nullptr) {}
35
~CXFA_FFTextEdit()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
LoadWidget()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
UpdateWidgetProperty()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
OnLButtonDown(uint32_t dwFlags,const CFX_PointF & point)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
OnRButtonDown(uint32_t dwFlags,const CFX_PointF & point)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
OnRButtonUp(uint32_t dwFlags,const CFX_PointF & point)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
OnSetFocus(CXFA_FFWidget * pOldWidget)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
OnKillFocus(CXFA_FFWidget * pNewWidget)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
CommitData()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
ValidateNumberField(const WideString & wsText)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
IsDataChanged()211 bool CXFA_FFTextEdit::IsDataChanged() {
212 return (m_dwStatus & XFA_WidgetStatus_TextEditValueChanged) != 0;
213 }
214
GetAlignment()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
UpdateFWLData()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
OnTextChanged(CFWL_Widget * pWidget,const WideString & wsChanged,const WideString & wsPrevText)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
OnTextFull(CFWL_Widget * pWidget)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
CheckWord(const ByteStringView & sWord)328 bool CXFA_FFTextEdit::CheckWord(const ByteStringView& sWord) {
329 return sWord.IsEmpty() ||
330 m_pNode->GetWidgetAcc()->GetUIType() != XFA_Element::TextEdit;
331 }
332
OnProcessMessage(CFWL_Message * pMessage)333 void CXFA_FFTextEdit::OnProcessMessage(CFWL_Message* pMessage) {
334 m_pOldDelegate->OnProcessMessage(pMessage);
335 }
336
OnProcessEvent(CFWL_Event * pEvent)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
OnDrawWidget(CXFA_Graphics * pGraphics,const CFX_Matrix & matrix)363 void CXFA_FFTextEdit::OnDrawWidget(CXFA_Graphics* pGraphics,
364 const CFX_Matrix& matrix) {
365 m_pOldDelegate->OnDrawWidget(pGraphics, matrix);
366 }
367
CanUndo()368 bool CXFA_FFTextEdit::CanUndo() {
369 return ToEdit(m_pNormalWidget.get())->CanUndo();
370 }
371
CanRedo()372 bool CXFA_FFTextEdit::CanRedo() {
373 return ToEdit(m_pNormalWidget.get())->CanRedo();
374 }
375
Undo()376 bool CXFA_FFTextEdit::Undo() {
377 return ToEdit(m_pNormalWidget.get())->Undo();
378 }
379
Redo()380 bool CXFA_FFTextEdit::Redo() {
381 return ToEdit(m_pNormalWidget.get())->Redo();
382 }
383
CanCopy()384 bool CXFA_FFTextEdit::CanCopy() {
385 return ToEdit(m_pNormalWidget.get())->HasSelection();
386 }
387
CanCut()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
CanPaste()394 bool CXFA_FFTextEdit::CanPaste() {
395 return !(ToEdit(m_pNormalWidget.get())->GetStylesEx() &
396 FWL_STYLEEXT_EDT_ReadOnly);
397 }
398
CanSelectAll()399 bool CXFA_FFTextEdit::CanSelectAll() {
400 return ToEdit(m_pNormalWidget.get())->GetTextLength() > 0;
401 }
402
Copy()403 Optional<WideString> CXFA_FFTextEdit::Copy() {
404 return ToEdit(m_pNormalWidget.get())->Copy();
405 }
406
Cut()407 Optional<WideString> CXFA_FFTextEdit::Cut() {
408 return ToEdit(m_pNormalWidget.get())->Cut();
409 }
410
Paste(const WideString & wsPaste)411 bool CXFA_FFTextEdit::Paste(const WideString& wsPaste) {
412 return ToEdit(m_pNormalWidget.get())->Paste(wsPaste);
413 }
414
SelectAll()415 void CXFA_FFTextEdit::SelectAll() {
416 ToEdit(m_pNormalWidget.get())->SelectAll();
417 }
418
Delete()419 void CXFA_FFTextEdit::Delete() {
420 ToEdit(m_pNormalWidget.get())->ClearText();
421 }
422
DeSelect()423 void CXFA_FFTextEdit::DeSelect() {
424 ToEdit(m_pNormalWidget.get())->ClearSelection();
425 }
426
GetFormFieldType()427 FormFieldType CXFA_FFTextEdit::GetFormFieldType() {
428 return FormFieldType::kXFA_TextField;
429 }
430