Home | History | Annotate | Download | only in pdfwindow
      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/pdfwindow/PWL_Edit.h"
      8 
      9 #include <memory>
     10 #include <vector>
     11 
     12 #include "core/fpdfapi/font/cpdf_font.h"
     13 #include "core/fpdfdoc/cpvt_word.h"
     14 #include "core/fxcrt/fx_safe_types.h"
     15 #include "core/fxcrt/fx_xml.h"
     16 #include "core/fxge/cfx_graphstatedata.h"
     17 #include "core/fxge/cfx_pathdata.h"
     18 #include "core/fxge/cfx_renderdevice.h"
     19 #include "core/fxge/fx_font.h"
     20 #include "fpdfsdk/fxedit/fxet_edit.h"
     21 #include "fpdfsdk/pdfwindow/PWL_Caret.h"
     22 #include "fpdfsdk/pdfwindow/PWL_EditCtrl.h"
     23 #include "fpdfsdk/pdfwindow/PWL_FontMap.h"
     24 #include "fpdfsdk/pdfwindow/PWL_ScrollBar.h"
     25 #include "fpdfsdk/pdfwindow/PWL_Utils.h"
     26 #include "fpdfsdk/pdfwindow/PWL_Wnd.h"
     27 #include "public/fpdf_fwlevent.h"
     28 #include "third_party/base/stl_util.h"
     29 
     30 CPWL_Edit::CPWL_Edit()
     31     : m_pFillerNotify(nullptr), m_bFocus(false), m_pFormFiller(nullptr) {}
     32 
     33 CPWL_Edit::~CPWL_Edit() {
     34   ASSERT(m_bFocus == false);
     35 }
     36 
     37 CFX_ByteString CPWL_Edit::GetClassName() const {
     38   return PWL_CLASSNAME_EDIT;
     39 }
     40 
     41 void CPWL_Edit::OnDestroy() {}
     42 
     43 void CPWL_Edit::SetText(const CFX_WideString& csText) {
     44   CFX_WideString swText = csText;
     45   if (!HasFlag(PES_RICH)) {
     46     m_pEdit->SetText(swText);
     47     return;
     48   }
     49 
     50   CFX_ByteString sValue = CFX_ByteString::FromUnicode(swText);
     51   std::unique_ptr<CXML_Element> pXML(
     52       CXML_Element::Parse(sValue.c_str(), sValue.GetLength()));
     53   if (!pXML) {
     54     m_pEdit->SetText(swText);
     55     return;
     56   }
     57 
     58   int32_t nCount = pXML->CountChildren();
     59   bool bFirst = true;
     60 
     61   swText.clear();
     62 
     63   for (int32_t i = 0; i < nCount; i++) {
     64     CXML_Element* pSubElement = pXML->GetElement(i);
     65     if (!pSubElement)
     66       continue;
     67 
     68     CFX_ByteString tag = pSubElement->GetTagName();
     69     if (tag.EqualNoCase("p")) {
     70       int nChild = pSubElement->CountChildren();
     71       CFX_WideString swSection;
     72       for (int32_t j = 0; j < nChild; j++)
     73         swSection += pSubElement->GetContent(j);
     74 
     75       if (bFirst)
     76         bFirst = false;
     77       else
     78         swText += FWL_VKEY_Return;
     79       swText += swSection;
     80     }
     81   }
     82 
     83   m_pEdit->SetText(swText);
     84 }
     85 
     86 void CPWL_Edit::RePosChildWnd() {
     87   if (CPWL_ScrollBar* pVSB = GetVScrollBar()) {
     88     CFX_FloatRect rcWindow = m_rcOldWindow;
     89     CFX_FloatRect rcVScroll =
     90         CFX_FloatRect(rcWindow.right, rcWindow.bottom,
     91                       rcWindow.right + PWL_SCROLLBAR_WIDTH, rcWindow.top);
     92     pVSB->Move(rcVScroll, true, false);
     93   }
     94 
     95   if (m_pEditCaret && !HasFlag(PES_TEXTOVERFLOW))
     96     m_pEditCaret->SetClipRect(CPWL_Utils::InflateRect(
     97         GetClientRect(), 1.0f));  // +1 for caret beside border
     98 
     99   CPWL_EditCtrl::RePosChildWnd();
    100 }
    101 
    102 CFX_FloatRect CPWL_Edit::GetClientRect() const {
    103   CFX_FloatRect rcClient = CPWL_Utils::DeflateRect(
    104       GetWindowRect(), (FX_FLOAT)(GetBorderWidth() + GetInnerBorderWidth()));
    105 
    106   if (CPWL_ScrollBar* pVSB = GetVScrollBar()) {
    107     if (pVSB->IsVisible()) {
    108       rcClient.right -= PWL_SCROLLBAR_WIDTH;
    109     }
    110   }
    111 
    112   return rcClient;
    113 }
    114 
    115 void CPWL_Edit::SetAlignFormatV(PWL_EDIT_ALIGNFORMAT_V nFormat, bool bPaint) {
    116   m_pEdit->SetAlignmentV((int32_t)nFormat, bPaint);
    117 }
    118 
    119 bool CPWL_Edit::CanSelectAll() const {
    120   return GetSelectWordRange() != m_pEdit->GetWholeWordRange();
    121 }
    122 
    123 bool CPWL_Edit::CanClear() const {
    124   return !IsReadOnly() && m_pEdit->IsSelected();
    125 }
    126 
    127 bool CPWL_Edit::CanCopy() const {
    128   return !HasFlag(PES_PASSWORD) && !HasFlag(PES_NOREAD) &&
    129          m_pEdit->IsSelected();
    130 }
    131 
    132 bool CPWL_Edit::CanCut() const {
    133   return CanCopy() && !IsReadOnly();
    134 }
    135 void CPWL_Edit::CutText() {
    136   if (!CanCut())
    137     return;
    138   m_pEdit->Clear();
    139 }
    140 
    141 void CPWL_Edit::OnCreated() {
    142   CPWL_EditCtrl::OnCreated();
    143 
    144   if (CPWL_ScrollBar* pScroll = GetVScrollBar()) {
    145     pScroll->RemoveFlag(PWS_AUTOTRANSPARENT);
    146     pScroll->SetTransparency(255);
    147   }
    148 
    149   SetParamByFlag();
    150 
    151   m_rcOldWindow = GetWindowRect();
    152 
    153   m_pEdit->SetOprNotify(this);
    154   m_pEdit->EnableOprNotify(true);
    155 }
    156 
    157 void CPWL_Edit::SetParamByFlag() {
    158   if (HasFlag(PES_RIGHT)) {
    159     m_pEdit->SetAlignmentH(2, false);
    160   } else if (HasFlag(PES_MIDDLE)) {
    161     m_pEdit->SetAlignmentH(1, false);
    162   } else {
    163     m_pEdit->SetAlignmentH(0, false);
    164   }
    165 
    166   if (HasFlag(PES_BOTTOM)) {
    167     m_pEdit->SetAlignmentV(2, false);
    168   } else if (HasFlag(PES_CENTER)) {
    169     m_pEdit->SetAlignmentV(1, false);
    170   } else {
    171     m_pEdit->SetAlignmentV(0, false);
    172   }
    173 
    174   if (HasFlag(PES_PASSWORD)) {
    175     m_pEdit->SetPasswordChar('*', false);
    176   }
    177 
    178   m_pEdit->SetMultiLine(HasFlag(PES_MULTILINE), false);
    179   m_pEdit->SetAutoReturn(HasFlag(PES_AUTORETURN), false);
    180   m_pEdit->SetAutoFontSize(HasFlag(PWS_AUTOFONTSIZE), false);
    181   m_pEdit->SetAutoScroll(HasFlag(PES_AUTOSCROLL), false);
    182   m_pEdit->EnableUndo(HasFlag(PES_UNDO));
    183 
    184   if (HasFlag(PES_TEXTOVERFLOW)) {
    185     SetClipRect(CFX_FloatRect(0.0f, 0.0f, 0.0f, 0.0f));
    186     m_pEdit->SetTextOverflow(true, false);
    187   } else {
    188     if (m_pEditCaret) {
    189       m_pEditCaret->SetClipRect(CPWL_Utils::InflateRect(
    190           GetClientRect(), 1.0f));  // +1 for caret beside border
    191     }
    192   }
    193 }
    194 
    195 void CPWL_Edit::GetThisAppearanceStream(CFX_ByteTextBuf& sAppStream) {
    196   CPWL_Wnd::GetThisAppearanceStream(sAppStream);
    197 
    198   CFX_FloatRect rcClient = GetClientRect();
    199   CFX_ByteTextBuf sLine;
    200 
    201   int32_t nCharArray = m_pEdit->GetCharArray();
    202 
    203   if (nCharArray > 0) {
    204     switch (GetBorderStyle()) {
    205       case BorderStyle::SOLID: {
    206         sLine << "q\n"
    207               << GetBorderWidth() << " w\n"
    208               << CPWL_Utils::GetColorAppStream(GetBorderColor(), false)
    209                      .AsStringC()
    210               << " 2 J 0 j\n";
    211 
    212         for (int32_t i = 1; i < nCharArray; i++) {
    213           sLine << rcClient.left +
    214                        ((rcClient.right - rcClient.left) / nCharArray) * i
    215                 << " " << rcClient.bottom << " m\n"
    216                 << rcClient.left +
    217                        ((rcClient.right - rcClient.left) / nCharArray) * i
    218                 << " " << rcClient.top << " l S\n";
    219         }
    220 
    221         sLine << "Q\n";
    222         break;
    223       }
    224       case BorderStyle::DASH: {
    225         sLine << "q\n"
    226               << GetBorderWidth() << " w\n"
    227               << CPWL_Utils::GetColorAppStream(GetBorderColor(), false)
    228                      .AsStringC()
    229               << " 2 J 0 j\n"
    230               << "[" << GetBorderDash().nDash << " " << GetBorderDash().nGap
    231               << "] " << GetBorderDash().nPhase << " d\n";
    232 
    233         for (int32_t i = 1; i < nCharArray; i++) {
    234           sLine << rcClient.left +
    235                        ((rcClient.right - rcClient.left) / nCharArray) * i
    236                 << " " << rcClient.bottom << " m\n"
    237                 << rcClient.left +
    238                        ((rcClient.right - rcClient.left) / nCharArray) * i
    239                 << " " << rcClient.top << " l S\n";
    240         }
    241 
    242         sLine << "Q\n";
    243         break;
    244       }
    245       default:
    246         break;
    247     }
    248   }
    249 
    250   sAppStream << sLine;
    251 
    252   CFX_ByteTextBuf sText;
    253   CFX_PointF ptOffset;
    254   CPVT_WordRange wrWhole = m_pEdit->GetWholeWordRange();
    255   CPVT_WordRange wrSelect = GetSelectWordRange();
    256   CPVT_WordRange wrVisible =
    257       HasFlag(PES_TEXTOVERFLOW) ? wrWhole : m_pEdit->GetVisibleWordRange();
    258 
    259   CPVT_WordRange wrSelBefore(wrWhole.BeginPos, wrSelect.BeginPos);
    260   CPVT_WordRange wrSelAfter(wrSelect.EndPos, wrWhole.EndPos);
    261   CPVT_WordRange wrTemp =
    262       CPWL_Utils::OverlapWordRange(GetSelectWordRange(), wrVisible);
    263   CFX_ByteString sEditSel =
    264       CPWL_Utils::GetEditSelAppStream(m_pEdit.get(), ptOffset, &wrTemp);
    265 
    266   if (sEditSel.GetLength() > 0)
    267     sText << CPWL_Utils::GetColorAppStream(PWL_DEFAULT_SELBACKCOLOR).AsStringC()
    268           << sEditSel.AsStringC();
    269 
    270   wrTemp = CPWL_Utils::OverlapWordRange(wrVisible, wrSelBefore);
    271   CFX_ByteString sEditBefore = CPWL_Utils::GetEditAppStream(
    272       m_pEdit.get(), ptOffset, &wrTemp, !HasFlag(PES_CHARARRAY),
    273       m_pEdit->GetPasswordChar());
    274 
    275   if (sEditBefore.GetLength() > 0)
    276     sText << "BT\n"
    277           << CPWL_Utils::GetColorAppStream(GetTextColor()).AsStringC()
    278           << sEditBefore.AsStringC() << "ET\n";
    279 
    280   wrTemp = CPWL_Utils::OverlapWordRange(wrVisible, wrSelect);
    281   CFX_ByteString sEditMid = CPWL_Utils::GetEditAppStream(
    282       m_pEdit.get(), ptOffset, &wrTemp, !HasFlag(PES_CHARARRAY),
    283       m_pEdit->GetPasswordChar());
    284 
    285   if (sEditMid.GetLength() > 0)
    286     sText << "BT\n"
    287           << CPWL_Utils::GetColorAppStream(CPWL_Color(COLORTYPE_GRAY, 1))
    288                  .AsStringC()
    289           << sEditMid.AsStringC() << "ET\n";
    290 
    291   wrTemp = CPWL_Utils::OverlapWordRange(wrVisible, wrSelAfter);
    292   CFX_ByteString sEditAfter = CPWL_Utils::GetEditAppStream(
    293       m_pEdit.get(), ptOffset, &wrTemp, !HasFlag(PES_CHARARRAY),
    294       m_pEdit->GetPasswordChar());
    295 
    296   if (sEditAfter.GetLength() > 0)
    297     sText << "BT\n"
    298           << CPWL_Utils::GetColorAppStream(GetTextColor()).AsStringC()
    299           << sEditAfter.AsStringC() << "ET\n";
    300 
    301   if (sText.GetLength() > 0) {
    302     CFX_FloatRect rect = GetClientRect();
    303     sAppStream << "q\n/Tx BMC\n";
    304 
    305     if (!HasFlag(PES_TEXTOVERFLOW))
    306       sAppStream << rect.left << " " << rect.bottom << " "
    307                  << rect.right - rect.left << " " << rect.top - rect.bottom
    308                  << " re W n\n";
    309 
    310     sAppStream << sText;
    311 
    312     sAppStream << "EMC\nQ\n";
    313   }
    314 }
    315 
    316 void CPWL_Edit::DrawThisAppearance(CFX_RenderDevice* pDevice,
    317                                    CFX_Matrix* pUser2Device) {
    318   CPWL_Wnd::DrawThisAppearance(pDevice, pUser2Device);
    319 
    320   CFX_FloatRect rcClient = GetClientRect();
    321   CFX_ByteTextBuf sLine;
    322 
    323   int32_t nCharArray = m_pEdit->GetCharArray();
    324   FX_SAFE_INT32 nCharArraySafe = nCharArray;
    325   nCharArraySafe -= 1;
    326   nCharArraySafe *= 2;
    327 
    328   if (nCharArray > 0 && nCharArraySafe.IsValid()) {
    329     switch (GetBorderStyle()) {
    330       case BorderStyle::SOLID: {
    331         CFX_GraphStateData gsd;
    332         gsd.m_LineWidth = (FX_FLOAT)GetBorderWidth();
    333 
    334         CFX_PathData path;
    335 
    336         for (int32_t i = 0; i < nCharArray - 1; i++) {
    337           path.AppendPoint(
    338               CFX_PointF(
    339                   rcClient.left +
    340                       ((rcClient.right - rcClient.left) / nCharArray) * (i + 1),
    341                   rcClient.bottom),
    342               FXPT_TYPE::MoveTo, false);
    343           path.AppendPoint(
    344               CFX_PointF(
    345                   rcClient.left +
    346                       ((rcClient.right - rcClient.left) / nCharArray) * (i + 1),
    347                   rcClient.top),
    348               FXPT_TYPE::LineTo, false);
    349         }
    350         if (!path.GetPoints().empty()) {
    351           pDevice->DrawPath(&path, pUser2Device, &gsd, 0,
    352                             GetBorderColor().ToFXColor(255), FXFILL_ALTERNATE);
    353         }
    354         break;
    355       }
    356       case BorderStyle::DASH: {
    357         CFX_GraphStateData gsd;
    358         gsd.m_LineWidth = (FX_FLOAT)GetBorderWidth();
    359 
    360         gsd.SetDashCount(2);
    361         gsd.m_DashArray[0] = (FX_FLOAT)GetBorderDash().nDash;
    362         gsd.m_DashArray[1] = (FX_FLOAT)GetBorderDash().nGap;
    363         gsd.m_DashPhase = (FX_FLOAT)GetBorderDash().nPhase;
    364 
    365         CFX_PathData path;
    366         for (int32_t i = 0; i < nCharArray - 1; i++) {
    367           path.AppendPoint(
    368               CFX_PointF(
    369                   rcClient.left +
    370                       ((rcClient.right - rcClient.left) / nCharArray) * (i + 1),
    371                   rcClient.bottom),
    372               FXPT_TYPE::MoveTo, false);
    373           path.AppendPoint(
    374               CFX_PointF(
    375                   rcClient.left +
    376                       ((rcClient.right - rcClient.left) / nCharArray) * (i + 1),
    377                   rcClient.top),
    378               FXPT_TYPE::LineTo, false);
    379         }
    380         if (!path.GetPoints().empty()) {
    381           pDevice->DrawPath(&path, pUser2Device, &gsd, 0,
    382                             GetBorderColor().ToFXColor(255), FXFILL_ALTERNATE);
    383         }
    384         break;
    385       }
    386       default:
    387         break;
    388     }
    389   }
    390 
    391   CFX_FloatRect rcClip;
    392   CPVT_WordRange wrRange = m_pEdit->GetVisibleWordRange();
    393   CPVT_WordRange* pRange = nullptr;
    394   if (!HasFlag(PES_TEXTOVERFLOW)) {
    395     rcClip = GetClientRect();
    396     pRange = &wrRange;
    397   }
    398 
    399   CFX_SystemHandler* pSysHandler = GetSystemHandler();
    400   CFX_Edit::DrawEdit(pDevice, pUser2Device, m_pEdit.get(),
    401                      GetTextColor().ToFXColor(GetTransparency()), rcClip,
    402                      CFX_PointF(), pRange, pSysHandler, m_pFormFiller);
    403 }
    404 
    405 bool CPWL_Edit::OnLButtonDown(const CFX_PointF& point, uint32_t nFlag) {
    406   CPWL_Wnd::OnLButtonDown(point, nFlag);
    407 
    408   if (HasFlag(PES_TEXTOVERFLOW) || ClientHitTest(point)) {
    409     if (m_bMouseDown)
    410       InvalidateRect();
    411 
    412     m_bMouseDown = true;
    413     SetCapture();
    414 
    415     m_pEdit->OnMouseDown(point, IsSHIFTpressed(nFlag), IsCTRLpressed(nFlag));
    416   }
    417 
    418   return true;
    419 }
    420 
    421 bool CPWL_Edit::OnLButtonDblClk(const CFX_PointF& point, uint32_t nFlag) {
    422   CPWL_Wnd::OnLButtonDblClk(point, nFlag);
    423 
    424   if (HasFlag(PES_TEXTOVERFLOW) || ClientHitTest(point)) {
    425     m_pEdit->SelectAll();
    426   }
    427 
    428   return true;
    429 }
    430 
    431 bool CPWL_Edit::OnRButtonUp(const CFX_PointF& point, uint32_t nFlag) {
    432   if (m_bMouseDown)
    433     return false;
    434 
    435   CPWL_Wnd::OnRButtonUp(point, nFlag);
    436 
    437   if (!HasFlag(PES_TEXTOVERFLOW) && !ClientHitTest(point))
    438     return true;
    439 
    440   CFX_SystemHandler* pSH = GetSystemHandler();
    441   if (!pSH)
    442     return false;
    443 
    444   SetFocus();
    445 
    446   return false;
    447 }
    448 
    449 void CPWL_Edit::OnSetFocus() {
    450   SetEditCaret(true);
    451   if (!IsReadOnly()) {
    452     if (IPWL_FocusHandler* pFocusHandler = GetFocusHandler())
    453       pFocusHandler->OnSetFocus(this);
    454   }
    455   m_bFocus = true;
    456 }
    457 
    458 void CPWL_Edit::OnKillFocus() {
    459   ShowVScrollBar(false);
    460   m_pEdit->SelectNone();
    461   SetCaret(false, CFX_PointF(), CFX_PointF());
    462   SetCharSet(FXFONT_ANSI_CHARSET);
    463   m_bFocus = false;
    464 }
    465 
    466 void CPWL_Edit::SetCharSpace(FX_FLOAT fCharSpace) {
    467   m_pEdit->SetCharSpace(fCharSpace);
    468 }
    469 
    470 CFX_ByteString CPWL_Edit::GetSelectAppearanceStream(
    471     const CFX_PointF& ptOffset) const {
    472   CPVT_WordRange wr = GetSelectWordRange();
    473   return CPWL_Utils::GetEditSelAppStream(m_pEdit.get(), ptOffset, &wr);
    474 }
    475 
    476 CPVT_WordRange CPWL_Edit::GetSelectWordRange() const {
    477   if (m_pEdit->IsSelected()) {
    478     int32_t nStart = -1;
    479     int32_t nEnd = -1;
    480 
    481     m_pEdit->GetSel(nStart, nEnd);
    482 
    483     CPVT_WordPlace wpStart = m_pEdit->WordIndexToWordPlace(nStart);
    484     CPVT_WordPlace wpEnd = m_pEdit->WordIndexToWordPlace(nEnd);
    485 
    486     return CPVT_WordRange(wpStart, wpEnd);
    487   }
    488 
    489   return CPVT_WordRange();
    490 }
    491 
    492 CFX_ByteString CPWL_Edit::GetTextAppearanceStream(
    493     const CFX_PointF& ptOffset) const {
    494   CFX_ByteTextBuf sRet;
    495   CFX_ByteString sEdit = CPWL_Utils::GetEditAppStream(m_pEdit.get(), ptOffset);
    496   if (sEdit.GetLength() > 0) {
    497     sRet << "BT\n"
    498          << CPWL_Utils::GetColorAppStream(GetTextColor()).AsStringC()
    499          << sEdit.AsStringC() << "ET\n";
    500   }
    501   return sRet.MakeString();
    502 }
    503 
    504 CFX_ByteString CPWL_Edit::GetCaretAppearanceStream(
    505     const CFX_PointF& ptOffset) const {
    506   if (m_pEditCaret)
    507     return m_pEditCaret->GetCaretAppearanceStream(ptOffset);
    508 
    509   return CFX_ByteString();
    510 }
    511 
    512 CFX_PointF CPWL_Edit::GetWordRightBottomPoint(const CPVT_WordPlace& wpWord) {
    513   CFX_Edit_Iterator* pIterator = m_pEdit->GetIterator();
    514   CPVT_WordPlace wpOld = pIterator->GetAt();
    515   pIterator->SetAt(wpWord);
    516 
    517   CFX_PointF pt;
    518   CPVT_Word word;
    519   if (pIterator->GetWord(word)) {
    520     pt = CFX_PointF(word.ptWord.x + word.fWidth, word.ptWord.y + word.fDescent);
    521   }
    522   pIterator->SetAt(wpOld);
    523   return pt;
    524 }
    525 
    526 bool CPWL_Edit::IsTextFull() const {
    527   return m_pEdit->IsTextFull();
    528 }
    529 
    530 FX_FLOAT CPWL_Edit::GetCharArrayAutoFontSize(CPDF_Font* pFont,
    531                                              const CFX_FloatRect& rcPlate,
    532                                              int32_t nCharArray) {
    533   if (pFont && !pFont->IsStandardFont()) {
    534     FX_RECT rcBBox;
    535     pFont->GetFontBBox(rcBBox);
    536 
    537     CFX_FloatRect rcCell = rcPlate;
    538     FX_FLOAT xdiv = rcCell.Width() / nCharArray * 1000.0f / rcBBox.Width();
    539     FX_FLOAT ydiv = -rcCell.Height() * 1000.0f / rcBBox.Height();
    540 
    541     return xdiv < ydiv ? xdiv : ydiv;
    542   }
    543 
    544   return 0.0f;
    545 }
    546 
    547 void CPWL_Edit::SetCharArray(int32_t nCharArray) {
    548   if (HasFlag(PES_CHARARRAY) && nCharArray > 0) {
    549     m_pEdit->SetCharArray(nCharArray);
    550     m_pEdit->SetTextOverflow(true, true);
    551 
    552     if (HasFlag(PWS_AUTOFONTSIZE)) {
    553       if (IPVT_FontMap* pFontMap = GetFontMap()) {
    554         FX_FLOAT fFontSize = GetCharArrayAutoFontSize(
    555             pFontMap->GetPDFFont(0), GetClientRect(), nCharArray);
    556         if (fFontSize > 0.0f) {
    557           m_pEdit->SetAutoFontSize(false, true);
    558           m_pEdit->SetFontSize(fFontSize);
    559         }
    560       }
    561     }
    562   }
    563 }
    564 
    565 void CPWL_Edit::SetLimitChar(int32_t nLimitChar) {
    566   m_pEdit->SetLimitChar(nLimitChar);
    567 }
    568 
    569 void CPWL_Edit::ReplaceSel(const CFX_WideString& wsText) {
    570   m_pEdit->Clear();
    571   m_pEdit->InsertText(wsText, FXFONT_DEFAULT_CHARSET);
    572 }
    573 
    574 CFX_FloatRect CPWL_Edit::GetFocusRect() const {
    575   return CFX_FloatRect();
    576 }
    577 
    578 void CPWL_Edit::ShowVScrollBar(bool bShow) {
    579   if (CPWL_ScrollBar* pScroll = GetVScrollBar()) {
    580     if (bShow) {
    581       if (!pScroll->IsVisible()) {
    582         pScroll->SetVisible(true);
    583         CFX_FloatRect rcWindow = GetWindowRect();
    584         m_rcOldWindow = rcWindow;
    585         rcWindow.right += PWL_SCROLLBAR_WIDTH;
    586         Move(rcWindow, true, true);
    587       }
    588     } else {
    589       if (pScroll->IsVisible()) {
    590         pScroll->SetVisible(false);
    591         Move(m_rcOldWindow, true, true);
    592       }
    593     }
    594   }
    595 }
    596 
    597 bool CPWL_Edit::IsVScrollBarVisible() const {
    598   if (CPWL_ScrollBar* pScroll = GetVScrollBar()) {
    599     return pScroll->IsVisible();
    600   }
    601 
    602   return false;
    603 }
    604 
    605 bool CPWL_Edit::OnKeyDown(uint16_t nChar, uint32_t nFlag) {
    606   if (m_bMouseDown)
    607     return true;
    608 
    609   if (nChar == FWL_VKEY_Delete) {
    610     if (m_pFillerNotify) {
    611       bool bRC = true;
    612       bool bExit = false;
    613       CFX_WideString strChange;
    614       CFX_WideString strChangeEx;
    615 
    616       int nSelStart = 0;
    617       int nSelEnd = 0;
    618       GetSel(nSelStart, nSelEnd);
    619 
    620       if (nSelStart == nSelEnd)
    621         nSelEnd = nSelStart + 1;
    622       m_pFillerNotify->OnBeforeKeyStroke(GetAttachedData(), strChange,
    623                                          strChangeEx, nSelStart, nSelEnd, true,
    624                                          bRC, bExit, nFlag);
    625       if (!bRC)
    626         return false;
    627       if (bExit)
    628         return false;
    629     }
    630   }
    631 
    632   bool bRet = CPWL_EditCtrl::OnKeyDown(nChar, nFlag);
    633 
    634   // In case of implementation swallow the OnKeyDown event.
    635   if (IsProceedtoOnChar(nChar, nFlag))
    636     return true;
    637 
    638   return bRet;
    639 }
    640 
    641 /**
    642 *In case of implementation swallow the OnKeyDown event.
    643 *If the event is swallowed, implementation may do other unexpected things, which
    644 *is not the control means to do.
    645 */
    646 bool CPWL_Edit::IsProceedtoOnChar(uint16_t nKeyCode, uint32_t nFlag) {
    647   bool bCtrl = IsCTRLpressed(nFlag);
    648   bool bAlt = IsALTpressed(nFlag);
    649   if (bCtrl && !bAlt) {
    650     // hot keys for edit control.
    651     switch (nKeyCode) {
    652       case 'C':
    653       case 'V':
    654       case 'X':
    655       case 'A':
    656       case 'Z':
    657         return true;
    658       default:
    659         break;
    660     }
    661   }
    662   // control characters.
    663   switch (nKeyCode) {
    664     case FWL_VKEY_Escape:
    665     case FWL_VKEY_Back:
    666     case FWL_VKEY_Return:
    667     case FWL_VKEY_Space:
    668       return true;
    669     default:
    670       return false;
    671   }
    672 }
    673 
    674 bool CPWL_Edit::OnChar(uint16_t nChar, uint32_t nFlag) {
    675   if (m_bMouseDown)
    676     return true;
    677 
    678   bool bRC = true;
    679   bool bExit = false;
    680 
    681   if (!IsCTRLpressed(nFlag)) {
    682     if (m_pFillerNotify) {
    683       CFX_WideString swChange;
    684 
    685       int nSelStart = 0;
    686       int nSelEnd = 0;
    687       GetSel(nSelStart, nSelEnd);
    688 
    689       switch (nChar) {
    690         case FWL_VKEY_Back:
    691           if (nSelStart == nSelEnd)
    692             nSelStart = nSelEnd - 1;
    693           break;
    694         case FWL_VKEY_Return:
    695           break;
    696         default:
    697           swChange += nChar;
    698           break;
    699       }
    700 
    701       CFX_WideString strChangeEx;
    702       m_pFillerNotify->OnBeforeKeyStroke(GetAttachedData(), swChange,
    703                                          strChangeEx, nSelStart, nSelEnd, true,
    704                                          bRC, bExit, nFlag);
    705     }
    706   }
    707 
    708   if (!bRC)
    709     return true;
    710   if (bExit)
    711     return false;
    712 
    713   if (IPVT_FontMap* pFontMap = GetFontMap()) {
    714     int32_t nOldCharSet = GetCharSet();
    715     int32_t nNewCharSet =
    716         pFontMap->CharSetFromUnicode(nChar, FXFONT_DEFAULT_CHARSET);
    717     if (nOldCharSet != nNewCharSet) {
    718       SetCharSet(nNewCharSet);
    719     }
    720   }
    721 
    722   return CPWL_EditCtrl::OnChar(nChar, nFlag);
    723 }
    724 
    725 bool CPWL_Edit::OnMouseWheel(short zDelta,
    726                              const CFX_PointF& point,
    727                              uint32_t nFlag) {
    728   if (HasFlag(PES_MULTILINE)) {
    729     CFX_PointF ptScroll = GetScrollPos();
    730 
    731     if (zDelta > 0) {
    732       ptScroll.y += GetFontSize();
    733     } else {
    734       ptScroll.y -= GetFontSize();
    735     }
    736     SetScrollPos(ptScroll);
    737 
    738     return true;
    739   }
    740 
    741   return false;
    742 }
    743 
    744 void CPWL_Edit::OnInsertReturn(const CPVT_WordPlace& place,
    745                                const CPVT_WordPlace& oldplace) {
    746   if (HasFlag(PES_SPELLCHECK)) {
    747     m_pEdit->RefreshWordRange(CombineWordRange(GetLatinWordsRange(oldplace),
    748                                                GetLatinWordsRange(place)));
    749   }
    750 }
    751 
    752 void CPWL_Edit::OnBackSpace(const CPVT_WordPlace& place,
    753                             const CPVT_WordPlace& oldplace) {
    754   if (HasFlag(PES_SPELLCHECK)) {
    755     m_pEdit->RefreshWordRange(CombineWordRange(GetLatinWordsRange(oldplace),
    756                                                GetLatinWordsRange(place)));
    757   }
    758 }
    759 
    760 void CPWL_Edit::OnDelete(const CPVT_WordPlace& place,
    761                          const CPVT_WordPlace& oldplace) {
    762   if (HasFlag(PES_SPELLCHECK)) {
    763     m_pEdit->RefreshWordRange(CombineWordRange(GetLatinWordsRange(oldplace),
    764                                                GetLatinWordsRange(place)));
    765   }
    766 }
    767 
    768 void CPWL_Edit::OnClear(const CPVT_WordPlace& place,
    769                         const CPVT_WordPlace& oldplace) {
    770   if (HasFlag(PES_SPELLCHECK)) {
    771     m_pEdit->RefreshWordRange(CombineWordRange(GetLatinWordsRange(oldplace),
    772                                                GetLatinWordsRange(place)));
    773   }
    774 }
    775 
    776 void CPWL_Edit::OnInsertWord(const CPVT_WordPlace& place,
    777                              const CPVT_WordPlace& oldplace) {
    778   if (HasFlag(PES_SPELLCHECK)) {
    779     m_pEdit->RefreshWordRange(CombineWordRange(GetLatinWordsRange(oldplace),
    780                                                GetLatinWordsRange(place)));
    781   }
    782 }
    783 
    784 void CPWL_Edit::OnInsertText(const CPVT_WordPlace& place,
    785                              const CPVT_WordPlace& oldplace) {
    786   if (HasFlag(PES_SPELLCHECK)) {
    787     m_pEdit->RefreshWordRange(CombineWordRange(GetLatinWordsRange(oldplace),
    788                                                GetLatinWordsRange(place)));
    789   }
    790 }
    791 
    792 CPVT_WordRange CPWL_Edit::CombineWordRange(const CPVT_WordRange& wr1,
    793                                            const CPVT_WordRange& wr2) {
    794   CPVT_WordRange wrRet;
    795 
    796   if (wr1.BeginPos.WordCmp(wr2.BeginPos) < 0) {
    797     wrRet.BeginPos = wr1.BeginPos;
    798   } else {
    799     wrRet.BeginPos = wr2.BeginPos;
    800   }
    801 
    802   if (wr1.EndPos.WordCmp(wr2.EndPos) < 0) {
    803     wrRet.EndPos = wr2.EndPos;
    804   } else {
    805     wrRet.EndPos = wr1.EndPos;
    806   }
    807 
    808   return wrRet;
    809 }
    810 
    811 CPVT_WordRange CPWL_Edit::GetLatinWordsRange(const CFX_PointF& point) const {
    812   return GetSameWordsRange(m_pEdit->SearchWordPlace(point), true, false);
    813 }
    814 
    815 CPVT_WordRange CPWL_Edit::GetLatinWordsRange(
    816     const CPVT_WordPlace& place) const {
    817   return GetSameWordsRange(place, true, false);
    818 }
    819 
    820 CPVT_WordRange CPWL_Edit::GetArabicWordsRange(
    821     const CPVT_WordPlace& place) const {
    822   return GetSameWordsRange(place, false, true);
    823 }
    824 
    825 #define PWL_ISARABICWORD(word) \
    826   ((word >= 0x0600 && word <= 0x06FF) || (word >= 0xFB50 && word <= 0xFEFC))
    827 
    828 CPVT_WordRange CPWL_Edit::GetSameWordsRange(const CPVT_WordPlace& place,
    829                                             bool bLatin,
    830                                             bool bArabic) const {
    831   CPVT_WordRange range;
    832 
    833   CFX_Edit_Iterator* pIterator = m_pEdit->GetIterator();
    834   CPVT_Word wordinfo;
    835   CPVT_WordPlace wpStart(place), wpEnd(place);
    836   pIterator->SetAt(place);
    837 
    838   if (bLatin) {
    839     while (pIterator->NextWord()) {
    840       if (!pIterator->GetWord(wordinfo) ||
    841           !FX_EDIT_ISLATINWORD(wordinfo.Word)) {
    842         break;
    843       }
    844 
    845       wpEnd = pIterator->GetAt();
    846     }
    847   } else if (bArabic) {
    848     while (pIterator->NextWord()) {
    849       if (!pIterator->GetWord(wordinfo) || !PWL_ISARABICWORD(wordinfo.Word))
    850         break;
    851 
    852       wpEnd = pIterator->GetAt();
    853     }
    854   }
    855 
    856   pIterator->SetAt(place);
    857 
    858   if (bLatin) {
    859     do {
    860       if (!pIterator->GetWord(wordinfo) ||
    861           !FX_EDIT_ISLATINWORD(wordinfo.Word)) {
    862         break;
    863       }
    864 
    865       wpStart = pIterator->GetAt();
    866     } while (pIterator->PrevWord());
    867   } else if (bArabic) {
    868     do {
    869       if (!pIterator->GetWord(wordinfo) || !PWL_ISARABICWORD(wordinfo.Word))
    870         break;
    871 
    872       wpStart = pIterator->GetAt();
    873     } while (pIterator->PrevWord());
    874   }
    875 
    876   range.Set(wpStart, wpEnd);
    877   return range;
    878 }
    879