Home | History | Annotate | Download | only in fde
      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 "xfa/fde/cfde_txtedtengine.h"
      8 
      9 #include <algorithm>
     10 
     11 #include "third_party/base/ptr_util.h"
     12 #include "xfa/fde/cfde_txtedtbuf.h"
     13 #include "xfa/fde/cfde_txtedtdorecord_deleterange.h"
     14 #include "xfa/fde/cfde_txtedtdorecord_insert.h"
     15 #include "xfa/fde/cfde_txtedtpage.h"
     16 #include "xfa/fde/cfde_txtedtparag.h"
     17 #include "xfa/fde/ifx_chariter.h"
     18 #include "xfa/fde/tto/fde_textout.h"
     19 #include "xfa/fgas/layout/fgas_textbreak.h"
     20 #include "xfa/fwl/cfwl_edit.h"
     21 
     22 namespace {
     23 
     24 const uint32_t kPageWidthMax = 0xffff;
     25 const uint32_t kUnicodeParagraphSeparator = 0x2029;
     26 
     27 }  // namespace
     28 
     29 FDE_TXTEDTPARAMS::FDE_TXTEDTPARAMS()
     30     : fPlateWidth(0),
     31       fPlateHeight(0),
     32       nLineCount(0),
     33       dwLayoutStyles(0),
     34       dwAlignment(0),
     35       dwMode(0),
     36       fFontSize(10.0f),
     37       dwFontColor(0xff000000),
     38       fLineSpace(10.0f),
     39       fTabWidth(36),
     40       bTabEquidistant(false),
     41       wDefChar(0xFEFF),
     42       wLineBreakChar('\n'),
     43       nCharRotation(0),
     44       nLineEnd(0),
     45       nHorzScale(100),
     46       fCharSpace(0),
     47       pEventSink(nullptr) {}
     48 
     49 FDE_TXTEDTPARAMS::~FDE_TXTEDTPARAMS() {}
     50 
     51 FDE_TXTEDT_TEXTCHANGE_INFO::FDE_TXTEDT_TEXTCHANGE_INFO() {}
     52 
     53 FDE_TXTEDT_TEXTCHANGE_INFO::~FDE_TXTEDT_TEXTCHANGE_INFO() {}
     54 
     55 CFDE_TxtEdtEngine::CFDE_TxtEdtEngine()
     56     : m_pTxtBuf(new CFDE_TxtEdtBuf()),
     57       m_nPageLineCount(20),
     58       m_nLineCount(0),
     59       m_nAnchorPos(-1),
     60       m_nLayoutPos(0),
     61       m_fCaretPosReserve(0.0),
     62       m_nCaret(0),
     63       m_bBefore(true),
     64       m_nCaretPage(0),
     65       m_dwFindFlags(0),
     66       m_bLock(false),
     67       m_nLimit(0),
     68       m_wcAliasChar(L'*'),
     69       m_nFirstLineEnd(FDE_TXTEDIT_LINEEND_Auto),
     70       m_bAutoLineEnd(true),
     71       m_wLineEnd(kUnicodeParagraphSeparator) {
     72   m_bAutoLineEnd = (m_Param.nLineEnd == FDE_TXTEDIT_LINEEND_Auto);
     73 }
     74 
     75 CFDE_TxtEdtEngine::~CFDE_TxtEdtEngine() {
     76   RemoveAllParags();
     77   RemoveAllPages();
     78   m_Param.pEventSink = nullptr;
     79   ClearSelection();
     80 }
     81 
     82 void CFDE_TxtEdtEngine::SetEditParams(const FDE_TXTEDTPARAMS& params) {
     83   if (!m_pTextBreak)
     84     m_pTextBreak = pdfium::MakeUnique<CFX_TxtBreak>(FX_TXTBREAKPOLICY_None);
     85 
     86   m_Param = params;
     87   m_wLineEnd = params.wLineBreakChar;
     88   m_bAutoLineEnd = (m_Param.nLineEnd == FDE_TXTEDIT_LINEEND_Auto);
     89   UpdateTxtBreak();
     90 }
     91 
     92 FDE_TXTEDTPARAMS* CFDE_TxtEdtEngine::GetEditParams() {
     93   return &m_Param;
     94 }
     95 
     96 int32_t CFDE_TxtEdtEngine::CountPages() const {
     97   if (m_nLineCount == 0) {
     98     return 0;
     99   }
    100   return ((m_nLineCount - 1) / m_nPageLineCount) + 1;
    101 }
    102 
    103 IFDE_TxtEdtPage* CFDE_TxtEdtEngine::GetPage(int32_t nIndex) {
    104   if (m_PagePtrArray.GetSize() <= nIndex) {
    105     return nullptr;
    106   }
    107   return m_PagePtrArray[nIndex];
    108 }
    109 
    110 void CFDE_TxtEdtEngine::SetTextByStream(
    111     const CFX_RetainPtr<IFGAS_Stream>& pStream) {
    112   ResetEngine();
    113   int32_t nIndex = 0;
    114   if (pStream && pStream->GetLength()) {
    115     int32_t nStreamLength = pStream->GetLength();
    116     bool bValid = true;
    117     if (m_nLimit > 0 && nStreamLength > m_nLimit) {
    118       bValid = false;
    119     }
    120     bool bPreIsCR = false;
    121     if (bValid) {
    122       uint8_t bom[4];
    123       int32_t nPos = pStream->GetBOM(bom);
    124       pStream->Seek(FX_STREAMSEEK_Begin, nPos);
    125       int32_t nPlateSize = std::min(nStreamLength, m_pTxtBuf->GetChunkSize());
    126       FX_WCHAR* lpwstr = FX_Alloc(FX_WCHAR, nPlateSize);
    127       bool bEos = false;
    128       while (!bEos) {
    129         int32_t nRead = pStream->ReadString(lpwstr, nPlateSize, bEos);
    130         bPreIsCR = ReplaceParagEnd(lpwstr, nRead, bPreIsCR);
    131         m_pTxtBuf->Insert(nIndex, lpwstr, nRead);
    132         nIndex += nRead;
    133       }
    134       FX_Free(lpwstr);
    135     }
    136   }
    137   m_pTxtBuf->Insert(nIndex, &m_wLineEnd, 1);
    138   RebuildParagraphs();
    139 }
    140 
    141 void CFDE_TxtEdtEngine::SetText(const CFX_WideString& wsText) {
    142   ResetEngine();
    143   int32_t nLength = wsText.GetLength();
    144   if (nLength > 0) {
    145     CFX_WideString wsTemp;
    146     FX_WCHAR* lpBuffer = wsTemp.GetBuffer(nLength);
    147     FXSYS_memcpy(lpBuffer, wsText.c_str(), nLength * sizeof(FX_WCHAR));
    148     ReplaceParagEnd(lpBuffer, nLength, false);
    149     wsTemp.ReleaseBuffer(nLength);
    150     if (m_nLimit > 0 && nLength > m_nLimit) {
    151       wsTemp.Delete(m_nLimit, nLength - m_nLimit);
    152       nLength = m_nLimit;
    153     }
    154     m_pTxtBuf->SetText(wsTemp);
    155   }
    156   m_pTxtBuf->Insert(nLength, &m_wLineEnd, 1);
    157   RebuildParagraphs();
    158 }
    159 
    160 int32_t CFDE_TxtEdtEngine::GetTextLength() const {
    161   return GetTextBufLength();
    162 }
    163 
    164 CFX_WideString CFDE_TxtEdtEngine::GetText(int32_t nStart,
    165                                           int32_t nCount) const {
    166   int32_t nTextBufLength = GetTextBufLength();
    167   if (nCount == -1)
    168     nCount = nTextBufLength - nStart;
    169 
    170   CFX_WideString wsText = m_pTxtBuf->GetRange(nStart, nCount);
    171   RecoverParagEnd(wsText);
    172   return wsText;
    173 }
    174 
    175 void CFDE_TxtEdtEngine::ClearText() {
    176   DeleteRange(0, -1);
    177 }
    178 
    179 int32_t CFDE_TxtEdtEngine::GetCaretRect(CFX_RectF& rtCaret) const {
    180   rtCaret = m_rtCaret;
    181   return m_nCaret;
    182 }
    183 
    184 int32_t CFDE_TxtEdtEngine::GetCaretPos() const {
    185   if (IsLocked()) {
    186     return 0;
    187   }
    188   return m_nCaret + (m_bBefore ? 0 : 1);
    189 }
    190 
    191 int32_t CFDE_TxtEdtEngine::SetCaretPos(int32_t nIndex, bool bBefore) {
    192   if (IsLocked()) {
    193     return 0;
    194   }
    195   ASSERT(nIndex >= 0 && nIndex <= GetTextBufLength());
    196   if (m_PagePtrArray.GetSize() <= m_nCaretPage) {
    197     return 0;
    198   }
    199   m_bBefore = bBefore;
    200   m_nCaret = nIndex;
    201   MovePage2Char(m_nCaret);
    202   GetCaretRect(m_rtCaret, m_nCaretPage, m_nCaret, m_bBefore);
    203   if (!m_bBefore) {
    204     m_nCaret++;
    205     m_bBefore = true;
    206   }
    207   m_fCaretPosReserve = m_rtCaret.left;
    208   m_Param.pEventSink->OnCaretChanged();
    209   m_nAnchorPos = -1;
    210   return m_nCaret;
    211 }
    212 
    213 int32_t CFDE_TxtEdtEngine::MoveCaretPos(FDE_TXTEDTMOVECARET eMoveCaret,
    214                                         bool bShift,
    215                                         bool bCtrl) {
    216   if (IsLocked()) {
    217     return 0;
    218   }
    219   if (m_PagePtrArray.GetSize() <= m_nCaretPage) {
    220     return 0;
    221   }
    222   bool bSelChange = false;
    223   if (IsSelect()) {
    224     ClearSelection();
    225     bSelChange = true;
    226   }
    227   if (bShift) {
    228     if (m_nAnchorPos == -1) {
    229       m_nAnchorPos = m_nCaret;
    230     }
    231   } else {
    232     m_nAnchorPos = -1;
    233   }
    234 
    235   switch (eMoveCaret) {
    236     case MC_Left: {
    237       bool bBefore = true;
    238       int32_t nIndex = MoveBackward(bBefore);
    239       if (nIndex >= 0) {
    240         UpdateCaretRect(nIndex, bBefore);
    241       }
    242       break;
    243     }
    244     case MC_Right: {
    245       bool bBefore = true;
    246       int32_t nIndex = MoveForward(bBefore);
    247       if (nIndex >= 0) {
    248         UpdateCaretRect(nIndex, bBefore);
    249       }
    250       break;
    251     }
    252     case MC_Up: {
    253       CFX_PointF ptCaret;
    254       if (MoveUp(ptCaret)) {
    255         UpdateCaretIndex(ptCaret);
    256       }
    257       break;
    258     }
    259     case MC_Down: {
    260       CFX_PointF ptCaret;
    261       if (MoveDown(ptCaret)) {
    262         UpdateCaretIndex(ptCaret);
    263       }
    264       break;
    265     }
    266     case MC_WordBackward:
    267       break;
    268     case MC_WordForward:
    269       break;
    270     case MC_LineStart:
    271       MoveLineStart();
    272       break;
    273     case MC_LineEnd:
    274       MoveLineEnd();
    275       break;
    276     case MC_ParagStart:
    277       MoveParagStart();
    278       break;
    279     case MC_ParagEnd:
    280       MoveParagEnd();
    281       break;
    282     case MC_PageDown:
    283       break;
    284     case MC_PageUp:
    285       break;
    286     case MC_Home:
    287       MoveHome();
    288       break;
    289     case MC_End:
    290       MoveEnd();
    291       break;
    292     default:
    293       break;
    294   }
    295   if (bShift && m_nAnchorPos != -1 && (m_nAnchorPos != m_nCaret)) {
    296     AddSelRange(std::min(m_nAnchorPos, m_nCaret),
    297                 FXSYS_abs(m_nAnchorPos - m_nCaret));
    298     m_Param.pEventSink->OnSelChanged();
    299   }
    300   if (bSelChange)
    301     m_Param.pEventSink->OnSelChanged();
    302 
    303   return m_nCaret;
    304 }
    305 
    306 void CFDE_TxtEdtEngine::Lock() {
    307   m_bLock = true;
    308 }
    309 
    310 void CFDE_TxtEdtEngine::Unlock() {
    311   m_bLock = false;
    312 }
    313 
    314 bool CFDE_TxtEdtEngine::IsLocked() const {
    315   return m_bLock;
    316 }
    317 
    318 int32_t CFDE_TxtEdtEngine::Insert(int32_t nStart,
    319                                   const FX_WCHAR* lpText,
    320                                   int32_t nLength) {
    321   if (IsLocked()) {
    322     return FDE_TXTEDT_MODIFY_RET_F_Locked;
    323   }
    324   CFX_WideString wsTemp;
    325   FX_WCHAR* lpBuffer = wsTemp.GetBuffer(nLength);
    326   FXSYS_memcpy(lpBuffer, lpText, nLength * sizeof(FX_WCHAR));
    327   ReplaceParagEnd(lpBuffer, nLength, false);
    328   wsTemp.ReleaseBuffer(nLength);
    329   bool bPart = false;
    330   if (m_nLimit > 0) {
    331     int32_t nTotalLength = GetTextBufLength();
    332     int32_t nCount = m_SelRangePtrArr.GetSize();
    333     for (int32_t i = 0; i < nCount; i++) {
    334       FDE_TXTEDTSELRANGE* lpSelRange = m_SelRangePtrArr.GetAt(i);
    335       nTotalLength -= lpSelRange->nCount;
    336     }
    337     int32_t nExpectLength = nTotalLength + nLength;
    338     if (nTotalLength == m_nLimit) {
    339       return FDE_TXTEDT_MODIFY_RET_F_Full;
    340     }
    341     if (nExpectLength > m_nLimit) {
    342       nLength -= (nExpectLength - m_nLimit);
    343       bPart = true;
    344     }
    345   }
    346   if ((m_Param.dwMode & FDE_TEXTEDITMODE_LimitArea_Vert) ||
    347       (m_Param.dwMode & FDE_TEXTEDITMODE_LimitArea_Horz)) {
    348     int32_t nTemp = nLength;
    349     if (m_Param.dwMode & FDE_TEXTEDITMODE_Password) {
    350       while (nLength > 0) {
    351         CFX_WideString wsText = GetPreInsertText(m_nCaret, lpBuffer, nLength);
    352         int32_t nTotal = wsText.GetLength();
    353         FX_WCHAR* lpBuf = wsText.GetBuffer(nTotal);
    354         for (int32_t i = 0; i < nTotal; i++) {
    355           lpBuf[i] = m_wcAliasChar;
    356         }
    357         wsText.ReleaseBuffer(nTotal);
    358         if (IsFitArea(wsText)) {
    359           break;
    360         }
    361         nLength--;
    362       }
    363     } else {
    364       while (nLength > 0) {
    365         CFX_WideString wsText = GetPreInsertText(m_nCaret, lpBuffer, nLength);
    366         if (IsFitArea(wsText)) {
    367           break;
    368         }
    369         nLength--;
    370       }
    371     }
    372     if (nLength == 0) {
    373       return FDE_TXTEDT_MODIFY_RET_F_Full;
    374     }
    375     if (nLength < nTemp) {
    376       bPart = true;
    377     }
    378   }
    379   if (m_Param.dwMode & FDE_TEXTEDITMODE_Validate) {
    380     CFX_WideString wsText = GetPreInsertText(m_nCaret, lpBuffer, nLength);
    381     if (!m_Param.pEventSink->OnValidate(wsText))
    382       return FDE_TXTEDT_MODIFY_RET_F_Invalidate;
    383   }
    384   if (IsSelect()) {
    385     DeleteSelect();
    386   }
    387   m_Param.pEventSink->OnAddDoRecord(
    388       pdfium::MakeUnique<CFDE_TxtEdtDoRecord_Insert>(this, m_nCaret, lpBuffer,
    389                                                      nLength));
    390 
    391   m_ChangeInfo.wsPrevText = GetText(0, -1);
    392   Inner_Insert(m_nCaret, lpBuffer, nLength);
    393   m_ChangeInfo.nChangeType = FDE_TXTEDT_TEXTCHANGE_TYPE_Insert;
    394   m_ChangeInfo.wsInsert = CFX_WideString(lpBuffer, nLength);
    395   nStart = m_nCaret;
    396   nStart += nLength;
    397   FX_WCHAR wChar = m_pTxtBuf->GetCharByIndex(nStart - 1);
    398   bool bBefore = true;
    399   if (wChar != L'\n' && wChar != L'\r') {
    400     nStart--;
    401     bBefore = false;
    402   }
    403   SetCaretPos(nStart, bBefore);
    404   m_Param.pEventSink->OnTextChanged(m_ChangeInfo);
    405   return bPart ? FDE_TXTEDT_MODIFY_RET_S_Part : FDE_TXTEDT_MODIFY_RET_S_Normal;
    406 }
    407 
    408 int32_t CFDE_TxtEdtEngine::Delete(int32_t nStart, bool bBackspace) {
    409   if (IsLocked()) {
    410     return FDE_TXTEDT_MODIFY_RET_F_Locked;
    411   }
    412   if (IsSelect()) {
    413     DeleteSelect();
    414     return FDE_TXTEDT_MODIFY_RET_S_Normal;
    415   }
    416 
    417   int32_t nCount = 1;
    418   if (bBackspace) {
    419     if (nStart == 0) {
    420       return FDE_TXTEDT_MODIFY_RET_F_Boundary;
    421     }
    422     if (nStart > 2 && m_pTxtBuf->GetCharByIndex(nStart - 1) == L'\n' &&
    423         m_pTxtBuf->GetCharByIndex(nStart - 2) == L'\r') {
    424       nStart--;
    425       nCount++;
    426     }
    427     nStart--;
    428   } else {
    429     if (nStart == GetTextBufLength()) {
    430       return FDE_TXTEDT_MODIFY_RET_F_Full;
    431     }
    432     if ((nStart + 1 < GetTextBufLength()) &&
    433         (m_pTxtBuf->GetCharByIndex(nStart) == L'\r') &&
    434         (m_pTxtBuf->GetCharByIndex(nStart + 1) == L'\n')) {
    435       nCount++;
    436     }
    437   }
    438   if (m_Param.dwMode & FDE_TEXTEDITMODE_Validate) {
    439     CFX_WideString wsText = GetPreDeleteText(nStart, nCount);
    440     if (!m_Param.pEventSink->OnValidate(wsText))
    441       return FDE_TXTEDT_MODIFY_RET_F_Invalidate;
    442   }
    443   CFX_WideString wsRange = m_pTxtBuf->GetRange(nStart, nCount);
    444   m_Param.pEventSink->OnAddDoRecord(
    445       pdfium::MakeUnique<CFDE_TxtEdtDoRecord_DeleteRange>(this, nStart,
    446                                                           m_nCaret, wsRange));
    447 
    448   m_ChangeInfo.nChangeType = FDE_TXTEDT_TEXTCHANGE_TYPE_Delete;
    449   m_ChangeInfo.wsDelete = GetText(nStart, nCount);
    450   Inner_DeleteRange(nStart, nCount);
    451   SetCaretPos(nStart + ((!bBackspace && nStart > 0) ? -1 : 0),
    452               (bBackspace || nStart == 0));
    453   m_Param.pEventSink->OnTextChanged(m_ChangeInfo);
    454   return FDE_TXTEDT_MODIFY_RET_S_Normal;
    455 }
    456 
    457 int32_t CFDE_TxtEdtEngine::DeleteRange(int32_t nStart, int32_t nCount) {
    458   if (IsLocked())
    459     return FDE_TXTEDT_MODIFY_RET_F_Locked;
    460   if (nCount == -1)
    461     nCount = GetTextBufLength();
    462   if (nCount == 0)
    463     return FDE_TXTEDT_MODIFY_RET_S_Normal;
    464   if (m_Param.dwMode & FDE_TEXTEDITMODE_Validate) {
    465     CFX_WideString wsText = GetPreDeleteText(nStart, nCount);
    466     if (!m_Param.pEventSink->OnValidate(wsText))
    467       return FDE_TXTEDT_MODIFY_RET_F_Invalidate;
    468   }
    469   DeleteRange_DoRecord(nStart, nCount);
    470   m_Param.pEventSink->OnTextChanged(m_ChangeInfo);
    471   SetCaretPos(nStart, true);
    472   return FDE_TXTEDT_MODIFY_RET_S_Normal;
    473 }
    474 
    475 int32_t CFDE_TxtEdtEngine::Replace(int32_t nStart,
    476                                    int32_t nLength,
    477                                    const CFX_WideString& wsReplace) {
    478   if (IsLocked())
    479     return FDE_TXTEDT_MODIFY_RET_F_Locked;
    480   if (nStart < 0 || (nStart + nLength > GetTextBufLength()))
    481     return FDE_TXTEDT_MODIFY_RET_F_Boundary;
    482   if (m_Param.dwMode & FDE_TEXTEDITMODE_Validate) {
    483     CFX_WideString wsText = GetPreReplaceText(
    484         nStart, nLength, wsReplace.c_str(), wsReplace.GetLength());
    485     if (!m_Param.pEventSink->OnValidate(wsText))
    486       return FDE_TXTEDT_MODIFY_RET_F_Invalidate;
    487   }
    488   if (IsSelect())
    489     ClearSelection();
    490 
    491   m_ChangeInfo.nChangeType = FDE_TXTEDT_TEXTCHANGE_TYPE_Replace;
    492   m_ChangeInfo.wsDelete = GetText(nStart, nLength);
    493   if (nLength > 0)
    494     Inner_DeleteRange(nStart, nLength);
    495 
    496   int32_t nTextLength = wsReplace.GetLength();
    497   if (nTextLength > 0)
    498     Inner_Insert(nStart, wsReplace.c_str(), nTextLength);
    499 
    500   m_ChangeInfo.wsInsert = CFX_WideString(wsReplace.c_str(), nTextLength);
    501   nStart += nTextLength;
    502   FX_WCHAR wChar = m_pTxtBuf->GetCharByIndex(nStart - 1);
    503   bool bBefore = true;
    504   if (wChar != L'\n' && wChar != L'\r') {
    505     nStart--;
    506     bBefore = false;
    507   }
    508   SetCaretPos(nStart, bBefore);
    509   m_Param.pEventSink->OnPageUnload(m_nCaretPage);
    510   m_Param.pEventSink->OnPageLoad(m_nCaretPage);
    511   m_Param.pEventSink->OnTextChanged(m_ChangeInfo);
    512   return FDE_TXTEDT_MODIFY_RET_S_Normal;
    513 }
    514 
    515 void CFDE_TxtEdtEngine::SetLimit(int32_t nLimit) {
    516   m_nLimit = nLimit;
    517 }
    518 
    519 void CFDE_TxtEdtEngine::SetAliasChar(FX_WCHAR wcAlias) {
    520   m_wcAliasChar = wcAlias;
    521 }
    522 
    523 void CFDE_TxtEdtEngine::RemoveSelRange(int32_t nStart, int32_t nCount) {
    524   FDE_TXTEDTSELRANGE* lpTemp = nullptr;
    525   int32_t nRangeCount = m_SelRangePtrArr.GetSize();
    526   int32_t i = 0;
    527   for (i = 0; i < nRangeCount; i++) {
    528     lpTemp = m_SelRangePtrArr[i];
    529     if (lpTemp->nStart == nStart && lpTemp->nCount == nCount) {
    530       delete lpTemp;
    531       m_SelRangePtrArr.RemoveAt(i);
    532       return;
    533     }
    534   }
    535 }
    536 
    537 void CFDE_TxtEdtEngine::AddSelRange(int32_t nStart, int32_t nCount) {
    538   if (nCount == -1) {
    539     nCount = GetTextLength() - nStart;
    540   }
    541   int32_t nSize = m_SelRangePtrArr.GetSize();
    542   if (nSize <= 0) {
    543     FDE_TXTEDTSELRANGE* lpSelRange = new FDE_TXTEDTSELRANGE;
    544     lpSelRange->nStart = nStart;
    545     lpSelRange->nCount = nCount;
    546     m_SelRangePtrArr.Add(lpSelRange);
    547     m_Param.pEventSink->OnSelChanged();
    548     return;
    549   }
    550   FDE_TXTEDTSELRANGE* lpTemp = nullptr;
    551   lpTemp = m_SelRangePtrArr[nSize - 1];
    552   if (nStart >= lpTemp->nStart + lpTemp->nCount) {
    553     FDE_TXTEDTSELRANGE* lpSelRange = new FDE_TXTEDTSELRANGE;
    554     lpSelRange->nStart = nStart;
    555     lpSelRange->nCount = nCount;
    556     m_SelRangePtrArr.Add(lpSelRange);
    557     m_Param.pEventSink->OnSelChanged();
    558     return;
    559   }
    560   int32_t nEnd = nStart + nCount - 1;
    561   bool bBegin = false;
    562   int32_t nRangeBgn = 0;
    563   int32_t nRangeCnt = 0;
    564   for (int32_t i = 0; i < nSize; i++) {
    565     lpTemp = m_SelRangePtrArr[i];
    566     int32_t nTempBgn = lpTemp->nStart;
    567     int32_t nTempEnd = nTempBgn + lpTemp->nCount - 1;
    568     if (bBegin) {
    569       if (nEnd < nTempBgn) {
    570         break;
    571       } else if (nStart >= nTempBgn && nStart <= nTempEnd) {
    572         nRangeCnt++;
    573         break;
    574       }
    575       nRangeCnt++;
    576     } else {
    577       if (nStart <= nTempEnd) {
    578         nRangeBgn = i;
    579         if (nEnd < nTempBgn) {
    580           break;
    581         }
    582         nRangeCnt = 1;
    583         bBegin = true;
    584       }
    585     }
    586   }
    587   if (nRangeCnt == 0) {
    588     FDE_TXTEDTSELRANGE* lpSelRange = new FDE_TXTEDTSELRANGE;
    589     lpSelRange->nStart = nStart;
    590     lpSelRange->nCount = nCount;
    591     m_SelRangePtrArr.InsertAt(nRangeBgn, lpSelRange);
    592   } else {
    593     lpTemp = m_SelRangePtrArr[nRangeBgn];
    594     lpTemp->nStart = nStart;
    595     lpTemp->nCount = nCount;
    596     nRangeCnt--;
    597     nRangeBgn++;
    598     while (nRangeCnt--) {
    599       delete m_SelRangePtrArr[nRangeBgn];
    600       m_SelRangePtrArr.RemoveAt(nRangeBgn);
    601     }
    602   }
    603   m_Param.pEventSink->OnSelChanged();
    604 }
    605 
    606 int32_t CFDE_TxtEdtEngine::CountSelRanges() const {
    607   return m_SelRangePtrArr.GetSize();
    608 }
    609 
    610 int32_t CFDE_TxtEdtEngine::GetSelRange(int32_t nIndex, int32_t* nStart) const {
    611   if (nStart)
    612     *nStart = m_SelRangePtrArr[nIndex]->nStart;
    613   return m_SelRangePtrArr[nIndex]->nCount;
    614 }
    615 
    616 void CFDE_TxtEdtEngine::ClearSelection() {
    617   int32_t nCount = m_SelRangePtrArr.GetSize();
    618   for (int i = 0; i < nCount; ++i)
    619     delete m_SelRangePtrArr[i];
    620   m_SelRangePtrArr.RemoveAll();
    621   if (nCount && m_Param.pEventSink)
    622     m_Param.pEventSink->OnSelChanged();
    623 }
    624 
    625 bool CFDE_TxtEdtEngine::Redo(const IFDE_TxtEdtDoRecord* pDoRecord) {
    626   if (IsLocked())
    627     return false;
    628   return pDoRecord->Redo();
    629 }
    630 
    631 bool CFDE_TxtEdtEngine::Undo(const IFDE_TxtEdtDoRecord* pDoRecord) {
    632   if (IsLocked())
    633     return false;
    634   return pDoRecord->Undo();
    635 }
    636 
    637 int32_t CFDE_TxtEdtEngine::StartLayout() {
    638   Lock();
    639   RemoveAllPages();
    640   m_nLayoutPos = 0;
    641   m_nLineCount = 0;
    642   return 0;
    643 }
    644 
    645 int32_t CFDE_TxtEdtEngine::DoLayout(IFX_Pause* pPause) {
    646   int32_t nCount = m_ParagPtrArray.GetSize();
    647   CFDE_TxtEdtParag* pParag = nullptr;
    648   int32_t nLineCount = 0;
    649   for (; m_nLayoutPos < nCount; m_nLayoutPos++) {
    650     pParag = m_ParagPtrArray[m_nLayoutPos];
    651     pParag->CalcLines();
    652     nLineCount += pParag->GetLineCount();
    653     if (nLineCount > m_nPageLineCount && pPause && pPause->NeedToPauseNow()) {
    654       m_nLineCount += nLineCount;
    655       return (++m_nLayoutPos * 100) / nCount;
    656     }
    657   }
    658   m_nLineCount += nLineCount;
    659   return 100;
    660 }
    661 
    662 void CFDE_TxtEdtEngine::EndLayout() {
    663   UpdatePages();
    664   int32_t nLength = GetTextLength();
    665   if (m_nCaret > nLength)
    666     m_nCaret = nLength;
    667 
    668   int32_t nIndex = m_nCaret;
    669   if (!m_bBefore)
    670     nIndex--;
    671 
    672   m_rtCaret = CFX_RectF(0, 0, 1, m_Param.fFontSize);
    673   Unlock();
    674 }
    675 
    676 CFDE_TxtEdtBuf* CFDE_TxtEdtEngine::GetTextBuf() const {
    677   return m_pTxtBuf.get();
    678 }
    679 
    680 int32_t CFDE_TxtEdtEngine::GetTextBufLength() const {
    681   return m_pTxtBuf->GetTextLength() - 1;
    682 }
    683 
    684 CFX_TxtBreak* CFDE_TxtEdtEngine::GetTextBreak() const {
    685   return m_pTextBreak.get();
    686 }
    687 
    688 int32_t CFDE_TxtEdtEngine::GetLineCount() const {
    689   return m_nLineCount;
    690 }
    691 
    692 int32_t CFDE_TxtEdtEngine::GetPageLineCount() const {
    693   return m_nPageLineCount;
    694 }
    695 
    696 int32_t CFDE_TxtEdtEngine::CountParags() const {
    697   return m_ParagPtrArray.GetSize();
    698 }
    699 
    700 CFDE_TxtEdtParag* CFDE_TxtEdtEngine::GetParag(int32_t nParagIndex) const {
    701   return m_ParagPtrArray[nParagIndex];
    702 }
    703 
    704 IFX_CharIter* CFDE_TxtEdtEngine::CreateCharIter() {
    705   if (!m_pTxtBuf)
    706     return nullptr;
    707   return new CFDE_TxtEdtBuf::Iterator(m_pTxtBuf.get());
    708 }
    709 
    710 int32_t CFDE_TxtEdtEngine::Line2Parag(int32_t nStartParag,
    711                                       int32_t nStartLineofParag,
    712                                       int32_t nLineIndex,
    713                                       int32_t& nStartLine) const {
    714   int32_t nLineTotal = nStartLineofParag;
    715   int32_t nCount = m_ParagPtrArray.GetSize();
    716   CFDE_TxtEdtParag* pParag = nullptr;
    717   int32_t i = nStartParag;
    718   for (; i < nCount; i++) {
    719     pParag = m_ParagPtrArray[i];
    720     nLineTotal += pParag->GetLineCount();
    721     if (nLineTotal > nLineIndex) {
    722       break;
    723     }
    724   }
    725   nStartLine = nLineTotal - pParag->GetLineCount();
    726   return i;
    727 }
    728 
    729 CFX_WideString CFDE_TxtEdtEngine::GetPreDeleteText(int32_t nIndex,
    730                                                    int32_t nLength) {
    731   CFX_WideString wsText = GetText(0, GetTextBufLength());
    732   wsText.Delete(nIndex, nLength);
    733   return wsText;
    734 }
    735 
    736 CFX_WideString CFDE_TxtEdtEngine::GetPreInsertText(int32_t nIndex,
    737                                                    const FX_WCHAR* lpText,
    738                                                    int32_t nLength) {
    739   CFX_WideString wsText = GetText(0, GetTextBufLength());
    740   int32_t nSelIndex = 0;
    741   int32_t nSelLength = 0;
    742   int32_t nSelCount = CountSelRanges();
    743   while (nSelCount--) {
    744     nSelLength = GetSelRange(nSelCount, &nSelIndex);
    745     wsText.Delete(nSelIndex, nSelLength);
    746     nIndex = nSelIndex;
    747   }
    748   CFX_WideString wsTemp;
    749   int32_t nOldLength = wsText.GetLength();
    750   const FX_WCHAR* pOldBuffer = wsText.c_str();
    751   FX_WCHAR* lpBuffer = wsTemp.GetBuffer(nOldLength + nLength);
    752   FXSYS_memcpy(lpBuffer, pOldBuffer, (nIndex) * sizeof(FX_WCHAR));
    753   FXSYS_memcpy(lpBuffer + nIndex, lpText, nLength * sizeof(FX_WCHAR));
    754   FXSYS_memcpy(lpBuffer + nIndex + nLength, pOldBuffer + nIndex,
    755                (nOldLength - nIndex) * sizeof(FX_WCHAR));
    756   wsTemp.ReleaseBuffer(nOldLength + nLength);
    757   wsText = wsTemp;
    758   return wsText;
    759 }
    760 
    761 CFX_WideString CFDE_TxtEdtEngine::GetPreReplaceText(int32_t nIndex,
    762                                                     int32_t nOriginLength,
    763                                                     const FX_WCHAR* lpText,
    764                                                     int32_t nLength) {
    765   CFX_WideString wsText = GetText(0, GetTextBufLength());
    766   int32_t nSelIndex = 0;
    767   int32_t nSelLength = 0;
    768   int32_t nSelCount = CountSelRanges();
    769   while (nSelCount--) {
    770     nSelLength = GetSelRange(nSelCount, &nSelIndex);
    771     wsText.Delete(nSelIndex, nSelLength);
    772   }
    773   wsText.Delete(nIndex, nOriginLength);
    774   int32_t i = 0;
    775   for (i = 0; i < nLength; i++)
    776     wsText.Insert(nIndex++, lpText[i]);
    777 
    778   return wsText;
    779 }
    780 
    781 void CFDE_TxtEdtEngine::Inner_Insert(int32_t nStart,
    782                                      const FX_WCHAR* lpText,
    783                                      int32_t nLength) {
    784   ASSERT(nLength > 0);
    785   FDE_TXTEDTPARAGPOS ParagPos;
    786   TextPos2ParagPos(nStart, ParagPos);
    787   m_Param.pEventSink->OnPageUnload(m_nCaretPage);
    788   int32_t nParagCount = m_ParagPtrArray.GetSize();
    789   int32_t i = 0;
    790   for (i = ParagPos.nParagIndex + 1; i < nParagCount; i++)
    791     m_ParagPtrArray[i]->IncrementStartIndex(nLength);
    792 
    793   CFDE_TxtEdtParag* pParag = m_ParagPtrArray[ParagPos.nParagIndex];
    794   int32_t nReserveLineCount = pParag->GetLineCount();
    795   int32_t nReserveCharStart = pParag->GetStartIndex();
    796   int32_t nLeavePart = ParagPos.nCharIndex;
    797   int32_t nCutPart = pParag->GetTextLength() - ParagPos.nCharIndex;
    798   int32_t nTextStart = 0;
    799   FX_WCHAR wCurChar = L' ';
    800   const FX_WCHAR* lpPos = lpText;
    801   bool bFirst = true;
    802   int32_t nParagIndex = ParagPos.nParagIndex;
    803   for (i = 0; i < nLength; i++, lpPos++) {
    804     wCurChar = *lpPos;
    805     if (wCurChar == m_wLineEnd) {
    806       if (bFirst) {
    807         pParag->SetTextLength(nLeavePart + (i - nTextStart + 1));
    808         pParag->SetLineCount(-1);
    809         nReserveCharStart += pParag->GetTextLength();
    810         bFirst = false;
    811       } else {
    812         pParag = new CFDE_TxtEdtParag(this);
    813         pParag->SetLineCount(-1);
    814         pParag->SetTextLength(i - nTextStart + 1);
    815         pParag->SetStartIndex(nReserveCharStart);
    816         m_ParagPtrArray.InsertAt(++nParagIndex, pParag);
    817         nReserveCharStart += pParag->GetTextLength();
    818       }
    819       nTextStart = i + 1;
    820     }
    821   }
    822   if (bFirst) {
    823     pParag->IncrementTextLength(nLength);
    824     pParag->SetLineCount(-1);
    825     bFirst = false;
    826   } else {
    827     pParag = new CFDE_TxtEdtParag(this);
    828     pParag->SetLineCount(-1);
    829     pParag->SetTextLength(nLength - nTextStart + nCutPart);
    830     pParag->SetStartIndex(nReserveCharStart);
    831     m_ParagPtrArray.InsertAt(++nParagIndex, pParag);
    832   }
    833   m_pTxtBuf->Insert(nStart, lpText, nLength);
    834   int32_t nTotalLineCount = 0;
    835   for (i = ParagPos.nParagIndex; i <= nParagIndex; i++) {
    836     pParag = m_ParagPtrArray[i];
    837     pParag->CalcLines();
    838     nTotalLineCount += pParag->GetLineCount();
    839   }
    840   m_nLineCount += nTotalLineCount - nReserveLineCount;
    841   m_Param.pEventSink->OnPageLoad(m_nCaretPage);
    842   UpdatePages();
    843 }
    844 
    845 void CFDE_TxtEdtEngine::Inner_DeleteRange(int32_t nStart, int32_t nCount) {
    846   if (nCount == -1) {
    847     nCount = m_pTxtBuf->GetTextLength() - nStart;
    848   }
    849   int32_t nEnd = nStart + nCount - 1;
    850   ASSERT(nStart >= 0 && nEnd < m_pTxtBuf->GetTextLength());
    851   m_Param.pEventSink->OnPageUnload(m_nCaretPage);
    852   FDE_TXTEDTPARAGPOS ParagPosBgn, ParagPosEnd;
    853   TextPos2ParagPos(nStart, ParagPosBgn);
    854   TextPos2ParagPos(nEnd, ParagPosEnd);
    855   CFDE_TxtEdtParag* pParag = m_ParagPtrArray[ParagPosEnd.nParagIndex];
    856   bool bLastParag = false;
    857   if (ParagPosEnd.nCharIndex == pParag->GetTextLength() - 1) {
    858     if (ParagPosEnd.nParagIndex < m_ParagPtrArray.GetSize() - 1) {
    859       ParagPosEnd.nParagIndex++;
    860     } else {
    861       bLastParag = true;
    862     }
    863   }
    864   int32_t nTotalLineCount = 0;
    865   int32_t nTotalCharCount = 0;
    866   int32_t i = 0;
    867   for (i = ParagPosBgn.nParagIndex; i <= ParagPosEnd.nParagIndex; i++) {
    868     CFDE_TxtEdtParag* pTextParag = m_ParagPtrArray[i];
    869     pTextParag->CalcLines();
    870     nTotalLineCount += pTextParag->GetLineCount();
    871     nTotalCharCount += pTextParag->GetTextLength();
    872   }
    873   m_pTxtBuf->Delete(nStart, nCount);
    874   int32_t nNextParagIndex = (ParagPosBgn.nCharIndex == 0 && bLastParag)
    875                                 ? ParagPosBgn.nParagIndex
    876                                 : (ParagPosBgn.nParagIndex + 1);
    877   for (i = nNextParagIndex; i <= ParagPosEnd.nParagIndex; i++) {
    878     delete m_ParagPtrArray[nNextParagIndex];
    879     m_ParagPtrArray.RemoveAt(nNextParagIndex);
    880   }
    881   if (!(bLastParag && ParagPosBgn.nCharIndex == 0)) {
    882     pParag = m_ParagPtrArray[ParagPosBgn.nParagIndex];
    883     pParag->SetTextLength(nTotalCharCount - nCount);
    884     pParag->CalcLines();
    885     nTotalLineCount -= pParag->GetTextLength();
    886   }
    887   int32_t nParagCount = m_ParagPtrArray.GetSize();
    888   for (i = nNextParagIndex; i < nParagCount; i++)
    889     m_ParagPtrArray[i]->DecrementStartIndex(nCount);
    890 
    891   m_nLineCount -= nTotalLineCount;
    892   UpdatePages();
    893   int32_t nPageCount = CountPages();
    894   if (m_nCaretPage >= nPageCount) {
    895     m_nCaretPage = nPageCount - 1;
    896   }
    897   m_Param.pEventSink->OnPageLoad(m_nCaretPage);
    898 }
    899 
    900 void CFDE_TxtEdtEngine::DeleteRange_DoRecord(int32_t nStart,
    901                                              int32_t nCount,
    902                                              bool bSel) {
    903   ASSERT(nStart >= 0);
    904   if (nCount == -1) {
    905     nCount = GetTextLength() - nStart;
    906   }
    907   ASSERT((nStart + nCount) <= m_pTxtBuf->GetTextLength());
    908 
    909   CFX_WideString wsRange = m_pTxtBuf->GetRange(nStart, nCount);
    910   m_Param.pEventSink->OnAddDoRecord(
    911       pdfium::MakeUnique<CFDE_TxtEdtDoRecord_DeleteRange>(
    912           this, nStart, m_nCaret, wsRange, bSel));
    913 
    914   m_ChangeInfo.nChangeType = FDE_TXTEDT_TEXTCHANGE_TYPE_Delete;
    915   m_ChangeInfo.wsDelete = GetText(nStart, nCount);
    916   Inner_DeleteRange(nStart, nCount);
    917 }
    918 
    919 void CFDE_TxtEdtEngine::ResetEngine() {
    920   RemoveAllPages();
    921   RemoveAllParags();
    922   ClearSelection();
    923   m_nCaret = 0;
    924   m_pTxtBuf->Clear(false);
    925   m_nCaret = 0;
    926 }
    927 
    928 void CFDE_TxtEdtEngine::RebuildParagraphs() {
    929   RemoveAllParags();
    930   FX_WCHAR wChar = L' ';
    931   int32_t nParagStart = 0;
    932   int32_t nIndex = 0;
    933   std::unique_ptr<IFX_CharIter> pIter(
    934       new CFDE_TxtEdtBuf::Iterator(m_pTxtBuf.get()));
    935   pIter->SetAt(0);
    936   do {
    937     wChar = pIter->GetChar();
    938     nIndex = pIter->GetAt();
    939     if (wChar == m_wLineEnd) {
    940       CFDE_TxtEdtParag* pParag = new CFDE_TxtEdtParag(this);
    941       pParag->SetStartIndex(nParagStart);
    942       pParag->SetTextLength(nIndex - nParagStart + 1);
    943       pParag->SetLineCount(-1);
    944       m_ParagPtrArray.Add(pParag);
    945       nParagStart = nIndex + 1;
    946     }
    947   } while (pIter->Next());
    948 }
    949 
    950 void CFDE_TxtEdtEngine::RemoveAllParags() {
    951   for (int32_t i = 0; i < m_ParagPtrArray.GetSize(); ++i)
    952     delete m_ParagPtrArray[i];
    953   m_ParagPtrArray.RemoveAll();
    954 }
    955 
    956 void CFDE_TxtEdtEngine::RemoveAllPages() {
    957   for (int32_t i = 0; i < m_PagePtrArray.GetSize(); i++)
    958     delete m_PagePtrArray[i];
    959   m_PagePtrArray.RemoveAll();
    960 }
    961 
    962 void CFDE_TxtEdtEngine::UpdateParags() {
    963   int32_t nCount = m_ParagPtrArray.GetSize();
    964   if (nCount == 0) {
    965     return;
    966   }
    967   CFDE_TxtEdtParag* pParag = nullptr;
    968   int32_t nLineCount = 0;
    969   int32_t i = 0;
    970   for (i = 0; i < nCount; i++) {
    971     pParag = m_ParagPtrArray[i];
    972     if (pParag->GetLineCount() == -1)
    973       pParag->CalcLines();
    974 
    975     nLineCount += pParag->GetLineCount();
    976   }
    977   m_nLineCount = nLineCount;
    978 }
    979 
    980 void CFDE_TxtEdtEngine::UpdatePages() {
    981   if (m_nLineCount == 0)
    982     return;
    983 
    984   int32_t nPageCount = (m_nLineCount - 1) / (m_nPageLineCount) + 1;
    985   int32_t nSize = m_PagePtrArray.GetSize();
    986   if (nSize == nPageCount)
    987     return;
    988 
    989   if (nSize > nPageCount) {
    990     for (int32_t i = nSize - 1; i >= nPageCount; i--) {
    991       delete m_PagePtrArray[i];
    992       m_PagePtrArray.RemoveAt(i);
    993     }
    994     return;
    995   }
    996   if (nSize < nPageCount) {
    997     for (int32_t i = nSize; i < nPageCount; i++)
    998       m_PagePtrArray.Add(IFDE_TxtEdtPage::Create(this, i));
    999     return;
   1000   }
   1001 }
   1002 
   1003 void CFDE_TxtEdtEngine::UpdateTxtBreak() {
   1004   uint32_t dwStyle = m_pTextBreak->GetLayoutStyles();
   1005   if (m_Param.dwMode & FDE_TEXTEDITMODE_MultiLines) {
   1006     dwStyle &= ~FX_TXTLAYOUTSTYLE_SingleLine;
   1007   } else {
   1008     dwStyle |= FX_TXTLAYOUTSTYLE_SingleLine;
   1009   }
   1010   dwStyle &= ~FX_TXTLAYOUTSTYLE_VerticalLayout;
   1011   dwStyle &= ~FX_TXTLAYOUTSTYLE_ReverseLine;
   1012   dwStyle &= ~FX_TXTLAYOUTSTYLE_RTLReadingOrder;
   1013 
   1014   if (m_Param.dwLayoutStyles & FDE_TEXTEDITLAYOUT_CombText) {
   1015     dwStyle |= FX_TXTLAYOUTSTYLE_CombText;
   1016   } else {
   1017     dwStyle &= ~FX_TXTLAYOUTSTYLE_CombText;
   1018   }
   1019 
   1020   dwStyle &= ~FX_TXTLAYOUTSTYLE_VerticalChars;
   1021   dwStyle &= ~FX_TXTLAYOUTSTYLE_ExpandTab;
   1022   dwStyle &= ~FX_TXTLAYOUTSTYLE_ArabicContext;
   1023   dwStyle &= ~FX_TXTLAYOUTSTYLE_ArabicShapes;
   1024 
   1025   m_pTextBreak->SetLayoutStyles(dwStyle);
   1026   uint32_t dwAligment = 0;
   1027   if (m_Param.dwAlignment & FDE_TEXTEDITALIGN_Justified) {
   1028     dwAligment |= FX_TXTLINEALIGNMENT_Justified;
   1029   }
   1030   if (m_Param.dwAlignment & FDE_TEXTEDITALIGN_Center) {
   1031     dwAligment |= FX_TXTLINEALIGNMENT_Center;
   1032   } else if (m_Param.dwAlignment & FDE_TEXTEDITALIGN_Right) {
   1033     dwAligment |= FX_TXTLINEALIGNMENT_Right;
   1034   }
   1035   m_pTextBreak->SetAlignment(dwAligment);
   1036 
   1037   if (m_Param.dwMode & FDE_TEXTEDITMODE_AutoLineWrap) {
   1038     m_pTextBreak->SetLineWidth(m_Param.fPlateWidth);
   1039   } else {
   1040     m_pTextBreak->SetLineWidth(kPageWidthMax);
   1041   }
   1042 
   1043   m_nPageLineCount = m_Param.nLineCount;
   1044   if (m_Param.dwLayoutStyles & FDE_TEXTEDITLAYOUT_CombText) {
   1045     FX_FLOAT fCombWidth = m_Param.fPlateWidth;
   1046     if (m_nLimit > 0) {
   1047       fCombWidth /= m_nLimit;
   1048     }
   1049     m_pTextBreak->SetCombWidth(fCombWidth);
   1050   }
   1051   m_pTextBreak->SetFont(m_Param.pFont);
   1052   m_pTextBreak->SetFontSize(m_Param.fFontSize);
   1053   m_pTextBreak->SetTabWidth(m_Param.fTabWidth, m_Param.bTabEquidistant);
   1054   m_pTextBreak->SetDefaultChar(m_Param.wDefChar);
   1055   m_pTextBreak->SetParagraphBreakChar(m_Param.wLineBreakChar);
   1056   m_pTextBreak->SetCharRotation(m_Param.nCharRotation);
   1057   m_pTextBreak->SetLineBreakTolerance(m_Param.fFontSize * 0.2f);
   1058   m_pTextBreak->SetHorizontalScale(m_Param.nHorzScale);
   1059   m_pTextBreak->SetCharSpace(m_Param.fCharSpace);
   1060 }
   1061 
   1062 bool CFDE_TxtEdtEngine::ReplaceParagEnd(FX_WCHAR*& lpText,
   1063                                         int32_t& nLength,
   1064                                         bool bPreIsCR) {
   1065   for (int32_t i = 0; i < nLength; i++) {
   1066     FX_WCHAR wc = lpText[i];
   1067     switch (wc) {
   1068       case L'\r': {
   1069         lpText[i] = m_wLineEnd;
   1070         bPreIsCR = true;
   1071       } break;
   1072       case L'\n': {
   1073         if (bPreIsCR == true) {
   1074           int32_t nNext = i + 1;
   1075           if (nNext < nLength) {
   1076             FXSYS_memmove(lpText + i, lpText + nNext,
   1077                           (nLength - nNext) * sizeof(FX_WCHAR));
   1078           }
   1079           i--;
   1080           nLength--;
   1081           bPreIsCR = false;
   1082           if (m_bAutoLineEnd) {
   1083             m_nFirstLineEnd = FDE_TXTEDIT_LINEEND_CRLF;
   1084             m_bAutoLineEnd = false;
   1085           }
   1086         } else {
   1087           lpText[i] = m_wLineEnd;
   1088           if (m_bAutoLineEnd) {
   1089             m_nFirstLineEnd = FDE_TXTEDIT_LINEEND_LF;
   1090             m_bAutoLineEnd = false;
   1091           }
   1092         }
   1093       } break;
   1094       default: {
   1095         if (bPreIsCR && m_bAutoLineEnd) {
   1096           m_nFirstLineEnd = FDE_TXTEDIT_LINEEND_CR;
   1097           m_bAutoLineEnd = false;
   1098         }
   1099         bPreIsCR = false;
   1100       } break;
   1101     }
   1102   }
   1103   return bPreIsCR;
   1104 }
   1105 
   1106 void CFDE_TxtEdtEngine::RecoverParagEnd(CFX_WideString& wsText) const {
   1107   FX_WCHAR wc = (m_nFirstLineEnd == FDE_TXTEDIT_LINEEND_CR) ? L'\n' : L'\r';
   1108   if (m_nFirstLineEnd == FDE_TXTEDIT_LINEEND_CRLF) {
   1109     CFX_ArrayTemplate<int32_t> PosArr;
   1110     int32_t nLength = wsText.GetLength();
   1111     int32_t i = 0;
   1112     FX_WCHAR* lpPos = const_cast<FX_WCHAR*>(wsText.c_str());
   1113     for (i = 0; i < nLength; i++, lpPos++) {
   1114       if (*lpPos == m_wLineEnd) {
   1115         *lpPos = wc;
   1116         PosArr.Add(i);
   1117       }
   1118     }
   1119     const FX_WCHAR* lpSrcBuf = wsText.c_str();
   1120     CFX_WideString wsTemp;
   1121     int32_t nCount = PosArr.GetSize();
   1122     FX_WCHAR* lpDstBuf = wsTemp.GetBuffer(nLength + nCount);
   1123     int32_t nDstPos = 0;
   1124     int32_t nSrcPos = 0;
   1125     for (i = 0; i < nCount; i++) {
   1126       int32_t nPos = PosArr[i];
   1127       int32_t nCopyLen = nPos - nSrcPos + 1;
   1128       FXSYS_memcpy(lpDstBuf + nDstPos, lpSrcBuf + nSrcPos,
   1129                    nCopyLen * sizeof(FX_WCHAR));
   1130       nDstPos += nCopyLen;
   1131       nSrcPos += nCopyLen;
   1132       lpDstBuf[nDstPos] = L'\n';
   1133       nDstPos++;
   1134     }
   1135     if (nSrcPos < nLength) {
   1136       FXSYS_memcpy(lpDstBuf + nDstPos, lpSrcBuf + nSrcPos,
   1137                    (nLength - nSrcPos) * sizeof(FX_WCHAR));
   1138     }
   1139     wsTemp.ReleaseBuffer(nLength + nCount);
   1140     wsText = wsTemp;
   1141   } else {
   1142     int32_t nLength = wsText.GetLength();
   1143     FX_WCHAR* lpBuf = const_cast<FX_WCHAR*>(wsText.c_str());
   1144     for (int32_t i = 0; i < nLength; i++, lpBuf++) {
   1145       if (*lpBuf == m_wLineEnd)
   1146         *lpBuf = wc;
   1147     }
   1148   }
   1149 }
   1150 
   1151 int32_t CFDE_TxtEdtEngine::MovePage2Char(int32_t nIndex) {
   1152   ASSERT(nIndex >= 0);
   1153   ASSERT(nIndex <= m_pTxtBuf->GetTextLength());
   1154   if (m_nCaretPage >= 0) {
   1155     IFDE_TxtEdtPage* pPage = m_PagePtrArray[m_nCaretPage];
   1156     m_Param.pEventSink->OnPageLoad(m_nCaretPage);
   1157     int32_t nPageCharStart = pPage->GetCharStart();
   1158     int32_t nPageCharCount = pPage->GetCharCount();
   1159     if (nIndex >= nPageCharStart && nIndex < nPageCharStart + nPageCharCount) {
   1160       m_Param.pEventSink->OnPageUnload(m_nCaretPage);
   1161       return m_nCaretPage;
   1162     }
   1163     m_Param.pEventSink->OnPageUnload(m_nCaretPage);
   1164   }
   1165   CFDE_TxtEdtParag* pParag = nullptr;
   1166   int32_t nLineCount = 0;
   1167   int32_t nParagCount = m_ParagPtrArray.GetSize();
   1168   int32_t i = 0;
   1169   for (i = 0; i < nParagCount; i++) {
   1170     pParag = m_ParagPtrArray[i];
   1171     if (pParag->GetStartIndex() <= nIndex &&
   1172         nIndex < (pParag->GetStartIndex() + pParag->GetTextLength())) {
   1173       break;
   1174     }
   1175     nLineCount += pParag->GetLineCount();
   1176   }
   1177   pParag->LoadParag();
   1178   int32_t nLineStart = -1;
   1179   int32_t nLineCharCount = -1;
   1180   for (i = 0; i < pParag->GetLineCount(); i++) {
   1181     pParag->GetLineRange(i, nLineStart, nLineCharCount);
   1182     if (nLineStart <= nIndex && nIndex < (nLineStart + nLineCharCount))
   1183       break;
   1184   }
   1185   ASSERT(i < pParag->GetLineCount());
   1186   nLineCount += (i + 1);
   1187   m_nCaretPage = (nLineCount - 1) / m_nPageLineCount + 1 - 1;
   1188   pParag->UnloadParag();
   1189   return m_nCaretPage;
   1190 }
   1191 
   1192 void CFDE_TxtEdtEngine::TextPos2ParagPos(int32_t nIndex,
   1193                                          FDE_TXTEDTPARAGPOS& ParagPos) const {
   1194   ASSERT(nIndex >= 0 && nIndex < m_pTxtBuf->GetTextLength());
   1195   int32_t nCount = m_ParagPtrArray.GetSize();
   1196   int32_t nBgn = 0;
   1197   int32_t nMid = 0;
   1198   int32_t nEnd = nCount - 1;
   1199   while (nEnd > nBgn) {
   1200     nMid = (nBgn + nEnd) / 2;
   1201     CFDE_TxtEdtParag* pParag = m_ParagPtrArray[nMid];
   1202     if (nIndex < pParag->GetStartIndex())
   1203       nEnd = nMid - 1;
   1204     else if (nIndex >= (pParag->GetStartIndex() + pParag->GetTextLength()))
   1205       nBgn = nMid + 1;
   1206     else
   1207       break;
   1208   }
   1209   if (nBgn == nEnd)
   1210     nMid = nBgn;
   1211 
   1212   ASSERT(nIndex >= m_ParagPtrArray[nMid]->GetStartIndex() &&
   1213          (nIndex < m_ParagPtrArray[nMid]->GetStartIndex() +
   1214                        m_ParagPtrArray[nMid]->GetTextLength()));
   1215   ParagPos.nParagIndex = nMid;
   1216   ParagPos.nCharIndex = nIndex - m_ParagPtrArray[nMid]->GetStartIndex();
   1217 }
   1218 
   1219 int32_t CFDE_TxtEdtEngine::MoveForward(bool& bBefore) {
   1220   if (m_nCaret == m_pTxtBuf->GetTextLength() - 1)
   1221     return -1;
   1222 
   1223   int32_t nCaret = m_nCaret;
   1224   if ((nCaret + 1 < m_pTxtBuf->GetTextLength()) &&
   1225       (m_pTxtBuf->GetCharByIndex(nCaret) == L'\r') &&
   1226       (m_pTxtBuf->GetCharByIndex(nCaret + 1) == L'\n')) {
   1227     nCaret++;
   1228   }
   1229   nCaret++;
   1230   bBefore = true;
   1231   return nCaret;
   1232 }
   1233 
   1234 int32_t CFDE_TxtEdtEngine::MoveBackward(bool& bBefore) {
   1235   if (m_nCaret == 0)
   1236     return false;
   1237 
   1238   int32_t nCaret = m_nCaret;
   1239   if (nCaret > 2 && m_pTxtBuf->GetCharByIndex(nCaret - 1) == L'\n' &&
   1240       m_pTxtBuf->GetCharByIndex(nCaret - 2) == L'\r') {
   1241     nCaret--;
   1242   }
   1243   nCaret--;
   1244   bBefore = true;
   1245   return nCaret;
   1246 }
   1247 
   1248 bool CFDE_TxtEdtEngine::MoveUp(CFX_PointF& ptCaret) {
   1249   IFDE_TxtEdtPage* pPage = GetPage(m_nCaretPage);
   1250   const CFX_RectF& rtContent = pPage->GetContentsBox();
   1251   ptCaret.x = m_fCaretPosReserve;
   1252   ptCaret.y = m_rtCaret.top + m_rtCaret.height / 2 - m_Param.fLineSpace;
   1253   if (ptCaret.y < rtContent.top) {
   1254     if (m_nCaretPage == 0) {
   1255       return false;
   1256     }
   1257     ptCaret.y -= rtContent.top;
   1258     m_nCaretPage--;
   1259     IFDE_TxtEdtPage* pCurPage = GetPage(m_nCaretPage);
   1260     ptCaret.y += pCurPage->GetContentsBox().bottom();
   1261   }
   1262 
   1263   return true;
   1264 }
   1265 
   1266 bool CFDE_TxtEdtEngine::MoveDown(CFX_PointF& ptCaret) {
   1267   IFDE_TxtEdtPage* pPage = GetPage(m_nCaretPage);
   1268   const CFX_RectF& rtContent = pPage->GetContentsBox();
   1269   ptCaret.x = m_fCaretPosReserve;
   1270   ptCaret.y = m_rtCaret.top + m_rtCaret.height / 2 + m_Param.fLineSpace;
   1271   if (ptCaret.y >= rtContent.bottom()) {
   1272     if (m_nCaretPage == CountPages() - 1) {
   1273       return false;
   1274     }
   1275     ptCaret.y -= rtContent.bottom();
   1276     m_nCaretPage++;
   1277     IFDE_TxtEdtPage* pCurPage = GetPage(m_nCaretPage);
   1278     ptCaret.y += pCurPage->GetContentsBox().top;
   1279   }
   1280   return true;
   1281 }
   1282 
   1283 bool CFDE_TxtEdtEngine::MoveLineStart() {
   1284   int32_t nIndex = m_bBefore ? m_nCaret : m_nCaret - 1;
   1285   FDE_TXTEDTPARAGPOS ParagPos;
   1286   TextPos2ParagPos(nIndex, ParagPos);
   1287   CFDE_TxtEdtParag* pParag = m_ParagPtrArray[ParagPos.nParagIndex];
   1288   pParag->LoadParag();
   1289   int32_t nLineCount = pParag->GetLineCount();
   1290   int32_t i = 0;
   1291   int32_t nStart = 0;
   1292   int32_t nCount = 0;
   1293   for (; i < nLineCount; i++) {
   1294     pParag->GetLineRange(i, nStart, nCount);
   1295     if (nIndex >= nStart && nIndex < nStart + nCount) {
   1296       break;
   1297     }
   1298   }
   1299   UpdateCaretRect(nStart, true);
   1300   pParag->UnloadParag();
   1301   return true;
   1302 }
   1303 
   1304 bool CFDE_TxtEdtEngine::MoveLineEnd() {
   1305   int32_t nIndex = m_bBefore ? m_nCaret : m_nCaret - 1;
   1306   FDE_TXTEDTPARAGPOS ParagPos;
   1307   TextPos2ParagPos(nIndex, ParagPos);
   1308   CFDE_TxtEdtParag* pParag = m_ParagPtrArray[ParagPos.nParagIndex];
   1309   pParag->LoadParag();
   1310   int32_t nLineCount = pParag->GetLineCount();
   1311   int32_t i = 0;
   1312   int32_t nStart = 0;
   1313   int32_t nCount = 0;
   1314   for (; i < nLineCount; i++) {
   1315     pParag->GetLineRange(i, nStart, nCount);
   1316     if (nIndex >= nStart && nIndex < nStart + nCount) {
   1317       break;
   1318     }
   1319   }
   1320   nIndex = nStart + nCount - 1;
   1321   ASSERT(nIndex <= GetTextBufLength());
   1322   FX_WCHAR wChar = m_pTxtBuf->GetCharByIndex(nIndex);
   1323   bool bBefore = false;
   1324   if (nIndex <= GetTextBufLength()) {
   1325     if (wChar == L'\r') {
   1326       bBefore = true;
   1327     } else if (wChar == L'\n' && nIndex > nStart) {
   1328       bBefore = true;
   1329       nIndex--;
   1330       wChar = m_pTxtBuf->GetCharByIndex(nIndex);
   1331       if (wChar != L'\r') {
   1332         nIndex++;
   1333       }
   1334     }
   1335   }
   1336   UpdateCaretRect(nIndex, bBefore);
   1337   pParag->UnloadParag();
   1338   return true;
   1339 }
   1340 
   1341 bool CFDE_TxtEdtEngine::MoveParagStart() {
   1342   int32_t nIndex = m_bBefore ? m_nCaret : m_nCaret - 1;
   1343   FDE_TXTEDTPARAGPOS ParagPos;
   1344   TextPos2ParagPos(nIndex, ParagPos);
   1345   CFDE_TxtEdtParag* pParag = m_ParagPtrArray[ParagPos.nParagIndex];
   1346   UpdateCaretRect(pParag->GetStartIndex(), true);
   1347   return true;
   1348 }
   1349 
   1350 bool CFDE_TxtEdtEngine::MoveParagEnd() {
   1351   int32_t nIndex = m_bBefore ? m_nCaret : m_nCaret - 1;
   1352   FDE_TXTEDTPARAGPOS ParagPos;
   1353   TextPos2ParagPos(nIndex, ParagPos);
   1354   CFDE_TxtEdtParag* pParag = m_ParagPtrArray[ParagPos.nParagIndex];
   1355   nIndex = pParag->GetStartIndex() + pParag->GetTextLength() - 1;
   1356   FX_WCHAR wChar = m_pTxtBuf->GetCharByIndex(nIndex);
   1357   if (wChar == L'\n' && nIndex > 0) {
   1358     nIndex--;
   1359     wChar = m_pTxtBuf->GetCharByIndex(nIndex);
   1360     if (wChar != L'\r') {
   1361       nIndex++;
   1362     }
   1363   }
   1364   UpdateCaretRect(nIndex, true);
   1365   return true;
   1366 }
   1367 
   1368 bool CFDE_TxtEdtEngine::MoveHome() {
   1369   UpdateCaretRect(0, true);
   1370   return true;
   1371 }
   1372 
   1373 bool CFDE_TxtEdtEngine::MoveEnd() {
   1374   UpdateCaretRect(GetTextBufLength(), true);
   1375   return true;
   1376 }
   1377 
   1378 bool CFDE_TxtEdtEngine::IsFitArea(CFX_WideString& wsText) {
   1379   std::unique_ptr<CFDE_TextOut> pTextOut(new CFDE_TextOut);
   1380   pTextOut->SetLineSpace(m_Param.fLineSpace);
   1381   pTextOut->SetFont(m_Param.pFont);
   1382   pTextOut->SetFontSize(m_Param.fFontSize);
   1383   uint32_t dwStyle = 0;
   1384   if (!(m_Param.dwMode & FDE_TEXTEDITMODE_MultiLines))
   1385     dwStyle |= FDE_TTOSTYLE_SingleLine;
   1386 
   1387   CFX_RectF rcText;
   1388   if (m_Param.dwMode & FDE_TEXTEDITMODE_AutoLineWrap) {
   1389     dwStyle |= FDE_TTOSTYLE_LineWrap;
   1390     rcText.width = m_Param.fPlateWidth;
   1391   } else {
   1392     rcText.width = 65535;
   1393   }
   1394   pTextOut->SetStyles(dwStyle);
   1395   wsText += L"\n";
   1396   pTextOut->CalcLogicSize(wsText.c_str(), wsText.GetLength(), rcText);
   1397   wsText.Delete(wsText.GetLength() - 1);
   1398   if ((m_Param.dwMode & FDE_TEXTEDITMODE_LimitArea_Horz) &&
   1399       (rcText.width > m_Param.fPlateWidth)) {
   1400     return false;
   1401   }
   1402   if ((m_Param.dwMode & FDE_TEXTEDITMODE_LimitArea_Vert) &&
   1403       (rcText.height > m_Param.fLineSpace * m_Param.nLineCount)) {
   1404     return false;
   1405   }
   1406   return true;
   1407 }
   1408 
   1409 void CFDE_TxtEdtEngine::UpdateCaretRect(int32_t nIndex, bool bBefore) {
   1410   MovePage2Char(nIndex);
   1411   GetCaretRect(m_rtCaret, m_nCaretPage, nIndex, bBefore);
   1412   m_nCaret = nIndex;
   1413   m_bBefore = bBefore;
   1414   if (!m_bBefore) {
   1415     m_nCaret++;
   1416     m_bBefore = true;
   1417   }
   1418   m_fCaretPosReserve = m_rtCaret.left;
   1419   m_Param.pEventSink->OnCaretChanged();
   1420 }
   1421 
   1422 void CFDE_TxtEdtEngine::GetCaretRect(CFX_RectF& rtCaret,
   1423                                      int32_t nPageIndex,
   1424                                      int32_t nCaret,
   1425                                      bool bBefore) {
   1426   IFDE_TxtEdtPage* pPage = m_PagePtrArray[m_nCaretPage];
   1427   m_Param.pEventSink->OnPageLoad(m_nCaretPage);
   1428   bool bCombText = !!(m_Param.dwLayoutStyles & FDE_TEXTEDITLAYOUT_CombText);
   1429   int32_t nIndexInpage = nCaret - pPage->GetCharStart();
   1430   if (bBefore && bCombText && nIndexInpage > 0) {
   1431     nIndexInpage--;
   1432     bBefore = false;
   1433   }
   1434   int32_t nBIDILevel = pPage->GetCharRect(nIndexInpage, rtCaret, bCombText);
   1435   if ((!FX_IsOdd(nBIDILevel) && !bBefore) ||
   1436       (FX_IsOdd(nBIDILevel) && bBefore)) {
   1437     rtCaret.Offset(rtCaret.width - 1.0f, 0);
   1438   }
   1439   if (rtCaret.width == 0 && rtCaret.left > 1.0f)
   1440     rtCaret.left -= 1.0f;
   1441 
   1442   rtCaret.width = 1.0f;
   1443 
   1444   m_Param.pEventSink->OnPageUnload(m_nCaretPage);
   1445 }
   1446 
   1447 void CFDE_TxtEdtEngine::UpdateCaretIndex(const CFX_PointF& ptCaret) {
   1448   IFDE_TxtEdtPage* pPage = m_PagePtrArray[m_nCaretPage];
   1449   m_Param.pEventSink->OnPageLoad(m_nCaretPage);
   1450   m_nCaret = pPage->GetCharIndex(ptCaret, m_bBefore);
   1451   GetCaretRect(m_rtCaret, m_nCaretPage, m_nCaret, m_bBefore);
   1452   if (!m_bBefore) {
   1453     m_nCaret++;
   1454     m_bBefore = true;
   1455   }
   1456   m_Param.pEventSink->OnCaretChanged();
   1457   m_Param.pEventSink->OnPageUnload(m_nCaretPage);
   1458 }
   1459 
   1460 bool CFDE_TxtEdtEngine::IsSelect() {
   1461   return m_SelRangePtrArr.GetSize() > 0;
   1462 }
   1463 
   1464 void CFDE_TxtEdtEngine::DeleteSelect() {
   1465   int32_t nCountRange = CountSelRanges();
   1466   if (nCountRange > 0) {
   1467     int32_t nSelStart = 0;
   1468     while (nCountRange > 0) {
   1469       int32_t nSelCount = GetSelRange(--nCountRange, &nSelStart);
   1470       delete m_SelRangePtrArr[nCountRange];
   1471       m_SelRangePtrArr.RemoveAt(nCountRange);
   1472       DeleteRange_DoRecord(nSelStart, nSelCount, true);
   1473     }
   1474     ClearSelection();
   1475     m_Param.pEventSink->OnTextChanged(m_ChangeInfo);
   1476     m_Param.pEventSink->OnSelChanged();
   1477     SetCaretPos(nSelStart, true);
   1478     return;
   1479   }
   1480 }
   1481