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