• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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