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