1 // Copyright 2014 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 "fpdfsdk/pwl/cpwl_edit.h"
8
9 #include <algorithm>
10 #include <memory>
11 #include <sstream>
12 #include <utility>
13
14 #include "constants/ascii.h"
15 #include "core/fpdfapi/font/cpdf_font.h"
16 #include "core/fpdfdoc/cpvt_word.h"
17 #include "core/fpdfdoc/ipvt_fontmap.h"
18 #include "core/fxcrt/fx_safe_types.h"
19 #include "core/fxge/cfx_fillrenderoptions.h"
20 #include "core/fxge/cfx_graphstatedata.h"
21 #include "core/fxge/cfx_path.h"
22 #include "core/fxge/cfx_renderdevice.h"
23 #include "core/fxge/fx_font.h"
24 #include "fpdfsdk/pwl/cpwl_caret.h"
25 #include "fpdfsdk/pwl/cpwl_edit_impl.h"
26 #include "fpdfsdk/pwl/cpwl_scroll_bar.h"
27 #include "fpdfsdk/pwl/cpwl_wnd.h"
28 #include "fpdfsdk/pwl/ipwl_fillernotify.h"
29 #include "public/fpdf_fwlevent.h"
30 #include "third_party/base/check.h"
31
CPWL_Edit(const CreateParams & cp,std::unique_ptr<IPWL_FillerNotify::PerWindowData> pAttachedData)32 CPWL_Edit::CPWL_Edit(
33 const CreateParams& cp,
34 std::unique_ptr<IPWL_FillerNotify::PerWindowData> pAttachedData)
35 : CPWL_Wnd(cp, std::move(pAttachedData)),
36 m_pEditImpl(std::make_unique<CPWL_EditImpl>()) {
37 GetCreationParams()->eCursorType = IPWL_FillerNotify::CursorStyle::kVBeam;
38 }
39
~CPWL_Edit()40 CPWL_Edit::~CPWL_Edit() {
41 DCHECK(!m_bFocus);
42 }
43
SetText(const WideString & csText)44 void CPWL_Edit::SetText(const WideString& csText) {
45 m_pEditImpl->SetText(csText);
46 m_pEditImpl->Paint();
47 }
48
RePosChildWnd()49 bool CPWL_Edit::RePosChildWnd() {
50 if (CPWL_ScrollBar* pVSB = GetVScrollBar()) {
51 CFX_FloatRect rcWindow = m_rcOldWindow;
52 CFX_FloatRect rcVScroll =
53 CFX_FloatRect(rcWindow.right, rcWindow.bottom,
54 rcWindow.right + CPWL_ScrollBar::kWidth, rcWindow.top);
55
56 ObservedPtr<CPWL_Edit> thisObserved(this);
57 pVSB->Move(rcVScroll, true, false);
58 if (!thisObserved)
59 return false;
60 }
61
62 if (m_pCaret && !HasFlag(PES_TEXTOVERFLOW)) {
63 CFX_FloatRect rect = GetClientRect();
64 if (!rect.IsEmpty()) {
65 // +1 for caret beside border
66 rect.Inflate(1.0f, 1.0f);
67 rect.Normalize();
68 }
69 m_pCaret->SetClipRect(rect);
70 }
71
72 m_pEditImpl->SetPlateRect(GetClientRect());
73 m_pEditImpl->Paint();
74 return true;
75 }
76
GetClientRect() const77 CFX_FloatRect CPWL_Edit::GetClientRect() const {
78 float width = static_cast<float>(GetBorderWidth() + GetInnerBorderWidth());
79 CFX_FloatRect rcClient = GetWindowRect().GetDeflated(width, width);
80 CPWL_ScrollBar* pVSB = GetVScrollBar();
81 if (pVSB && pVSB->IsVisible())
82 rcClient.right -= CPWL_ScrollBar::kWidth;
83 return rcClient;
84 }
85
SetAlignFormatVerticalCenter()86 void CPWL_Edit::SetAlignFormatVerticalCenter() {
87 m_pEditImpl->SetAlignmentV(static_cast<int32_t>(PEAV_CENTER));
88 m_pEditImpl->Paint();
89 }
90
CanSelectAll() const91 bool CPWL_Edit::CanSelectAll() const {
92 return GetSelectWordRange() != m_pEditImpl->GetWholeWordRange();
93 }
94
CanCopy() const95 bool CPWL_Edit::CanCopy() const {
96 return !HasFlag(PES_PASSWORD) && m_pEditImpl->IsSelected();
97 }
98
CanCut() const99 bool CPWL_Edit::CanCut() const {
100 return CanCopy() && !IsReadOnly();
101 }
102
CutText()103 void CPWL_Edit::CutText() {
104 if (!CanCut())
105 return;
106 m_pEditImpl->ClearSelection();
107 }
108
OnCreated()109 void CPWL_Edit::OnCreated() {
110 SetFontSize(GetCreationParams()->fFontSize);
111 m_pEditImpl->SetFontMap(GetFontMap());
112 m_pEditImpl->SetNotify(this);
113 m_pEditImpl->Initialize();
114
115 if (CPWL_ScrollBar* pScroll = GetVScrollBar()) {
116 pScroll->RemoveFlag(PWS_AUTOTRANSPARENT);
117 pScroll->SetTransparency(255);
118 }
119
120 SetParamByFlag();
121 m_rcOldWindow = GetWindowRect();
122 }
123
SetParamByFlag()124 void CPWL_Edit::SetParamByFlag() {
125 if (HasFlag(PES_RIGHT)) {
126 m_pEditImpl->SetAlignmentH(2);
127 } else if (HasFlag(PES_MIDDLE)) {
128 m_pEditImpl->SetAlignmentH(1);
129 } else {
130 m_pEditImpl->SetAlignmentH(0);
131 }
132
133 if (HasFlag(PES_CENTER)) {
134 m_pEditImpl->SetAlignmentV(1);
135 } else {
136 m_pEditImpl->SetAlignmentV(0);
137 }
138
139 if (HasFlag(PES_PASSWORD)) {
140 m_pEditImpl->SetPasswordChar('*');
141 }
142
143 m_pEditImpl->SetMultiLine(HasFlag(PES_MULTILINE));
144 m_pEditImpl->SetAutoReturn(HasFlag(PES_AUTORETURN));
145 m_pEditImpl->SetAutoFontSize(HasFlag(PWS_AUTOFONTSIZE));
146 m_pEditImpl->SetAutoScroll(HasFlag(PES_AUTOSCROLL));
147 m_pEditImpl->EnableUndo(HasFlag(PES_UNDO));
148
149 if (HasFlag(PES_TEXTOVERFLOW)) {
150 SetClipRect(CFX_FloatRect());
151 m_pEditImpl->SetTextOverflow(true);
152 } else {
153 if (m_pCaret) {
154 CFX_FloatRect rect = GetClientRect();
155 if (!rect.IsEmpty()) {
156 // +1 for caret beside border
157 rect.Inflate(1.0f, 1.0f);
158 rect.Normalize();
159 }
160 m_pCaret->SetClipRect(rect);
161 }
162 }
163 }
164
DrawThisAppearance(CFX_RenderDevice * pDevice,const CFX_Matrix & mtUser2Device)165 void CPWL_Edit::DrawThisAppearance(CFX_RenderDevice* pDevice,
166 const CFX_Matrix& mtUser2Device) {
167 CPWL_Wnd::DrawThisAppearance(pDevice, mtUser2Device);
168
169 const CFX_FloatRect rcClient = GetClientRect();
170 const BorderStyle border_style = GetBorderStyle();
171 const int32_t nCharArray = m_pEditImpl->GetCharArray();
172 bool draw_border = nCharArray > 0 && (border_style == BorderStyle::kSolid ||
173 border_style == BorderStyle::kDash);
174 if (draw_border) {
175 FX_SAFE_INT32 nCharArraySafe = nCharArray;
176 nCharArraySafe -= 1;
177 nCharArraySafe *= 2;
178 draw_border = nCharArraySafe.IsValid();
179 }
180
181 if (draw_border) {
182 CFX_GraphStateData gsd;
183 gsd.m_LineWidth = GetBorderWidth();
184 if (border_style == BorderStyle::kDash) {
185 gsd.m_DashArray = {static_cast<float>(GetBorderDash().nDash),
186 static_cast<float>(GetBorderDash().nGap)};
187 gsd.m_DashPhase = GetBorderDash().nPhase;
188 }
189
190 const float width = (rcClient.right - rcClient.left) / nCharArray;
191 CFX_Path path;
192 CFX_PointF bottom(0, rcClient.bottom);
193 CFX_PointF top(0, rcClient.top);
194 for (int32_t i = 0; i < nCharArray - 1; ++i) {
195 bottom.x = rcClient.left + width * (i + 1);
196 top.x = bottom.x;
197 path.AppendPoint(bottom, CFX_Path::Point::Type::kMove);
198 path.AppendPoint(top, CFX_Path::Point::Type::kLine);
199 }
200 if (!path.GetPoints().empty()) {
201 pDevice->DrawPath(path, &mtUser2Device, &gsd, 0,
202 GetBorderColor().ToFXColor(255),
203 CFX_FillRenderOptions::EvenOddOptions());
204 }
205 }
206
207 CFX_FloatRect rcClip;
208 CPVT_WordRange wrRange = m_pEditImpl->GetVisibleWordRange();
209 CPVT_WordRange* pRange = nullptr;
210 if (!HasFlag(PES_TEXTOVERFLOW)) {
211 rcClip = GetClientRect();
212 pRange = &wrRange;
213 }
214 m_pEditImpl->DrawEdit(
215 pDevice, mtUser2Device, GetTextColor().ToFXColor(GetTransparency()),
216 rcClip, CFX_PointF(), pRange, GetFillerNotify(), GetAttachedData());
217 }
218
OnSetFocus()219 void CPWL_Edit::OnSetFocus() {
220 ObservedPtr<CPWL_Edit> observed_ptr(this);
221 SetEditCaret(true);
222 if (!observed_ptr)
223 return;
224
225 if (!IsReadOnly()) {
226 CPWL_Wnd::ProviderIface* pProvider = GetProvider();
227 if (pProvider) {
228 pProvider->OnSetFocusForEdit(this);
229 if (!observed_ptr)
230 return;
231 }
232 }
233 m_bFocus = true;
234 }
235
OnKillFocus()236 void CPWL_Edit::OnKillFocus() {
237 ObservedPtr<CPWL_Edit> observed_ptr(this);
238 CPWL_ScrollBar* pScroll = GetVScrollBar();
239 if (pScroll && pScroll->IsVisible()) {
240 pScroll->SetVisible(false);
241 if (!observed_ptr)
242 return;
243
244 if (!Move(m_rcOldWindow, true, true))
245 return;
246 }
247
248 m_pEditImpl->SelectNone();
249 if (!observed_ptr)
250 return;
251
252 if (!SetCaret(false, CFX_PointF(), CFX_PointF()))
253 return;
254
255 SetCharSet(FX_Charset::kANSI);
256 m_bFocus = false;
257 }
258
GetSelectWordRange() const259 CPVT_WordRange CPWL_Edit::GetSelectWordRange() const {
260 if (!m_pEditImpl->IsSelected())
261 return CPVT_WordRange();
262
263 int32_t nStart;
264 int32_t nEnd;
265 std::tie(nStart, nEnd) = m_pEditImpl->GetSelection();
266
267 CPVT_WordPlace wpStart = m_pEditImpl->WordIndexToWordPlace(nStart);
268 CPVT_WordPlace wpEnd = m_pEditImpl->WordIndexToWordPlace(nEnd);
269 return CPVT_WordRange(wpStart, wpEnd);
270 }
271
IsTextFull() const272 bool CPWL_Edit::IsTextFull() const {
273 return m_pEditImpl->IsTextFull();
274 }
275
GetCharArrayAutoFontSize(const CPDF_Font * pFont,const CFX_FloatRect & rcPlate,int32_t nCharArray)276 float CPWL_Edit::GetCharArrayAutoFontSize(const CPDF_Font* pFont,
277 const CFX_FloatRect& rcPlate,
278 int32_t nCharArray) {
279 if (!pFont || pFont->IsStandardFont())
280 return 0.0f;
281
282 const FX_RECT& rcBBox = pFont->GetFontBBox();
283
284 CFX_FloatRect rcCell = rcPlate;
285 float xdiv = rcCell.Width() / nCharArray * 1000.0f / rcBBox.Width();
286 float ydiv = -rcCell.Height() * 1000.0f / rcBBox.Height();
287
288 return xdiv < ydiv ? xdiv : ydiv;
289 }
290
SetCharArray(int32_t nCharArray)291 void CPWL_Edit::SetCharArray(int32_t nCharArray) {
292 if (!HasFlag(PES_CHARARRAY) || nCharArray <= 0)
293 return;
294
295 m_pEditImpl->SetCharArray(nCharArray);
296 m_pEditImpl->SetTextOverflow(true);
297 m_pEditImpl->Paint();
298
299 if (!HasFlag(PWS_AUTOFONTSIZE))
300 return;
301
302 IPVT_FontMap* pFontMap = GetFontMap();
303 if (!pFontMap)
304 return;
305
306 float fFontSize = GetCharArrayAutoFontSize(pFontMap->GetPDFFont(0).Get(),
307 GetClientRect(), nCharArray);
308 if (fFontSize <= 0.0f)
309 return;
310
311 m_pEditImpl->SetAutoFontSize(false);
312 m_pEditImpl->SetFontSize(fFontSize);
313 m_pEditImpl->Paint();
314 }
315
SetLimitChar(int32_t nLimitChar)316 void CPWL_Edit::SetLimitChar(int32_t nLimitChar) {
317 m_pEditImpl->SetLimitChar(nLimitChar);
318 m_pEditImpl->Paint();
319 }
320
GetFocusRect() const321 CFX_FloatRect CPWL_Edit::GetFocusRect() const {
322 return CFX_FloatRect();
323 }
324
IsVScrollBarVisible() const325 bool CPWL_Edit::IsVScrollBarVisible() const {
326 CPWL_ScrollBar* pScroll = GetVScrollBar();
327 return pScroll && pScroll->IsVisible();
328 }
329
OnKeyDown(FWL_VKEYCODE nKeyCode,Mask<FWL_EVENTFLAG> nFlag)330 bool CPWL_Edit::OnKeyDown(FWL_VKEYCODE nKeyCode, Mask<FWL_EVENTFLAG> nFlag) {
331 if (m_bMouseDown)
332 return true;
333
334 if (nKeyCode == FWL_VKEY_Delete) {
335 WideString strChange;
336 WideString strChangeEx;
337
338 int nSelStart;
339 int nSelEnd;
340 std::tie(nSelStart, nSelEnd) = GetSelection();
341
342 if (nSelStart == nSelEnd)
343 nSelEnd = nSelStart + 1;
344
345 ObservedPtr<CPWL_Wnd> thisObserved(this);
346
347 bool bRC;
348 bool bExit;
349 std::tie(bRC, bExit) = GetFillerNotify()->OnBeforeKeyStroke(
350 GetAttachedData(), strChange, strChangeEx, nSelStart, nSelEnd, true,
351 nFlag);
352
353 if (!thisObserved)
354 return false;
355
356 if (!bRC)
357 return false;
358 if (bExit)
359 return false;
360 }
361
362 bool bRet = OnKeyDownInternal(nKeyCode, nFlag);
363
364 // In case of implementation swallow the OnKeyDown event.
365 if (IsProceedtoOnChar(nKeyCode, nFlag))
366 return true;
367
368 return bRet;
369 }
370
371 // static
IsProceedtoOnChar(FWL_VKEYCODE nKeyCode,Mask<FWL_EVENTFLAG> nFlag)372 bool CPWL_Edit::IsProceedtoOnChar(FWL_VKEYCODE nKeyCode,
373 Mask<FWL_EVENTFLAG> nFlag) {
374 bool bCtrl = IsPlatformShortcutKey(nFlag);
375 bool bAlt = IsALTKeyDown(nFlag);
376 if (bCtrl && !bAlt) {
377 // hot keys for edit control.
378 switch (nKeyCode) {
379 case FWL_VKEY_A:
380 case FWL_VKEY_C:
381 case FWL_VKEY_V:
382 case FWL_VKEY_X:
383 case FWL_VKEY_Z:
384 return true;
385 default:
386 break;
387 }
388 }
389 // control characters.
390 switch (nKeyCode) {
391 case FWL_VKEY_Escape:
392 case FWL_VKEY_Back:
393 case FWL_VKEY_Return:
394 case FWL_VKEY_Space:
395 return true;
396 default:
397 return false;
398 }
399 }
400
OnChar(uint16_t nChar,Mask<FWL_EVENTFLAG> nFlag)401 bool CPWL_Edit::OnChar(uint16_t nChar, Mask<FWL_EVENTFLAG> nFlag) {
402 if (m_bMouseDown)
403 return true;
404
405 bool bRC = true;
406 bool bExit = false;
407
408 if (!IsCTRLKeyDown(nFlag)) {
409 WideString swChange;
410 int nSelStart;
411 int nSelEnd;
412 std::tie(nSelStart, nSelEnd) = GetSelection();
413
414 switch (nChar) {
415 case pdfium::ascii::kBackspace:
416 if (nSelStart == nSelEnd)
417 nSelStart = nSelEnd - 1;
418 break;
419 case pdfium::ascii::kReturn:
420 break;
421 default:
422 swChange += nChar;
423 break;
424 }
425
426 ObservedPtr<CPWL_Wnd> thisObserved(this);
427
428 WideString strChangeEx;
429 std::tie(bRC, bExit) = GetFillerNotify()->OnBeforeKeyStroke(
430 GetAttachedData(), swChange, strChangeEx, nSelStart, nSelEnd, true,
431 nFlag);
432
433 if (!thisObserved)
434 return false;
435 }
436
437 if (!bRC)
438 return true;
439 if (bExit)
440 return false;
441
442 if (IPVT_FontMap* pFontMap = GetFontMap()) {
443 FX_Charset nOldCharSet = GetCharSet();
444 FX_Charset nNewCharSet =
445 pFontMap->CharSetFromUnicode(nChar, FX_Charset::kDefault);
446 if (nOldCharSet != nNewCharSet) {
447 SetCharSet(nNewCharSet);
448 }
449 }
450
451 return OnCharInternal(nChar, nFlag);
452 }
453
OnMouseWheel(Mask<FWL_EVENTFLAG> nFlag,const CFX_PointF & point,const CFX_Vector & delta)454 bool CPWL_Edit::OnMouseWheel(Mask<FWL_EVENTFLAG> nFlag,
455 const CFX_PointF& point,
456 const CFX_Vector& delta) {
457 if (!HasFlag(PES_MULTILINE))
458 return false;
459
460 CFX_PointF ptScroll = GetScrollPos();
461 if (delta.y > 0)
462 ptScroll.y += GetFontSize();
463 else
464 ptScroll.y -= GetFontSize();
465 SetScrollPos(ptScroll);
466 return true;
467 }
468
OnDestroy()469 void CPWL_Edit::OnDestroy() {
470 m_pCaret.ExtractAsDangling();
471 }
472
IsWndHorV() const473 bool CPWL_Edit::IsWndHorV() const {
474 CFX_Matrix mt = GetWindowMatrix();
475 return mt.Transform(CFX_PointF(1, 1)).y == mt.Transform(CFX_PointF(0, 1)).y;
476 }
477
SetCursor()478 void CPWL_Edit::SetCursor() {
479 if (IsValid()) {
480 GetFillerNotify()->SetCursor(IsWndHorV()
481 ? IPWL_FillerNotify::CursorStyle::kVBeam
482 : IPWL_FillerNotify::CursorStyle::kHBeam);
483 }
484 }
485
GetSelectedText()486 WideString CPWL_Edit::GetSelectedText() {
487 return m_pEditImpl->GetSelectedText();
488 }
489
ReplaceAndKeepSelection(const WideString & text)490 void CPWL_Edit::ReplaceAndKeepSelection(const WideString& text) {
491 m_pEditImpl->ReplaceAndKeepSelection(text);
492 }
493
ReplaceSelection(const WideString & text)494 void CPWL_Edit::ReplaceSelection(const WideString& text) {
495 m_pEditImpl->ReplaceSelection(text);
496 }
497
SelectAllText()498 bool CPWL_Edit::SelectAllText() {
499 m_pEditImpl->SelectAll();
500 return true;
501 }
502
SetScrollInfo(const PWL_SCROLL_INFO & info)503 void CPWL_Edit::SetScrollInfo(const PWL_SCROLL_INFO& info) {
504 if (CPWL_Wnd* pChild = GetVScrollBar())
505 pChild->SetScrollInfo(info);
506 }
507
SetScrollPosition(float pos)508 void CPWL_Edit::SetScrollPosition(float pos) {
509 if (CPWL_Wnd* pChild = GetVScrollBar())
510 pChild->SetScrollPosition(pos);
511 }
512
ScrollWindowVertically(float pos)513 void CPWL_Edit::ScrollWindowVertically(float pos) {
514 m_pEditImpl->SetScrollPos(CFX_PointF(m_pEditImpl->GetScrollPos().x, pos));
515 }
516
CreateChildWnd(const CreateParams & cp)517 void CPWL_Edit::CreateChildWnd(const CreateParams& cp) {
518 if (!IsReadOnly())
519 CreateEditCaret(cp);
520 }
521
CreateEditCaret(const CreateParams & cp)522 void CPWL_Edit::CreateEditCaret(const CreateParams& cp) {
523 if (m_pCaret)
524 return;
525
526 CreateParams ecp = cp;
527 ecp.dwFlags = PWS_NOREFRESHCLIP;
528 ecp.dwBorderWidth = 0;
529 ecp.nBorderStyle = BorderStyle::kSolid;
530 ecp.rcRectWnd = CFX_FloatRect();
531
532 auto pCaret = std::make_unique<CPWL_Caret>(ecp, CloneAttachedData());
533 m_pCaret = pCaret.get();
534 m_pCaret->SetInvalidRect(GetClientRect());
535 AddChild(std::move(pCaret));
536 m_pCaret->Realize();
537 }
538
SetFontSize(float fFontSize)539 void CPWL_Edit::SetFontSize(float fFontSize) {
540 m_pEditImpl->SetFontSize(fFontSize);
541 m_pEditImpl->Paint();
542 }
543
GetFontSize() const544 float CPWL_Edit::GetFontSize() const {
545 return m_pEditImpl->GetFontSize();
546 }
547
OnKeyDownInternal(FWL_VKEYCODE nKeyCode,Mask<FWL_EVENTFLAG> nFlag)548 bool CPWL_Edit::OnKeyDownInternal(FWL_VKEYCODE nKeyCode,
549 Mask<FWL_EVENTFLAG> nFlag) {
550 if (m_bMouseDown)
551 return true;
552
553 bool bRet = CPWL_Wnd::OnKeyDown(nKeyCode, nFlag);
554
555 // FILTER
556 switch (nKeyCode) {
557 default:
558 return false;
559 case FWL_VKEY_Delete:
560 case FWL_VKEY_Up:
561 case FWL_VKEY_Down:
562 case FWL_VKEY_Left:
563 case FWL_VKEY_Right:
564 case FWL_VKEY_Home:
565 case FWL_VKEY_End:
566 case FWL_VKEY_Insert:
567 case FWL_VKEY_A:
568 case FWL_VKEY_C:
569 case FWL_VKEY_V:
570 case FWL_VKEY_X:
571 case FWL_VKEY_Z:
572 break;
573 }
574
575 if (nKeyCode == FWL_VKEY_Delete && m_pEditImpl->IsSelected())
576 nKeyCode = FWL_VKEY_Unknown;
577
578 switch (nKeyCode) {
579 case FWL_VKEY_Delete:
580 Delete();
581 return true;
582 case FWL_VKEY_Insert:
583 if (IsSHIFTKeyDown(nFlag))
584 PasteText();
585 return true;
586 case FWL_VKEY_Up:
587 m_pEditImpl->OnVK_UP(IsSHIFTKeyDown(nFlag));
588 return true;
589 case FWL_VKEY_Down:
590 m_pEditImpl->OnVK_DOWN(IsSHIFTKeyDown(nFlag));
591 return true;
592 case FWL_VKEY_Left:
593 m_pEditImpl->OnVK_LEFT(IsSHIFTKeyDown(nFlag));
594 return true;
595 case FWL_VKEY_Right:
596 m_pEditImpl->OnVK_RIGHT(IsSHIFTKeyDown(nFlag));
597 return true;
598 case FWL_VKEY_Home:
599 m_pEditImpl->OnVK_HOME(IsSHIFTKeyDown(nFlag), IsCTRLKeyDown(nFlag));
600 return true;
601 case FWL_VKEY_End:
602 m_pEditImpl->OnVK_END(IsSHIFTKeyDown(nFlag), IsCTRLKeyDown(nFlag));
603 return true;
604 case FWL_VKEY_Unknown:
605 if (!IsSHIFTKeyDown(nFlag))
606 ClearSelection();
607 else
608 CutText();
609 return true;
610 default:
611 break;
612 }
613
614 return bRet;
615 }
616
OnCharInternal(uint16_t nChar,Mask<FWL_EVENTFLAG> nFlag)617 bool CPWL_Edit::OnCharInternal(uint16_t nChar, Mask<FWL_EVENTFLAG> nFlag) {
618 if (m_bMouseDown)
619 return true;
620
621 CPWL_Wnd::OnChar(nChar, nFlag);
622
623 // FILTER
624 switch (nChar) {
625 case pdfium::ascii::kNewline:
626 case pdfium::ascii::kEscape:
627 return false;
628 default:
629 break;
630 }
631
632 bool bCtrl = IsPlatformShortcutKey(nFlag);
633 bool bAlt = IsALTKeyDown(nFlag);
634 bool bShift = IsSHIFTKeyDown(nFlag);
635
636 uint16_t word = nChar;
637
638 if (bCtrl && !bAlt) {
639 switch (nChar) {
640 case pdfium::ascii::kControlC:
641 CopyText();
642 return true;
643 case pdfium::ascii::kControlV:
644 PasteText();
645 return true;
646 case pdfium::ascii::kControlX:
647 CutText();
648 return true;
649 case pdfium::ascii::kControlA:
650 SelectAllText();
651 return true;
652 case pdfium::ascii::kControlZ:
653 if (bShift)
654 Redo();
655 else
656 Undo();
657 return true;
658 default:
659 if (nChar < 32)
660 return false;
661 }
662 }
663
664 if (IsReadOnly())
665 return true;
666
667 if (m_pEditImpl->IsSelected() && word == pdfium::ascii::kBackspace)
668 word = pdfium::ascii::kNul;
669
670 ClearSelection();
671
672 switch (word) {
673 case pdfium::ascii::kBackspace:
674 Backspace();
675 break;
676 case pdfium::ascii::kReturn:
677 InsertReturn();
678 break;
679 case pdfium::ascii::kNul:
680 break;
681 default:
682 InsertWord(word, GetCharSet());
683 break;
684 }
685
686 return true;
687 }
688
OnLButtonDown(Mask<FWL_EVENTFLAG> nFlag,const CFX_PointF & point)689 bool CPWL_Edit::OnLButtonDown(Mask<FWL_EVENTFLAG> nFlag,
690 const CFX_PointF& point) {
691 CPWL_Wnd::OnLButtonDown(nFlag, point);
692 if (HasFlag(PES_TEXTOVERFLOW) || ClientHitTest(point)) {
693 if (m_bMouseDown && !InvalidateRect(nullptr))
694 return true;
695
696 m_bMouseDown = true;
697 SetCapture();
698 m_pEditImpl->OnMouseDown(point, IsSHIFTKeyDown(nFlag),
699 IsCTRLKeyDown(nFlag));
700 }
701 return true;
702 }
703
OnLButtonUp(Mask<FWL_EVENTFLAG> nFlag,const CFX_PointF & point)704 bool CPWL_Edit::OnLButtonUp(Mask<FWL_EVENTFLAG> nFlag,
705 const CFX_PointF& point) {
706 CPWL_Wnd::OnLButtonUp(nFlag, point);
707 if (m_bMouseDown) {
708 // can receive keybord message
709 if (ClientHitTest(point) && !IsFocused())
710 SetFocus();
711
712 ReleaseCapture();
713 m_bMouseDown = false;
714 }
715 return true;
716 }
717
OnLButtonDblClk(Mask<FWL_EVENTFLAG> nFlag,const CFX_PointF & point)718 bool CPWL_Edit::OnLButtonDblClk(Mask<FWL_EVENTFLAG> nFlag,
719 const CFX_PointF& point) {
720 CPWL_Wnd::OnLButtonDblClk(nFlag, point);
721 if (HasFlag(PES_TEXTOVERFLOW) || ClientHitTest(point))
722 m_pEditImpl->SelectAll();
723
724 return true;
725 }
726
OnRButtonUp(Mask<FWL_EVENTFLAG> nFlag,const CFX_PointF & point)727 bool CPWL_Edit::OnRButtonUp(Mask<FWL_EVENTFLAG> nFlag,
728 const CFX_PointF& point) {
729 if (m_bMouseDown)
730 return false;
731
732 CPWL_Wnd::OnRButtonUp(nFlag, point);
733 if (!HasFlag(PES_TEXTOVERFLOW) && !ClientHitTest(point))
734 return true;
735
736 SetFocus();
737 return false;
738 }
739
OnMouseMove(Mask<FWL_EVENTFLAG> nFlag,const CFX_PointF & point)740 bool CPWL_Edit::OnMouseMove(Mask<FWL_EVENTFLAG> nFlag,
741 const CFX_PointF& point) {
742 CPWL_Wnd::OnMouseMove(nFlag, point);
743
744 if (m_bMouseDown)
745 m_pEditImpl->OnMouseMove(point, false, false);
746
747 return true;
748 }
749
SetEditCaret(bool bVisible)750 void CPWL_Edit::SetEditCaret(bool bVisible) {
751 CFX_PointF ptHead;
752 CFX_PointF ptFoot;
753 if (bVisible)
754 GetCaretInfo(&ptHead, &ptFoot);
755
756 SetCaret(bVisible, ptHead, ptFoot);
757 // Note, |this| may no longer be viable at this point. If more work needs to
758 // be done, check the return value of SetCaret().
759 }
760
GetCaretInfo(CFX_PointF * ptHead,CFX_PointF * ptFoot) const761 void CPWL_Edit::GetCaretInfo(CFX_PointF* ptHead, CFX_PointF* ptFoot) const {
762 CPWL_EditImpl::Iterator* pIterator = m_pEditImpl->GetIterator();
763 pIterator->SetAt(m_pEditImpl->GetCaret());
764 CPVT_Word word;
765 CPVT_Line line;
766 if (pIterator->GetWord(word)) {
767 ptHead->x = word.ptWord.x + word.fWidth;
768 ptHead->y = word.ptWord.y + word.fAscent;
769 ptFoot->x = word.ptWord.x + word.fWidth;
770 ptFoot->y = word.ptWord.y + word.fDescent;
771 } else if (pIterator->GetLine(line)) {
772 ptHead->x = line.ptLine.x;
773 ptHead->y = line.ptLine.y + line.fLineAscent;
774 ptFoot->x = line.ptLine.x;
775 ptFoot->y = line.ptLine.y + line.fLineDescent;
776 }
777 }
778
SetCaret(bool bVisible,const CFX_PointF & ptHead,const CFX_PointF & ptFoot)779 bool CPWL_Edit::SetCaret(bool bVisible,
780 const CFX_PointF& ptHead,
781 const CFX_PointF& ptFoot) {
782 if (!m_pCaret)
783 return true;
784
785 if (!IsFocused() || m_pEditImpl->IsSelected())
786 bVisible = false;
787
788 ObservedPtr<CPWL_Edit> thisObserved(this);
789 m_pCaret->SetCaret(bVisible, ptHead, ptFoot);
790 if (!thisObserved)
791 return false;
792
793 return true;
794 }
795
GetText()796 WideString CPWL_Edit::GetText() {
797 return m_pEditImpl->GetText();
798 }
799
SetSelection(int32_t nStartChar,int32_t nEndChar)800 void CPWL_Edit::SetSelection(int32_t nStartChar, int32_t nEndChar) {
801 m_pEditImpl->SetSelection(nStartChar, nEndChar);
802 }
803
GetSelection() const804 std::pair<int32_t, int32_t> CPWL_Edit::GetSelection() const {
805 return m_pEditImpl->GetSelection();
806 }
807
ClearSelection()808 void CPWL_Edit::ClearSelection() {
809 if (!IsReadOnly())
810 m_pEditImpl->ClearSelection();
811 }
812
SetScrollPos(const CFX_PointF & point)813 void CPWL_Edit::SetScrollPos(const CFX_PointF& point) {
814 m_pEditImpl->SetScrollPos(point);
815 }
816
GetScrollPos() const817 CFX_PointF CPWL_Edit::GetScrollPos() const {
818 return m_pEditImpl->GetScrollPos();
819 }
820
CopyText()821 void CPWL_Edit::CopyText() {}
822
PasteText()823 void CPWL_Edit::PasteText() {}
824
InsertWord(uint16_t word,FX_Charset nCharset)825 void CPWL_Edit::InsertWord(uint16_t word, FX_Charset nCharset) {
826 if (!IsReadOnly())
827 m_pEditImpl->InsertWord(word, nCharset);
828 }
829
InsertReturn()830 void CPWL_Edit::InsertReturn() {
831 if (!IsReadOnly())
832 m_pEditImpl->InsertReturn();
833 }
834
Delete()835 void CPWL_Edit::Delete() {
836 if (!IsReadOnly())
837 m_pEditImpl->Delete();
838 }
839
Backspace()840 void CPWL_Edit::Backspace() {
841 if (!IsReadOnly())
842 m_pEditImpl->Backspace();
843 }
844
CanUndo()845 bool CPWL_Edit::CanUndo() {
846 return !IsReadOnly() && m_pEditImpl->CanUndo();
847 }
848
CanRedo()849 bool CPWL_Edit::CanRedo() {
850 return !IsReadOnly() && m_pEditImpl->CanRedo();
851 }
852
Undo()853 bool CPWL_Edit::Undo() {
854 return CanUndo() && m_pEditImpl->Undo();
855 }
856
Redo()857 bool CPWL_Edit::Redo() {
858 return CanRedo() && m_pEditImpl->Redo();
859 }
860
SetReadyToInput()861 void CPWL_Edit::SetReadyToInput() {
862 if (m_bMouseDown) {
863 ReleaseCapture();
864 m_bMouseDown = false;
865 }
866 }
867