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