📄 ccrystaltextview.cpp
字号:
////////////////////////////////////////////////////////////////////////////
// File: CCrystalTextView.cpp
// Version: 1.0.0.0
// Created: 29-Dec-1998
//
// Author: Stcherbatchenko Andrei
// E-mail: windfall@gmx.de
//
// Implementation of the CCrystalTextView 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: missing UpdateCaret() in CCrystalTextView::SetFont
// FIX: missing UpdateCaret() in CCrystalTextView::RecalcVertScrollBar
// FIX: mistype in CCrystalTextView::RecalcPageLayouts + instead of +=
// FIX: removed condition 'm_nLineHeight < 20' in
// CCrystalTextView::CalcLineCharDim(). This caused painting defects
// when using very small fonts.
//
// FEATURE: Some experiments with smooth scrolling, controlled by
// m_bSmoothScroll member variable, by default turned off.
// See ScrollToLine function for implementation details.
////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////
// 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
//
// + FIX: ResetView() now virtual
// + FEATURE: Added OnEditOperation() virtual: base for auto-indent,
// smart indent etc.
////////////////////////////////////////////////////////////////////////////
#include "stdafx.h"
#include <malloc.h>
#include "editcmd.h"
#include "editreg.h"
#include "CCrystalTextView.h"
#include "CCrystalTextBuffer.h"
#include "CFindTextDlg.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
#define TAB_CHARACTER _T('\xBB')
#define SPACE_CHARACTER _T('\x95')
#define DEFAULT_PRINT_MARGIN 1000 // 10 millimeters
#define SMOOTH_SCROLL_FACTOR 6
////////////////////////////////////////////////////////////////////////////
// CCrystalTextView
IMPLEMENT_DYNCREATE(CCrystalTextView, CView)
HINSTANCE CCrystalTextView::s_hResourceInst = NULL;
BEGIN_MESSAGE_MAP(CCrystalTextView, CView)
//{{AFX_MSG_MAP(CCrystalTextView)
ON_WM_DESTROY()
ON_WM_ERASEBKGND()
ON_WM_SIZE()
ON_WM_VSCROLL()
ON_WM_SETCURSOR()
ON_WM_LBUTTONDOWN()
ON_WM_SETFOCUS()
ON_WM_HSCROLL()
ON_WM_LBUTTONUP()
ON_WM_MOUSEMOVE()
ON_WM_TIMER()
ON_WM_KILLFOCUS()
ON_WM_LBUTTONDBLCLK()
ON_COMMAND(ID_EDIT_COPY, OnEditCopy)
ON_UPDATE_COMMAND_UI(ID_EDIT_COPY, OnUpdateEditCopy)
ON_COMMAND(ID_EDIT_SELECT_ALL, OnEditSelectAll)
ON_UPDATE_COMMAND_UI(ID_EDIT_SELECT_ALL, OnUpdateEditSelectAll)
ON_WM_RBUTTONDOWN()
ON_WM_SYSCOLORCHANGE()
ON_WM_CREATE()
ON_COMMAND(ID_EDIT_FIND, OnEditFind)
ON_COMMAND(ID_EDIT_REPEAT, OnEditRepeat)
ON_UPDATE_COMMAND_UI(ID_EDIT_REPEAT, OnUpdateEditRepeat)
ON_COMMAND(ID_EDIT_FIND_PREVIOUS, OnEditFindPrevious)
ON_UPDATE_COMMAND_UI(ID_EDIT_FIND_PREVIOUS, OnUpdateEditFindPrevious)
//}}AFX_MSG_MAP
ON_COMMAND(ID_EDIT_CHAR_LEFT, OnCharLeft)
ON_COMMAND(ID_EDIT_EXT_CHAR_LEFT, OnExtCharLeft)
ON_COMMAND(ID_EDIT_CHAR_RIGHT, OnCharRight)
ON_COMMAND(ID_EDIT_EXT_CHAR_RIGHT, OnExtCharRight)
ON_COMMAND(ID_EDIT_WORD_LEFT, OnWordLeft)
ON_COMMAND(ID_EDIT_EXT_WORD_LEFT, OnExtWordLeft)
ON_COMMAND(ID_EDIT_WORD_RIGHT, OnWordRight)
ON_COMMAND(ID_EDIT_EXT_WORD_RIGHT, OnExtWordRight)
ON_COMMAND(ID_EDIT_LINE_UP, OnLineUp)
ON_COMMAND(ID_EDIT_EXT_LINE_UP, OnExtLineUp)
ON_COMMAND(ID_EDIT_LINE_DOWN, OnLineDown)
ON_COMMAND(ID_EDIT_EXT_LINE_DOWN, OnExtLineDown)
ON_COMMAND(ID_EDIT_SCROLL_UP, ScrollUp)
ON_COMMAND(ID_EDIT_SCROLL_DOWN, ScrollDown)
ON_COMMAND(ID_EDIT_PAGE_UP, OnPageUp)
ON_COMMAND(ID_EDIT_EXT_PAGE_UP, OnExtPageUp)
ON_COMMAND(ID_EDIT_PAGE_DOWN, OnPageDown)
ON_COMMAND(ID_EDIT_EXT_PAGE_DOWN, OnExtPageDown)
ON_COMMAND(ID_EDIT_LINE_END, OnLineEnd)
ON_COMMAND(ID_EDIT_EXT_LINE_END, OnExtLineEnd)
ON_COMMAND(ID_EDIT_HOME, OnHome)
ON_COMMAND(ID_EDIT_EXT_HOME, OnExtHome)
ON_COMMAND(ID_EDIT_TEXT_BEGIN, OnTextBegin)
ON_COMMAND(ID_EDIT_EXT_TEXT_BEGIN, OnExtTextBegin)
ON_COMMAND(ID_EDIT_TEXT_END, OnTextEnd)
ON_COMMAND(ID_EDIT_EXT_TEXT_END, OnExtTextEnd)
// Standard printing commands
ON_COMMAND(ID_FILE_PAGE_SETUP, OnFilePageSetup)
ON_COMMAND(ID_FILE_PRINT, CView::OnFilePrint)
ON_COMMAND(ID_FILE_PRINT_DIRECT, CView::OnFilePrint)
ON_COMMAND(ID_FILE_PRINT_PREVIEW, CView::OnFilePrintPreview)
// Status
ON_UPDATE_COMMAND_UI(ID_EDIT_INDICATOR_CRLF, OnUpdateIndicatorCRLF)
ON_UPDATE_COMMAND_UI(ID_EDIT_INDICATOR_POSITION, OnUpdateIndicatorPosition)
// Bookmarks
ON_COMMAND_RANGE(ID_EDIT_TOGGLE_BOOKMARK0, ID_EDIT_TOGGLE_BOOKMARK9, OnToggleBookmark)
ON_COMMAND_RANGE(ID_EDIT_GO_BOOKMARK0, ID_EDIT_GO_BOOKMARK9, OnGoBookmark)
ON_COMMAND(ID_EDIT_CLEAR_BOOKMARKS, OnClearBookmarks)
// More Bookmarks
ON_COMMAND(ID_EDIT_TOGGLE_BOOKMARK, OnToggleBookmark)
// ON_COMMAND(ID_EDIT_TOGGLE_LINEMARK, OnToggleLinemark)
ON_COMMAND(ID_EDIT_GOTO_NEXT_BOOKMARK, OnNextBookmark)
ON_COMMAND(ID_EDIT_GOTO_PREV_BOOKMARK, OnPrevBookmark)
ON_COMMAND(ID_EDIT_CLEAR_ALL_BOOKMARKS, OnClearAllBookmarks)
ON_UPDATE_COMMAND_UI(ID_EDIT_GOTO_NEXT_BOOKMARK, OnUpdateNextBookmark)
ON_UPDATE_COMMAND_UI(ID_EDIT_GOTO_PREV_BOOKMARK, OnUpdatePrevBookmark)
ON_UPDATE_COMMAND_UI(ID_EDIT_CLEAR_ALL_BOOKMARKS, OnUpdateClearAllBookmarks)
END_MESSAGE_MAP()
#define EXPAND_PRIMITIVE(impl, func) \
void CCrystalTextView::On##func() { impl(FALSE); } \
void CCrystalTextView::OnExt##func() { impl(TRUE); }
EXPAND_PRIMITIVE(MoveLeft, CharLeft)
EXPAND_PRIMITIVE(MoveRight, CharRight)
EXPAND_PRIMITIVE(MoveWordLeft, WordLeft)
EXPAND_PRIMITIVE(MoveWordRight, WordRight)
EXPAND_PRIMITIVE(MoveUp, LineUp)
EXPAND_PRIMITIVE(MoveDown, LineDown)
EXPAND_PRIMITIVE(MovePgUp, PageUp)
EXPAND_PRIMITIVE(MovePgDn, PageDown)
EXPAND_PRIMITIVE(MoveHome, Home)
EXPAND_PRIMITIVE(MoveEnd, LineEnd)
EXPAND_PRIMITIVE(MoveCtrlHome, TextBegin)
EXPAND_PRIMITIVE(MoveCtrlEnd, TextEnd)
#undef EXPAND_PRIMITIVE
/////////////////////////////////////////////////////////////////////////////
// CCrystalTextView construction/destruction
CCrystalTextView::CCrystalTextView()
{
AFX_ZERO_INIT_OBJECT(CView);
m_bSelMargin = TRUE;
ResetView();
}
CCrystalTextView::~CCrystalTextView()
{
ASSERT(m_hAccel == NULL);
ASSERT(m_pCacheBitmap == NULL);
ASSERT(m_pTextBuffer == NULL); // Must be correctly detached
if (m_pszLastFindWhat != NULL)
free(m_pszLastFindWhat);
if (m_pdwParseCookies != NULL)
delete m_pdwParseCookies;
if (m_pnActualLineLength != NULL)
delete m_pnActualLineLength;
}
BOOL CCrystalTextView::PreCreateWindow(CREATESTRUCT& cs)
{
CWnd *pParentWnd = CWnd::FromHandlePermanent(cs.hwndParent);
if (pParentWnd == NULL || ! pParentWnd->IsKindOf(RUNTIME_CLASS(CSplitterWnd)))
{
// View must always create its own scrollbars,
// if only it's not used within splitter
cs.style |= (WS_HSCROLL | WS_VSCROLL);
}
cs.lpszClass = AfxRegisterWndClass(CS_DBLCLKS);
return CView::PreCreateWindow(cs);
}
/////////////////////////////////////////////////////////////////////////////
// CCrystalTextView drawing
void CCrystalTextView::GetSelection(CPoint &ptStart, CPoint &ptEnd)
{
PrepareSelBounds();
ptStart = m_ptDrawSelStart;
ptEnd = m_ptDrawSelEnd;
}
CCrystalTextBuffer *CCrystalTextView::LocateTextBuffer()
{
return NULL;
}
int CCrystalTextView::GetLineActualLength(int nLineIndex)
{
int nLineCount = GetLineCount();
ASSERT(nLineCount > 0);
ASSERT(nLineIndex >= 0 && nLineIndex < nLineCount);
if (m_pnActualLineLength == NULL)
{
m_pnActualLineLength = new int[nLineCount];
memset(m_pnActualLineLength, 0xff, sizeof(int) * nLineCount);
m_nActualLengthArraySize = nLineCount;
}
if (m_pnActualLineLength[nLineIndex] >= 0)
return m_pnActualLineLength[nLineIndex];
// Actual line length is not determined yet, let's calculate a little
int nActualLength = 0;
int nLength = GetLineLength(nLineIndex);
if (nLength > 0)
{
LPCTSTR pszLine = GetLineChars(nLineIndex);
LPTSTR pszChars = (LPTSTR) _alloca(sizeof(TCHAR) * (nLength + 1));
memcpy(pszChars, pszLine, sizeof(TCHAR) * nLength);
pszChars[nLength] = 0;
LPTSTR pszCurrent = pszChars;
int nTabSize = GetTabSize();
for (;;)
{
#ifdef _UNICODE
LPTSTR psz = wcschr(pszCurrent, L'\t');
#else
LPTSTR psz = strchr(pszCurrent, '\t');
#endif
if (psz == NULL)
{
nActualLength += (pszChars + nLength - pszCurrent);
break;
}
nActualLength += (psz - pszCurrent);
nActualLength += (nTabSize - nActualLength % nTabSize);
pszCurrent = psz + 1;
}
}
m_pnActualLineLength[nLineIndex] = nActualLength;
return nActualLength;
}
void CCrystalTextView::ScrollToChar(int nNewOffsetChar, BOOL bNoSmoothScroll /*= FALSE*/, BOOL bTrackScrollBar /*= TRUE*/)
{
// For now, ignoring bNoSmoothScroll and m_bSmoothScroll
if (m_nOffsetChar != nNewOffsetChar)
{
int nScrollChars = m_nOffsetChar - nNewOffsetChar;
m_nOffsetChar = nNewOffsetChar;
CRect rcScroll;
GetClientRect(&rcScroll);
rcScroll.left += GetMarginWidth();
ScrollWindow(nScrollChars * GetCharWidth(), 0, &rcScroll, &rcScroll);
UpdateWindow();
if (bTrackScrollBar)
RecalcHorzScrollBar(TRUE);
}
}
void CCrystalTextView::ScrollToLine(int nNewTopLine, BOOL bNoSmoothScroll /*= FALSE*/, BOOL bTrackScrollBar /*= TRUE*/)
{
if (m_nTopLine != nNewTopLine)
{
if (bNoSmoothScroll || ! m_bSmoothScroll)
{
int nScrollLines = m_nTopLine - nNewTopLine;
m_nTopLine = nNewTopLine;
ScrollWindow(0, nScrollLines * GetLineHeight());
UpdateWindow();
if (bTrackScrollBar)
RecalcVertScrollBar(TRUE);
}
else
{
// Do smooth scrolling
int nLineHeight = GetLineHeight();
if (m_nTopLine > nNewTopLine)
{
int nIncrement = (m_nTopLine - nNewTopLine) / SMOOTH_SCROLL_FACTOR + 1;
while (m_nTopLine != nNewTopLine)
{
int nTopLine = m_nTopLine - nIncrement;
if (nTopLine < nNewTopLine)
nTopLine = nNewTopLine;
int nScrollLines = nTopLine - m_nTopLine;
m_nTopLine = nTopLine;
ScrollWindow(0, - nLineHeight * nScrollLines);
UpdateWindow();
if (bTrackScrollBar)
RecalcVertScrollBar(TRUE);
}
}
else
{
int nIncrement = (nNewTopLine - m_nTopLine) / SMOOTH_SCROLL_FACTOR + 1;
while (m_nTopLine != nNewTopLine)
{
int nTopLine = m_nTopLine + nIncrement;
if (nTopLine > nNewTopLine)
nTopLine = nNewTopLine;
int nScrollLines = nTopLine - m_nTopLine;
m_nTopLine = nTopLine;
ScrollWindow(0, - nLineHeight * nScrollLines);
UpdateWindow();
if (bTrackScrollBar)
RecalcVertScrollBar(TRUE);
}
}
}
}
}
void CCrystalTextView::ExpandChars(LPCTSTR pszChars, int nOffset, int nCount, CString &line)
{
if (nCount <= 0)
{
line = _T("");
return;
}
int nTabSize = GetTabSize();
int nActualOffset = 0;
for (int I = 0; I < nOffset; I ++)
{
if (pszChars[I] == _T('\t'))
nActualOffset += (nTabSize - nActualOffset % nTabSize);
else
nActualOffset ++;
}
pszChars += nOffset;
int nLength = nCount;
int nTabCount = 0;
for (I = 0; I < nLength; I ++)
{
if (pszChars[I] == _T('\t'))
nTabCount ++;
}
LPTSTR pszBuf = line.GetBuffer(nLength + nTabCount * (nTabSize - 1) + 1);
int nCurPos = 0;
if (nTabCount > 0 || m_bViewTabs)
{
for (I = 0; I < nLength; I ++)
{
if (pszChars[I] == _T('\t'))
{
int nSpaces = nTabSize - (nActualOffset + nCurPos) % nTabSize;
if (m_bViewTabs)
{
pszBuf[nCurPos ++] = TAB_CHARACTER;
nSpaces --;
}
while (nSpaces > 0)
{
pszBuf[nCurPos ++] = _T(' ');
nSpaces --;
}
}
else
{
if (pszChars[I] == _T(' ') && m_bViewTabs)
pszBuf[nCurPos] = SPACE_CHARACTER;
else
pszBuf[nCurPos] = pszChars[I];
nCurPos ++;
}
}
}
else
{
memcpy(pszBuf, pszChars, sizeof(TCHAR) * nLength);
nCurPos = nLength;
}
pszBuf[nCurPos] = 0;
line.ReleaseBuffer();
}
void CCrystalTextView::DrawLineHelperImpl(CDC *pdc, CPoint &ptOrigin, const CRect &rcClip,
LPCTSTR pszChars, int nOffset, int nCount)
{
ASSERT(nCount >= 0);
if (nCount > 0)
{
CString line;
ExpandChars(pszChars, nOffset, nCount, line);
int nWidth = rcClip.right - ptOrigin.x;
if (nWidth > 0)
{
int nCharWidth = GetCharWidth();
int nCount = line.GetLength();
int nCountFit = nWidth / nCharWidth + 1;
if (nCount > nCountFit)
nCount = nCountFit;
#ifdef _DEBUG
//CSize sz = pdc->GetTextExtent(line, nCount);
//ASSERT(sz.cx == m_nCharWidth * nCount);
#endif
/*
CRect rcBounds = rcClip;
rcBounds.left = ptOrigin.x;
rcBounds.right = rcBounds.left + GetCharWidth() * nCount;
pdc->ExtTextOut(rcBounds.left, rcBounds.top, ETO_OPAQUE, &rcBounds, NULL, 0, NULL);
*/
VERIFY(pdc->ExtTextOut(ptOrigin.x, ptOrigin.y, ETO_CLIPPED, &rcClip, line, nCount, NULL));
}
ptOrigin.x += GetCharWidth() * line.GetLength();
}
}
void CCrystalTextView::DrawLineHelper(CDC *pdc, CPoint &ptOrigin, const CRect &rcClip, int nColorIndex,
LPCTSTR pszChars, int nOffset, int nCount, CPoint ptTextPos)
{
if (nCount > 0)
{
if (m_bFocused || m_bShowInactiveSelection)
{
int nSelBegin = 0, nSelEnd = 0;
if (m_ptDrawSelStart.y > ptTextPos.y)
{
nSelBegin = nCount;
}
else
if (m_ptDrawSelStart.y == ptTextPos.y)
{
nSelBegin = m_ptDrawSelStart.x - ptTextPos.x;
if (nSelBegin < 0)
nSelBegin = 0;
if (nSelBegin > nCount)
nSelBegin = nCount;
}
if (m_ptDrawSelEnd.y > ptTextPos.y)
{
nSelEnd = nCount;
}
else
if (m_ptDrawSelEnd.y == ptTextPos.y)
{
nSelEnd = m_ptDrawSelEnd.x - ptTextPos.x;
if (nSelEnd < 0)
nSelEnd = 0;
if (nSelEnd > nCount)
nSelEnd = nCount;
}
ASSERT(nSelBegin >= 0 && nSelBegin <= nCount);
ASSERT(nSelEnd >= 0 && nSelEnd <= nCount);
ASSERT(nSelBegin <= nSelEnd);
// Draw part of the text before selection
if (nSelBegin > 0)
{
DrawLineHelperImpl(pdc, ptOrigin, rcClip, pszChars, nOffset, nSelBegin);
}
if (nSelBegin < nSelEnd)
{
COLORREF crOldBk = pdc->SetBkColor(GetColor(COLORINDEX_SELBKGND));
COLORREF crOldText = pdc->SetTextColor(GetColor(COLORINDEX_SELTEXT));
DrawLineHelperImpl(pdc, ptOrigin, rcClip, pszChars, nOffset + nSelBegin, nSelEnd - nSelBegin);
pdc->SetBkColor(crOldBk);
pdc->SetTextColor(crOldText);
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -