/* 指定範囲のデータを置換(削除 & データを挿入) Fromを含む位置からToの直前を含むデータを削除する Fromの位置へテキストを挿入する */ void CSearchAgent::ReplaceData( DocLineReplaceArg* pArg ) { // MY_RUNNINGTIMER( cRunningTimer, "CDocLineMgr::ReplaceData()" ); /* 挿入によって増えた行の数 */ pArg->nInsLineNum = CLogicInt(0); /* 削除した行の総数 */ pArg->nDeletedLineNum = CLogicInt(0); /* 削除されたデータ */ if( pArg->pcmemDeleted ){ pArg->pcmemDeleted->clear(); } CDocLine* pCDocLine; CDocLine* pCDocLinePrev; CDocLine* pCDocLineNext; int nWorkPos; int nWorkLen; const wchar_t* pLine; int nLineLen; int i; CLogicInt nAllLinesOld; int nProgress; CDocLine::MarkType markNext; // May 15, 2000 HWND hwndCancel = NULL; // 初期化 HWND hwndProgress = NULL; // 初期化 pArg->ptNewPos = pArg->sDelRange.GetFrom(); /* 大量のデータを操作するとき */ CDlgCancel* pCDlgCancel = NULL; class CDLgCandelCloser{ CDlgCancel*& m_pDlg; public: CDLgCandelCloser(CDlgCancel*& pDlg): m_pDlg(pDlg){} ~CDLgCandelCloser(){ if( NULL != m_pDlg ){ // 進捗ダイアログを表示しない場合と同じ動きになるようにダイアログは遅延破棄する // ここで pCDlgCancel を delete すると delete から戻るまでの間に // ダイアログ破棄 -> 編集画面へフォーカス移動 -> キャレット位置調整 // まで一気に動くので無効なレイアウト情報参照で異常終了することがある m_pDlg->DeleteAsync(); // 自動破棄を遅延実行する // 2008.05.28 ryoji } } }; CDLgCandelCloser closer(pCDlgCancel); const CLogicInt nDelLines = pArg->sDelRange.GetTo().y - pArg->sDelRange.GetFrom().y; const CLogicInt nEditLines = std::max<CLogicInt>(CLogicInt(1), nDelLines + CLogicInt(pArg->pInsData ? pArg->pInsData->size(): 0)); if( 3000 < nEditLines ){ /* 進捗ダイアログの表示 */ pCDlgCancel = new CDlgCancel; if( NULL != ( hwndCancel = pCDlgCancel->DoModeless( ::GetModuleHandle( NULL ), NULL, IDD_OPERATIONRUNNING ) ) ){ hwndProgress = ::GetDlgItem( hwndCancel, IDC_PROGRESS ); Progress_SetRange( hwndProgress, 0, 101 ); Progress_SetPos( hwndProgress, 0 ); } } int nProgressOld = 0; // バッファを確保 if( pArg->pcmemDeleted ){ pArg->pcmemDeleted->reserve( pArg->sDelRange.GetTo().y + CLogicInt(1) - pArg->sDelRange.GetFrom().y ); } // 2012.01.10 行内の削除&挿入のときの操作を1つにする bool bChangeOneLine = false; // 行内の挿入 bool bInsOneLine = false; bool bLastEOLReplace = false; // 「最後改行」を「最後改行」で置換 if( pArg->pInsData && 0 < pArg->pInsData->size() ){ const CNativeW& cmemLine = pArg->pInsData->back().cmemLine; int nLen = cmemLine.GetStringLength(); const wchar_t* pInsLine = cmemLine.GetStringPtr(); if( 0 < nLen && WCODE::IsLineDelimiter(pInsLine[nLen - 1], GetDllShareData().m_Common.m_sEdit.m_bEnableExtEol) ){ // 行挿入 bLastEOLReplace = true; // 仮。後で修正 }else{ if( 1 == pArg->pInsData->size() ){ bChangeOneLine = true; // 「abc\ndef」=>「123」のような置換もtrueなのに注意 } } } const wchar_t* pInsData = L""; int nInsLen = 0; int nSetSeq = 0; if( bChangeOneLine ){ nInsLen = pArg->pInsData->back().cmemLine.GetStringLength(); pInsData = pArg->pInsData->back().cmemLine.GetStringPtr(); nSetSeq = pArg->pInsData->back().nSeq; } /* 現在行の情報を得る */ pCDocLine = m_pcDocLineMgr->GetLine( pArg->sDelRange.GetTo().GetY2() ); i = pArg->sDelRange.GetTo().y; if( 0 < pArg->sDelRange.GetTo().y && NULL == pCDocLine ){ pCDocLine = m_pcDocLineMgr->GetLine( pArg->sDelRange.GetTo().GetY2() - CLogicInt(1) ); i--; } bool bFirstLine = true; bool bSetMark = false; /* 後ろから処理していく */ for( ; i >= pArg->sDelRange.GetFrom().y && NULL != pCDocLine; i-- ){ pLine = pCDocLine->GetPtr(); // 2002/2/10 aroka CMemory変更 nLineLen = pCDocLine->GetLengthWithEOL(); // 2002/2/10 aroka CMemory変更 pCDocLinePrev = pCDocLine->GetPrevLine(); pCDocLineNext = pCDocLine->GetNextLine(); /* 現在行の削除開始位置を調べる */ if( i == pArg->sDelRange.GetFrom().y ){ nWorkPos = pArg->sDelRange.GetFrom().x; }else{ nWorkPos = 0; } /* 現在行の削除データ長を調べる */ if( i == pArg->sDelRange.GetTo().y ){ nWorkLen = pArg->sDelRange.GetTo().x - nWorkPos; }else{ nWorkLen = nLineLen - nWorkPos; // 2002/2/10 aroka CMemory変更 } if( 0 == nWorkLen ){ /* 前の行へ */ goto prev_line; } /* 改行も削除するんかぃのぉ・・・? */ if( EOL_NONE != pCDocLine->GetEol() && nWorkPos + nWorkLen > nLineLen - pCDocLine->GetEol().GetLen() // 2002/2/10 aroka CMemory変更 ){ /* 削除する長さに改行も含める */ nWorkLen = nLineLen - nWorkPos; // 2002/2/10 aroka CMemory変更 } /* 行全体の削除 */ if( nWorkLen >= nLineLen ){ // 2002/2/10 aroka CMemory変更 /* 削除した行の総数 */ ++(pArg->nDeletedLineNum); /* 行オブジェクトの削除、リスト変更、行数-- */ if( pArg->pcmemDeleted ){ CLineData tmp; pArg->pcmemDeleted->push_back(tmp); CLineData& delLine = pArg->pcmemDeleted->back(); delLine.cmemLine.swap(pCDocLine->_GetDocLineData()); // CDocLine書き換え delLine.nSeq = CModifyVisitor().GetLineModifiedSeq(pCDocLine); } m_pcDocLineMgr->DeleteLine( pCDocLine ); pCDocLine = NULL; } /* 次の行と連結するような削除 */ else if( nWorkPos + nWorkLen >= nLineLen ){ // 2002/2/10 aroka CMemory変更 if( pArg->pcmemDeleted ){ if( pCDocLineNext && 0 == pArg->pcmemDeleted->size() ){ // 1行以内の行末削除のときだけ、次の行のseqが保存されないので必要 // 2014.01.07 最後が改行の範囲を最後が改行のデータで置換した場合を変更 if( !bLastEOLReplace ){ CLineData tmp; pArg->pcmemDeleted->push_back(tmp); CLineData& delLine = pArg->pcmemDeleted->back(); delLine.cmemLine.SetString(L""); delLine.nSeq = CModifyVisitor().GetLineModifiedSeq(pCDocLineNext); } } CLineData tmp; pArg->pcmemDeleted->push_back(tmp); CLineData& delLine = pArg->pcmemDeleted->back(); delLine.cmemLine.SetString(&pLine[nWorkPos], nWorkLen); delLine.nSeq = CModifyVisitor().GetLineModifiedSeq(pCDocLine); } /* 次の行がある */ if( pCDocLineNext ){ /* 次の行のデータを最後に追加 */ // 改行を削除するような置換 int nNewLen = nWorkPos + pCDocLineNext->GetLengthWithEOL() + nInsLen; if( nWorkLen <= nWorkPos && nLineLen <= nNewLen + 10 ){ // 行を連結して1行にするような操作の高速化 // 削除が元データの有効長以下で行の長さが伸びるか少し減る場合reallocを試みる static CDocLine* pDocLinePrevAccess = NULL; static int nAccessCount = 0; int nBufferReserve = nNewLen; if( pDocLinePrevAccess == pCDocLine ){ if( 100 < nAccessCount ){ if( 1000 < nNewLen ){ int n = 1000; while( n < nNewLen ){ n += n / 5; // 20%づつ伸ばす } nBufferReserve = n; } }else{ nAccessCount++; } }else{ pDocLinePrevAccess = pCDocLine; nAccessCount = 0; } CNativeW& ref = pCDocLine->_GetDocLineData(); ref.AllocStringBuffer(nBufferReserve); ref._SetStringLength(nWorkPos); ref.AppendString(pInsData, nInsLen); ref.AppendNativeData(pCDocLineNext->_GetDocLineDataWithEOL()); pCDocLine->SetEol(); }else{ CNativeW tmp; tmp.AllocStringBuffer(nNewLen); tmp.AppendString(pLine, nWorkPos); tmp.AppendString(pInsData, nInsLen); tmp.AppendNativeData(pCDocLineNext->_GetDocLineDataWithEOL()); pCDocLine->SetDocLineStringMove(&tmp); } if( bChangeOneLine ){ pArg->nInsSeq = CModifyVisitor().GetLineModifiedSeq(pCDocLine); CModifyVisitor().SetLineModified(pCDocLine, nSetSeq); if( !bInsOneLine ){ pArg->ptNewPos.x = pArg->ptNewPos.x + nInsLen; bInsOneLine = true; } }else{ CModifyVisitor().SetLineModified(pCDocLine, pArg->nDelSeq); // 削除される行のマーク類を保存 markNext = pCDocLineNext->m_sMark; bSetMark = true; } /* 次の行 行オブジェクトの削除 */ m_pcDocLineMgr->DeleteLine( pCDocLineNext ); pCDocLineNext = NULL; /* 削除した行の総数 */ ++(pArg->nDeletedLineNum); }else{ /* 行内データ削除 */ CNativeW tmp; tmp.SetString(pLine, nWorkPos); pCDocLine->SetDocLineStringMove(&tmp); CModifyVisitor().SetLineModified(pCDocLine, pArg->nDelSeq); /* 変更フラグ */ } } else{ /* 行内だけの削除 */ if( pArg->pcmemDeleted ){ CLineData tmp; pArg->pcmemDeleted->push_back(tmp); CLineData& delLine = pArg->pcmemDeleted->back(); delLine.cmemLine.SetString(&pLine[nWorkPos], nWorkLen); delLine.nSeq = CModifyVisitor().GetLineModifiedSeq(pCDocLine); } {// 20020119 aroka ブロック内に pWork を閉じ込めた // 2002/2/10 aroka CMemory変更 何度も GetLength,GetPtr をよばない。 int nNewLen = nLineLen - nWorkLen + nInsLen; int nAfterLen = nLineLen - (nWorkPos + nWorkLen); if( pCDocLine->_GetDocLineData().capacity() * 9 / 10 < nNewLen && nNewLen <= pCDocLine->_GetDocLineData().capacity() ){ CNativeW& ref = pCDocLine->_GetDocLineData(); WCHAR* pBuf = const_cast<WCHAR*>(ref.GetStringPtr()); if( nWorkLen != nInsLen ){ wmemmove(&pBuf[nWorkPos + nInsLen], &pLine[nWorkPos + nWorkLen], nAfterLen); } wmemcpy(&pBuf[nWorkPos], pInsData, nInsLen); ref._SetStringLength(nNewLen); }else{ int nBufferSize = 16; if( 1000 < nNewLen ){ nBufferSize = 1000; while( nBufferSize < nNewLen ){ nBufferSize += nBufferSize / 20; // 5%づつ伸ばす } } CNativeW tmp; tmp.AllocStringBuffer(nBufferSize); tmp.AppendString(pLine, nWorkPos); tmp.AppendString(pInsData, nInsLen); tmp.AppendString(&pLine[nWorkPos + nWorkLen], nAfterLen); pCDocLine->SetDocLineStringMove(&tmp); } } if( bChangeOneLine ){ pArg->nInsSeq = CModifyVisitor().GetLineModifiedSeq(pCDocLine); CModifyVisitor().SetLineModified(pCDocLine, nSetSeq); pArg->ptNewPos.x = pArg->ptNewPos.x + nInsLen; bInsOneLine = true; pInsData = L""; nInsLen = 0; }else{ CModifyVisitor().SetLineModified(pCDocLine, pArg->nDelSeq); } if( bFirstLine ){ bLastEOLReplace = false; } } bFirstLine = false; prev_line:; /* 直前の行のオブジェクトのポインタ */ pCDocLine = pCDocLinePrev; /* 最近参照した行番号と行データ */ --m_pcDocLineMgr->m_nPrevReferLine; m_pcDocLineMgr->m_pCodePrevRefer = pCDocLine; if( NULL != hwndCancel){ int nLines = pArg->sDelRange.GetTo().y - i; if( 0 == (nLines % 32) ){ nProgress = ::MulDiv(nLines, 100, nEditLines); if( nProgressOld != nProgress ){ nProgressOld = nProgress; Progress_SetPos( hwndProgress, nProgress + 1 ); Progress_SetPos( hwndProgress, nProgress ); } } } } if( pArg->pcmemDeleted ){ // 下から格納されているのでひっくり返す std::reverse(pArg->pcmemDeleted->begin(), pArg->pcmemDeleted->end()); } if( bInsOneLine ){ // 挿入済み return; } /* データ挿入処理 */ if( NULL == pArg->pInsData || 0 == pArg->pInsData->size() ){ pArg->nInsSeq = 0; return; } nAllLinesOld= m_pcDocLineMgr->GetLineCount(); pArg->ptNewPos.y = pArg->sDelRange.GetFrom().y; /* 挿入された部分の次の位置の行 */ pArg->ptNewPos.x = 0; /* 挿入された部分の次の位置のデータ位置 */ /* 挿入データを行終端で区切った行数カウンタ */ pCDocLine = m_pcDocLineMgr->GetLine( pArg->sDelRange.GetFrom().GetY2() ); int nInsSize = pArg->pInsData->size(); bool bInsertLineMode = false; bool bLastInsert = false; { CNativeW& cmemLine = pArg->pInsData->back().cmemLine; int nLen = cmemLine.GetStringLength(); const wchar_t* pInsLine = cmemLine.GetStringPtr(); if( 0 < nLen && WCODE::IsLineDelimiter(pInsLine[nLen - 1], GetDllShareData().m_Common.m_sEdit.m_bEnableExtEol) ){ if( 0 == pArg->sDelRange.GetFrom().x ){ // 挿入データの最後が改行で行頭に挿入するとき、現在行を維持する bInsertLineMode = true; if( pCDocLine && m_pcDocLineMgr->m_pCodePrevRefer == pCDocLine ){ m_pcDocLineMgr->m_pCodePrevRefer = pCDocLine->GetPrevLine(); if( m_pcDocLineMgr->m_pCodePrevRefer ){ m_pcDocLineMgr->m_nPrevReferLine--; } } } }else{ bLastInsert = true; nInsSize--; } } CStringRef cPrevLine; CStringRef cNextLine; CNativeW cmemCurLine; if( NULL == pCDocLine ){ /* ここでNULLが帰ってくるということは、*/ /* 全テキストの最後の次の行を追加しようとしていることを示す */ pArg->nInsSeq = 0; }else{ // 2002/2/10 aroka 何度も GetPtr を呼ばない if( !bInsertLineMode ){ cmemCurLine.swap(pCDocLine->_GetDocLineData()); pLine = cmemCurLine.GetStringPtr(&nLineLen); cPrevLine = CStringRef(pLine, pArg->sDelRange.GetFrom().x); cNextLine = CStringRef(&pLine[pArg->sDelRange.GetFrom().x], nLineLen - pArg->sDelRange.GetFrom().x); pArg->nInsSeq = CModifyVisitor().GetLineModifiedSeq(pCDocLine); }else{ pArg->nInsSeq = 0; } } int nCount; for( nCount = 0; nCount < nInsSize; nCount++ ){ CNativeW& cmemLine = (*pArg->pInsData)[nCount].cmemLine; #ifdef _DEBUG int nLen = cmemLine.GetStringLength(); const wchar_t* pInsLine = cmemLine.GetStringPtr(); assert( 0 < nLen && WCODE::IsLineDelimiter(pInsLine[nLen - 1], GetDllShareData().m_Common.m_sEdit.m_bEnableExtEol) ); #endif { if( NULL == pCDocLine ){ CDocLine* pCDocLineNew = m_pcDocLineMgr->AddNewLine(); /* 挿入データを行終端で区切った行数カウンタ */ if( 0 == nCount ){ CNativeW tmp; tmp.AllocStringBuffer(cPrevLine.GetLength() + cmemLine.GetStringLength()); tmp.AppendString(cPrevLine.GetPtr(), cPrevLine.GetLength()); tmp.AppendNativeData(cmemLine); pCDocLineNew->SetDocLineStringMove(&tmp); } else{ pCDocLineNew->SetDocLineStringMove(&cmemLine); } CModifyVisitor().SetLineModified(pCDocLineNew, (*pArg->pInsData)[nCount].nSeq); } else{ /* 挿入データを行終端で区切った行数カウンタ */ if( 0 == nCount && !bInsertLineMode ){ if( cmemCurLine.GetStringLength() - cPrevLine.GetLength() < cmemCurLine.GetStringLength() / 100 && cPrevLine.GetLength() + cmemLine.GetStringLength() <= cmemCurLine.GetStringLength() && cmemCurLine.capacity() / 2 <= cPrevLine.GetLength() + cmemLine.GetStringLength() ){ // 行のうちNextになるのが1%以下で行が短くなるなら再利用する(長い一行を分割する場合の最適化) CNativeW tmp; // Nextを退避 tmp.SetString(cNextLine.GetPtr(), cNextLine.GetLength()); cmemCurLine.swap(tmp); tmp._SetStringLength(cPrevLine.GetLength()); tmp.AppendNativeData(cmemLine); pCDocLine->SetDocLineStringMove(&tmp); cNextLine = CStringRef(cmemCurLine.GetStringPtr(), cmemCurLine.GetStringLength()); }else{ CNativeW tmp; tmp.AllocStringBuffer(cPrevLine.GetLength() + cmemLine.GetStringLength()); tmp.AppendString(cPrevLine.GetPtr(), cPrevLine.GetLength()); tmp.AppendNativeData(cmemLine); pCDocLine->SetDocLineStringMove(&tmp); } CModifyVisitor().SetLineModified(pCDocLine, (*pArg->pInsData)[nCount].nSeq); pCDocLine = pCDocLine->GetNextLine(); } else{ CDocLine* pCDocLineNew = m_pcDocLineMgr->InsertNewLine(pCDocLine); //pCDocLineの前に挿入 pCDocLineNew->SetDocLineStringMove(&cmemLine); CModifyVisitor().SetLineModified(pCDocLineNew, (*pArg->pInsData)[nCount].nSeq); } } /* 挿入データを行終端で区切った行数カウンタ */ ++(pArg->ptNewPos.y); /* 挿入された部分の次の位置の行 */ if( NULL != hwndCancel ){ if( 0 == (nCount % 32) ){ nProgress = ::MulDiv(nCount + nDelLines, 100, nEditLines); if( nProgressOld != nProgress ){ nProgressOld = nProgress; Progress_SetPos( hwndProgress, nProgress + 1 ); Progress_SetPos( hwndProgress, nProgress ); } } } } } if( bLastInsert || 0 < cNextLine.GetLength() ){ CNativeW cNull; CStringRef cNullStr(L"", 0); CNativeW& cmemLine = bLastInsert ? pArg->pInsData->back().cmemLine : cNull; const CStringRef& cPrevLine2 = ((0 == nCount) ? cPrevLine: cNullStr); int nSeq = pArg->pInsData->back().nSeq; int nLen = cmemLine.GetStringLength(); CNativeW tmp; tmp.AllocStringBuffer(cPrevLine2.GetLength() + cmemLine.GetStringLength() + cNextLine.GetLength()); tmp.AppendString(cPrevLine2.GetPtr(), cPrevLine2.GetLength()); tmp.AppendNativeData(cmemLine); tmp.AppendString(cNextLine.GetPtr(), cNextLine.GetLength()); if( NULL == pCDocLine ){ CDocLine* pCDocLineNew = m_pcDocLineMgr->AddNewLine(); //末尾に追加 pCDocLineNew->SetDocLineStringMove(&tmp); pCDocLineNew->m_sMark = markNext; if( !bLastEOLReplace || !bSetMark ){ CModifyVisitor().SetLineModified(pCDocLineNew, nSeq); } pArg->ptNewPos.x = nLen; /* 挿入された部分の次の位置のデータ位置 */ }else{ if( 0 == nCount ){ // 行の中間に挿入(削除データがなかった。1文字入力など) }else{ // 複数行挿入の最後の行 pCDocLine = m_pcDocLineMgr->InsertNewLine(pCDocLine); //pCDocLineの前に挿入 pCDocLine->m_sMark = markNext; } pCDocLine->SetDocLineStringMove(&tmp); if( !bLastEOLReplace || !bSetMark ){ CModifyVisitor().SetLineModified(pCDocLine, nSeq); } pArg->ptNewPos.x = cPrevLine2.GetLength() + nLen; /* 挿入された部分の次の位置のデータ位置 */ } } pArg->nInsLineNum = m_pcDocLineMgr->GetLineCount() - nAllLinesOld; return; }