-// undo/redo functions
-
-BOOL CGhostTextBuffer::
-Undo (CCrystalTextView * pSource, CPoint & ptCursorPos)
-{
- ASSERT (CanUndo ());
- ASSERT ((m_aUndoBuf[0].m_dwFlags & UNDO_BEGINGROUP) != 0);
- BOOL failed = FALSE;
- int tmpPos = m_nUndoPosition;
-
- while (!failed)
- {
- --tmpPos;
- GhostUndoRecord ur = m_aUndoBuf[tmpPos];
- // Undo records are stored in file line numbers
- // and must be converted to apparent (screen) line numbers for use
- CPoint apparent_ptStartPos = ur.m_ptStartPos;
- apparent_ptStartPos.y = ComputeApparentLine(ur.m_ptStartPos.y, ur.m_ptStartPos_nGhost);
- CPoint apparent_ptEndPos = ur.m_ptEndPos;
- apparent_ptEndPos.y = ComputeApparentLine(ur.m_ptEndPos.y, ur.m_ptEndPos_nGhost);
-
- if (ur.m_ptStartPos_nGhost > 0)
- // if we need a ghost line at position apparent_ptStartPos.y
- if (apparent_ptStartPos.y >= m_aLines.size() || (GetLineFlags(apparent_ptStartPos.y) & LF_GHOST) == 0)
- {
- // if we don't find it, we insert it
- InsertGhostLine (pSource, apparent_ptStartPos.y);
- // and recompute apparent_ptEndPos
- apparent_ptEndPos.y = ComputeApparentLine (ur.m_ptEndPos.y, ur.m_ptEndPos_nGhost);
- }
-
- // EndPos defined only for UNDO_INSERT (when we delete)
- if (ur.m_dwFlags & UNDO_INSERT && ur.m_ptEndPos_nGhost > 0)
- // if we need a ghost line at position apparent_ptStartPos.y
- if (apparent_ptEndPos.y >= m_aLines.size() || (GetLineFlags(apparent_ptEndPos.y) & LF_GHOST) == 0)
- {
- // if we don't find it, we insert it
- InsertGhostLine (pSource, apparent_ptEndPos.y);
- }
-
- if (ur.m_dwFlags & UNDO_INSERT)
- {
- // WINMERGE -- Check that text in undo buffer matches text in
- // file buffer. If not, then rescan() has moved lines and undo
- // is skipped.
-
- // we need to put the cursor before the deleted section
- CString text;
- ur.m_redo_ptEndPos.x = apparent_ptEndPos.x;
- ur.m_redo_ptEndPos.y = ComputeRealLineAndGhostAdjustment (apparent_ptEndPos.y, ur.m_redo_ptEndPos_nGhost);
-
- // flags are going to be deleted so we store them now
- int bLastLineGhost = ((GetLineFlags(apparent_ptEndPos.y) & LF_GHOST) != 0);
-
- const size_t size = m_aLines.size();
- if ((apparent_ptStartPos.y < size) &&
- (apparent_ptStartPos.x <= m_aLines[apparent_ptStartPos.y].Length()) &&
- (apparent_ptEndPos.y < size) &&
- (apparent_ptEndPos.x <= m_aLines[apparent_ptEndPos.y].Length()))
- {
- GetTextWithoutEmptys (apparent_ptStartPos.y, apparent_ptStartPos.x, apparent_ptEndPos.y, apparent_ptEndPos.x, text, CRLF_STYLE_AUTOMATIC, FALSE);
- if (text.GetLength() == ur.GetTextLength() && memcmp(text, ur.GetText(), text.GetLength() * sizeof(TCHAR)) == 0)
- {
- VERIFY (CCrystalTextBuffer::DeleteText (pSource,
- apparent_ptStartPos.y, apparent_ptStartPos.x, apparent_ptEndPos.y, apparent_ptEndPos.x,
- 0, FALSE, FALSE));
- ptCursorPos = apparent_ptStartPos;
- }
- else
- {
- //..Try to ensure that we are undoing correctly...
- // Just compare the text as it was before Undo operation
-#ifdef _ADVANCED_BUGCHECK
- ASSERT(0);
-#endif
- failed = TRUE;
- break;
- }
-
- }
- else
- {
- failed = TRUE;
- break;
- }
-
- OnNotifyLineHasBeenEdited(apparent_ptStartPos.y);
-
- // default : the remaining line inherits the status of the last line of the deleted block
- SetLineFlag(apparent_ptStartPos.y, LF_GHOST, bLastLineGhost, FALSE, FALSE);
-
- // the number of real lines must be the same before the action and after undo
- int nNumberDeletedRealLines = ur.m_ptEndPos.y - ur.m_ptStartPos.y;
- if (nNumberDeletedRealLines == ur.m_nRealLinesCreated)
- ;
- else if (nNumberDeletedRealLines == ur.m_nRealLinesCreated-1)
- // we inserted in a ghost line (which then became real), we must send it back to its world
- SetLineFlag(apparent_ptStartPos.y, LF_GHOST, TRUE, FALSE, FALSE);
- else
- ASSERT(0);
-
- // it is not easy to know when Recompute so we do it always
- RecomputeRealityMapping();
-
- RecomputeEOL (pSource, apparent_ptStartPos.y, apparent_ptStartPos.y);
- }
- else
- {
- int nEndLine, nEndChar;
- VERIFY(CCrystalTextBuffer::InsertText (pSource,
- apparent_ptStartPos.y, apparent_ptStartPos.x, ur.GetText (), ur.GetTextLength (), nEndLine, nEndChar,
- 0, FALSE));
- ptCursorPos = m_ptLastChange;
-
- // for the flags, the logic is nearly the same as in insertText
- int bFirstLineGhost = ((GetLineFlags(apparent_ptStartPos.y) & LF_GHOST) != 0);
- // when inserting an EOL terminated text into a ghost line,
- // there is a dicrepancy between nInsertedLines and nEndLine-nRealLine
- int bDiscrepancyInInsertedLines;
- if (bFirstLineGhost && nEndChar == 0 && ApparentLastRealLine() >= nEndLine)
- bDiscrepancyInInsertedLines = TRUE;
- else
- bDiscrepancyInInsertedLines = FALSE;
-
- int i;
- for (i = apparent_ptStartPos.y ; i < nEndLine ; i++)
- OnNotifyLineHasBeenEdited(i);
- if (bDiscrepancyInInsertedLines == 0)
- OnNotifyLineHasBeenEdited(i);
-
- // We know the number of real lines in the deleted block (including partial lines for extremities)
- // there may be more lines (difficult to explain) then they must be ghost
- for (i = apparent_ptStartPos.y ; i < apparent_ptStartPos.y + ur.m_nRealLinesInDeletedBlock ; i++)
- SetLineFlag (i, LF_GHOST, FALSE, FALSE, FALSE);
- for ( ; i <= nEndLine ; i++)
- {
- if (i < nEndLine)
- SetLineFlag (i, LF_GHOST, TRUE, FALSE, FALSE);
- else if (apparent_ptStartPos.x != 0 || nEndChar != 0)
- SetLineFlag (i, LF_GHOST, TRUE, FALSE, FALSE);
- }
-
- // it is not easy to know when Recompute so we do it always
- RecomputeRealityMapping();
-
- RecomputeEOL (pSource, apparent_ptStartPos.y, nEndLine);
- }
-
- // store infos for redo
- ur.m_redo_ptStartPos.x = apparent_ptStartPos.x;
- ur.m_redo_ptStartPos.y = ComputeRealLineAndGhostAdjustment( apparent_ptStartPos.y, ur.m_redo_ptStartPos_nGhost);
- if (ur.m_dwFlags & UNDO_INSERT)
- ur.m_redo_ptEndPos = CPoint( -1, 0 );
- else
- {
- ur.m_redo_ptEndPos.x = m_ptLastChange.x;
- ur.m_redo_ptEndPos.y = ComputeRealLineAndGhostAdjustment (m_ptLastChange.y, ur.m_redo_ptEndPos_nGhost);
- }
-
- // restore line revision numbers
- int naSavedRevisonNumbersSize = (int) ur.m_paSavedRevisonNumbers->GetSize();
- for (int i = 0; i < naSavedRevisonNumbersSize; i++)
- m_aLines[apparent_ptStartPos.y + i].m_dwRevisionNumber = (*ur.m_paSavedRevisonNumbers)[i];
-
- m_aUndoBuf[tmpPos] = ur;
-
- if (ur.m_dwFlags & UNDO_BEGINGROUP)
- break;
- }
- if (m_bModified && m_nSyncPosition == tmpPos)
- SetModified (FALSE);
- if (!m_bModified && m_nSyncPosition != tmpPos)
- SetModified (TRUE);
- if (failed)
- {
- // If the Undo failed, clear the entire Undo/Redo stack
- // Not only can we not Redo the failed Undo, but the Undo
- // may have partially completed (if in a group)
- m_nUndoPosition = 0;
- m_aUndoBuf.clear();
- }
- else
- {
- m_nUndoPosition = tmpPos;
- }
- return !failed;
-}
-
-BOOL CGhostTextBuffer::
-Redo (CCrystalTextView * pSource, CPoint & ptCursorPos)
-{
- ASSERT (CanRedo ());
- ASSERT ((m_aUndoBuf[0].m_dwFlags & UNDO_BEGINGROUP) != 0);
- ASSERT ((m_aUndoBuf[m_nUndoPosition].m_dwFlags & UNDO_BEGINGROUP) != 0);
-
- while(1)
- {
- GhostUndoRecord ur = m_aUndoBuf[m_nUndoPosition];
- CPoint apparent_ptStartPos = ur.m_redo_ptStartPos;
- apparent_ptStartPos.y = ComputeApparentLine (ur.m_redo_ptStartPos.y, ur.m_redo_ptStartPos_nGhost);
- CPoint apparent_ptEndPos = ur.m_redo_ptEndPos;
- apparent_ptEndPos.y = ComputeApparentLine (ur.m_redo_ptEndPos.y, ur.m_redo_ptEndPos_nGhost);
-
- if (ur.m_redo_ptStartPos_nGhost > 0)
- // we need a ghost line at position apparent_ptStartPos.y
- if (apparent_ptStartPos.y >= m_aLines.size() || (GetLineFlags(apparent_ptStartPos.y) & LF_GHOST) == 0)
- {
- // if we don't find it, we insert it
- InsertGhostLine (pSource, apparent_ptStartPos.y);
- // and recompute apparent_ptEndPos
- apparent_ptEndPos.y = ComputeApparentLine (ur.m_redo_ptEndPos.y, ur.m_redo_ptEndPos_nGhost);
- }
-
- // EndPos defined only for UNDO_DELETE (when we delete)
- if ((ur.m_dwFlags & UNDO_INSERT) == 0 && ur.m_redo_ptEndPos_nGhost > 0)
- // we need a ghost line at position apparent_ptStartPos.y
- if (apparent_ptEndPos.y >= m_aLines.size() || (GetLineFlags(apparent_ptEndPos.y) & LF_GHOST) == 0)
- {
- // if we don't find it, we insert it
- InsertGhostLine (pSource, apparent_ptEndPos.y);
- }
-
- // now we can use normal (CGhostTextBuffer::) insertTxt or deleteText
- if (ur.m_dwFlags & UNDO_INSERT)
- {
- int nEndLine, nEndChar;
- VERIFY(InsertText (pSource, apparent_ptStartPos.y, apparent_ptStartPos.x,
- ur.GetText(), ur.GetTextLength(), nEndLine, nEndChar, 0, FALSE));
- ptCursorPos = m_ptLastChange;
- }
- else
- {
-#ifdef _ADVANCED_BUGCHECK
- CString text;
- GetTextWithoutEmptys (apparent_ptStartPos.y, apparent_ptStartPos.x, apparent_ptEndPos.y, apparent_ptEndPos.x, text, CRLF_STYLE_AUTOMATIC, FALSE);
- ASSERT(text.GetLength() == ur.GetTextLength() && memcmp(text, ur.GetText(), text.GetLength() * sizeof(TCHAR)) == 0);
-#endif
- VERIFY(DeleteText(pSource, apparent_ptStartPos.y, apparent_ptStartPos.x,
- apparent_ptEndPos.y, apparent_ptEndPos.x, 0, FALSE, FALSE));
- ptCursorPos = apparent_ptStartPos;
- }
- m_nUndoPosition++;
- if (m_nUndoPosition == m_aUndoBuf.size ())
- break;
- if ((m_aUndoBuf[m_nUndoPosition].m_dwFlags & UNDO_BEGINGROUP) != 0)
- break;
- }
-
- if (m_bModified && m_nSyncPosition == m_nUndoPosition)
- SetModified (FALSE);
- if (!m_bModified && m_nSyncPosition != m_nUndoPosition)
- SetModified (TRUE);
- return TRUE;
-}
-
-
-/**
-we must set both our m_bUndoBeginGroup and the one of CCrystalTextBuffer
-*/
-void CGhostTextBuffer::BeginUndoGroup (BOOL bMergeWithPrevious /*= FALSE*/ )
-{
- ASSERT (!m_bUndoGroup);
- m_bUndoGroup = TRUE;
- m_bUndoBeginGroup = m_nUndoPosition == 0 || !bMergeWithPrevious;
- CCrystalTextBuffer::m_bUndoBeginGroup = m_bUndoBeginGroup;
-}
-
-/** Use ou own flushing function as we need to use our own m_aUndoBuf */
-void CGhostTextBuffer::FlushUndoGroup (CCrystalTextView * pSource)
-{
- ASSERT (m_bUndoGroup);
- if (pSource != NULL)
- {
- ASSERT (m_nUndoPosition == m_aUndoBuf.size ());
- if (m_nUndoPosition > 0)
- {
- pSource->OnEditOperation (m_aUndoBuf[m_nUndoPosition - 1].m_nAction,
- m_aUndoBuf[m_nUndoPosition - 1].GetText (), m_aUndoBuf[m_nUndoPosition - 1].GetTextLength ());
- }
- }
- m_bUndoGroup = FALSE;
-}
-
-
-/** The CPoint received parameters are apparent (on screen) line numbers */
-void CGhostTextBuffer::AddUndoRecord (BOOL bInsert, const CPoint & ptStartPos,
- const CPoint & ptEndPos, LPCTSTR pszText, int cchText,
- int nRealLinesChanged, int nActionType, CDWordArray *paSavedRevisonNumbers)
-{
- // Forgot to call BeginUndoGroup()?
- ASSERT (m_bUndoGroup);
- ASSERT (m_aUndoBuf.size () == 0 || (m_aUndoBuf[0].m_dwFlags & UNDO_BEGINGROUP) != 0);
-
- // Strip unnecessary undo records (edit after undo wipes all potential redo records)
- int nBufSize = (int) m_aUndoBuf.size ();
- if (m_nUndoPosition < nBufSize)
- {
- m_aUndoBuf.resize (m_nUndoPosition);
- }
-
- // Add new record
- GhostUndoRecord ur;
- ur.m_dwFlags = bInsert ? UNDO_INSERT : 0;
- ur.m_nAction = nActionType;
- if (m_bUndoBeginGroup)
- {
- ur.m_dwFlags |= UNDO_BEGINGROUP;
- m_bUndoBeginGroup = FALSE;
- }
- ur.m_ptStartPos = ptStartPos;
- ur.m_ptEndPos = ptEndPos;
- ur.m_ptStartPos.y = ComputeRealLineAndGhostAdjustment( ptStartPos.y, ur.m_ptStartPos_nGhost);
- ur.m_ptEndPos.y = ComputeRealLineAndGhostAdjustment( ptEndPos.y, ur.m_ptEndPos_nGhost);
- if (bInsert)
- ur.m_nRealLinesCreated = nRealLinesChanged;
- else
- ur.m_nRealLinesInDeletedBlock = nRealLinesChanged;
- ur.SetText (pszText, cchText);
- ur.m_paSavedRevisonNumbers = paSavedRevisonNumbers;
-
- // Optimize memory allocation
- if (m_aUndoBuf.capacity() == m_aUndoBuf.size())
- {
- if (m_aUndoBuf.size() == 0)
- m_aUndoBuf.reserve(16);
- else if (m_aUndoBuf.size() < 1025)
- m_aUndoBuf.reserve(m_aUndoBuf.size() * 2);
- else
- m_aUndoBuf.reserve(m_aUndoBuf.size() + 1024);
- }
- m_aUndoBuf.push_back (ur);
- m_nUndoPosition = (int) m_aUndoBuf.size ();
-}
-
-
-
-
-////////////////////////////////////////////////////////////////////////////