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 "third_party/base/ptr_util.h"
12 #include "xfa/fwl/cfwl_datetimepicker.h"
13 #include "xfa/fwl/cfwl_edit.h"
14 #include "xfa/fwl/cfwl_eventtarget.h"
15 #include "xfa/fwl/cfwl_eventtextwillchange.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/cxfa_ffdocview.h"
23 #include "xfa/fxfa/parser/cxfa_barcode.h"
24 #include "xfa/fxfa/parser/cxfa_node.h"
25 #include "xfa/fxfa/parser/cxfa_para.h"
26
27 namespace {
28
ToEdit(CFWL_Widget * widget)29 CFWL_Edit* ToEdit(CFWL_Widget* widget) {
30 return static_cast<CFWL_Edit*>(widget);
31 }
32
33 } // namespace
34
CXFA_FFTextEdit(CXFA_Node * pNode)35 CXFA_FFTextEdit::CXFA_FFTextEdit(CXFA_Node* pNode) : CXFA_FFField(pNode) {}
36
~CXFA_FFTextEdit()37 CXFA_FFTextEdit::~CXFA_FFTextEdit() {
38 if (GetNormalWidget()) {
39 CFWL_NoteDriver* pNoteDriver =
40 GetNormalWidget()->GetOwnerApp()->GetNoteDriver();
41 pNoteDriver->UnregisterEventTarget(GetNormalWidget());
42 }
43 }
44
LoadWidget()45 bool CXFA_FFTextEdit::LoadWidget() {
46 ASSERT(!IsLoaded());
47
48 // Prevents destruction of the CXFA_ContentLayoutItem that owns |this|.
49 RetainPtr<CXFA_ContentLayoutItem> retain_layout(m_pLayoutItem.Get());
50
51 auto pNewWidget = pdfium::MakeUnique<CFWL_Edit>(
52 GetFWLApp(), pdfium::MakeUnique<CFWL_WidgetProperties>(), nullptr);
53 CFWL_Edit* pFWLEdit = pNewWidget.get();
54 SetNormalWidget(std::move(pNewWidget));
55 pFWLEdit->SetAdapterIface(this);
56
57 CFWL_NoteDriver* pNoteDriver = pFWLEdit->GetOwnerApp()->GetNoteDriver();
58 pNoteDriver->RegisterEventTarget(pFWLEdit, pFWLEdit);
59 m_pOldDelegate = pFWLEdit->GetDelegate();
60 pFWLEdit->SetDelegate(this);
61
62 {
63 CFWL_Widget::ScopedUpdateLock update_lock(pFWLEdit);
64 UpdateWidgetProperty();
65 pFWLEdit->SetText(m_pNode->GetValue(XFA_VALUEPICTURE_Display));
66 }
67
68 return CXFA_FFField::LoadWidget();
69 }
70
UpdateWidgetProperty()71 void CXFA_FFTextEdit::UpdateWidgetProperty() {
72 CFWL_Edit* pWidget = ToEdit(GetNormalWidget());
73 if (!pWidget)
74 return;
75
76 uint32_t dwStyle = 0;
77 uint32_t dwExtendedStyle =
78 FWL_STYLEEXT_EDT_ShowScrollbarFocus | FWL_STYLEEXT_EDT_OuterScrollbar;
79 dwExtendedStyle |= UpdateUIProperty();
80 if (m_pNode->IsMultiLine()) {
81 dwExtendedStyle |= FWL_STYLEEXT_EDT_MultiLine | FWL_STYLEEXT_EDT_WantReturn;
82 if (!m_pNode->IsVerticalScrollPolicyOff()) {
83 dwStyle |= FWL_WGTSTYLE_VScroll;
84 dwExtendedStyle |= FWL_STYLEEXT_EDT_AutoVScroll;
85 }
86 } else if (!m_pNode->IsHorizontalScrollPolicyOff()) {
87 dwExtendedStyle |= FWL_STYLEEXT_EDT_AutoHScroll;
88 }
89 if (!m_pNode->IsOpenAccess() || !GetDoc()->GetXFADoc()->IsInteractive()) {
90 dwExtendedStyle |= FWL_STYLEEXT_EDT_ReadOnly;
91 dwExtendedStyle |= FWL_STYLEEXT_EDT_MultiLine;
92 }
93
94 XFA_Element eType;
95 int32_t iMaxChars;
96 std::tie(eType, iMaxChars) = m_pNode->GetMaxChars();
97 if (eType == XFA_Element::ExData)
98 iMaxChars = 0;
99
100 Optional<int32_t> numCells = m_pNode->GetNumberOfCells();
101 if (!numCells) {
102 pWidget->SetLimit(iMaxChars);
103 } else if (*numCells == 0) {
104 dwExtendedStyle |= FWL_STYLEEXT_EDT_CombText;
105 pWidget->SetLimit(iMaxChars > 0 ? iMaxChars : 1);
106 } else {
107 dwExtendedStyle |= FWL_STYLEEXT_EDT_CombText;
108 pWidget->SetLimit(*numCells);
109 }
110
111 dwExtendedStyle |= GetAlignment();
112 GetNormalWidget()->ModifyStyles(dwStyle, 0xFFFFFFFF);
113 GetNormalWidget()->ModifyStylesEx(dwExtendedStyle, 0xFFFFFFFF);
114 }
115
AcceptsFocusOnButtonDown(uint32_t dwFlags,const CFX_PointF & point,FWL_MouseCommand command)116 bool CXFA_FFTextEdit::AcceptsFocusOnButtonDown(uint32_t dwFlags,
117 const CFX_PointF& point,
118 FWL_MouseCommand command) {
119 if (command == FWL_MouseCommand::RightButtonDown && !m_pNode->IsOpenAccess())
120 return false;
121 if (!PtInActiveRect(point))
122 return false;
123
124 return true;
125 }
126
OnLButtonDown(uint32_t dwFlags,const CFX_PointF & point)127 bool CXFA_FFTextEdit::OnLButtonDown(uint32_t dwFlags, const CFX_PointF& point) {
128 // Prevents destruction of the CXFA_ContentLayoutItem that owns |this|.
129 RetainPtr<CXFA_ContentLayoutItem> retainer(m_pLayoutItem.Get());
130
131 if (!IsFocused()) {
132 GetLayoutItem()->SetStatusBits(XFA_WidgetStatus_Focused);
133 UpdateFWLData();
134 InvalidateRect();
135 }
136 SetButtonDown(true);
137 SendMessageToFWLWidget(pdfium::MakeUnique<CFWL_MessageMouse>(
138 GetNormalWidget(), FWL_MouseCommand::LeftButtonDown, dwFlags,
139 FWLToClient(point)));
140
141 return true;
142 }
143
OnRButtonDown(uint32_t dwFlags,const CFX_PointF & point)144 bool CXFA_FFTextEdit::OnRButtonDown(uint32_t dwFlags, const CFX_PointF& point) {
145 // Prevents destruction of the CXFA_ContentLayoutItem that owns |this|.
146 RetainPtr<CXFA_ContentLayoutItem> retainer(m_pLayoutItem.Get());
147
148 if (!IsFocused()) {
149 GetLayoutItem()->SetStatusBits(XFA_WidgetStatus_Focused);
150 UpdateFWLData();
151 InvalidateRect();
152 }
153 SetButtonDown(true);
154 SendMessageToFWLWidget(pdfium::MakeUnique<CFWL_MessageMouse>(
155 nullptr, FWL_MouseCommand::RightButtonDown, dwFlags, FWLToClient(point)));
156
157 return true;
158 }
159
OnRButtonUp(uint32_t dwFlags,const CFX_PointF & point)160 bool CXFA_FFTextEdit::OnRButtonUp(uint32_t dwFlags, const CFX_PointF& point) {
161 // Prevents destruction of the CXFA_ContentLayoutItem that owns |this|.
162 RetainPtr<CXFA_ContentLayoutItem> retainer(m_pLayoutItem.Get());
163
164 if (!CXFA_FFField::OnRButtonUp(dwFlags, point))
165 return false;
166
167 GetDoc()->GetDocEnvironment()->PopupMenu(this, point);
168 return true;
169 }
170
OnSetFocus(CXFA_FFWidget * pOldWidget)171 bool CXFA_FFTextEdit::OnSetFocus(CXFA_FFWidget* pOldWidget) {
172 // Prevents destruction of the CXFA_ContentLayoutItem that owns |this|.
173 RetainPtr<CXFA_ContentLayoutItem> retainer(m_pLayoutItem.Get());
174
175 ObservedPtr<CXFA_FFWidget> pOldWatched(pOldWidget);
176 GetLayoutItem()->ClearStatusBits(XFA_WidgetStatus_TextEditValueChanged);
177 if (!IsFocused()) {
178 GetLayoutItem()->SetStatusBits(XFA_WidgetStatus_Focused);
179 UpdateFWLData();
180 InvalidateRect();
181 }
182 if (!CXFA_FFWidget::OnSetFocus(pOldWatched.Get()))
183 return false;
184
185 SendMessageToFWLWidget(
186 pdfium::MakeUnique<CFWL_MessageSetFocus>(nullptr, GetNormalWidget()));
187
188 return true;
189 }
190
OnKillFocus(CXFA_FFWidget * pNewWidget)191 bool CXFA_FFTextEdit::OnKillFocus(CXFA_FFWidget* pNewWidget) {
192 // Prevents destruction of the CXFA_ContentLayoutItem that owns |this|.
193 RetainPtr<CXFA_ContentLayoutItem> retainer(m_pLayoutItem.Get());
194
195 ObservedPtr<CXFA_FFWidget> pNewWatched(pNewWidget);
196 SendMessageToFWLWidget(
197 pdfium::MakeUnique<CFWL_MessageKillFocus>(nullptr, GetNormalWidget()));
198
199 GetLayoutItem()->ClearStatusBits(XFA_WidgetStatus_Focused);
200 SetEditScrollOffset();
201 ProcessCommittedData();
202 UpdateFWLData();
203 InvalidateRect();
204
205 if (!CXFA_FFWidget::OnKillFocus(pNewWatched.Get()))
206 return false;
207
208 GetLayoutItem()->ClearStatusBits(XFA_WidgetStatus_TextEditValueChanged);
209 return true;
210 }
211
CommitData()212 bool CXFA_FFTextEdit::CommitData() {
213 WideString wsText = ToEdit(GetNormalWidget())->GetText();
214 if (m_pNode->SetValue(XFA_VALUEPICTURE_Edit, wsText)) {
215 GetDoc()->GetDocView()->UpdateUIDisplay(m_pNode.Get(), this);
216 return true;
217 }
218 ValidateNumberField(wsText);
219 return false;
220 }
221
ValidateNumberField(const WideString & wsText)222 void CXFA_FFTextEdit::ValidateNumberField(const WideString& wsText) {
223 if (GetNode()->GetFFWidgetType() != XFA_FFWidgetType::kNumericEdit)
224 return;
225
226 IXFA_AppProvider* pAppProvider = GetAppProvider();
227 if (!pAppProvider)
228 return;
229
230 WideString wsSomField = GetNode()->GetSOMExpression();
231 pAppProvider->MsgBox(
232 wsText + WideString::FromASCII(" can not contain ") + wsSomField,
233 pAppProvider->GetAppTitle(), static_cast<uint32_t>(AlertIcon::kError),
234 static_cast<uint32_t>(AlertButton::kOK));
235 }
236
IsDataChanged()237 bool CXFA_FFTextEdit::IsDataChanged() {
238 return GetLayoutItem()->TestStatusBits(XFA_WidgetStatus_TextEditValueChanged);
239 }
240
GetAlignment()241 uint32_t CXFA_FFTextEdit::GetAlignment() {
242 CXFA_Para* para = m_pNode->GetParaIfExists();
243 if (!para)
244 return 0;
245
246 uint32_t dwExtendedStyle = 0;
247 switch (para->GetHorizontalAlign()) {
248 case XFA_AttributeValue::Center:
249 dwExtendedStyle |= FWL_STYLEEXT_EDT_HCenter;
250 break;
251 case XFA_AttributeValue::Justify:
252 dwExtendedStyle |= FWL_STYLEEXT_EDT_Justified;
253 break;
254 case XFA_AttributeValue::JustifyAll:
255 case XFA_AttributeValue::Radix:
256 break;
257 case XFA_AttributeValue::Right:
258 dwExtendedStyle |= FWL_STYLEEXT_EDT_HFar;
259 break;
260 default:
261 dwExtendedStyle |= FWL_STYLEEXT_EDT_HNear;
262 break;
263 }
264
265 switch (para->GetVerticalAlign()) {
266 case XFA_AttributeValue::Middle:
267 dwExtendedStyle |= FWL_STYLEEXT_EDT_VCenter;
268 break;
269 case XFA_AttributeValue::Bottom:
270 dwExtendedStyle |= FWL_STYLEEXT_EDT_VFar;
271 break;
272 default:
273 dwExtendedStyle |= FWL_STYLEEXT_EDT_VNear;
274 break;
275 }
276 return dwExtendedStyle;
277 }
278
UpdateFWLData()279 bool CXFA_FFTextEdit::UpdateFWLData() {
280 CFWL_Edit* pEdit = ToEdit(GetNormalWidget());
281 if (!pEdit)
282 return false;
283
284 // Prevents destruction of the CXFA_ContentLayoutItem that owns |this|.
285 RetainPtr<CXFA_ContentLayoutItem> retainer(m_pLayoutItem.Get());
286 XFA_VALUEPICTURE eType = XFA_VALUEPICTURE_Display;
287 if (IsFocused())
288 eType = XFA_VALUEPICTURE_Edit;
289
290 bool bUpdate = false;
291 if (m_pNode->GetFFWidgetType() == XFA_FFWidgetType::kTextEdit &&
292 !m_pNode->GetNumberOfCells()) {
293 XFA_Element elementType;
294 int32_t iMaxChars;
295 std::tie(elementType, iMaxChars) = m_pNode->GetMaxChars();
296 if (elementType == XFA_Element::ExData)
297 iMaxChars = eType == XFA_VALUEPICTURE_Edit ? iMaxChars : 0;
298 if (pEdit->GetLimit() != iMaxChars) {
299 pEdit->SetLimit(iMaxChars);
300 bUpdate = true;
301 }
302 } else if (m_pNode->GetFFWidgetType() == XFA_FFWidgetType::kBarcode) {
303 int32_t nDataLen = 0;
304 if (eType == XFA_VALUEPICTURE_Edit) {
305 nDataLen = static_cast<CXFA_Barcode*>(m_pNode->GetUIChildNode())
306 ->GetDataLength()
307 .value_or(0);
308 }
309
310 pEdit->SetLimit(nDataLen);
311 bUpdate = true;
312 }
313 WideString wsText = m_pNode->GetValue(eType);
314 WideString wsOldText = pEdit->GetText();
315 if (wsText != wsOldText || (eType == XFA_VALUEPICTURE_Edit && bUpdate)) {
316 pEdit->SetTextSkipNotify(wsText);
317 bUpdate = true;
318 }
319 if (bUpdate)
320 GetNormalWidget()->Update();
321
322 return true;
323 }
324
OnTextWillChange(CFWL_Widget * pWidget,CFWL_EventTextWillChange * event)325 void CXFA_FFTextEdit::OnTextWillChange(CFWL_Widget* pWidget,
326 CFWL_EventTextWillChange* event) {
327 // Prevents destruction of the CXFA_ContentLayoutItem that owns |this|.
328 RetainPtr<CXFA_ContentLayoutItem> retainer(m_pLayoutItem.Get());
329
330 GetLayoutItem()->SetStatusBits(XFA_WidgetStatus_TextEditValueChanged);
331
332 CXFA_EventParam eParam;
333 eParam.m_eType = XFA_EVENT_Change;
334 eParam.m_wsChange = event->change_text;
335 eParam.m_pTarget = m_pNode.Get();
336 eParam.m_wsPrevText = event->previous_text;
337 eParam.m_iSelStart = static_cast<int32_t>(event->selection_start);
338 eParam.m_iSelEnd = static_cast<int32_t>(event->selection_end);
339
340 m_pNode->ProcessEvent(GetDocView(), XFA_AttributeValue::Change, &eParam);
341
342 // Copy the data back out of the EventParam and into the TextChanged event so
343 // it can propagate back to the calling widget.
344 event->cancelled = eParam.m_bCancelAction;
345 event->change_text = std::move(eParam.m_wsChange);
346 event->selection_start = static_cast<size_t>(eParam.m_iSelStart);
347 event->selection_end = static_cast<size_t>(eParam.m_iSelEnd);
348 }
349
OnTextFull(CFWL_Widget * pWidget)350 void CXFA_FFTextEdit::OnTextFull(CFWL_Widget* pWidget) {
351 // Prevents destruction of the CXFA_ContentLayoutItem that owns |this|.
352 RetainPtr<CXFA_ContentLayoutItem> retainer(m_pLayoutItem.Get());
353
354 CXFA_EventParam eParam;
355 eParam.m_eType = XFA_EVENT_Full;
356 eParam.m_pTarget = m_pNode.Get();
357 m_pNode->ProcessEvent(GetDocView(), XFA_AttributeValue::Full, &eParam);
358 }
359
OnProcessMessage(CFWL_Message * pMessage)360 void CXFA_FFTextEdit::OnProcessMessage(CFWL_Message* pMessage) {
361 // Prevents destruction of the CXFA_ContentLayoutItem that owns |this|.
362 RetainPtr<CXFA_ContentLayoutItem> retainer(m_pLayoutItem.Get());
363
364 m_pOldDelegate->OnProcessMessage(pMessage);
365 }
366
OnProcessEvent(CFWL_Event * pEvent)367 void CXFA_FFTextEdit::OnProcessEvent(CFWL_Event* pEvent) {
368 // Prevents destruction of the CXFA_ContentLayoutItem that owns |this|.
369 RetainPtr<CXFA_ContentLayoutItem> retain_layout(m_pLayoutItem.Get());
370
371 CXFA_FFField::OnProcessEvent(pEvent);
372 switch (pEvent->GetType()) {
373 case CFWL_Event::Type::TextWillChange:
374 OnTextWillChange(GetNormalWidget(),
375 static_cast<CFWL_EventTextWillChange*>(pEvent));
376 break;
377 case CFWL_Event::Type::TextFull:
378 OnTextFull(GetNormalWidget());
379 break;
380 default:
381 break;
382 }
383 m_pOldDelegate->OnProcessEvent(pEvent);
384 }
385
OnDrawWidget(CXFA_Graphics * pGraphics,const CFX_Matrix & matrix)386 void CXFA_FFTextEdit::OnDrawWidget(CXFA_Graphics* pGraphics,
387 const CFX_Matrix& matrix) {
388 // Prevents destruction of the CXFA_ContentLayoutItem that owns |this|.
389 RetainPtr<CXFA_ContentLayoutItem> retainer(m_pLayoutItem.Get());
390
391 m_pOldDelegate->OnDrawWidget(pGraphics, matrix);
392 }
393
CanUndo()394 bool CXFA_FFTextEdit::CanUndo() {
395 return ToEdit(GetNormalWidget())->CanUndo();
396 }
397
CanRedo()398 bool CXFA_FFTextEdit::CanRedo() {
399 return ToEdit(GetNormalWidget())->CanRedo();
400 }
401
CanCopy()402 bool CXFA_FFTextEdit::CanCopy() {
403 return ToEdit(GetNormalWidget())->HasSelection();
404 }
405
CanCut()406 bool CXFA_FFTextEdit::CanCut() {
407 if (ToEdit(GetNormalWidget())->GetStylesEx() & FWL_STYLEEXT_EDT_ReadOnly)
408 return false;
409 return ToEdit(GetNormalWidget())->HasSelection();
410 }
411
CanPaste()412 bool CXFA_FFTextEdit::CanPaste() {
413 return !(ToEdit(GetNormalWidget())->GetStylesEx() &
414 FWL_STYLEEXT_EDT_ReadOnly);
415 }
416
CanSelectAll()417 bool CXFA_FFTextEdit::CanSelectAll() {
418 return ToEdit(GetNormalWidget())->GetTextLength() > 0;
419 }
420
Undo()421 bool CXFA_FFTextEdit::Undo() {
422 // Prevents destruction of the CXFA_ContentLayoutItem that owns |this|.
423 RetainPtr<CXFA_ContentLayoutItem> retain_layout(m_pLayoutItem.Get());
424
425 return ToEdit(GetNormalWidget())->Undo();
426 }
427
Redo()428 bool CXFA_FFTextEdit::Redo() {
429 // Prevents destruction of the CXFA_ContentLayoutItem that owns |this|.
430 RetainPtr<CXFA_ContentLayoutItem> retain_layout(m_pLayoutItem.Get());
431
432 return ToEdit(GetNormalWidget())->Redo();
433 }
434
Copy()435 Optional<WideString> CXFA_FFTextEdit::Copy() {
436 // Prevents destruction of the CXFA_ContentLayoutItem that owns |this|.
437 RetainPtr<CXFA_ContentLayoutItem> retain_layout(m_pLayoutItem.Get());
438
439 return ToEdit(GetNormalWidget())->Copy();
440 }
441
Cut()442 Optional<WideString> CXFA_FFTextEdit::Cut() {
443 // Prevents destruction of the CXFA_ContentLayoutItem that owns |this|.
444 RetainPtr<CXFA_ContentLayoutItem> retain_layout(m_pLayoutItem.Get());
445
446 return ToEdit(GetNormalWidget())->Cut();
447 }
448
Paste(const WideString & wsPaste)449 bool CXFA_FFTextEdit::Paste(const WideString& wsPaste) {
450 // Prevents destruction of the CXFA_ContentLayoutItem that owns |this|.
451 RetainPtr<CXFA_ContentLayoutItem> retain_layout(m_pLayoutItem.Get());
452
453 return ToEdit(GetNormalWidget())->Paste(wsPaste);
454 }
455
SelectAll()456 void CXFA_FFTextEdit::SelectAll() {
457 // Prevents destruction of the CXFA_ContentLayoutItem that owns |this|.
458 RetainPtr<CXFA_ContentLayoutItem> retain_layout(m_pLayoutItem.Get());
459
460 ToEdit(GetNormalWidget())->SelectAll();
461 }
462
Delete()463 void CXFA_FFTextEdit::Delete() {
464 // Prevents destruction of the CXFA_ContentLayoutItem that owns |this|.
465 RetainPtr<CXFA_ContentLayoutItem> retain_layout(m_pLayoutItem.Get());
466
467 ToEdit(GetNormalWidget())->ClearText();
468 }
469
DeSelect()470 void CXFA_FFTextEdit::DeSelect() {
471 // Prevents destruction of the CXFA_ContentLayoutItem that owns |this|.
472 RetainPtr<CXFA_ContentLayoutItem> retain_layout(m_pLayoutItem.Get());
473
474 ToEdit(GetNormalWidget())->ClearSelection();
475 }
476
GetText()477 WideString CXFA_FFTextEdit::GetText() {
478 return ToEdit(GetNormalWidget())->GetText();
479 }
480
GetFormFieldType()481 FormFieldType CXFA_FFTextEdit::GetFormFieldType() {
482 return FormFieldType::kXFA_TextField;
483 }
484