1 ////////////////////////////////////////////////////////////////////////////
2 // File: ccrystaltextbuffer.cpp
4 // Created: 29-Dec-1998
6 // Author: Stcherbatchenko Andrei
7 // E-mail: windfall@gmx.de
9 // Implementation of the CCrystalTextBuffer class, a part of Crystal Edit -
10 // syntax coloring text editor.
12 // You are free to use or modify this code to the following restrictions:
13 // - Acknowledge me somewhere in your about box, simple "Parts of code by.."
14 // will be enough. If you can't (or don't want to), contact me personally.
15 // - LEAVE THIS HEADER INTACT
16 ////////////////////////////////////////////////////////////////////////////
18 ////////////////////////////////////////////////////////////////////////////
20 // + FIX: unnecessary 'HANDLE' in CCrystalTextBuffer::SaveToFile
21 ////////////////////////////////////////////////////////////////////////////
23 ////////////////////////////////////////////////////////////////////////////
25 // Paul Selormey, James R. Twine:
26 // + FEATURE: description for Undo/Redo actions
27 // + FEATURE: multiple MSVC-like bookmarks
28 // + FEATURE: 'Disable backspace at beginning of line' option
29 // + FEATURE: 'Disable drag-n-drop editing' option
31 // + FEATURE: changed layout of SUndoRecord. Now takes less memory
32 ////////////////////////////////////////////////////////////////////////////
34 ////////////////////////////////////////////////////////////////////////////
37 // + FEATURE: some other things I've forgotten ...
39 // ... it's being edited very rapidly so sorry for non-commented
40 // and maybe "ugly" code ...
41 ////////////////////////////////////////////////////////////////////////////
43 ////////////////////////////////////////////////////////////////////////////
45 // Sven Wiegand (search for "//BEGIN SW" to find my changes):
46 // + FEATURE: Remembering the text-position of the latest change.
47 ////////////////////////////////////////////////////////////////////////////
49 ////////////////////////////////////////////////////////////////////////////
52 // + FIX: Setting m_ptLastChange to the beginning of the selection in
53 // InternalDeleteText(), so that position is valid in any case.
54 // Editor won't crash any more i.e. by selecting whole buffer and
55 // deleting it and then executing ID_EDIT_GOTO_LAST_CHANGE-command.
56 ////////////////////////////////////////////////////////////////////////////
58 * @file ccrystaltextbuffer.cpp
60 * @brief Code for CCrystalTextBuffer class
68 #include "UndoRecord.h"
69 #include "ccrystaltextbuffer.h"
70 #include "ccrystaltextview.h"
71 #include "utils/filesup.h"
72 #include "utils/cs2cs.h"
75 #pragma message("Include <afxpriv.h> in your stdafx.h to avoid this message")
85 int CCrystalTextBuffer::m_nDefaultEncoding = -1;
88 /////////////////////////////////////////////////////////////////////////////
89 // CCrystalTextBuffer::CUpdateContext
91 void CCrystalTextBuffer::CInsertContext::
92 RecalcPoint (CPoint & ptPoint)
94 ASSERT (m_ptEnd.y > m_ptStart.y ||
95 m_ptEnd.y == m_ptStart.y && m_ptEnd.x >= m_ptStart.x);
96 if (ptPoint.y < m_ptStart.y)
98 if (ptPoint.y > m_ptStart.y)
100 ptPoint.y += (m_ptEnd.y - m_ptStart.y);
103 if (ptPoint.x <= m_ptStart.x)
105 ptPoint.y += (m_ptEnd.y - m_ptStart.y);
106 ptPoint.x = m_ptEnd.x + (ptPoint.x - m_ptStart.x);
109 void CCrystalTextBuffer::CDeleteContext::
110 RecalcPoint (CPoint & ptPoint)
112 ASSERT (m_ptEnd.y > m_ptStart.y ||
113 m_ptEnd.y == m_ptStart.y && m_ptEnd.x >= m_ptStart.x);
114 if (ptPoint.y < m_ptStart.y)
116 if (ptPoint.y > m_ptEnd.y)
118 ptPoint.y -= (m_ptEnd.y - m_ptStart.y);
121 if (ptPoint.y == m_ptEnd.y && ptPoint.x >= m_ptEnd.x)
123 ptPoint.y = m_ptStart.y;
124 ptPoint.x = m_ptStart.x + (ptPoint.x - m_ptEnd.x);
127 if (ptPoint.y == m_ptStart.y)
129 if (ptPoint.x > m_ptStart.x)
130 ptPoint.x = m_ptStart.x;
137 /////////////////////////////////////////////////////////////////////////////
138 // CCrystalTextBuffer
140 IMPLEMENT_DYNCREATE (CCrystalTextBuffer, CCmdTarget)
142 CCrystalTextBuffer::CCrystalTextBuffer ()
147 m_nCRLFMode = CRLFSTYLE::DOS;
149 m_bCreateBackupFile = false;
150 m_nSyncPosition = m_nUndoPosition = 0;
151 m_bInsertTabs = true;
154 m_ptLastChange.x = m_ptLastChange.y = -1;
156 m_nSourceEncoding = m_nDefaultEncoding;
157 m_dwCurrentRevisionNumber = 0;
158 m_dwRevisionNumberOnSave = 0;
159 m_bUndoGroup = m_bUndoBeginGroup = false;
162 m_bAllowNewlinesInQuotes = true;
163 m_cFieldDelimiter = '\t';
164 m_cFieldEnclosure = '"';
165 m_bTableEditing = false;
166 m_pSharedTableProps.reset(new SharedTableProperties());
167 m_pSharedTableProps->m_textBufferList.push_back(this);
170 CCrystalTextBuffer:: ~ CCrystalTextBuffer ()
172 ASSERT (!m_bInit); // You must call FreeAll() before deleting the object
176 BEGIN_MESSAGE_MAP (CCrystalTextBuffer, CCmdTarget)
177 //{{AFX_MSG_MAP(CCrystalTextBuffer)
182 /////////////////////////////////////////////////////////////////////////////
183 // CCrystalTextBuffer message handlers
186 * @brief Insert the same line once or several times
188 * @param nPosition : not defined (or -1) = add lines at the end of array
190 void CCrystalTextBuffer::InsertLine (LPCTSTR pszLine, size_t nLength,
191 int nPosition /*= -1*/, int nCount /*= 1*/ )
193 ASSERT(nLength != -1);
196 line.Create(pszLine, nLength);
198 // nPosition not defined ? Insert at end of array
200 nPosition = (int) m_aLines.size();
202 // insert all lines in one pass
203 std::vector<LineInfo>::iterator iter = m_aLines.begin() + nPosition;
204 m_aLines.insert(iter, nCount, line);
206 // create text data for lines after the first one
207 for (int ic = 1; ic < nCount; ic++)
210 li.Create(pszLine, nLength);
211 m_aLines[nPosition + ic] = li;
215 // Warning : this function is also used during rescan
216 // and this trace will appear even after the initial load
217 int nLines = (int) m_aLines.size();
218 if (nLines / 5000 != (nLines-nCount) / 5000)
219 TRACE1 ("%d lines loaded!\n", nLines);
223 // Add characters to end of specified line
224 // Specified line must not have any EOL characters
225 void CCrystalTextBuffer::
226 AppendLine (int nLineIndex, LPCTSTR pszChars, size_t nLength, bool bDetectEol)
228 ASSERT(nLength != -1);
233 LineInfo & li = m_aLines[nLineIndex];
234 li.Append(pszChars, nLength, bDetectEol);
238 * @brief Copy line range [line1;line2] to range starting at newline1
240 * NB: Lines are assigned, not inserted
248 * l=10 lines[105] = lines[10]
249 * l=9 lines[104] = lines[9]
250 * l=8 lines[103] = lines[8]
258 * l=40 lines[10] = lines[40]
259 * l=41 lines[11] = lines[41]
260 * l=42 lines[12] = lines[42]
262 void CCrystalTextBuffer::MoveLine(int line1, int line2, int newline1)
264 int ldiff = newline1 - line1;
267 for (int l = line2; l >= line1; l--)
268 m_aLines[l+ldiff] = m_aLines[l];
272 for (int l = line1; l <= line2; l++)
273 m_aLines[l+ldiff] = m_aLines[l];
277 void CCrystalTextBuffer::SetEmptyLine (int nPosition, int nCount /*= 1*/ )
279 for (int i = 0; i < nCount; i++)
283 m_aLines[nPosition + i] = li;
287 void CCrystalTextBuffer::
291 std::vector<LineInfo>::iterator iter = m_aLines.begin();
292 std::vector<LineInfo>::iterator end = m_aLines.end();
300 // Undo buffer will be cleared by its destructor
304 m_ptLastChange.x = m_ptLastChange.y = -1;
308 bool CCrystalTextBuffer::
309 InitNew (CRLFSTYLE nCrlfStyle /*= CRLFSTYLE::DOS*/ )
312 ASSERT (m_aLines.size() == 0);
313 ASSERT (nCrlfStyle != CRLFSTYLE::AUTOMATIC && nCrlfStyle != CRLFSTYLE::MIXED);
314 InsertLine (_T (""), 0);
317 m_nCRLFMode = nCrlfStyle;
319 m_bInsertTabs = true;
321 m_nSyncPosition = m_nUndoPosition = 0;
322 m_bUndoGroup = m_bUndoBeginGroup = false;
323 ASSERT (m_aUndoBuf.size () == 0);
324 UpdateViews (nullptr, nullptr, UPDATE_RESET);
326 m_ptLastChange.x = m_ptLastChange.y = -1;
331 bool CCrystalTextBuffer::
335 ASSERT (m_bInit); // Text buffer not yet initialized.
336 // You must call InitNew() or LoadFromFile() first!
341 void CCrystalTextBuffer::SetReadOnly (bool bReadOnly /*= true*/ )
343 ASSERT (m_bInit); // Text buffer not yet initialized.
344 // You must call InitNew() or LoadFromFile() first!
346 m_bReadOnly = bReadOnly;
350 // WinMerge has own routine for loading
351 #ifdef CRYSTALEDIT_ENABLELOADER
353 static LPCTSTR crlfs[] =
355 _T ("\x0d\x0a"), // DOS/Windows style
356 _T ("\x0a"), // UNIX style
357 _T ("\x0a") // Macintosh style
360 bool CCrystalTextBuffer::
361 LoadFromFile (LPCTSTR pszFileName, CRLFSTYLE nCrlfStyle /*= CRLFSTYLE::AUTOMATIC*/ )
364 ASSERT (m_aLines.size() == 0);
366 HANDLE hFile = nullptr;
367 int nCurrentMax = 256;
368 char *pcLineBuf = new char[nCurrentMax];
370 bool bSuccess = false;
372 int nExt = GetExtPosition (pszFileName);
373 if (pszFileName[nExt] == _T ('.'))
375 CrystalLineParser::TextDefinition *def = CrystalLineParser::GetTextType (pszFileName + nExt);
376 if (def && def->encoding != -1)
377 m_nSourceEncoding = def->encoding;
381 DWORD dwFileAttributes =::GetFileAttributes (pszFileName);
382 if (dwFileAttributes == (DWORD) - 1)
385 hFile =::CreateFile (pszFileName, GENERIC_READ, FILE_SHARE_READ + FILE_SHARE_WRITE, nullptr,
386 OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, nullptr);
387 if (hFile == INVALID_HANDLE_VALUE)
390 int nCurrentLength = 0;
392 const DWORD dwBufSize = 32768;
393 LPSTR pcBuf = (LPSTR) _alloca (dwBufSize);
395 if (!::ReadFile (hFile, pcBuf, dwBufSize, &dwCurSize, nullptr))
398 if (nCrlfStyle == CRLFSTYLE::AUTOMATIC)
400 // Try to determine current CRLF mode based on first line
402 for (I = 0; I < dwCurSize; I++)
404 if ((pcBuf[I] == _T('\x0d')) || (pcBuf[I] == _T('\x0a')))
409 // By default (or in the case of empty file), set DOS style
410 nCrlfStyle = CRLFSTYLE::DOS;
414 // Otherwise, analyse the first occurance of line-feed character
415 if (pcBuf[I] == _T('\x0a'))
417 nCrlfStyle = CRLFSTYLE::UNIX;
421 if (I < dwCurSize - 1 && pcBuf[I + 1] == _T ('\x0a'))
422 nCrlfStyle = CRLFSTYLE::DOS;
424 nCrlfStyle = CRLFSTYLE::MAC;
429 ASSERT (nCrlfStyle != CRLFSTYLE::AUTOMATIC && nCrlfStyle != CRLFSTYLE::MIXED);
430 m_nCRLFMode = nCrlfStyle;
432 m_aLines.reserve(4096);
435 while (dwBufPtr < dwCurSize)
437 char c = pcBuf[dwBufPtr];
439 if (dwBufPtr == dwCurSize && dwCurSize == dwBufSize)
441 if (!::ReadFile (hFile, pcBuf, dwBufSize, &dwCurSize, nullptr))
446 pcLineBuf[nCurrentLength] = c;
448 if (nCurrentLength == nCurrentMax)
450 // Reallocate line buffer
452 char *pcNewLineBuf = new char[nCurrentMax];
453 memcpy(pcNewLineBuf, pcLineBuf, sizeof(char) * (nCurrentMax - 256));
455 pcLineBuf = pcNewLineBuf;
458 // detect both types of EOL for each line
459 // handles mixed mode files.
460 // Perry (2002-11-26): What about MAC files ? They don't have 0x0A at all. I think this doesn't handle them.
463 pcLineBuf[nCurrentLength] = '\0';
465 wchar_t *buf = new wchar_t[nCurrentLength];
466 int len = MultiByteToWideChar(CP_UTF8, 0, pcLineBuf, nCurrentLength, buf, nCurrentLength);
467 if (m_nSourceEncoding >= 0)
468 iconvert(buf, m_nSourceEncoding, 1, m_nSourceEncoding == 15);
469 InsertLine(buf, len);
472 if (m_nSourceEncoding >= 0)
473 iconvert (pcLineBuf, m_nSourceEncoding, 1, m_nSourceEncoding == 15);
474 InsertLine (pcLineBuf, nCurrentLength);
480 pcLineBuf[nCurrentLength] = 0;
482 wchar_t *buf = new wchar_t[nCurrentLength];
483 int len = MultiByteToWideChar(CP_UTF8, 0, pcLineBuf, nCurrentLength, buf, nCurrentLength);
484 InsertLine(buf, len);
487 InsertLine (&pcLineBuf[0], nCurrentLength);
490 ASSERT (m_aLines.size() > 0); // At least one empty line must present
493 m_bReadOnly = (dwFileAttributes & FILE_ATTRIBUTE_READONLY) != 0;
495 m_bUndoGroup = m_bUndoBeginGroup = false;
496 m_nSyncPosition = m_nUndoPosition = 0;
497 ASSERT (m_aUndoBuf.size () == 0);
500 RetypeViews (pszFileName);
501 UpdateViews (nullptr, nullptr, UPDATE_RESET);
506 if (hFile != nullptr && hFile != INVALID_HANDLE_VALUE)
507 ::CloseHandle (hFile);
510 m_ptLastChange.x = m_ptLastChange.y = -1;
514 #endif // #if 0 loadfromfile
516 // WinMerge has own routine for saving
517 #ifdef CRYSTALEDIT_ENABLESAVER
518 bool CCrystalTextBuffer::SaveToFile(LPCTSTR pszFileName,
519 CRLFSTYLE nCrlfStyle /*= CRLFSTYLE::AUTOMATIC*/,
520 bool bClearModifiedFlag /*= true*/)
522 ASSERT (nCrlfStyle == CRLFSTYLE::AUTOMATIC || nCrlfStyle == CRLFSTYLE::DOS ||
523 nCrlfStyle == CRLFSTYLE::UNIX || nCrlfStyle == CRLFSTYLE::MAC);
525 HANDLE hTempFile = INVALID_HANDLE_VALUE;
526 HANDLE hSearch = INVALID_HANDLE_VALUE;
527 TCHAR szTempFileDir[_MAX_PATH + 1];
528 TCHAR szTempFileName[_MAX_PATH + 1];
529 TCHAR szBackupFileName[_MAX_PATH + 1];
530 bool bSuccess = false;
532 TCHAR drive[_MAX_PATH], dir[_MAX_PATH], name[_MAX_PATH], ext[_MAX_PATH];
534 _wsplitpath_s (pszFileName, drive, dir, name, ext);
536 _splitpath_s (pszFileName, drive, dir, name, ext);
538 _tcscpy_s (szTempFileDir, drive);
539 _tcscat_s (szTempFileDir, dir);
540 _tcscpy_s (szBackupFileName, pszFileName);
541 _tcscat_s (szBackupFileName, _T (".bak"));
543 if (::GetTempFileName(szTempFileDir, _T("CRE"), 0, szTempFileName) == 0)
546 hTempFile =::CreateFile (szTempFileName, GENERIC_WRITE, 0, nullptr,
547 CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr);
548 if (hTempFile == INVALID_HANDLE_VALUE)
551 if (nCrlfStyle == CRLFSTYLE::AUTOMATIC)
552 nCrlfStyle = m_nCRLFMode;
554 ASSERT (nCrlfStyle != CRLFSTYLE::AUTOMATIC && nCrlfStyle != CRLFSTYLE::MIXED);
555 LPCTSTR pszCRLF = crlfs[static_cast<int>(nCrlfStyle)];
556 int nCRLFLength = static_cast<int>(_tcslen (pszCRLF));
558 int nLineCount = static_cast<int>(m_aLines.size());
559 for (int nLine = 0; nLine < nLineCount; nLine++)
561 int nLength = static_cast<int>(m_aLines[nLine].Length());
562 DWORD dwWrittenBytes;
565 LPCTSTR pszLine = m_aLines[nLine].GetLine(0);
566 if (m_nSourceEncoding >= 0)
569 iconvert_new (m_aLines[nLine].GetLine(0), &pszBuf, 1, m_nSourceEncoding, m_nSourceEncoding == 15);
571 std::unique_ptr<char> abuf{ new char[nLength * 4] };
572 nLength = WideCharToMultiByte (CP_UTF8, 0, pszBuf, nLength, abuf.get(), nLength * 4, nullptr, nullptr);
573 if (!::WriteFile (hTempFile, abuf.get(), nLength, &dwWrittenBytes, nullptr))
575 if (!::WriteFile (hTempFile, pszBuf, nLength, &dwWrittenBytes, nullptr))
586 std::unique_ptr<char> abuf{ new char[nLength * 4] };
587 nLength = WideCharToMultiByte (CP_UTF8, 0, pszLine, nLength, abuf.get(), nLength * 4, nullptr, nullptr);
588 if (!::WriteFile (hTempFile, abuf.get(), nLength, &dwWrittenBytes, nullptr))
590 if (!::WriteFile (hTempFile, pszLine, nLength, &dwWrittenBytes, nullptr))
594 if (nLength != (int)dwWrittenBytes)
597 if (nLine < nLineCount - 1) // Last line must not end with CRLF
601 std::unique_ptr<char> abuf{ new char[nCRLFLength * 4] };
602 nCRLFLength = WideCharToMultiByte (CP_UTF8, 0, pszCRLF, nCRLFLength, abuf.get(), nCRLFLength * 4, nullptr, nullptr);
603 if (!::WriteFile (hTempFile, abuf.get(), nCRLFLength, &dwWrittenBytes, nullptr))
605 if (!::WriteFile (hTempFile, pszCRLF, nCRLFLength, &dwWrittenBytes, nullptr))
608 if (nCRLFLength != (int) dwWrittenBytes)
612 ::CloseHandle (hTempFile);
613 hTempFile = INVALID_HANDLE_VALUE;
615 if (m_bCreateBackupFile)
618 hSearch =::FindFirstFile (pszFileName, &wfd);
619 if (hSearch != INVALID_HANDLE_VALUE)
621 // File exist - create backup file
622 ::DeleteFile (szBackupFileName);
623 if (!::MoveFile (pszFileName, szBackupFileName))
625 ::FindClose (hSearch);
626 hSearch = INVALID_HANDLE_VALUE;
631 ::DeleteFile (pszFileName);
634 // Move temporary file to target name
635 if (!::MoveFile (szTempFileName, pszFileName))
638 if (bClearModifiedFlag)
641 m_nSyncPosition = m_nUndoPosition;
645 // remember revision number on save
646 m_dwRevisionNumberOnSave = m_dwCurrentRevisionNumber;
648 // redraw line revision marks
649 UpdateViews (nullptr, nullptr, UPDATE_FLAGSONLY);
653 if (hSearch != INVALID_HANDLE_VALUE)
654 ::FindClose (hSearch);
655 if (hTempFile != INVALID_HANDLE_VALUE)
656 ::CloseHandle (hTempFile);
657 ::DeleteFile (szTempFileName);
661 #endif // #if 0 savetofile
663 // Default EOL to use if editor has to manufacture one
664 // (this occurs with ghost lines)
665 void CCrystalTextBuffer::
666 SetCRLFMode (CRLFSTYLE nCRLFMode)
668 if (nCRLFMode==CRLFSTYLE::AUTOMATIC)
669 nCRLFMode = CRLFSTYLE::DOS;
670 m_nCRLFMode = nCRLFMode;
672 ASSERT(m_nCRLFMode == CRLFSTYLE::DOS || m_nCRLFMode == CRLFSTYLE::UNIX ||
673 m_nCRLFMode == CRLFSTYLE::MAC || m_nCRLFMode == CRLFSTYLE::MIXED);
676 bool CCrystalTextBuffer::
679 LPCTSTR lpEOLtoApply = GetDefaultEol();
680 bool bChanged = false;
681 for (size_t i = 0 ; i < m_aLines.size(); i++)
683 // the last real line has no EOL
684 if (!m_aLines[i].HasEol())
686 bChanged |= ChangeLineEol(static_cast<int>(i), lpEOLtoApply);
695 int CCrystalTextBuffer::
696 GetLineCount () const
698 ASSERT (m_bInit); // Text buffer not yet initialized.
699 ASSERT (m_aLines.size() < INT_MAX);
700 // You must call InitNew() or LoadFromFile() first!
702 return (int) m_aLines.size ();
705 // number of characters in line (excluding any trailing eol characters)
706 int CCrystalTextBuffer::
707 GetLineLength (int nLine) const
709 ASSERT (m_bInit); // Text buffer not yet initialized.
710 ASSERT (m_aLines[nLine].Length() < INT_MAX);
711 // You must call InitNew() or LoadFromFile() first!
713 return static_cast<int>(m_aLines[nLine].Length());
716 // number of characters in line (including any trailing eol characters)
717 int CCrystalTextBuffer::
718 GetFullLineLength (int nLine) const
720 ASSERT (m_bInit); // Text buffer not yet initialized.
721 if (nLine >= static_cast<int>(m_aLines.size()))
726 ASSERT (m_aLines[nLine].FullLength() < INT_MAX);
727 // You must call InitNew() or LoadFromFile() first!
729 return static_cast<int>(m_aLines[nLine].FullLength());
732 // get pointer to any trailing eol characters (pointer to empty string if none)
733 LPCTSTR CCrystalTextBuffer::
734 GetLineEol (int nLine) const
736 ASSERT (m_bInit); // Text buffer not yet initialized.
737 if (m_aLines[nLine].HasEol())
738 return m_aLines[nLine].GetEol();
743 bool CCrystalTextBuffer::
744 ChangeLineEol (int nLine, LPCTSTR lpEOL)
746 LineInfo & li = m_aLines[nLine];
747 return li.ChangeEol(lpEOL);
750 LPCTSTR CCrystalTextBuffer::
751 GetLineChars (int nLine) const
753 ASSERT (m_bInit); // Text buffer not yet initialized.
754 // You must call InitNew() or LoadFromFile() first!
756 return m_aLines[nLine].GetLine();
759 DWORD CCrystalTextBuffer::
760 GetLineFlags (int nLine) const
762 ASSERT (m_bInit); // Text buffer not yet initialized.
763 // You must call InitNew() or LoadFromFile() first!
765 if (nLine < 0 || nLine >= static_cast<int>(m_aLines.size()))
767 ASSERT(false); // nLine is out of range.
771 return m_aLines[nLine].m_dwFlags;
775 * @brief Get line revision number.
777 * @param nLine Index of the line to get the revision number.
779 DWORD CCrystalTextBuffer::
780 GetLineRevisionNumber (int nLine) const
782 ASSERT (m_bInit); // Text buffer not yet initialized.
783 // You must call InitNew() or LoadFromFile() first!
785 return m_aLines[nLine].m_dwRevisionNumber;
789 FlagToIndex (DWORD dwFlag)
792 while ((dwFlag & 1) == 0)
794 dwFlag = dwFlag >> 1;
799 dwFlag = dwFlag & 0xFFFFFFFE;
806 int CCrystalTextBuffer::
807 FindLineWithFlag (DWORD dwFlag) const
809 const size_t nSize = m_aLines.size();
810 for (size_t L = 0; L < nSize; L++)
812 if ((m_aLines[L].m_dwFlags & dwFlag) != 0)
818 int CCrystalTextBuffer::
819 GetLineWithFlag (DWORD dwFlag) const
821 int nFlagIndex =::FlagToIndex (dwFlag);
824 ASSERT (false); // Invalid flag passed in
828 return FindLineWithFlag (dwFlag);
831 void CCrystalTextBuffer::
832 SetLineFlag (int nLine, DWORD dwFlag, bool bSet, bool bRemoveFromPreviousLine /*= true*/, bool bUpdate /*= true*/)
834 ASSERT (m_bInit); // Text buffer not yet initialized.
835 // You must call InitNew() or LoadFromFile() first!
837 int nFlagIndex =::FlagToIndex (dwFlag);
838 if (nFlagIndex < 0 && (nLine == -1 || bRemoveFromPreviousLine))
840 ASSERT (false); // Invalid flag passed in
848 nLine = FindLineWithFlag (dwFlag);
851 bRemoveFromPreviousLine = false;
854 if (nLine < 0 || nLine >= static_cast<int>(m_aLines.size()))
856 ASSERT(false); // nLine is out of range.
860 DWORD dwNewFlags = m_aLines[nLine].m_dwFlags;
866 dwNewFlags = dwNewFlags | dwFlag;
869 dwNewFlags = dwNewFlags & ~dwFlag;
871 if (m_aLines[nLine].m_dwFlags != dwNewFlags)
873 if (bRemoveFromPreviousLine)
875 int nPrevLine = FindLineWithFlag (dwFlag);
880 ASSERT ((m_aLines[nPrevLine].m_dwFlags & dwFlag) != 0);
881 m_aLines[nPrevLine].m_dwFlags &= ~dwFlag;
883 UpdateViews (nullptr, nullptr, UPDATE_SINGLELINE | UPDATE_FLAGSONLY, nPrevLine);
888 ASSERT (nPrevLine == nLine);
892 m_aLines[nLine].m_dwFlags = dwNewFlags;
894 UpdateViews (nullptr, nullptr, UPDATE_SINGLELINE | UPDATE_FLAGSONLY, nLine);
900 * @brief Get text of specified line range (excluding ghost lines)
902 void CCrystalTextBuffer:: /* virtual base */
903 GetTextWithoutEmptys(int nStartLine, int nStartChar,
904 int nEndLine, int nEndChar,
905 CString &text, CRLFSTYLE nCrlfStyle /*= CRLFSTYLE::AUTOMATIC */,
906 bool bExcludeInvisibleLines/*= true*/) const
908 GetText(nStartLine, nStartChar, nEndLine, nEndChar, text, (nCrlfStyle == CRLFSTYLE::AUTOMATIC) ? nullptr : GetStringEol (nCrlfStyle), bExcludeInvisibleLines);
912 void CCrystalTextBuffer::
913 GetText (int nStartLine, int nStartChar, int nEndLine, int nEndChar,
914 CString & text, LPCTSTR pszCRLF /*= nullptr*/, bool bExcludeInvisibleLines/*= true*/) const
916 ASSERT (m_bInit); // Text buffer not yet initialized.
917 // You must call InitNew() or LoadFromFile() first!
919 ASSERT (m_aLines.size() < INT_MAX);
920 ASSERT (m_aLines[nStartLine].Length() < INT_MAX);
921 ASSERT (nStartLine >= 0 && nStartLine < (int)m_aLines.size ());
922 ASSERT (nStartChar >= 0 && nStartChar <= (int)m_aLines[nStartLine].Length());
923 ASSERT (nEndLine >= 0 && nEndLine < (int)m_aLines.size ());
924 ASSERT (nEndChar >= 0 && nEndChar <= (int)m_aLines[nEndLine].Length());
925 ASSERT (nStartLine < nEndLine || nStartLine == nEndLine && nStartChar <= nEndChar);
926 // some edit functions (copy...) should do nothing when there is no selection.
927 // assert to be sure to catch these 'do nothing' cases.
928 ASSERT (nStartLine != nEndLine || nStartChar != nEndChar);
934 for (int L = nStartLine; L <= nEndLine; L++)
936 nBufSize += m_aLines[L].Length();
937 pszCurCRLF = pszCRLF ? pszCRLF : m_aLines[L].GetEol();
938 pszCurCRLF = pszCurCRLF ? pszCurCRLF : _T("");
939 nCRLFLength = static_cast<int>(_tcslen(pszCurCRLF));
940 nBufSize += nCRLFLength;
943 LPTSTR pszBuf = text.GetBuffer (static_cast<int>(nBufSize));
945 const LineInfo &startLine = m_aLines[nStartLine];
946 if (nStartLine < nEndLine)
948 ptrdiff_t nCount = startLine.Length() - nStartChar;
951 memcpy (pszBuf, startLine.GetLine(nStartChar), sizeof (TCHAR) * nCount);
954 pszCurCRLF = pszCRLF ? pszCRLF : startLine.GetEol();
955 pszCurCRLF = pszCurCRLF ? pszCurCRLF : _T("");
956 nCRLFLength = static_cast<int>(_tcslen(pszCurCRLF));
957 memcpy (pszBuf, pszCurCRLF, sizeof (TCHAR) * nCRLFLength);
958 pszBuf += nCRLFLength;
959 for (int I = nStartLine + 1; I < nEndLine; I++)
961 if (bExcludeInvisibleLines && (GetLineFlags (I) & LF_INVISIBLE))
963 const LineInfo &li = m_aLines[I];
964 nCount = li.Length();
967 memcpy (pszBuf, li.GetLine(), sizeof (TCHAR) * nCount);
970 pszCurCRLF = pszCRLF ? pszCRLF : li.GetEol();
971 pszCurCRLF = pszCurCRLF ? pszCurCRLF : _T("");
972 nCRLFLength = static_cast<int>(_tcslen(pszCurCRLF));
973 memcpy (pszBuf, pszCurCRLF, sizeof (TCHAR) * nCRLFLength);
974 pszBuf += nCRLFLength;
978 memcpy (pszBuf, m_aLines[nEndLine].GetLine(), sizeof (TCHAR) * nEndChar);
984 int nCount = nEndChar - nStartChar;
985 memcpy (pszBuf, startLine.GetLine(nStartChar), sizeof (TCHAR) * nCount);
988 text.ReleaseBuffer ((int) (pszBuf - text));
992 void CCrystalTextBuffer::
993 AddView (CCrystalTextView * pView)
995 m_lpViews.AddTail (pView);
998 void CCrystalTextBuffer::
999 RemoveView (CCrystalTextView * pView)
1001 POSITION pos = m_lpViews.GetHeadPosition ();
1002 while (pos != nullptr)
1004 POSITION thispos = pos;
1005 CCrystalTextView *pvw = m_lpViews.GetNext (pos);
1008 m_lpViews.RemoveAt (thispos);
1015 CrystalLineParser::TextDefinition *CCrystalTextBuffer::
1016 RetypeViews (LPCTSTR lpszFileName)
1018 POSITION pos = m_lpViews.GetHeadPosition ();
1019 CString sNew = GetExt (lpszFileName);
1020 CrystalLineParser::TextDefinition *def = CrystalLineParser::GetTextType (sNew);
1021 while (pos != nullptr)
1023 CCrystalTextView *pView = m_lpViews.GetNext (pos);
1024 pView->SetTextType (def);
1029 void CCrystalTextBuffer::
1030 UpdateViews (CCrystalTextView * pSource, CUpdateContext * pContext, DWORD dwUpdateFlags, int nLineIndex /*= -1*/ )
1032 POSITION pos = m_lpViews.GetHeadPosition ();
1033 while (pos != nullptr)
1035 CCrystalTextView *pView = m_lpViews.GetNext (pos);
1036 pView->UpdateView (pSource, pContext, dwUpdateFlags, nLineIndex);
1041 * @brief Delete text from the buffer.
1042 * @param [in] pSource A view from which the text is deleted.
1043 * @param [in] nStartLine Starting line for the deletion.
1044 * @param [in] nStartChar Starting char position for the deletion.
1045 * @param [in] nEndLine Ending line for the deletion.
1046 * @param [in] nEndChar Ending char position for the deletion.
1047 * @return true if the deletion succeeded, false otherwise.
1048 * @note Line numbers are apparent (screen) line numbers, not real
1049 * line numbers in the file.
1051 bool CCrystalTextBuffer::
1052 InternalDeleteText (CCrystalTextView * pSource, int nStartLine, int nStartChar,
1053 int nEndLine, int nEndChar)
1055 ASSERT (m_bInit); // Text buffer not yet initialized.
1056 // You must call InitNew() or LoadFromFile() first!
1058 ASSERT (m_aLines.size() < INT_MAX);
1059 ASSERT (m_aLines[nStartLine].Length() < INT_MAX);
1060 ASSERT (nStartLine >= 0 && nStartLine < (int)m_aLines.size ());
1061 ASSERT (nStartChar >= 0 && nStartChar <= (int)m_aLines[nStartLine].Length());
1062 ASSERT (nEndLine >= 0 && nEndLine < (int)m_aLines.size ());
1063 ASSERT (nEndChar >= 0 && nEndChar <= (int)m_aLines[nEndLine].FullLength());
1064 ASSERT (nStartLine < nEndLine || nStartLine == nEndLine && nStartChar <= nEndChar);
1065 // some edit functions (delete...) should do nothing when there is no selection.
1066 // assert to be sure to catch these 'do nothing' cases.
1067 // ASSERT (nStartLine != nEndLine || nStartChar != nEndChar);
1071 CDeleteContext context;
1072 context.m_ptStart.y = nStartLine;
1073 context.m_ptStart.x = nStartChar;
1074 context.m_ptEnd.y = nEndLine;
1075 context.m_ptEnd.x = nEndChar;
1076 if (nStartLine == nEndLine)
1078 // delete part of one line
1079 m_aLines[nStartLine].Delete(nStartChar, nEndChar);
1081 if (pSource!=nullptr)
1082 UpdateViews (pSource, &context, UPDATE_SINGLELINE | UPDATE_HORZRANGE, nStartLine);
1086 // delete multiple lines
1087 const ptrdiff_t nRestCount = m_aLines[nEndLine].FullLength() - nEndChar;
1088 CString sTail(m_aLines[nEndLine].GetLine(nEndChar), static_cast<int>(nRestCount));
1089 DWORD dwFlags = GetLineFlags (nEndLine);
1091 const int nDelCount = nEndLine - nStartLine;
1092 for (int L = nStartLine + 1; L <= nEndLine; L++)
1093 m_aLines[L].Clear();
1094 std::vector<LineInfo>::iterator iterBegin = m_aLines.begin() + nStartLine + 1;
1095 std::vector<LineInfo>::iterator iterEnd = iterBegin + nDelCount;
1096 m_aLines.erase(iterBegin, iterEnd);
1098 // nEndLine is no more valid
1099 m_aLines[nStartLine].DeleteEnd(nStartChar);
1102 AppendLine (nStartLine, sTail, sTail.GetLength());
1104 if (nStartChar == 0)
1105 m_aLines[nStartLine].m_dwFlags = dwFlags;
1107 if (pSource!=nullptr)
1108 UpdateViews (pSource, &context, UPDATE_HORZRANGE | UPDATE_VERTRANGE, nStartLine);
1114 // remember current cursor position as last editing position
1115 m_ptLastChange = context.m_ptStart;
1121 // Remove the last [bytes] characters from specified line, and return them
1122 // (EOL characters are included)
1123 CString CCrystalTextBuffer::
1124 StripTail (int i, size_t bytes)
1126 LineInfo & li = m_aLines[i];
1127 // Must at least take off the EOL characters
1128 ASSERT(bytes >= li.FullLength() - li.Length());
1130 const ptrdiff_t offset = li.FullLength() - bytes;
1131 // Must not take off more than exist
1132 ASSERT(offset >= 0);
1134 CString ret(li.GetLine(offset), static_cast<int>(bytes));
1135 li.DeleteEnd(offset);
1141 * @brief Insert text to the buffer.
1142 * @param [in] pSource A view to which the text is added.
1143 * @param [in] nLine Line to add the text.
1144 * @param [in] nPos Position in the line to insert the text.
1145 * @param [in] pszText The text to insert.
1146 * @param [in] cchText The length of the text.
1147 * @param [out] nEndLine Line number of last added line in the buffer.
1148 * @param [out] nEndChar Character position of the end of the added text
1150 * @return true if the insertion succeeded, false otherwise.
1151 * @note Line numbers are apparent (screen) line numbers, not real
1152 * line numbers in the file.
1154 bool CCrystalTextBuffer::
1155 InternalInsertText (CCrystalTextView * pSource, int nLine, int nPos,
1156 LPCTSTR pszText, size_t cchText, int &nEndLine, int &nEndChar)
1158 ASSERT (m_bInit); // Text buffer not yet initialized.
1159 // You must call InitNew() or LoadFromFile() first!
1161 ASSERT (m_aLines.size() < INT_MAX);
1162 ASSERT (m_aLines[nLine].Length() < INT_MAX);
1163 ASSERT (nLine >= 0 && nLine < (int)m_aLines.size ());
1164 ASSERT (nPos >= 0 && nPos <= (int)m_aLines[nLine].Length());
1168 CInsertContext context;
1169 context.m_ptStart.x = nPos;
1170 context.m_ptStart.y = nLine;
1174 int nRestCount = GetFullLineLength(nLine) - nPos;
1178 // remove end of line (we'll put it back on afterwards)
1179 sTail = StripTail(nLine, nRestCount);
1182 bool bInQuote = false;
1183 if (m_bTableEditing && m_bAllowNewlinesInQuotes)
1185 const TCHAR* pszLine = m_aLines[nLine].GetLine ();
1186 for (int j = 0; j < nPos; ++j)
1188 if (pszLine[j] == m_cFieldEnclosure)
1189 bInQuote = !bInQuote;
1193 int nInsertedLines = 0;
1194 int nCurrentLine = nLine;
1198 size_t nTextPos = 0;
1199 // advance to end of line
1200 if (m_bTableEditing && m_bAllowNewlinesInQuotes)
1202 while (nTextPos < cchText && (bInQuote || !LineInfo::IsEol(pszText[nTextPos])))
1204 if (pszText[nTextPos] == m_cFieldEnclosure)
1205 bInQuote = !bInQuote;
1211 while (nTextPos < cchText && !LineInfo::IsEol(pszText[nTextPos]))
1214 // advance after EOL of line
1215 if (nTextPos < cchText)
1218 LPCTSTR eol = &pszText[nTextPos];
1220 if (nTextPos < cchText && LineInfo::IsDosEol(eol))
1224 // The first line of the new text is appended to the start line
1225 // All succeeding lines are inserted
1226 if (nCurrentLine == nLine)
1228 AppendLine (nLine, pszText, nTextPos, !bInQuote);
1232 InsertLine (pszText, nTextPos, nCurrentLine);
1237 if (nTextPos == cchText)
1239 // we just finished our insert
1240 // now we have to reattach the tail
1243 nEndLine = nCurrentLine+1;
1248 nEndLine = nCurrentLine;
1249 nEndChar = GetFullLineLength(nEndLine);
1251 if (!sTail.IsEmpty())
1255 InsertLine(sTail, sTail.GetLength(), nEndLine);
1259 AppendLine (nEndLine, sTail, nRestCount);
1261 if (nEndLine == GetLineCount())
1263 // We left cursor after last screen line
1264 // which is an illegal cursor position
1265 // so manufacture a new trailing line
1266 InsertLine(_T(""), 0);
1273 pszText += nTextPos;
1274 cchText -= nTextPos;
1277 // Compute the context : all positions after context.m_ptStart are
1278 // shifted accordingly to (context.m_ptEnd - context.m_ptStart)
1279 // The begin point is the insertion point.
1280 // The end point is more tedious : if we insert in a ghost line, we reuse it,
1281 // so we insert fewer lines than the number of lines in the text buffer
1282 if (nEndLine - nLine != nInsertedLines)
1284 context.m_ptEnd.y = nLine + nInsertedLines;
1285 context.m_ptEnd.x = GetFullLineLength(context.m_ptEnd.y);
1289 context.m_ptEnd.x = nEndChar;
1290 context.m_ptEnd.y = nEndLine;
1294 if (pSource!=nullptr)
1296 if (nInsertedLines > 0)
1297 UpdateViews (pSource, &context, UPDATE_HORZRANGE | UPDATE_VERTRANGE, nLine);
1299 UpdateViews (pSource, &context, UPDATE_SINGLELINE | UPDATE_HORZRANGE, nLine);
1305 // remember current cursor position as last editing position
1306 m_ptLastChange.x = nEndChar;
1307 m_ptLastChange.y = nEndLine;
1311 bool CCrystalTextBuffer::
1314 ASSERT (m_aUndoBuf.size () < INT_MAX);
1315 ASSERT (m_nUndoPosition >= 0 && m_nUndoPosition <= (int)m_aUndoBuf.size ());
1316 return m_nUndoPosition > 0;
1319 bool CCrystalTextBuffer::
1322 ASSERT (m_aUndoBuf.size () < INT_MAX);
1323 ASSERT (m_nUndoPosition >= 0 && m_nUndoPosition <= (int)m_aUndoBuf.size ());
1324 return m_nUndoPosition < static_cast<int>(m_aUndoBuf.size ());
1327 POSITION CCrystalTextBuffer::
1328 GetUndoActionCode (int & nAction, POSITION pos /*= nullptr*/ ) const
1330 ASSERT (CanUndo ()); // Please call CanUndo() first
1332 ASSERT ((m_aUndoBuf[0].m_dwFlags & UNDO_BEGINGROUP) != 0);
1337 // Start from beginning
1338 nPosition = m_nUndoPosition;
1342 nPosition = reinterpret_cast<intptr_t>(pos);
1343 ASSERT (nPosition > 0 && nPosition < m_nUndoPosition);
1344 ASSERT ((m_aUndoBuf[nPosition].m_dwFlags & UNDO_BEGINGROUP) != 0);
1347 // Advance to next undo group
1349 std::vector<UndoRecord>::const_iterator iter = m_aUndoBuf.begin () + nPosition;
1350 while (((*iter).m_dwFlags & UNDO_BEGINGROUP) == 0)
1357 nAction = (*iter).m_nAction;
1359 // Now, if we stop at zero position, this will be the last action,
1360 // since we return (POSITION) nPosition
1361 return (POSITION) nPosition;
1364 POSITION CCrystalTextBuffer::
1365 GetRedoActionCode (int & nAction, POSITION pos /*= nullptr*/ ) const
1367 ASSERT (CanRedo ()); // Please call CanRedo() before!
1369 ASSERT ((m_aUndoBuf[0].m_dwFlags & UNDO_BEGINGROUP) != 0);
1370 ASSERT ((m_aUndoBuf[m_nUndoPosition].m_dwFlags & UNDO_BEGINGROUP) != 0);
1375 // Start from beginning
1376 nPosition = m_nUndoPosition;
1380 nPosition = reinterpret_cast<intptr_t>(pos);
1381 ASSERT (nPosition > m_nUndoPosition);
1382 ASSERT ((m_aUndoBuf[nPosition].m_dwFlags & UNDO_BEGINGROUP) != 0);
1386 nAction = m_aUndoBuf[nPosition].m_nAction;
1388 // Advance to next undo group
1390 if (nPosition < static_cast<intptr_t>(m_aUndoBuf.size ()))
1392 std::vector<UndoRecord>::const_iterator iter = m_aUndoBuf.begin () + nPosition;
1393 while (iter != m_aUndoBuf.end () && ((*iter).m_dwFlags & UNDO_BEGINGROUP) == 0)
1399 if (nPosition >= static_cast<intptr_t>(m_aUndoBuf.size ()))
1400 return nullptr; // No more redo actions!
1402 return (POSITION) nPosition;
1405 POSITION CCrystalTextBuffer::
1406 GetUndoDescription (CString & desc, POSITION pos /*= nullptr*/ ) const
1409 POSITION retValue = GetUndoActionCode(nAction, pos);
1412 if (!GetActionDescription (nAction, desc))
1413 desc.Empty (); // Use empty string as description
1418 POSITION CCrystalTextBuffer::
1419 GetRedoDescription (CString & desc, POSITION pos /*= nullptr*/ ) const
1422 POSITION retValue = GetRedoActionCode(nAction, pos);
1425 if (!GetActionDescription (nAction, desc))
1426 desc.Empty (); // Use empty string as description
1432 bool CCrystalTextBuffer:: /* virtual base */
1433 UndoInsert(CCrystalTextView * pSource, CPoint & ptCursorPos, const CPoint apparent_ptStartPos, CPoint const apparent_ptEndPos, const UndoRecord & ur)
1435 if (DeleteText (pSource, apparent_ptStartPos.y, apparent_ptStartPos.x, apparent_ptEndPos.y, apparent_ptEndPos.x, 0, false, false))
1437 ptCursorPos = apparent_ptStartPos;
1444 bool CCrystalTextBuffer:: /* virtual base */
1445 Undo (CCrystalTextView * pSource, CPoint & ptCursorPos)
1447 ASSERT (CanUndo ());
1448 ASSERT ((m_aUndoBuf[0].m_dwFlags & UNDO_BEGINGROUP) != 0);
1449 bool failed = false;
1450 int tmpPos = m_nUndoPosition;
1452 // Hide caret to quickly undo changes made by Replace All operation
1453 bool bCursorHiddenSaved = false;
1456 bCursorHiddenSaved = pSource->m_bCursorHidden;
1457 pSource->m_bCursorHidden = true;
1463 const UndoRecord ur = GetUndoRecord(tmpPos);
1464 // Undo records are stored in file line numbers
1465 // and must be converted to apparent (screen) line numbers for use
1466 CPoint apparent_ptStartPos = ur.m_ptStartPos;
1467 CPoint apparent_ptEndPos = ur.m_ptEndPos;
1469 if (ur.m_dwFlags & UNDO_INSERT)
1471 if (!UndoInsert(pSource, ptCursorPos, apparent_ptStartPos, apparent_ptEndPos, ur))
1476 // ptCursorPos = apparent_ptStartPos;
1480 int nEndLine, nEndChar;
1481 if (!InsertText(pSource, apparent_ptStartPos.y, apparent_ptStartPos.x, ur.GetText(), ur.GetTextLength(), nEndLine, nEndChar, 0, false))
1487 ptCursorPos = m_ptLastChange;
1490 // restore line revision numbers
1491 RestoreRevisionNumbers(ur.m_ptStartPos.y, ur.m_paSavedRevisionNumbers);
1493 if (ur.m_dwFlags & UNDO_BEGINGROUP)
1496 if (m_bModified && m_nSyncPosition == tmpPos)
1497 SetModified (false);
1498 if (!m_bModified && m_nSyncPosition != tmpPos)
1502 // If the Undo failed, clear the entire Undo/Redo stack
1503 // Not only can we not Redo the failed Undo, but the Undo
1504 // may have partially completed (if in a group)
1505 m_nUndoPosition = 0;
1506 m_aUndoBuf.clear ();
1510 m_nUndoPosition = tmpPos;
1515 pSource->m_bCursorHidden = bCursorHiddenSaved;
1516 pSource->UpdateCaret();
1522 bool CCrystalTextBuffer:: /* virtual base */
1523 Redo (CCrystalTextView * pSource, CPoint & ptCursorPos)
1525 ASSERT (CanRedo ());
1526 ASSERT ((m_aUndoBuf[0].m_dwFlags & UNDO_BEGINGROUP) != 0);
1527 ASSERT ((m_aUndoBuf[m_nUndoPosition].m_dwFlags & UNDO_BEGINGROUP) != 0);
1529 // Hide caret to quickly redo changes made by Replace All operation
1530 bool bCursorHiddenSaved = false;
1533 bCursorHiddenSaved = pSource->m_bCursorHidden;
1534 pSource->m_bCursorHidden = true;
1539 const UndoRecord ur = GetUndoRecord(m_nUndoPosition);
1540 CPoint apparent_ptStartPos = ur.m_ptStartPos;
1541 CPoint apparent_ptEndPos = ur.m_ptEndPos;
1543 // now we can use normal insertTxt or deleteText
1544 if (ur.m_dwFlags & UNDO_INSERT)
1546 int nEndLine, nEndChar;
1547 VERIFY(InsertText (pSource, apparent_ptStartPos.y, apparent_ptStartPos.x,
1548 ur.GetText(), ur.GetTextLength(), nEndLine, nEndChar, 0, false));
1549 ptCursorPos = m_ptLastChange;
1553 if (apparent_ptStartPos != apparent_ptEndPos)
1557 GetTextWithoutEmptys (apparent_ptStartPos.y, apparent_ptStartPos.x, apparent_ptEndPos.y, apparent_ptEndPos.x, text, CRLFSTYLE::AUTOMATIC, false);
1558 ASSERT (static_cast<size_t>(text.GetLength()) == ur.GetTextLength() && memcmp(text, ur.GetText(), text.GetLength() * sizeof(TCHAR)) == 0);
1560 VERIFY(DeleteText(pSource, apparent_ptStartPos.y, apparent_ptStartPos.x,
1561 apparent_ptEndPos.y, apparent_ptEndPos.x, 0, false, false));
1563 ptCursorPos = apparent_ptStartPos;
1566 if (static_cast<size_t>(m_nUndoPosition) == m_aUndoBuf.size())
1568 if ((m_aUndoBuf[m_nUndoPosition].m_dwFlags & UNDO_BEGINGROUP) != 0)
1572 if (m_bModified && m_nSyncPosition == m_nUndoPosition)
1573 SetModified (false);
1574 if (!m_bModified && m_nSyncPosition != m_nUndoPosition)
1579 pSource->m_bCursorHidden = bCursorHiddenSaved;
1580 pSource->UpdateCaret();
1586 // the CPoint parameters are apparent (on screen) line numbers
1588 void CCrystalTextBuffer:: /* virtual base */
1589 AddUndoRecord (bool bInsert, const CPoint & ptStartPos,
1590 const CPoint & ptEndPos, LPCTSTR pszText, size_t cchText, int nActionType /*= CE_ACTION_UNKNOWN*/,
1591 CDWordArray *paSavedRevisionNumbers /*= nullptr*/)
1593 // Forgot to call BeginUndoGroup()?
1594 ASSERT (m_bUndoGroup);
1595 ASSERT (m_aUndoBuf.size () == 0 || (m_aUndoBuf[0].m_dwFlags & UNDO_BEGINGROUP) != 0);
1597 // Strip unnecessary undo records (edit after undo wipes all potential redo records)
1598 int nBufSize = (int) m_aUndoBuf.size ();
1599 if (m_nUndoPosition < nBufSize)
1601 m_aUndoBuf.resize (m_nUndoPosition);
1606 ur.m_dwFlags = bInsert ? UNDO_INSERT : 0;
1607 ur.m_nAction = nActionType;
1608 if (m_bUndoBeginGroup)
1610 ur.m_dwFlags |= UNDO_BEGINGROUP;
1611 m_bUndoBeginGroup = false;
1613 ur.m_ptStartPos = ptStartPos;
1614 ur.m_ptEndPos = ptEndPos;
1615 ur.SetText (pszText, cchText);
1616 ur.m_paSavedRevisionNumbers = paSavedRevisionNumbers;
1618 // Optimize memory allocation
1619 if (m_aUndoBuf.capacity() == m_aUndoBuf.size())
1621 if (m_aUndoBuf.size() == 0)
1622 m_aUndoBuf.reserve(16);
1623 else if (m_aUndoBuf.size() < 1025)
1624 m_aUndoBuf.reserve(m_aUndoBuf.size() * 2);
1626 m_aUndoBuf.reserve(m_aUndoBuf.size() + 1024);
1628 m_aUndoBuf.push_back (ur);
1629 m_nUndoPosition = (int) m_aUndoBuf.size ();
1634 * @brief Get EOL style string.
1635 * @param [in] nCRLFMode.
1636 * @return string of CRLF style.
1638 LPCTSTR CCrystalTextBuffer::GetStringEol(CRLFSTYLE nCRLFMode)
1642 case CRLFSTYLE::DOS: return _T("\r\n");
1643 case CRLFSTYLE::UNIX: return _T("\n");
1644 case CRLFSTYLE::MAC: return _T("\r");
1645 // If mixed or not defined
1646 default: return _T("\r\n");
1650 LPCTSTR CCrystalTextBuffer::GetDefaultEol() const
1652 return GetStringEol(m_nCRLFMode);
1656 * @brief Insert text to the buffer.
1657 * @param [in] pSource A view to which the text is added.
1658 * @param [in] nLine Line to add the text.
1659 * @param [in] nPos Position in the line to insert the text.
1660 * @param [in] pszText The text to insert.
1661 * @param [in] cchText The length of the text.
1662 * @param [out] nEndLine Line number of last added line in the buffer.
1663 * @param [out] nEndChar Character position of the end of the added text
1665 * @param [in] nAction Edit action.
1666 * @param [in] bHistory Save insertion for undo/redo?
1667 * @return true if the insertion succeeded, false otherwise.
1668 * @note Line numbers are apparent (screen) line numbers, not real
1669 * line numbers in the file.
1671 bool CCrystalTextBuffer:: /* virtual base */
1672 InsertText (CCrystalTextView * pSource, int nLine, int nPos, LPCTSTR pszText,
1673 size_t cchText, int &nEndLine, int &nEndChar, int nAction,
1674 bool bHistory /*= true*/)
1676 // save line revision numbers for undo
1677 CDWordArray *paSavedRevisionNumbers = new CDWordArray;
1678 paSavedRevisionNumbers->SetSize(1);
1679 (*paSavedRevisionNumbers)[0] = m_aLines[nLine].m_dwRevisionNumber;
1681 if (!InternalInsertText (pSource, nLine, nPos, pszText, cchText, nEndLine, nEndChar))
1683 delete paSavedRevisionNumbers;
1687 // update line revision numbers of modified lines
1688 m_dwCurrentRevisionNumber++;
1689 for (int i = nLine ; i < nEndLine; i++)
1690 m_aLines[i].m_dwRevisionNumber = m_dwCurrentRevisionNumber;
1691 if (nPos != 0 || nEndChar != 0)
1692 m_aLines[nEndLine].m_dwRevisionNumber = m_dwCurrentRevisionNumber;
1696 delete paSavedRevisionNumbers;
1700 bool bGroupFlag = false;
1707 AddUndoRecord (true, CPoint (nPos, nLine), CPoint (nEndChar, nEndLine),
1708 pszText, cchText, nAction, paSavedRevisionNumbers);
1711 FlushUndoGroup (pSource);
1717 * @brief Delete text from the buffer.
1718 * @param [in] pSource A view from which the text is deleted.
1719 * @param [in] nStartLine Starting line for the deletion.
1720 * @param [in] nStartChar Starting char position for the deletion.
1721 * @param [in] nEndLine Ending line for the deletion.
1722 * @param [in] nEndChar Ending char position for the deletion.
1723 * @param [in] nAction Edit action.
1724 * @param [in] bHistory Save deletion for undo/redo?
1725 * @param [in] bExcludeInvisibleLines Don't delete LF_INVISIBLE lines
1726 * @return true if the deletion succeeded, false otherwise.
1727 * @note Line numbers are apparent (screen) line numbers, not real
1728 * line numbers in the file.
1730 bool CCrystalTextBuffer:: /* virtual base */
1731 DeleteText (CCrystalTextView * pSource, int nStartLine, int nStartChar,
1732 int nEndLine, int nEndChar, int nAction, bool bHistory /*= true*/, bool bExcludeInvisibleLines /*= true*/)
1734 bool bGroupFlag = false;
1743 if (bExcludeInvisibleLines && pSource != nullptr && pSource->GetEnableHideLines ())
1745 for (int nLineIndex = nEndLine; nLineIndex >= nStartLine; nLineIndex--)
1747 if (!(GetLineFlags (nLineIndex) & LF_INVISIBLE))
1749 int nEndLine2 = nLineIndex;
1751 for (nStartLine2 = nLineIndex - 1; nStartLine2 >= nStartLine; nStartLine2--)
1753 if (GetLineFlags (nStartLine2) & LF_INVISIBLE)
1757 nLineIndex = nStartLine2;
1758 int nStartChar2 = (nStartLine == nStartLine2) ? nStartChar : 0;
1760 if (nEndLine == nEndLine2)
1761 nEndChar2 = nEndChar;
1767 if (!DeleteText2 (pSource, nStartLine2, nStartChar2, nEndLine2, nEndChar2, nAction, bHistory))
1774 if (!DeleteText2 (pSource, nStartLine, nStartChar, nEndLine, nEndChar, nAction, bHistory))
1779 FlushUndoGroup (pSource);
1784 CDWordArray *CCrystalTextBuffer::
1785 CopyRevisionNumbers(int nStartLine, int nEndLine) const
1787 // save line revision numbers for undo
1788 CDWordArray *paSavedRevisionNumbers = new CDWordArray;
1789 paSavedRevisionNumbers->SetSize(nEndLine - nStartLine + 1);
1790 for (int i = 0; i < nEndLine - nStartLine + 1; i++)
1791 (*paSavedRevisionNumbers)[i] = m_aLines[nStartLine + i].m_dwRevisionNumber;
1792 return paSavedRevisionNumbers;
1795 void CCrystalTextBuffer::
1796 RestoreRevisionNumbers(int nStartLine, CDWordArray *paSavedRevisionNumbers)
1798 for (int i = 0; i < paSavedRevisionNumbers->GetSize(); i++)
1799 m_aLines[nStartLine + i].m_dwRevisionNumber = (*paSavedRevisionNumbers)[i];
1802 bool CCrystalTextBuffer:: /* virtual base */
1803 DeleteText2 (CCrystalTextView * pSource, int nStartLine, int nStartChar,
1804 int nEndLine, int nEndChar, int nAction /* = CE_ACTION_UNKNOWN*/, bool bHistory /*= true*/)
1806 CString sTextToDelete;
1807 GetTextWithoutEmptys (nStartLine, nStartChar, nEndLine, nEndChar, sTextToDelete);
1809 // save line revision numbers for undo
1810 CDWordArray *paSavedRevisionNumbers = CopyRevisionNumbers(nStartLine, nEndLine);
1812 if (!InternalDeleteText (pSource, nStartLine, nStartChar, nEndLine, nEndChar))
1814 delete paSavedRevisionNumbers;
1818 // update line revision numbers of modified lines
1819 m_dwCurrentRevisionNumber++;
1820 m_aLines[nStartLine].m_dwRevisionNumber = m_dwCurrentRevisionNumber;
1824 delete paSavedRevisionNumbers;
1828 AddUndoRecord (false, CPoint (nStartChar, nStartLine), CPoint (nEndChar, nEndLine),
1829 sTextToDelete, sTextToDelete.GetLength(), nAction, paSavedRevisionNumbers);
1834 bool CCrystalTextBuffer::
1835 GetActionDescription (int nAction, CString & desc) const
1837 HINSTANCE hOldResHandle = AfxGetResourceHandle ();
1838 #ifdef CRYSEDIT_RES_HANDLE
1839 AfxSetResourceHandle (CRYSEDIT_RES_HANDLE);
1841 if (CCrystalTextView::s_hResourceInst != nullptr)
1842 AfxSetResourceHandle (CCrystalTextView::s_hResourceInst);
1844 bool bSuccess = false;
1847 case CE_ACTION_PASTE:
1848 bSuccess = !!desc.LoadString (IDS_EDITOP_PASTE);
1850 case CE_ACTION_DELSEL:
1851 bSuccess = !!desc.LoadString (IDS_EDITOP_DELSELECTION);
1854 bSuccess = !!desc.LoadString (IDS_EDITOP_CUT);
1856 case CE_ACTION_TYPING:
1857 bSuccess = !!desc.LoadString (IDS_EDITOP_TYPING);
1859 case CE_ACTION_BACKSPACE:
1860 bSuccess = !!desc.LoadString (IDS_EDITOP_BACKSPACE);
1862 case CE_ACTION_INDENT:
1863 bSuccess = !!desc.LoadString (IDS_EDITOP_INDENT);
1865 case CE_ACTION_DRAGDROP:
1866 bSuccess = !!desc.LoadString (IDS_EDITOP_DRAGDROP);
1868 case CE_ACTION_REPLACE:
1869 bSuccess = !!desc.LoadString (IDS_EDITOP_REPLACE);
1871 case CE_ACTION_DELETE:
1872 bSuccess = !!desc.LoadString (IDS_EDITOP_DELETE);
1874 case CE_ACTION_AUTOINDENT:
1875 bSuccess = !!desc.LoadString (IDS_EDITOP_AUTOINDENT);
1877 case CE_ACTION_AUTOCOMPLETE:
1878 bSuccess = !!desc.LoadString (IDS_EDITOP_AUTOCOMPLETE);
1880 case CE_ACTION_AUTOEXPAND:
1881 bSuccess = !!desc.LoadString (IDS_EDITOP_AUTOEXPAND);
1883 case CE_ACTION_LOWERCASE:
1884 bSuccess = !!desc.LoadString (IDS_EDITOP_LOWERCASE);
1886 case CE_ACTION_UPPERCASE:
1887 bSuccess = !!desc.LoadString (IDS_EDITOP_UPPERCASE);
1889 case CE_ACTION_SWAPCASE:
1890 bSuccess = !!desc.LoadString (IDS_EDITOP_SWAPCASE);
1892 case CE_ACTION_CAPITALIZE:
1893 bSuccess = !!desc.LoadString (IDS_EDITOP_CAPITALIZE);
1895 case CE_ACTION_SENTENCIZE:
1896 bSuccess = !!desc.LoadString (IDS_EDITOP_SENTENCIZE);
1898 case CE_ACTION_RECODE:
1899 bSuccess = !!desc.LoadString (IDS_EDITOP_RECODE);
1901 case CE_ACTION_SPELL:
1902 bSuccess = !!desc.LoadString (IDS_EDITOP_SPELL);
1904 default: /* case CE_ACTION_UNKNOWN: */
1905 bSuccess = !!desc.LoadString (IDS_EDITOP_UNKNOWN);
1907 AfxSetResourceHandle (hOldResHandle);
1911 void CCrystalTextBuffer:: /* virtual base */
1912 SetModified (bool bModified /*= true*/ )
1914 m_bModified = bModified;
1917 void CCrystalTextBuffer::
1918 BeginUndoGroup (bool bMergeWithPrevious /*= false*/ )
1920 ASSERT (!m_bUndoGroup);
1921 m_bUndoGroup = true;
1922 m_bUndoBeginGroup = m_nUndoPosition == 0 || !bMergeWithPrevious;
1925 void CCrystalTextBuffer::
1926 FlushUndoGroup (CCrystalTextView * pSource)
1928 ASSERT (m_bUndoGroup);
1929 if (pSource != nullptr)
1931 ASSERT (static_cast<size_t>(m_nUndoPosition) <= m_aUndoBuf.size());
1932 if (m_nUndoPosition > 0)
1934 pSource->OnEditOperation (m_aUndoBuf[m_nUndoPosition - 1].m_nAction, m_aUndoBuf[m_nUndoPosition - 1].GetText (), m_aUndoBuf[m_nUndoPosition - 1].GetTextLength ());
1937 m_bUndoGroup = false;
1940 int CCrystalTextBuffer::
1941 FindNextBookmarkLine (int nCurrentLine) const
1943 bool bWrapIt = true;
1944 DWORD dwFlags = GetLineFlags (nCurrentLine);
1945 if ((dwFlags & LF_BOOKMARKS) != 0)
1948 const size_t nSize = m_aLines.size ();
1951 while (nCurrentLine < static_cast<int>(nSize))
1953 if ((m_aLines[nCurrentLine].m_dwFlags & LF_BOOKMARKS) != 0)
1954 return nCurrentLine;
1958 // End of text reached
1962 // Start from the beginning of text
1969 int CCrystalTextBuffer::
1970 FindPrevBookmarkLine (int nCurrentLine) const
1972 bool bWrapIt = true;
1973 DWORD dwFlags = GetLineFlags (nCurrentLine);
1974 if ((dwFlags & LF_BOOKMARKS) != 0)
1977 const size_t nSize = m_aLines.size ();
1980 while (nCurrentLine >= 0)
1982 if ((m_aLines[nCurrentLine].m_dwFlags & LF_BOOKMARKS) != 0)
1983 return nCurrentLine;
1987 // Beginning of text reached
1991 // Start from the end of text
1993 nCurrentLine = (int) (nSize - 1);
1999 CPoint CCrystalTextBuffer::GetLastChangePos() const
2001 return m_ptLastChange;
2004 void CCrystalTextBuffer::RestoreLastChangePos(CPoint pt)
2006 m_ptLastChange = pt;
2011 * @brief Delete one or several lines
2013 void CCrystalTextBuffer::DeleteLine(int line, int nCount /*=1*/)
2015 for (int ic = 0; ic < nCount; ic++)
2016 m_aLines[line + ic].Clear();
2017 std::vector<LineInfo>::iterator iterBegin = m_aLines.begin() + line;
2018 std::vector<LineInfo>::iterator iterEnd = iterBegin + nCount;
2019 m_aLines.erase(iterBegin, iterEnd);
2022 int CCrystalTextBuffer::GetTabSize() const
2024 ASSERT( m_nTabSize >= 0 && m_nTabSize <= 64 );
2028 void CCrystalTextBuffer::SetTabSize(int nTabSize)
2030 ASSERT( nTabSize >= 0 && nTabSize <= 64 );
2031 m_nTabSize = nTabSize;
2034 int CCrystalTextBuffer::GetColumnWidth (int nColumnIndex) const
2036 ASSERT( nColumnIndex >= 0 );
2037 if (nColumnIndex < static_cast<int>(m_pSharedTableProps->m_aColumnWidths.size ()))
2038 return m_pSharedTableProps->m_aColumnWidths[nColumnIndex];
2043 void CCrystalTextBuffer::SetColumnWidth (int nColumnIndex, int nColumnWidth)
2045 ASSERT( nColumnIndex >= 0 );
2046 ASSERT( nColumnWidth >= 0 );
2047 if (nColumnIndex >= static_cast<int>(m_pSharedTableProps->m_aColumnWidths.size ()))
2048 m_pSharedTableProps->m_aColumnWidths.resize (nColumnIndex + 1, m_nTabSize);
2049 m_pSharedTableProps->m_aColumnWidths[nColumnIndex] = nColumnWidth;
2052 int CCrystalTextBuffer::GetColumnCount (int nLineIndex) const
2054 ASSERT( nLineIndex >= 0 );
2055 int nColumnCount = 0;
2056 const TCHAR* pszLine = GetLineChars (nLineIndex);
2057 int nLength = GetLineLength (nLineIndex);
2058 bool bInQuote = false;
2059 for (int j = 0; j < nLength; ++j)
2061 if (pszLine[j] == m_cFieldEnclosure)
2062 bInQuote = !bInQuote;
2063 else if (!bInQuote && pszLine[j] == m_cFieldDelimiter)
2066 return nColumnCount;
2069 void CCrystalTextBuffer::JoinLinesForTableEditingMode ()
2071 if (!m_bAllowNewlinesInQuotes)
2073 size_t nLineCount = m_aLines.size ();
2075 bool bInQuote = false;
2076 for (size_t i = 0; i < nLineCount;)
2078 const TCHAR* pszChars = m_aLines[i].GetLine ();
2079 const size_t nLineLength = m_aLines[i].FullLength ();
2080 for (; j < nLineLength; ++j)
2082 if (pszChars[j] == m_cFieldEnclosure)
2083 bInQuote = !bInQuote;
2085 m_aLines[i].m_dwRevisionNumber = 0;
2086 if (bInQuote && i < nLineCount - 1)
2088 std::basic_string<TCHAR> line(m_aLines[i].GetLine (), m_aLines[i].FullLength ());
2089 line.append (m_aLines[i + 1].GetLine (), m_aLines[i + 1].FullLength ());
2090 m_aLines[i].FreeBuffer ();
2091 m_aLines[i].Create (line.c_str (), line.size ());
2092 m_aLines[i + 1].FreeBuffer ();
2093 m_aLines.erase (m_aLines.begin () + i + 1);
2102 m_nUndoPosition = 0;
2103 m_bModified = false;
2106 void CCrystalTextBuffer::SplitLinesForTableEditingMode ()
2108 size_t nLineCount = m_aLines.size ();
2109 for (size_t i = 0; i < nLineCount; ++i)
2111 const TCHAR* pszChars = m_aLines[i].GetLine ();
2112 const size_t nLineLength = m_aLines[i].FullLength ();
2113 for (size_t j = 0; j < nLineLength; ++j)
2116 if (pszChars[j] == '\r')
2117 eols = (j < nLineLength - 1 && pszChars[j + 1] == '\n') ? 2 : 1;
2118 else if (pszChars[j] == '\n')
2123 lineInfo.Create (pszChars + j + eols, nLineLength - (j + eols));
2124 m_aLines.insert (m_aLines.begin () + i + 1, lineInfo);
2125 m_aLines[i].DeleteEnd (j + eols);
2126 m_aLines[i].m_dwRevisionNumber = 0;
2130 m_aUndoBuf.clear ();
2131 m_nUndoPosition = 0;
2132 m_bModified = false;
2135 void CCrystalTextBuffer::
2136 InvalidateColumns ()
2138 for (auto& buf : m_pSharedTableProps->m_textBufferList)
2140 POSITION pos = buf->m_lpViews.GetHeadPosition ();
2141 while (pos != nullptr)
2143 POSITION thispos = pos;
2144 CCrystalTextView* pView = buf->m_lpViews.GetNext (pos);
2145 pView->InvalidateScreenRect ();
2146 pView->UpdateView (nullptr, nullptr, UPDATE_HORZRANGE | UPDATE_VERTRANGE, -1);