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