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 "core/fxcrt/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 auto [eType, iMaxChars] = m_pNode->GetMaxChars();
98 if (eType == XFA_Element::ExData)
99 iMaxChars = 0;
100
101 std::optional<int32_t> numCells = m_pNode->GetNumberOfCells();
102 if (!numCells.has_value()) {
103 pWidget->SetLimit(iMaxChars);
104 } else if (numCells == 0) {
105 dwExtendedStyle |= FWL_STYLEEXT_EDT_CombText;
106 pWidget->SetLimit(iMaxChars > 0 ? iMaxChars : 1);
107 } else {
108 dwExtendedStyle |= FWL_STYLEEXT_EDT_CombText;
109 pWidget->SetLimit(numCells.value());
110 }
111
112 dwExtendedStyle |= GetAlignment();
113 GetNormalWidget()->ModifyStyles(dwStyle, 0xFFFFFFFF);
114 GetNormalWidget()->ModifyStyleExts(dwExtendedStyle, 0xFFFFFFFF);
115 }
116
AcceptsFocusOnButtonDown(Mask<XFA_FWL_KeyFlag> dwFlags,const CFX_PointF & point,CFWL_MessageMouse::MouseCommand command)117 bool CXFA_FFTextEdit::AcceptsFocusOnButtonDown(
118 Mask<XFA_FWL_KeyFlag> dwFlags,
119 const CFX_PointF& point,
120 CFWL_MessageMouse::MouseCommand command) {
121 if (command == CFWL_MessageMouse::MouseCommand::kRightButtonDown &&
122 !m_pNode->IsOpenAccess()) {
123 return false;
124 }
125 if (!PtInActiveRect(point))
126 return false;
127
128 return true;
129 }
130
OnLButtonDown(Mask<XFA_FWL_KeyFlag> dwFlags,const CFX_PointF & point)131 bool CXFA_FFTextEdit::OnLButtonDown(Mask<XFA_FWL_KeyFlag> dwFlags,
132 const CFX_PointF& point) {
133 if (!IsFocused()) {
134 GetLayoutItem()->SetStatusBits(XFA_WidgetStatus::kFocused);
135 UpdateFWLData();
136 InvalidateRect();
137 }
138 SetButtonDown(true);
139 CFWL_MessageMouse msg(GetNormalWidget(),
140 CFWL_MessageMouse::MouseCommand::kLeftButtonDown,
141 dwFlags, FWLToClient(point));
142 SendMessageToFWLWidget(&msg);
143 return true;
144 }
145
OnRButtonDown(Mask<XFA_FWL_KeyFlag> dwFlags,const CFX_PointF & point)146 bool CXFA_FFTextEdit::OnRButtonDown(Mask<XFA_FWL_KeyFlag> dwFlags,
147 const CFX_PointF& point) {
148 if (!IsFocused()) {
149 GetLayoutItem()->SetStatusBits(XFA_WidgetStatus::kFocused);
150 UpdateFWLData();
151 InvalidateRect();
152 }
153 SetButtonDown(true);
154 CFWL_MessageMouse msg(nullptr,
155 CFWL_MessageMouse::MouseCommand::kRightButtonDown,
156 dwFlags, FWLToClient(point));
157 SendMessageToFWLWidget(&msg);
158 return true;
159 }
160
OnRButtonUp(Mask<XFA_FWL_KeyFlag> dwFlags,const CFX_PointF & point)161 bool CXFA_FFTextEdit::OnRButtonUp(Mask<XFA_FWL_KeyFlag> dwFlags,
162 const CFX_PointF& point) {
163 if (!CXFA_FFField::OnRButtonUp(dwFlags, point))
164 return false;
165
166 GetDoc()->PopupMenu(this, point);
167 return true;
168 }
169
OnSetFocus(CXFA_FFWidget * pOldWidget)170 bool CXFA_FFTextEdit::OnSetFocus(CXFA_FFWidget* pOldWidget) {
171 GetLayoutItem()->ClearStatusBits(XFA_WidgetStatus::kTextEditValueChanged);
172 if (!IsFocused()) {
173 GetLayoutItem()->SetStatusBits(XFA_WidgetStatus::kFocused);
174 UpdateFWLData();
175 InvalidateRect();
176 }
177 if (!CXFA_FFWidget::OnSetFocus(pOldWidget))
178 return false;
179
180 CFWL_MessageSetFocus msg(GetNormalWidget());
181 SendMessageToFWLWidget(&msg);
182 return true;
183 }
184
OnKillFocus(CXFA_FFWidget * pNewWidget)185 bool CXFA_FFTextEdit::OnKillFocus(CXFA_FFWidget* pNewWidget) {
186 CFWL_MessageKillFocus msg(GetNormalWidget());
187 SendMessageToFWLWidget(&msg);
188
189 GetLayoutItem()->ClearStatusBits(XFA_WidgetStatus::kFocused);
190 SetEditScrollOffset();
191 ProcessCommittedData();
192 UpdateFWLData();
193 InvalidateRect();
194
195 if (!CXFA_FFWidget::OnKillFocus(pNewWidget))
196 return false;
197
198 GetLayoutItem()->ClearStatusBits(XFA_WidgetStatus::kTextEditValueChanged);
199 return true;
200 }
201
CommitData()202 bool CXFA_FFTextEdit::CommitData() {
203 WideString wsText = ToEdit(GetNormalWidget())->GetText();
204 if (m_pNode->SetValue(XFA_ValuePicture::kEdit, wsText)) {
205 GetDoc()->GetDocView()->UpdateUIDisplay(m_pNode.Get(), this);
206 return true;
207 }
208 ValidateNumberField(wsText);
209 return false;
210 }
211
ValidateNumberField(const WideString & wsText)212 void CXFA_FFTextEdit::ValidateNumberField(const WideString& wsText) {
213 if (GetNode()->GetFFWidgetType() != XFA_FFWidgetType::kNumericEdit)
214 return;
215
216 CXFA_FFApp::CallbackIface* pAppProvider = GetAppProvider();
217 if (!pAppProvider)
218 return;
219
220 WideString wsSomField = GetNode()->GetSOMExpression();
221 pAppProvider->MsgBox(
222 wsText + WideString::FromASCII(" can not contain ") + wsSomField,
223 pAppProvider->GetAppTitle(), static_cast<uint32_t>(AlertIcon::kError),
224 static_cast<uint32_t>(AlertButton::kOK));
225 }
226
IsDataChanged()227 bool CXFA_FFTextEdit::IsDataChanged() {
228 return GetLayoutItem()->TestStatusBits(
229 XFA_WidgetStatus::kTextEditValueChanged);
230 }
231
GetAlignment()232 uint32_t CXFA_FFTextEdit::GetAlignment() {
233 CXFA_Para* para = m_pNode->GetParaIfExists();
234 if (!para)
235 return 0;
236
237 uint32_t dwExtendedStyle = 0;
238 switch (para->GetHorizontalAlign()) {
239 case XFA_AttributeValue::Center:
240 dwExtendedStyle |= FWL_STYLEEXT_EDT_HCenter;
241 break;
242 case XFA_AttributeValue::Justify:
243 dwExtendedStyle |= FWL_STYLEEXT_EDT_Justified;
244 break;
245 case XFA_AttributeValue::JustifyAll:
246 case XFA_AttributeValue::Radix:
247 break;
248 case XFA_AttributeValue::Right:
249 dwExtendedStyle |= FWL_STYLEEXT_EDT_HFar;
250 break;
251 default:
252 dwExtendedStyle |= FWL_STYLEEXT_EDT_HNear;
253 break;
254 }
255
256 switch (para->GetVerticalAlign()) {
257 case XFA_AttributeValue::Middle:
258 dwExtendedStyle |= FWL_STYLEEXT_EDT_VCenter;
259 break;
260 case XFA_AttributeValue::Bottom:
261 dwExtendedStyle |= FWL_STYLEEXT_EDT_VFar;
262 break;
263 default:
264 dwExtendedStyle |= FWL_STYLEEXT_EDT_VNear;
265 break;
266 }
267 return dwExtendedStyle;
268 }
269
UpdateFWLData()270 bool CXFA_FFTextEdit::UpdateFWLData() {
271 CFWL_Edit* pEdit = ToEdit(GetNormalWidget());
272 if (!pEdit)
273 return false;
274
275 XFA_ValuePicture eType = XFA_ValuePicture::kDisplay;
276 if (IsFocused())
277 eType = XFA_ValuePicture::kEdit;
278
279 bool bUpdate = false;
280 if (m_pNode->GetFFWidgetType() == XFA_FFWidgetType::kTextEdit &&
281 !m_pNode->GetNumberOfCells().has_value()) {
282 auto [elementType, iMaxChars] = m_pNode->GetMaxChars();
283 if (elementType == XFA_Element::ExData)
284 iMaxChars = eType == XFA_ValuePicture::kEdit ? iMaxChars : 0;
285 if (pEdit->GetLimit() != iMaxChars) {
286 pEdit->SetLimit(iMaxChars);
287 bUpdate = true;
288 }
289 } else if (m_pNode->GetFFWidgetType() == XFA_FFWidgetType::kBarcode) {
290 int32_t nDataLen = 0;
291 if (eType == XFA_ValuePicture::kEdit) {
292 nDataLen = static_cast<CXFA_Barcode*>(m_pNode->GetUIChildNode())
293 ->GetDataLength()
294 .value_or(0);
295 }
296
297 pEdit->SetLimit(nDataLen);
298 bUpdate = true;
299 }
300 WideString wsText = m_pNode->GetValue(eType);
301 WideString wsOldText = pEdit->GetText();
302 if (wsText != wsOldText || (eType == XFA_ValuePicture::kEdit && bUpdate)) {
303 pEdit->SetTextSkipNotify(wsText);
304 bUpdate = true;
305 }
306 if (bUpdate)
307 GetNormalWidget()->Update();
308
309 return true;
310 }
311
OnTextWillChange(CFWL_Widget * pWidget,CFWL_EventTextWillChange * event)312 void CXFA_FFTextEdit::OnTextWillChange(CFWL_Widget* pWidget,
313 CFWL_EventTextWillChange* event) {
314 GetLayoutItem()->SetStatusBits(XFA_WidgetStatus::kTextEditValueChanged);
315
316 CXFA_EventParam eParam(XFA_EVENT_Change);
317 eParam.m_wsChange = event->GetChangeText();
318 eParam.m_wsPrevText = event->GetPreviousText();
319 eParam.m_iSelStart = static_cast<int32_t>(event->GetSelectionStart());
320 eParam.m_iSelEnd = static_cast<int32_t>(event->GetSelectionEnd());
321 m_pNode->ProcessEvent(GetDocView(), XFA_AttributeValue::Change, &eParam);
322
323 // Copy the data back out of the EventParam and into the TextChanged event so
324 // it can propagate back to the calling widget.
325 event->SetCancelled(eParam.m_bCancelAction);
326 event->SetChangeText(eParam.m_wsChange);
327 event->SetSelectionStart(static_cast<size_t>(eParam.m_iSelStart));
328 event->SetSelectionEnd(static_cast<size_t>(eParam.m_iSelEnd));
329 }
330
OnTextFull(CFWL_Widget * pWidget)331 void CXFA_FFTextEdit::OnTextFull(CFWL_Widget* pWidget) {
332 CXFA_EventParam eParam(XFA_EVENT_Full);
333 m_pNode->ProcessEvent(GetDocView(), XFA_AttributeValue::Full, &eParam);
334 }
335
OnProcessMessage(CFWL_Message * pMessage)336 void CXFA_FFTextEdit::OnProcessMessage(CFWL_Message* pMessage) {
337 m_pOldDelegate->OnProcessMessage(pMessage);
338 }
339
OnProcessEvent(CFWL_Event * pEvent)340 void CXFA_FFTextEdit::OnProcessEvent(CFWL_Event* pEvent) {
341 CXFA_FFField::OnProcessEvent(pEvent);
342 switch (pEvent->GetType()) {
343 case CFWL_Event::Type::TextWillChange:
344 OnTextWillChange(GetNormalWidget(),
345 static_cast<CFWL_EventTextWillChange*>(pEvent));
346 break;
347 case CFWL_Event::Type::TextFull:
348 OnTextFull(GetNormalWidget());
349 break;
350 default:
351 break;
352 }
353 m_pOldDelegate->OnProcessEvent(pEvent);
354 }
355
OnDrawWidget(CFGAS_GEGraphics * pGraphics,const CFX_Matrix & matrix)356 void CXFA_FFTextEdit::OnDrawWidget(CFGAS_GEGraphics* pGraphics,
357 const CFX_Matrix& matrix) {
358 m_pOldDelegate->OnDrawWidget(pGraphics, matrix);
359 }
360
CanUndo()361 bool CXFA_FFTextEdit::CanUndo() {
362 return ToEdit(GetNormalWidget())->CanUndo();
363 }
364
CanRedo()365 bool CXFA_FFTextEdit::CanRedo() {
366 return ToEdit(GetNormalWidget())->CanRedo();
367 }
368
CanCopy()369 bool CXFA_FFTextEdit::CanCopy() {
370 return ToEdit(GetNormalWidget())->HasSelection();
371 }
372
CanCut()373 bool CXFA_FFTextEdit::CanCut() {
374 if (ToEdit(GetNormalWidget())->GetStyleExts() & FWL_STYLEEXT_EDT_ReadOnly)
375 return false;
376 return ToEdit(GetNormalWidget())->HasSelection();
377 }
378
CanPaste()379 bool CXFA_FFTextEdit::CanPaste() {
380 return !(ToEdit(GetNormalWidget())->GetStyleExts() &
381 FWL_STYLEEXT_EDT_ReadOnly);
382 }
383
CanSelectAll()384 bool CXFA_FFTextEdit::CanSelectAll() {
385 return ToEdit(GetNormalWidget())->GetTextLength() > 0;
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
Copy()396 std::optional<WideString> CXFA_FFTextEdit::Copy() {
397 return ToEdit(GetNormalWidget())->Copy();
398 }
399
Cut()400 std::optional<WideString> CXFA_FFTextEdit::Cut() {
401 return ToEdit(GetNormalWidget())->Cut();
402 }
403
Paste(const WideString & wsPaste)404 bool CXFA_FFTextEdit::Paste(const WideString& wsPaste) {
405 return ToEdit(GetNormalWidget())->Paste(wsPaste);
406 }
407
SelectAll()408 void CXFA_FFTextEdit::SelectAll() {
409 ToEdit(GetNormalWidget())->SelectAll();
410 }
411
Delete()412 void CXFA_FFTextEdit::Delete() {
413 ToEdit(GetNormalWidget())->ClearText();
414 }
415
DeSelect()416 void CXFA_FFTextEdit::DeSelect() {
417 ToEdit(GetNormalWidget())->ClearSelection();
418 }
419
GetText()420 WideString CXFA_FFTextEdit::GetText() {
421 return ToEdit(GetNormalWidget())->GetText();
422 }
423
GetFormFieldType()424 FormFieldType CXFA_FFTextEdit::GetFormFieldType() {
425 return FormFieldType::kXFA_TextField;
426 }
427