Home | History | Annotate | Download | only in pwl
      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 
     32 CPWL_Edit::CPWL_Edit() : m_bFocus(false) {}
     33 
     34 CPWL_Edit::~CPWL_Edit() {
     35   ASSERT(!m_bFocus);
     36 }
     37 
     38 ByteString CPWL_Edit::GetClassName() const {
     39   return PWL_CLASSNAME_EDIT;
     40 }
     41 
     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 
     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 
    109 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 
    121 void CPWL_Edit::SetAlignFormatV(PWL_EDIT_ALIGNFORMAT_V nFormat, bool bPaint) {
    122   m_pEdit->SetAlignmentV((int32_t)nFormat, bPaint);
    123 }
    124 
    125 bool CPWL_Edit::CanSelectAll() const {
    126   return GetSelectWordRange() != m_pEdit->GetWholeWordRange();
    127 }
    128 
    129 bool CPWL_Edit::CanCopy() const {
    130   return !HasFlag(PES_PASSWORD) && !HasFlag(PES_NOREAD) &&
    131          m_pEdit->IsSelected();
    132 }
    133 
    134 bool CPWL_Edit::CanCut() const {
    135   return CanCopy() && !IsReadOnly();
    136 }
    137 void CPWL_Edit::CutText() {
    138   if (!CanCut())
    139     return;
    140   m_pEdit->ClearSelection();
    141 }
    142 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    374 void CPWL_Edit::SetCharSpace(float fCharSpace) {
    375   m_pEdit->SetCharSpace(fCharSpace);
    376 }
    377 
    378 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 
    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 
    406 bool CPWL_Edit::IsTextFull() const {
    407   return m_pEdit->IsTextFull();
    408 }
    409 
    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 
    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 
    449 void CPWL_Edit::SetLimitChar(int32_t nLimitChar) {
    450   m_pEdit->SetLimitChar(nLimitChar);
    451 }
    452 
    453 void CPWL_Edit::ReplaceSel(const WideString& wsText) {
    454   m_pEdit->ClearSelection();
    455   m_pEdit->InsertText(wsText, FX_CHARSET_Default);
    456 }
    457 
    458 CFX_FloatRect CPWL_Edit::GetFocusRect() const {
    459   return CFX_FloatRect();
    460 }
    461 
    462 bool CPWL_Edit::IsVScrollBarVisible() const {
    463   CPWL_ScrollBar* pScroll = GetVScrollBar();
    464   return pScroll && pScroll->IsVisible();
    465 }
    466 
    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
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    664 CPVT_WordRange CPWL_Edit::GetLatinWordsRange(const CFX_PointF& point) const {
    665   return GetSameWordsRange(m_pEdit->SearchWordPlace(point), true, false);
    666 }
    667 
    668 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 
    676 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