1 // Copyright 2014 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 "fpdfsdk/pwl/cpwl_edit.h"
8
9 #include <algorithm>
10 #include <memory>
11 #include <sstream>
12 #include <vector>
13
14 #include "core/fpdfapi/font/cpdf_font.h"
15 #include "core/fpdfdoc/cpvt_word.h"
16 #include "core/fxcrt/fx_safe_types.h"
17 #include "core/fxcrt/xml/cxml_content.h"
18 #include "core/fxcrt/xml/cxml_element.h"
19 #include "core/fxge/cfx_graphstatedata.h"
20 #include "core/fxge/cfx_pathdata.h"
21 #include "core/fxge/cfx_renderdevice.h"
22 #include "core/fxge/fx_font.h"
23 #include "fpdfsdk/pwl/cpwl_caret.h"
24 #include "fpdfsdk/pwl/cpwl_edit_ctrl.h"
25 #include "fpdfsdk/pwl/cpwl_edit_impl.h"
26 #include "fpdfsdk/pwl/cpwl_font_map.h"
27 #include "fpdfsdk/pwl/cpwl_scroll_bar.h"
28 #include "fpdfsdk/pwl/cpwl_wnd.h"
29 #include "public/fpdf_fwlevent.h"
30 #include "third_party/base/stl_util.h"
31
CPWL_Edit()32 CPWL_Edit::CPWL_Edit() : m_bFocus(false) {}
33
~CPWL_Edit()34 CPWL_Edit::~CPWL_Edit() {
35 ASSERT(!m_bFocus);
36 }
37
GetClassName() const38 ByteString CPWL_Edit::GetClassName() const {
39 return PWL_CLASSNAME_EDIT;
40 }
41
SetText(const WideString & csText)42 void CPWL_Edit::SetText(const WideString& csText) {
43 WideString swText = csText;
44 if (!HasFlag(PES_RICH)) {
45 m_pEdit->SetText(swText);
46 return;
47 }
48
49 ByteString sValue = ByteString::FromUnicode(swText);
50 std::unique_ptr<CXML_Element> pXML(
51 CXML_Element::Parse(sValue.c_str(), sValue.GetLength()));
52 if (!pXML) {
53 m_pEdit->SetText(swText);
54 return;
55 }
56 swText.clear();
57
58 bool bFirst = true;
59 size_t nCount = pXML->CountChildren();
60 for (size_t i = 0; i < nCount; ++i) {
61 CXML_Element* pSubElement = ToElement(pXML->GetChild(i));
62 if (!pSubElement || !pSubElement->GetTagName().EqualNoCase("p"))
63 continue;
64
65 WideString swSection;
66 size_t nSubChild = pSubElement->CountChildren();
67 for (size_t j = 0; j < nSubChild; ++j) {
68 CXML_Content* pSubContent = ToContent(pSubElement->GetChild(j));
69 if (pSubContent)
70 swSection += pSubContent->m_Content;
71 }
72 if (bFirst)
73 bFirst = false;
74 else
75 swText += FWL_VKEY_Return;
76 swText += swSection;
77 }
78
79 m_pEdit->SetText(swText);
80 }
81
RePosChildWnd()82 bool CPWL_Edit::RePosChildWnd() {
83 if (CPWL_ScrollBar* pVSB = GetVScrollBar()) {
84 CFX_FloatRect rcWindow = m_rcOldWindow;
85 CFX_FloatRect rcVScroll =
86 CFX_FloatRect(rcWindow.right, rcWindow.bottom,
87 rcWindow.right + PWL_SCROLLBAR_WIDTH, rcWindow.top);
88
89 ObservedPtr thisObserved(this);
90
91 pVSB->Move(rcVScroll, true, false);
92 if (!thisObserved)
93 return false;
94 }
95
96 if (m_pEditCaret && !HasFlag(PES_TEXTOVERFLOW)) {
97 CFX_FloatRect rect = GetClientRect();
98 if (!rect.IsEmpty()) {
99 // +1 for caret beside border
100 rect.Inflate(1.0f, 1.0f);
101 rect.Normalize();
102 }
103 m_pEditCaret->SetClipRect(rect);
104 }
105
106 return CPWL_EditCtrl::RePosChildWnd();
107 }
108
GetClientRect() const109 CFX_FloatRect CPWL_Edit::GetClientRect() const {
110 float width = static_cast<float>(GetBorderWidth() + GetInnerBorderWidth());
111 CFX_FloatRect rcClient = GetWindowRect().GetDeflated(width, width);
112 if (CPWL_ScrollBar* pVSB = GetVScrollBar()) {
113 if (pVSB->IsVisible()) {
114 rcClient.right -= PWL_SCROLLBAR_WIDTH;
115 }
116 }
117
118 return rcClient;
119 }
120
SetAlignFormatV(PWL_EDIT_ALIGNFORMAT_V nFormat,bool bPaint)121 void CPWL_Edit::SetAlignFormatV(PWL_EDIT_ALIGNFORMAT_V nFormat, bool bPaint) {
122 m_pEdit->SetAlignmentV((int32_t)nFormat, bPaint);
123 }
124
CanSelectAll() const125 bool CPWL_Edit::CanSelectAll() const {
126 return GetSelectWordRange() != m_pEdit->GetWholeWordRange();
127 }
128
CanCopy() const129 bool CPWL_Edit::CanCopy() const {
130 return !HasFlag(PES_PASSWORD) && !HasFlag(PES_NOREAD) &&
131 m_pEdit->IsSelected();
132 }
133
CanCut() const134 bool CPWL_Edit::CanCut() const {
135 return CanCopy() && !IsReadOnly();
136 }
CutText()137 void CPWL_Edit::CutText() {
138 if (!CanCut())
139 return;
140 m_pEdit->ClearSelection();
141 }
142
OnCreated()143 void CPWL_Edit::OnCreated() {
144 CPWL_EditCtrl::OnCreated();
145
146 if (CPWL_ScrollBar* pScroll = GetVScrollBar()) {
147 pScroll->RemoveFlag(PWS_AUTOTRANSPARENT);
148 pScroll->SetTransparency(255);
149 }
150
151 SetParamByFlag();
152
153 m_rcOldWindow = GetWindowRect();
154
155 m_pEdit->SetOperationNotify(this);
156 }
157
SetParamByFlag()158 void CPWL_Edit::SetParamByFlag() {
159 if (HasFlag(PES_RIGHT)) {
160 m_pEdit->SetAlignmentH(2, false);
161 } else if (HasFlag(PES_MIDDLE)) {
162 m_pEdit->SetAlignmentH(1, false);
163 } else {
164 m_pEdit->SetAlignmentH(0, false);
165 }
166
167 if (HasFlag(PES_BOTTOM)) {
168 m_pEdit->SetAlignmentV(2, false);
169 } else if (HasFlag(PES_CENTER)) {
170 m_pEdit->SetAlignmentV(1, false);
171 } else {
172 m_pEdit->SetAlignmentV(0, false);
173 }
174
175 if (HasFlag(PES_PASSWORD)) {
176 m_pEdit->SetPasswordChar('*', false);
177 }
178
179 m_pEdit->SetMultiLine(HasFlag(PES_MULTILINE), false);
180 m_pEdit->SetAutoReturn(HasFlag(PES_AUTORETURN), false);
181 m_pEdit->SetAutoFontSize(HasFlag(PWS_AUTOFONTSIZE), false);
182 m_pEdit->SetAutoScroll(HasFlag(PES_AUTOSCROLL), false);
183 m_pEdit->EnableUndo(HasFlag(PES_UNDO));
184
185 if (HasFlag(PES_TEXTOVERFLOW)) {
186 SetClipRect(CFX_FloatRect());
187 m_pEdit->SetTextOverflow(true, false);
188 } else {
189 if (m_pEditCaret) {
190 CFX_FloatRect rect = GetClientRect();
191 if (!rect.IsEmpty()) {
192 // +1 for caret beside border
193 rect.Inflate(1.0f, 1.0f);
194 rect.Normalize();
195 }
196 m_pEditCaret->SetClipRect(rect);
197 }
198 }
199 }
200
DrawThisAppearance(CFX_RenderDevice * pDevice,const CFX_Matrix & mtUser2Device)201 void CPWL_Edit::DrawThisAppearance(CFX_RenderDevice* pDevice,
202 const CFX_Matrix& mtUser2Device) {
203 CPWL_Wnd::DrawThisAppearance(pDevice, mtUser2Device);
204
205 CFX_FloatRect rcClient = GetClientRect();
206
207 int32_t nCharArray = m_pEdit->GetCharArray();
208 FX_SAFE_INT32 nCharArraySafe = nCharArray;
209 nCharArraySafe -= 1;
210 nCharArraySafe *= 2;
211
212 if (nCharArray > 0 && nCharArraySafe.IsValid()) {
213 switch (GetBorderStyle()) {
214 case BorderStyle::SOLID: {
215 CFX_GraphStateData gsd;
216 gsd.m_LineWidth = (float)GetBorderWidth();
217
218 CFX_PathData path;
219
220 for (int32_t i = 0; i < nCharArray - 1; i++) {
221 path.AppendPoint(
222 CFX_PointF(
223 rcClient.left +
224 ((rcClient.right - rcClient.left) / nCharArray) * (i + 1),
225 rcClient.bottom),
226 FXPT_TYPE::MoveTo, false);
227 path.AppendPoint(
228 CFX_PointF(
229 rcClient.left +
230 ((rcClient.right - rcClient.left) / nCharArray) * (i + 1),
231 rcClient.top),
232 FXPT_TYPE::LineTo, false);
233 }
234 if (!path.GetPoints().empty()) {
235 pDevice->DrawPath(&path, &mtUser2Device, &gsd, 0,
236 GetBorderColor().ToFXColor(255), FXFILL_ALTERNATE);
237 }
238 break;
239 }
240 case BorderStyle::DASH: {
241 CFX_GraphStateData gsd;
242 gsd.m_LineWidth = (float)GetBorderWidth();
243
244 gsd.SetDashCount(2);
245 gsd.m_DashArray[0] = (float)GetBorderDash().nDash;
246 gsd.m_DashArray[1] = (float)GetBorderDash().nGap;
247 gsd.m_DashPhase = (float)GetBorderDash().nPhase;
248
249 CFX_PathData path;
250 for (int32_t i = 0; i < nCharArray - 1; i++) {
251 path.AppendPoint(
252 CFX_PointF(
253 rcClient.left +
254 ((rcClient.right - rcClient.left) / nCharArray) * (i + 1),
255 rcClient.bottom),
256 FXPT_TYPE::MoveTo, false);
257 path.AppendPoint(
258 CFX_PointF(
259 rcClient.left +
260 ((rcClient.right - rcClient.left) / nCharArray) * (i + 1),
261 rcClient.top),
262 FXPT_TYPE::LineTo, false);
263 }
264 if (!path.GetPoints().empty()) {
265 pDevice->DrawPath(&path, &mtUser2Device, &gsd, 0,
266 GetBorderColor().ToFXColor(255), FXFILL_ALTERNATE);
267 }
268 break;
269 }
270 default:
271 break;
272 }
273 }
274
275 CFX_FloatRect rcClip;
276 CPVT_WordRange wrRange = m_pEdit->GetVisibleWordRange();
277 CPVT_WordRange* pRange = nullptr;
278 if (!HasFlag(PES_TEXTOVERFLOW)) {
279 rcClip = GetClientRect();
280 pRange = &wrRange;
281 }
282
283 CFX_SystemHandler* pSysHandler = GetSystemHandler();
284 CPWL_EditImpl::DrawEdit(pDevice, mtUser2Device, m_pEdit.get(),
285 GetTextColor().ToFXColor(GetTransparency()), rcClip,
286 CFX_PointF(), pRange, pSysHandler,
287 m_pFormFiller.Get());
288 }
289
OnLButtonDown(const CFX_PointF & point,uint32_t nFlag)290 bool CPWL_Edit::OnLButtonDown(const CFX_PointF& point, uint32_t nFlag) {
291 CPWL_Wnd::OnLButtonDown(point, nFlag);
292
293 if (HasFlag(PES_TEXTOVERFLOW) || ClientHitTest(point)) {
294 if (m_bMouseDown && !InvalidateRect(nullptr))
295 return true;
296
297 m_bMouseDown = true;
298 SetCapture();
299
300 m_pEdit->OnMouseDown(point, IsSHIFTpressed(nFlag), IsCTRLpressed(nFlag));
301 }
302
303 return true;
304 }
305
OnLButtonDblClk(const CFX_PointF & point,uint32_t nFlag)306 bool CPWL_Edit::OnLButtonDblClk(const CFX_PointF& point, uint32_t nFlag) {
307 CPWL_Wnd::OnLButtonDblClk(point, nFlag);
308
309 if (HasFlag(PES_TEXTOVERFLOW) || ClientHitTest(point)) {
310 m_pEdit->SelectAll();
311 }
312
313 return true;
314 }
315
OnRButtonUp(const CFX_PointF & point,uint32_t nFlag)316 bool CPWL_Edit::OnRButtonUp(const CFX_PointF& point, uint32_t nFlag) {
317 if (m_bMouseDown)
318 return false;
319
320 CPWL_Wnd::OnRButtonUp(point, nFlag);
321
322 if (!HasFlag(PES_TEXTOVERFLOW) && !ClientHitTest(point))
323 return true;
324
325 CFX_SystemHandler* pSH = GetSystemHandler();
326 if (!pSH)
327 return false;
328
329 SetFocus();
330
331 return false;
332 }
333
OnSetFocus()334 void CPWL_Edit::OnSetFocus() {
335 ObservedPtr observed_ptr(this);
336 SetEditCaret(true);
337 if (!observed_ptr)
338 return;
339
340 if (!IsReadOnly()) {
341 if (CPWL_Wnd::FocusHandlerIface* pFocusHandler = GetFocusHandler()) {
342 pFocusHandler->OnSetFocus(this);
343 if (!observed_ptr)
344 return;
345 }
346 }
347 m_bFocus = true;
348 }
349
OnKillFocus()350 void CPWL_Edit::OnKillFocus() {
351 ObservedPtr observed_ptr(this);
352
353 CPWL_ScrollBar* pScroll = GetVScrollBar();
354 if (pScroll && pScroll->IsVisible()) {
355 pScroll->SetVisible(false);
356 if (!observed_ptr)
357 return;
358
359 if (!Move(m_rcOldWindow, true, true))
360 return;
361 }
362
363 m_pEdit->SelectNone();
364 if (!observed_ptr)
365 return;
366
367 if (!SetCaret(false, CFX_PointF(), CFX_PointF()))
368 return;
369
370 SetCharSet(FX_CHARSET_ANSI);
371 m_bFocus = false;
372 }
373
SetCharSpace(float fCharSpace)374 void CPWL_Edit::SetCharSpace(float fCharSpace) {
375 m_pEdit->SetCharSpace(fCharSpace);
376 }
377
GetSelectWordRange() const378 CPVT_WordRange CPWL_Edit::GetSelectWordRange() const {
379 if (!m_pEdit->IsSelected())
380 return CPVT_WordRange();
381
382 int32_t nStart = -1;
383 int32_t nEnd = -1;
384
385 m_pEdit->GetSelection(nStart, nEnd);
386
387 CPVT_WordPlace wpStart = m_pEdit->WordIndexToWordPlace(nStart);
388 CPVT_WordPlace wpEnd = m_pEdit->WordIndexToWordPlace(nEnd);
389
390 return CPVT_WordRange(wpStart, wpEnd);
391 }
392
GetWordRightBottomPoint(const CPVT_WordPlace & wpWord)393 CFX_PointF CPWL_Edit::GetWordRightBottomPoint(const CPVT_WordPlace& wpWord) {
394 CPWL_EditImpl_Iterator* pIterator = m_pEdit->GetIterator();
395 CPVT_WordPlace wpOld = pIterator->GetAt();
396 pIterator->SetAt(wpWord);
397
398 CFX_PointF pt;
399 CPVT_Word word;
400 if (pIterator->GetWord(word))
401 pt = CFX_PointF(word.ptWord.x + word.fWidth, word.ptWord.y + word.fDescent);
402 pIterator->SetAt(wpOld);
403 return pt;
404 }
405
IsTextFull() const406 bool CPWL_Edit::IsTextFull() const {
407 return m_pEdit->IsTextFull();
408 }
409
GetCharArrayAutoFontSize(CPDF_Font * pFont,const CFX_FloatRect & rcPlate,int32_t nCharArray)410 float CPWL_Edit::GetCharArrayAutoFontSize(CPDF_Font* pFont,
411 const CFX_FloatRect& rcPlate,
412 int32_t nCharArray) {
413 if (!pFont || pFont->IsStandardFont())
414 return 0.0f;
415
416 FX_RECT rcBBox;
417 pFont->GetFontBBox(rcBBox);
418
419 CFX_FloatRect rcCell = rcPlate;
420 float xdiv = rcCell.Width() / nCharArray * 1000.0f / rcBBox.Width();
421 float ydiv = -rcCell.Height() * 1000.0f / rcBBox.Height();
422
423 return xdiv < ydiv ? xdiv : ydiv;
424 }
425
SetCharArray(int32_t nCharArray)426 void CPWL_Edit::SetCharArray(int32_t nCharArray) {
427 if (!HasFlag(PES_CHARARRAY) || nCharArray <= 0)
428 return;
429
430 m_pEdit->SetCharArray(nCharArray);
431 m_pEdit->SetTextOverflow(true, true);
432
433 if (!HasFlag(PWS_AUTOFONTSIZE))
434 return;
435
436 IPVT_FontMap* pFontMap = GetFontMap();
437 if (!pFontMap)
438 return;
439
440 float fFontSize = GetCharArrayAutoFontSize(pFontMap->GetPDFFont(0),
441 GetClientRect(), nCharArray);
442 if (fFontSize <= 0.0f)
443 return;
444
445 m_pEdit->SetAutoFontSize(false, true);
446 m_pEdit->SetFontSize(fFontSize);
447 }
448
SetLimitChar(int32_t nLimitChar)449 void CPWL_Edit::SetLimitChar(int32_t nLimitChar) {
450 m_pEdit->SetLimitChar(nLimitChar);
451 }
452
ReplaceSel(const WideString & wsText)453 void CPWL_Edit::ReplaceSel(const WideString& wsText) {
454 m_pEdit->ClearSelection();
455 m_pEdit->InsertText(wsText, FX_CHARSET_Default);
456 }
457
GetFocusRect() const458 CFX_FloatRect CPWL_Edit::GetFocusRect() const {
459 return CFX_FloatRect();
460 }
461
IsVScrollBarVisible() const462 bool CPWL_Edit::IsVScrollBarVisible() const {
463 CPWL_ScrollBar* pScroll = GetVScrollBar();
464 return pScroll && pScroll->IsVisible();
465 }
466
OnKeyDown(uint16_t nChar,uint32_t nFlag)467 bool CPWL_Edit::OnKeyDown(uint16_t nChar, uint32_t nFlag) {
468 if (m_bMouseDown)
469 return true;
470
471 if (nChar == FWL_VKEY_Delete) {
472 if (m_pFillerNotify) {
473 WideString strChange;
474 WideString strChangeEx;
475
476 int nSelStart = 0;
477 int nSelEnd = 0;
478 GetSelection(nSelStart, nSelEnd);
479
480 if (nSelStart == nSelEnd)
481 nSelEnd = nSelStart + 1;
482
483 CPWL_Wnd::ObservedPtr thisObserved(this);
484
485 bool bRC;
486 bool bExit;
487 std::tie(bRC, bExit) = m_pFillerNotify->OnBeforeKeyStroke(
488 GetAttachedData(), strChange, strChangeEx, nSelStart, nSelEnd, true,
489 nFlag);
490
491 if (!thisObserved)
492 return false;
493
494 if (!bRC)
495 return false;
496 if (bExit)
497 return false;
498 }
499 }
500
501 bool bRet = CPWL_EditCtrl::OnKeyDown(nChar, nFlag);
502
503 // In case of implementation swallow the OnKeyDown event.
504 if (IsProceedtoOnChar(nChar, nFlag))
505 return true;
506
507 return bRet;
508 }
509
510 // static
IsProceedtoOnChar(uint16_t nKeyCode,uint32_t nFlag)511 bool CPWL_Edit::IsProceedtoOnChar(uint16_t nKeyCode, uint32_t nFlag) {
512 bool bCtrl = IsCTRLpressed(nFlag);
513 bool bAlt = IsALTpressed(nFlag);
514 if (bCtrl && !bAlt) {
515 // hot keys for edit control.
516 switch (nKeyCode) {
517 case 'C':
518 case 'V':
519 case 'X':
520 case 'A':
521 case 'Z':
522 return true;
523 default:
524 break;
525 }
526 }
527 // control characters.
528 switch (nKeyCode) {
529 case FWL_VKEY_Escape:
530 case FWL_VKEY_Back:
531 case FWL_VKEY_Return:
532 case FWL_VKEY_Space:
533 return true;
534 default:
535 return false;
536 }
537 }
538
OnChar(uint16_t nChar,uint32_t nFlag)539 bool CPWL_Edit::OnChar(uint16_t nChar, uint32_t nFlag) {
540 if (m_bMouseDown)
541 return true;
542
543 bool bRC = true;
544 bool bExit = false;
545
546 if (!IsCTRLpressed(nFlag)) {
547 if (m_pFillerNotify) {
548 WideString swChange;
549
550 int nSelStart = 0;
551 int nSelEnd = 0;
552 GetSelection(nSelStart, nSelEnd);
553
554 switch (nChar) {
555 case FWL_VKEY_Back:
556 if (nSelStart == nSelEnd)
557 nSelStart = nSelEnd - 1;
558 break;
559 case FWL_VKEY_Return:
560 break;
561 default:
562 swChange += nChar;
563 break;
564 }
565
566 CPWL_Wnd::ObservedPtr thisObserved(this);
567
568 WideString strChangeEx;
569 std::tie(bRC, bExit) = m_pFillerNotify->OnBeforeKeyStroke(
570 GetAttachedData(), swChange, strChangeEx, nSelStart, nSelEnd, true,
571 nFlag);
572
573 if (!thisObserved)
574 return false;
575 }
576 }
577
578 if (!bRC)
579 return true;
580 if (bExit)
581 return false;
582
583 if (IPVT_FontMap* pFontMap = GetFontMap()) {
584 int32_t nOldCharSet = GetCharSet();
585 int32_t nNewCharSet =
586 pFontMap->CharSetFromUnicode(nChar, FX_CHARSET_Default);
587 if (nOldCharSet != nNewCharSet) {
588 SetCharSet(nNewCharSet);
589 }
590 }
591
592 return CPWL_EditCtrl::OnChar(nChar, nFlag);
593 }
594
OnMouseWheel(short zDelta,const CFX_PointF & point,uint32_t nFlag)595 bool CPWL_Edit::OnMouseWheel(short zDelta,
596 const CFX_PointF& point,
597 uint32_t nFlag) {
598 if (!HasFlag(PES_MULTILINE))
599 return false;
600
601 CFX_PointF ptScroll = GetScrollPos();
602 if (zDelta > 0)
603 ptScroll.y += GetFontSize();
604 else
605 ptScroll.y -= GetFontSize();
606 SetScrollPos(ptScroll);
607 return true;
608 }
609
OnInsertReturn(const CPVT_WordPlace & place,const CPVT_WordPlace & oldplace)610 void CPWL_Edit::OnInsertReturn(const CPVT_WordPlace& place,
611 const CPVT_WordPlace& oldplace) {
612 if (HasFlag(PES_SPELLCHECK)) {
613 m_pEdit->RefreshWordRange(CombineWordRange(GetLatinWordsRange(oldplace),
614 GetLatinWordsRange(place)));
615 }
616 }
617
OnBackSpace(const CPVT_WordPlace & place,const CPVT_WordPlace & oldplace)618 void CPWL_Edit::OnBackSpace(const CPVT_WordPlace& place,
619 const CPVT_WordPlace& oldplace) {
620 if (HasFlag(PES_SPELLCHECK)) {
621 m_pEdit->RefreshWordRange(CombineWordRange(GetLatinWordsRange(oldplace),
622 GetLatinWordsRange(place)));
623 }
624 }
625
OnDelete(const CPVT_WordPlace & place,const CPVT_WordPlace & oldplace)626 void CPWL_Edit::OnDelete(const CPVT_WordPlace& place,
627 const CPVT_WordPlace& oldplace) {
628 if (HasFlag(PES_SPELLCHECK)) {
629 m_pEdit->RefreshWordRange(CombineWordRange(GetLatinWordsRange(oldplace),
630 GetLatinWordsRange(place)));
631 }
632 }
633
OnClear(const CPVT_WordPlace & place,const CPVT_WordPlace & oldplace)634 void CPWL_Edit::OnClear(const CPVT_WordPlace& place,
635 const CPVT_WordPlace& oldplace) {
636 if (HasFlag(PES_SPELLCHECK)) {
637 m_pEdit->RefreshWordRange(CombineWordRange(GetLatinWordsRange(oldplace),
638 GetLatinWordsRange(place)));
639 }
640 }
641
OnInsertWord(const CPVT_WordPlace & place,const CPVT_WordPlace & oldplace)642 void CPWL_Edit::OnInsertWord(const CPVT_WordPlace& place,
643 const CPVT_WordPlace& oldplace) {
644 if (HasFlag(PES_SPELLCHECK)) {
645 m_pEdit->RefreshWordRange(CombineWordRange(GetLatinWordsRange(oldplace),
646 GetLatinWordsRange(place)));
647 }
648 }
649
OnInsertText(const CPVT_WordPlace & place,const CPVT_WordPlace & oldplace)650 void CPWL_Edit::OnInsertText(const CPVT_WordPlace& place,
651 const CPVT_WordPlace& oldplace) {
652 if (HasFlag(PES_SPELLCHECK)) {
653 m_pEdit->RefreshWordRange(CombineWordRange(GetLatinWordsRange(oldplace),
654 GetLatinWordsRange(place)));
655 }
656 }
657
CombineWordRange(const CPVT_WordRange & wr1,const CPVT_WordRange & wr2)658 CPVT_WordRange CPWL_Edit::CombineWordRange(const CPVT_WordRange& wr1,
659 const CPVT_WordRange& wr2) {
660 return CPVT_WordRange(std::min(wr1.BeginPos, wr2.BeginPos),
661 std::max(wr1.EndPos, wr2.EndPos));
662 }
663
GetLatinWordsRange(const CFX_PointF & point) const664 CPVT_WordRange CPWL_Edit::GetLatinWordsRange(const CFX_PointF& point) const {
665 return GetSameWordsRange(m_pEdit->SearchWordPlace(point), true, false);
666 }
667
GetLatinWordsRange(const CPVT_WordPlace & place) const668 CPVT_WordRange CPWL_Edit::GetLatinWordsRange(
669 const CPVT_WordPlace& place) const {
670 return GetSameWordsRange(place, true, false);
671 }
672
673 #define PWL_ISARABICWORD(word) \
674 ((word >= 0x0600 && word <= 0x06FF) || (word >= 0xFB50 && word <= 0xFEFC))
675
GetSameWordsRange(const CPVT_WordPlace & place,bool bLatin,bool bArabic) const676 CPVT_WordRange CPWL_Edit::GetSameWordsRange(const CPVT_WordPlace& place,
677 bool bLatin,
678 bool bArabic) const {
679 CPVT_WordRange range;
680
681 CPWL_EditImpl_Iterator* pIterator = m_pEdit->GetIterator();
682 CPVT_Word wordinfo;
683 CPVT_WordPlace wpStart(place), wpEnd(place);
684 pIterator->SetAt(place);
685
686 if (bLatin) {
687 while (pIterator->NextWord()) {
688 if (!pIterator->GetWord(wordinfo) ||
689 !FX_EDIT_ISLATINWORD(wordinfo.Word)) {
690 break;
691 }
692
693 wpEnd = pIterator->GetAt();
694 }
695 } else if (bArabic) {
696 while (pIterator->NextWord()) {
697 if (!pIterator->GetWord(wordinfo) || !PWL_ISARABICWORD(wordinfo.Word))
698 break;
699
700 wpEnd = pIterator->GetAt();
701 }
702 }
703
704 pIterator->SetAt(place);
705
706 if (bLatin) {
707 do {
708 if (!pIterator->GetWord(wordinfo) ||
709 !FX_EDIT_ISLATINWORD(wordinfo.Word)) {
710 break;
711 }
712
713 wpStart = pIterator->GetAt();
714 } while (pIterator->PrevWord());
715 } else if (bArabic) {
716 do {
717 if (!pIterator->GetWord(wordinfo) || !PWL_ISARABICWORD(wordinfo.Word))
718 break;
719
720 wpStart = pIterator->GetAt();
721 } while (pIterator->PrevWord());
722 }
723
724 range.Set(wpStart, wpEnd);
725 return range;
726 }
727