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