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