📄 ccrystaltextbuffer.cpp
字号:
////////////////////////////////////////////////////////////////////////////
// File: ccrystaltextbuffer.cpp
// Version: 1.0.0.0
// Created: 29-Dec-1998
//
// Author: Stcherbatchenko Andrei
// E-mail: windfall@gmx.de
//
// Implementation of the CCrystalTextBuffer class, a part of Crystal Edit -
// syntax coloring text editor.
//
// You are free to use or modify this code to the following restrictions:
// - Acknowledge me somewhere in your about box, simple "Parts of code by.."
// will be enough. If you can't (or don't want to), contact me personally.
// - LEAVE THIS HEADER INTACT
////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////
// 17-Feb-99
// + FIX: unnecessary 'HANDLE' in CCrystalTextBuffer::SaveToFile
////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////
// 21-Feb-99
// Paul Selormey, James R. Twine:
// + FEATURE: description for Undo/Redo actions
// + FEATURE: multiple MSVC-like bookmarks
// + FEATURE: 'Disable backspace at beginning of line' option
// + FEATURE: 'Disable drag-n-drop editing' option
//
// + FEATURE: changed layout of SUndoRecord. Now takes less memory
////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////
// 19-Jul-99
// Ferdinand Prantl:
// + FEATURE: some other things I've forgotten ...
//
// ... it's being edited very rapidly so sorry for non-commented
// and maybe "ugly" code ...
////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////
// ??-Aug-99
// Sven Wiegand (search for "//BEGIN SW" to find my changes):
// + FEATURE: Remembering the text-position of the latest change.
////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////
// 24-Oct-99
// Sven Wiegand
// + FIX: Setting m_ptLastChange to the beginning of the selection in
// InternalDeleteText(), so that position is valid in any case.
// Editor won't crash any more i.e. by selecting whole buffer and
// deleting it and then executing ID_EDIT_GOTO_LAST_CHANGE-command.
////////////////////////////////////////////////////////////////////////////
#include "stdafx.h"
#include <malloc.h>
#include "editcmd.h"
#include "ccrystaltextbuffer.h"
#include "ccrystaltextview.h"
#include "filesup.h"
#include "cs2cs.h"
#ifndef __AFXPRIV_H__
#pragma message("Include <afxpriv.h> in your stdafx.h to avoid this message")
#include <afxpriv.h>
#endif
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
// Line allocation granularity
#define CHAR_ALIGN 16
#define ALIGN_BUF_SIZE(size) ((size) / CHAR_ALIGN) * CHAR_ALIGN + CHAR_ALIGN;
#define UNDO_BUF_SIZE 1024
const TCHAR crlf[] = _T ("\r\n");
#ifdef _DEBUG
#define _ADVANCED_BUGCHECK 1
#endif
int CCrystalTextBuffer::m_nDefaultEncoding = -1;
/////////////////////////////////////////////////////////////////////////////
// CCrystalTextBuffer::SUndoRecord
void CCrystalTextBuffer::SUndoRecord::
SetText (LPCTSTR pszText)
{
m_pszText = NULL;
if (pszText != NULL && pszText[0] != _T ('\0'))
{
int nLength = _tcslen (pszText);
if (nLength > 1)
{
m_pszText = new TCHAR[(nLength + 1) * sizeof (TCHAR)];
_tcscpy (m_pszText, pszText);
}
else
{
m_szText[0] = pszText[0];
}
}
}
void CCrystalTextBuffer::SUndoRecord::
FreeText ()
{
if (HIWORD ((DWORD) m_pszText) != 0)
delete m_pszText;
}
/////////////////////////////////////////////////////////////////////////////
// CCrystalTextBuffer::CUpdateContext
void CCrystalTextBuffer::CInsertContext::
RecalcPoint (CPoint & ptPoint)
{
ASSERT (m_ptEnd.y > m_ptStart.y ||
m_ptEnd.y == m_ptStart.y && m_ptEnd.x >= m_ptStart.x);
if (ptPoint.y < m_ptStart.y)
return;
if (ptPoint.y > m_ptStart.y)
{
ptPoint.y += (m_ptEnd.y - m_ptStart.y);
return;
}
if (ptPoint.x <= m_ptStart.x)
return;
ptPoint.y += (m_ptEnd.y - m_ptStart.y);
ptPoint.x = m_ptEnd.x + (ptPoint.x - m_ptStart.x);
}
void CCrystalTextBuffer::CDeleteContext::
RecalcPoint (CPoint & ptPoint)
{
ASSERT (m_ptEnd.y > m_ptStart.y ||
m_ptEnd.y == m_ptStart.y && m_ptEnd.x >= m_ptStart.x);
if (ptPoint.y < m_ptStart.y)
return;
if (ptPoint.y > m_ptEnd.y)
{
ptPoint.y -= (m_ptEnd.y - m_ptStart.y);
return;
}
if (ptPoint.y == m_ptEnd.y && ptPoint.x >= m_ptEnd.x)
{
ptPoint.y = m_ptStart.y;
ptPoint.x = m_ptStart.x + (ptPoint.x - m_ptEnd.x);
return;
}
if (ptPoint.y == m_ptStart.y)
{
if (ptPoint.x > m_ptStart.x)
ptPoint.x = m_ptStart.x;
return;
}
ptPoint = m_ptStart;
}
/////////////////////////////////////////////////////////////////////////////
// CCrystalTextBuffer
IMPLEMENT_DYNCREATE (CCrystalTextBuffer, CCmdTarget)
CCrystalTextBuffer::CCrystalTextBuffer ()
{
m_bInit = FALSE;
m_bReadOnly = FALSE;
m_bModified = FALSE;
m_bCreateBackupFile = FALSE;
m_nUndoPosition = 0;
//BEGIN SW
m_ptLastChange.x = m_ptLastChange.y = -1;
//END SW
m_nSourceEncoding = m_nDefaultEncoding;
}
CCrystalTextBuffer:: ~ CCrystalTextBuffer ()
{
ASSERT (!m_bInit); // You must call FreeAll() before deleting the object
}
BEGIN_MESSAGE_MAP (CCrystalTextBuffer, CCmdTarget)
//{{AFX_MSG_MAP(CCrystalTextBuffer)
//}}AFX_MSG_MAP
END_MESSAGE_MAP ()
/////////////////////////////////////////////////////////////////////////////
// CCrystalTextBuffer message handlers
void CCrystalTextBuffer::InsertLine (LPCTSTR pszLine, int nLength /*= -1*/ , int nPosition /*= -1*/ )
{
if (nLength == -1)
{
if (pszLine == NULL)
nLength = 0;
else
nLength = _tcslen (pszLine);
}
SLineInfo li;
li.m_nLength = nLength;
li.m_nMax = ALIGN_BUF_SIZE (li.m_nLength);
ASSERT (li.m_nMax >= li.m_nLength);
if (li.m_nMax > 0)
li.m_pcLine = new TCHAR[li.m_nMax];
if (li.m_nLength > 0)
memcpy (li.m_pcLine, pszLine, sizeof (TCHAR) * li.m_nLength);
if (nPosition == -1)
m_aLines.Add (li);
else
m_aLines.InsertAt (nPosition, li);
#ifdef _DEBUG
int nLines = m_aLines.GetSize ();
if (nLines % 5000 == 0)
TRACE1 ("%d lines loaded!\n", nLines);
#endif
}
void CCrystalTextBuffer::
AppendLine (int nLineIndex, LPCTSTR pszChars, int nLength /*= -1*/ )
{
if (nLength == -1)
{
if (pszChars == NULL)
return;
nLength = _tcslen (pszChars);
}
if (nLength == 0)
return;
register SLineInfo & li = m_aLines[nLineIndex];
int nBufNeeded = li.m_nLength + nLength;
if (nBufNeeded > li.m_nMax)
{
li.m_nMax = ALIGN_BUF_SIZE (nBufNeeded);
ASSERT (li.m_nMax >= li.m_nLength + nLength);
TCHAR *pcNewBuf = new TCHAR[li.m_nMax];
if (li.m_nLength > 0)
memcpy (pcNewBuf, li.m_pcLine, sizeof (TCHAR) * li.m_nLength);
delete li.m_pcLine;
li.m_pcLine = pcNewBuf;
}
memcpy (li.m_pcLine + li.m_nLength, pszChars, sizeof (TCHAR) * nLength);
li.m_nLength += nLength;
ASSERT (li.m_nLength <= li.m_nMax);
}
void CCrystalTextBuffer::
FreeAll ()
{
// Free text
int nCount = m_aLines.GetSize ();
for (int I = 0; I < nCount; I++)
{
if (m_aLines[I].m_nMax > 0)
delete m_aLines[I].m_pcLine;
}
m_aLines.RemoveAll ();
// Free undo buffer
int nBufSize = m_aUndoBuf.GetSize ();
for (I = 0; I < nBufSize; I++)
m_aUndoBuf[I].FreeText ();
m_aUndoBuf.RemoveAll ();
m_bInit = FALSE;
//BEGIN SW
m_ptLastChange.x = m_ptLastChange.y = -1;
//END SW
}
BOOL CCrystalTextBuffer::
InitNew (int nCrlfStyle /*= CRLF_STYLE_DOS*/ )
{
ASSERT (!m_bInit);
ASSERT (m_aLines.GetSize () == 0);
ASSERT (nCrlfStyle >= 0 && nCrlfStyle <= 2);
InsertLine (_T (""));
m_bInit = TRUE;
m_bReadOnly = FALSE;
m_nCRLFMode = nCrlfStyle;
m_bModified = FALSE;
m_nSyncPosition = m_nUndoPosition = 0;
m_bUndoGroup = m_bUndoBeginGroup = FALSE;
m_nUndoBufSize = UNDO_BUF_SIZE;
ASSERT (m_aUndoBuf.GetSize () == 0);
UpdateViews (NULL, NULL, UPDATE_RESET);
//BEGIN SW
m_ptLastChange.x = m_ptLastChange.y = -1;
//END SW
return TRUE;
}
BOOL CCrystalTextBuffer::
GetReadOnly ()
const
{
ASSERT (m_bInit); // Text buffer not yet initialized.
// You must call InitNew() or LoadFromFile() first!
return m_bReadOnly;
}
void CCrystalTextBuffer::SetReadOnly (BOOL bReadOnly /*= TRUE*/ )
{
ASSERT (m_bInit); // Text buffer not yet initialized.
// You must call InitNew() or LoadFromFile() first!
m_bReadOnly = bReadOnly;
}
static LPCTSTR crlfs[] =
{
_T ("\x0d\x0a"), // DOS/Windows style
_T ("\x0a"), // UNIX style
_T ("\x0a") // Macintosh style
};
BOOL CCrystalTextBuffer::
LoadFromFile (LPCTSTR pszFileName, int nCrlfStyle /*= CRLF_STYLE_AUTOMATIC*/ )
{
ASSERT (!m_bInit);
ASSERT (m_aLines.GetSize () == 0);
HANDLE hFile = NULL;
int nCurrentMax = 256;
LPTSTR pcLineBuf = new TCHAR[nCurrentMax];
BOOL bSuccess = FALSE;
int nExt = GetExtPosition (pszFileName);
if (pszFileName[nExt] == _T ('.'))
nExt++;
CCrystalTextView::TextDefinition *def = CCrystalTextView::GetTextType (pszFileName + nExt);
if (def && def->encoding != -1)
m_nSourceEncoding = def->encoding;
__try
{
DWORD dwFileAttributes =::GetFileAttributes (pszFileName);
if (dwFileAttributes == (DWORD) - 1)
__leave;
hFile =::CreateFile (pszFileName, GENERIC_READ, FILE_SHARE_READ, NULL,
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL);
if (hFile == INVALID_HANDLE_VALUE)
__leave;
int nCurrentLength = 0;
const DWORD dwBufSize = 32768;
LPTSTR pcBuf = (LPTSTR) _alloca (dwBufSize);
DWORD dwCurSize;
if (!::ReadFile (hFile, pcBuf, dwBufSize, &dwCurSize, NULL))
__leave;
if (nCrlfStyle == CRLF_STYLE_AUTOMATIC)
{
// Try to determine current CRLF mode
for (DWORD I = 0; I < dwCurSize; I++)
{
if ((pcBuf[I] == _T('\x0d')) || (pcBuf[I] == _T('\x0a')))
break;
}
if (I == dwCurSize)
{
// By default (or in the case of empty file), set DOS style
nCrlfStyle = CRLF_STYLE_DOS;
}
else
{
// Otherwise, analyse the first occurance of line-feed character
if (pcBuf[I] == _T('\x0a'))
{
nCrlfStyle = CRLF_STYLE_UNIX;
}
else
{
if (I < dwCurSize - 1 && pcBuf[I + 1] == _T ('\x0a'))
nCrlfStyle = CRLF_STYLE_DOS;
else
nCrlfStyle = CRLF_STYLE_MAC;
}
}
}
ASSERT (nCrlfStyle >= 0 && nCrlfStyle <= 2);
m_nCRLFMode = nCrlfStyle;
LPCTSTR crlf = crlfs[nCrlfStyle];
m_aLines.SetSize (0, 4096);
DWORD dwBufPtr = 0;
int nCrlfPtr = 0;
while (dwBufPtr < dwCurSize)
{
int c = pcBuf[dwBufPtr];
dwBufPtr++;
if (dwBufPtr == dwCurSize && dwCurSize == dwBufSize)
{
if (!::ReadFile (hFile, pcBuf, dwBufSize, &dwCurSize, NULL))
__leave;
dwBufPtr = 0;
}
pcLineBuf[nCurrentLength] = (TCHAR) c;
nCurrentLength++;
if (nCurrentLength == nCurrentMax)
{
// Reallocate line buffer
nCurrentMax += 256;
LPTSTR pcNewBuf = new TCHAR[nCurrentMax];
memcpy (pcNewBuf, pcLineBuf, nCurrentLength);
delete pcLineBuf;
pcLineBuf = pcNewBuf;
}
if ((TCHAR) c == crlf[nCrlfPtr])
{
nCrlfPtr++;
if (crlf[nCrlfPtr] == 0)
{
pcLineBuf[nCurrentLength - nCrlfPtr] = 0;
if (m_nSourceEncoding >= 0)
iconvert (pcLineBuf, m_nSourceEncoding, 1, m_nSourceEncoding == 15);
InsertLine (pcLineBuf);
nCurrentLength = 0;
nCrlfPtr = 0;
}
}
else
nCrlfPtr = 0;
}
pcLineBuf[nCurrentLength] = 0;
InsertLine (pcLineBuf);
ASSERT (m_aLines.GetSize () > 0); // At least one empty line must present
m_bInit = TRUE;
m_bReadOnly = (dwFileAttributes & FILE_ATTRIBUTE_READONLY) != 0;
m_bModified = FALSE;
m_bUndoGroup = m_bUndoBeginGroup = FALSE;
m_nUndoBufSize = UNDO_BUF_SIZE;
m_nSyncPosition = m_nUndoPosition = 0;
ASSERT (m_aUndoBuf.GetSize () == 0);
bSuccess = TRUE;
RetypeViews (pszFileName);
UpdateViews (NULL, NULL, UPDATE_RESET);
}
__finally
{
if (pcLineBuf != NULL)
delete pcLineBuf;
if (hFile != NULL)
::CloseHandle (hFile);
}
//BEGIN SW
m_ptLastChange.x = m_ptLastChange.y = -1;
//END SW
return bSuccess;
}
BOOL CCrystalTextBuffer::
SaveToFile (LPCTSTR pszFileName, int nCrlfStyle /*= CRLF_STYLE_AUTOMATIC*/ , BOOL bClearModifiedFlag /*= TRUE*/ )
{
ASSERT (nCrlfStyle == CRLF_STYLE_AUTOMATIC || nCrlfStyle == CRLF_STYLE_DOS ||
nCrlfStyle == CRLF_STYLE_UNIX || nCrlfStyle == CRLF_STYLE_MAC);
ASSERT (m_bInit);
HANDLE hTempFile = INVALID_HANDLE_VALUE;
HANDLE hSearch = INVALID_HANDLE_VALUE;
TCHAR szTempFileDir[_MAX_PATH + 1];
TCHAR szTempFileName[_MAX_PATH + 1];
TCHAR szBackupFileName[_MAX_PATH + 1];
BOOL bSuccess = FALSE;
__try
{
TCHAR drive[_MAX_PATH], dir[_MAX_PATH], name[_MAX_PATH], ext[_MAX_PATH];
#ifdef _UNICODE
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -