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