📄 syneditview.cpp
字号:
////////////////////////////////////////////////////////////
// 文件: SynEditView.cpp
// 版本: 1.0.0.1
// 创建: 2002年1月23日
//
// 作者: 郑旭
// Website: http://www.easysrc.com
// E-mail: happyfly@netease.com
//
// CSynEditView语法编辑视实现代码
//
// 你可以自由使用或是改变CSynEditView的代码以适应你的需要,
// 但是请保留这段文字。
////////////////////////////////////////////////////////////
/*-----------------------------------------------------------------
| |
| 设计思路 : |
| 假设 SynCtrl 即是隐藏在 CRichEditView 后面可以用 |
| GetRichEditCtrl 函数取得的控制。 SynEditView 可看作覆盖在 |
| SynCtrl 表面的一层不透明的画布,在程序中我们自己作画,再将 |
| 画出来的内容覆盖在 SynCtrl 表面,即可作成语法关键字编辑效果 |
| 。虽然在程序中重载 OnPaint 函数但不作任何操作带来的后果是 |
| SynCtrl 不再重画,但是隐藏在 SynCtrl 后面的内容仍然存在, |
| 我们可以通过 GetLine 取得其文本信息,也可以通过 GetFont 取 |
| 得其字体信息!我们可以取出这些信息进行加工,再将加工后的东 |
| 西显示出来。通过仔细的调整各个参数,使得画出来的东西与不重 |
| 载SynCtrl 的 OnPaint 函数前 SynCtrl 画出来的东西完全重合, |
| 但只是把其中是关键字的部分进行着色,即可达到完美的语法编程 |
| 器的效果。 |
| |
-----------------------------------------------------------------*/
#include "stdafx.h"
#include <malloc.h>
#include "SynEditView.h"
#include "Mainfrm.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
DWORD CALLBACK EditStreamCallbackReadFromFile(DWORD dwCookie, LPBYTE pbBuff, LONG cb, LONG *pcb);
DWORD CALLBACK EditStreamCallbackWriteToFile(DWORD dwCookie, LPBYTE pbBuff, LONG cb, LONG *pcb);
DWORD CALLBACK EditStreamCallbackOut(DWORD dwCookie, LPBYTE pbBuff, LONG cb, LONG *pcb);
/////////////////////////////////////////////////////////////////////////////
// CSynEditView
IMPLEMENT_DYNCREATE(CSynEditView, CRichEditView)
BEGIN_MESSAGE_MAP(CSynEditView, CRichEditView)
//{{AFX_MSG_MAP(CSynEditView)
ON_WM_SIZE()
ON_WM_CREATE()
ON_WM_DESTROY()
ON_WM_HSCROLL()
ON_WM_KEYDOWN()
ON_WM_LBUTTONDOWN()
ON_WM_KEYUP()
ON_WM_VSCROLL()
ON_WM_PAINT()
ON_WM_SETFOCUS()
ON_WM_CHAR()
//}}AFX_MSG_MAP
// Standard printing commands
ON_COMMAND(ID_FILE_PRINT, CRichEditView::OnFilePrint)
ON_COMMAND(ID_FILE_PRINT_DIRECT, CRichEditView::OnFilePrint)
ON_COMMAND(ID_FILE_PRINT_PREVIEW, CRichEditView::OnFilePrintPreview)
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// CSynEditView construction/destruction
CSynEditView::CSynEditView()
{
ReadSettings();
m_nLineNumberCharNumber = 0;
m_nLineCount = 0;
m_nCharTabWidth = 1;
m_nCharSpaceWidth = 1;
m_nParseArraySize = 0;
m_nHorzPos = 0;
m_nCharNumberWidth = 1;
m_nLineHeight = 17;
m_nLeftMargin = 1;
m_nCurrentLine = 0;
m_bAllowDraw = TRUE;
m_bRealReturn = TRUE;
m_nDefaultLeftMargin = 0;
m_nBookMarksCount = 0;
m_bTrack = FALSE;
m_pdwParseCookies = NULL;
m_rcClient = NULL;
m_pCacheBitmap = NULL;
memset(anBookMarks, 0, sizeof(int)*256);
m_nHorzMaxOld = 0;
}
CSynEditView::~CSynEditView()
{
if (m_pCacheBitmap != NULL)
delete m_pCacheBitmap;
if (m_pdwParseCookies != NULL)
delete m_pdwParseCookies;
}
BOOL CSynEditView::PreCreateWindow(CREATESTRUCT& cs)
{
//*
if (LoadLibraryA("RICHED20.DLL") == NULL)
{
AfxMessageBox(_T("Fail to load \"riched20.dll\"."),
MB_OK | MB_ICONERROR);
PostMessage(WM_QUIT,0,0);
}
m_strClass = RICHEDIT_CLASSA; //for 2.0 class
//*/
return CRichEditView::PreCreateWindow(cs);
}
void CSynEditView::OnInitialUpdate()
{
CRichEditView::OnInitialUpdate();
// Set the printing margins (720 twips = 1/2 inch).
SetMargins(CRect(1000, 800, 800, 800));
ResetParseCookie();
SelectLanguage(m_nLanguage);
GetRichEditCtrl().HideSelection(TRUE, FALSE);
SetWrapMode(m_nWrapMode);
GetRichEditCtrl().SetReadOnly(m_bReadOnly);
SendMessage(EM_SETUNDOLIMIT, m_nUndoLimits, 0);
SetAutoWordSelect(m_bAutoWordSelect);
CSynEditView::m_nWordWrap = m_nWrapMode;
CSynEditView::WrapChanged();
ResetParseCookie();
if(m_nWrapMode == WrapNone)
GetRichEditCtrl().ShowScrollBar(SB_HORZ, TRUE);
else
GetRichEditCtrl().ShowScrollBar(SB_HORZ, FALSE);
InitEditorFont();
PostMessage( WM_PAINT, 0, 0 );
}
/////////////////////////////////////////////////////////////////////////////
// CSynEditView printing
BOOL CSynEditView::OnPreparePrinting(CPrintInfo* pInfo)
{
// default preparation
return DoPreparePrinting(pInfo);
}
/////////////////////////////////////////////////////////////////////////////
// CSynEditView diagnostics
#ifdef _DEBUG
void CSynEditView::AssertValid() const
{
CRichEditView::AssertValid();
}
void CSynEditView::Dump(CDumpContext& dc) const
{
CRichEditView::Dump(dc);
}
#endif //_DEBUG
/////////////////////////////////////////////////////////////////////////////
// CSynEditView message handlers
void CSynEditView::LoadFile(CString strFile)
{
SetWindowText(_T(""));
ResetParseCookie();
SetSynEditViewFont(m_lf);
CRichEditCtrl &SynCtrl = GetRichEditCtrl();
CFile* pInputFile = NULL;
try
{
pInputFile = new CFile(strFile, CFile::modeRead | CFile::shareDenyNone);
EDITSTREAM strm;
strm.dwCookie = (DWORD) pInputFile;
strm.pfnCallback = EditStreamCallbackReadFromFile;
long lResult = SynCtrl.StreamIn(SF_TEXT, strm);
pInputFile->Close();
}
catch (CFileException* pEx)
{
pEx->Delete();
}
delete pInputFile;
SynCtrl.SetModify(FALSE);
return;
}
//将文件中的内容装入到richeditctrl中,正常工作
DWORD CALLBACK EditStreamCallbackReadFromFile(DWORD dwCookie, LPBYTE pbBuff, LONG cb, LONG *pcb)
{
CFile* pFile = (CFile*) dwCookie;
ASSERT_KINDOF(CFile, pFile);
*pcb = pFile->Read(pbBuff, cb);
return 0;
}
//将richeditctrl中的内容写入到文件中,正常工作
DWORD CALLBACK EditStreamCallbackWriteToFile(DWORD dwCookie, LPBYTE pbBuff, LONG cb, LONG *pcb)
{
CFile* pFile = (CFile*) dwCookie;
pFile->Write(pbBuff, cb);
*pcb = cb;
return 0;
}
BOOL CSynEditView::IsFileExist(CString &strFile)
{
if(strFile.IsEmpty())
return FALSE;
FILE *file;
if((file=fopen(strFile, _T("r")))==NULL)
return FALSE;
fclose(file);
return TRUE;
}
void CSynEditView::SetSynEditViewFont(LOGFONT lf)
{
ResetParseCookie();
SetSynCtrlFont(lf);
WriteSettings();
}
void CSynEditView::SetSynCtrlFont(LOGFONT lf)
{
ShowWindow(SW_HIDE);
CString str;
EDITSTREAM stream;
stream.dwCookie = (DWORD)&str;
stream.pfnCallback = EditStreamCallbackOut;
GetRichEditCtrl().StreamOut(SF_TEXT, stream);
BOOL bModify = GetRichEditCtrl().GetModify();
m_font.DeleteObject();
m_font.CreateFontIndirect(&lf);
//先设置视的字体
SetFont(&m_font);
m_lf = lf;
/*
SetWindowText((" "));
GetRichEditCtrl().SetSel(0, 1);
CHARFORMAT cr = GetCharFormatSelection();
//设置视的字体可能改变,所以还要取其字体,看真正的结果,用GetFont不管用
m_lf.lfCharSet = cr.bCharSet;
m_lf.lfHeight = cr.yHeight/14;
strcpy(m_lf.lfFaceName, cr.szFaceName);
//*/
SetCharWidth();
SetLineHeight();
SetLeftMargin();
WriteSettings();
SetWindowText(str);
GetRichEditCtrl().SetModify(bModify);
ShowWindow(SW_SHOW);
}
void CSynEditView::SetSynCtrlTabSize(int nSize)
{
CRichEditCtrl &SynCtrl = GetRichEditCtrl();
CString str;
EDITSTREAM stream;
stream.dwCookie = (DWORD)&str;
stream.pfnCallback = EditStreamCallbackOut;
SynCtrl.StreamOut(SF_TEXT, stream);
//因为tab宽度一直是固定的,所以开始就取tab宽度
//又因为要计算最开始tab相当于几个数字宽,所以下面几行必须放在上面一行之后
//RichEditView开始时tab宽为一常数,即0.5英寸=720 points
SynCtrl.SetWindowText(_T("\t"));
CPoint p1 = SynCtrl.GetCharPos(0);
CPoint p2 = SynCtrl.GetCharPos(1);
m_nCharTabWidth = p2.x - p1.x; //取tab在字体m_lf下的以point表示的宽度
//设置TAB间隔
PARAFORMAT pf ;
pf.cbSize = sizeof(PARAFORMAT);
pf.dwMask = PFM_TABSTOPS ;
pf.cTabCount = MAX_TAB_STOPS;
SynCtrl.GetParaFormat( pf );
int nSynCtrlTabSize = pf.rgxTabs[0];
if(nSynCtrlTabSize == 0)
nSynCtrlTabSize = 720;
/*
Tab的计算方法:
1、先取SynCtrl默认的Tab宽度nSynCtrlTabSize(用英寸表示)
2、计算SynCtrl默认的Tab宽度对应几个数字m_nCharTabWidth / m_nCharNumberWidth
2、根据以上值计算每个数字的宽度nSynCtrlTabSize / (m_nCharTabWidth / m_nCharNumberWidth)
3、根据每个数字的宽度计算新的Tab宽度
*/
SetWindowText(""); //此行不能删除,不然设置tab宽度会失败
int nNewTab = int(nSynCtrlTabSize * 1.0 * nSize * m_nCharNumberWidth / m_nCharTabWidth);
pf.cTabCount = MAX_TAB_STOPS;
pf.dwMask = PFM_TABSTOPS;
for(int itab = 0; itab < pf.cTabCount; itab++ )
pf.rgxTabs[itab] = (itab+1) * nNewTab ;
SetParaFormat( pf );
m_nTabSize = nSize;
m_nCharTabWidth = nSize * m_nCharNumberWidth;
WriteSettings();
SetWindowText(str);
}
void CSynEditView::OnSize(UINT nType, int cx, int cy)
{
CRichEditView::OnSize(nType, cx, cy);
GetClientRect(&m_rcClient);
if (m_pCacheBitmap != NULL)
{
delete m_pCacheBitmap;
m_pCacheBitmap = NULL;
}
SetLeftMargin();
PostMessage(WM_PAINT, 0, 0);
}
void CSynEditView::LoadText(CString &strText)
{
ResetParseCookie();
SetSynEditViewFont(m_lf);
SetWindowText(strText);
GetRichEditCtrl().SetModify( FALSE );
}
void CSynEditView::DrawSingleLineColorText( CDC *cacheDC, int nLine, CRect rcLine)
{
COLORREF clrBkColor = m_clrBkColor;
CRect rtSrc(rcLine);
//显示当前行
if(m_bShowSelectLine) {
if(nLine == m_nCurrentLine && m_ptStart == m_ptEnd)
clrBkColor = m_clrBkCurLine;
else
clrBkColor = m_clrBkColor;
cacheDC->FillSolidRect(rcLine, clrBkColor);
}
////////////
CFont cf;
cf.CreateFontIndirect(&m_lf);
CFont *pOldFont = cacheDC->SelectObject(&cf);
CString strLine;
BOOL bRealReturn = GetLineString(nLine, strLine); //取指定行文本,并返回是否为硬回车标志
int nLength = strLine.GetLength();
int nActualItems = 0;
COLORINFO *ColorInfo = (COLORINFO *)_alloca( sizeof(COLORINFO) * (nLength + 1) );
DWORD dwCookie = GetParseCookie(nLine-1);
CString str;
if( m_bRealReturn && (m_nLanguage != _HTML) && (m_nLanguage != _XML))
dwCookie &= COOKIE_EXT_COMMENT;
m_bRealReturn = bRealReturn;
dwCookie = ParseLine(m_strArrayKeyWords, dwCookie, strLine, ColorInfo, nActualItems);
m_pdwParseCookies[nLine] = dwCookie;
COLORREF clr;
CSize sizeContinueStr(0, 0);
int nlen, nTabCount = 0;
if (nActualItems > 0)
{
ASSERT(ColorInfo[0].Pos >= 0 && ColorInfo[0].Pos <= nLength);
for (int I = 0; I < nActualItems; I ++)
{
nlen = ColorInfo[I + 1].Pos - ColorInfo[I].Pos;
if( I == nActualItems - 1 )
str = strLine.Mid(ColorInfo[I].Pos, strLine.GetLength() - ColorInfo[I].Pos);
else
str = strLine.Mid(ColorInfo[I].Pos, nlen);
if(str.IsEmpty())
continue;
clr = GetColor(ColorInfo[I].Color);
int nPos = str.Find(_T("\t"));
while(-1 != nPos)
{
//遇到一个tab字符时,先将前面的字符画出来
CString strTemp = str.Left(nPos);
JudgeInSeletioAndDrawText( cacheDC, rcLine, strTemp, clrBkColor, clr );
CSize sizeTemp = cacheDC->GetTextExtent(strTemp);
nTabCount += 1; //既然找到了tab,那么就加1吧
//把前面已经输出的字符串宽度换算成相应的tab个数
nTabCount += int((sizeTemp.cx+sizeContinueStr.cx) / m_nCharTabWidth);
int nOldLeft = rcLine.left;
//然后直接定位到下一个tab位置
rcLine.left = rtSrc.left + nTabCount * m_nCharTabWidth;
//开始画tab////////////////////////
BOOL bLeftInSel = IsStrInSelection(nOldLeft, rcLine.top, TRUE);
BOOL bRightInSel = IsStrInSelection(rcLine.left, rcLine.top, FALSE);
if(bLeftInSel && bRightInSel)
{
//如果tab在选择区域内,则要画背景
CRect rtTab(nOldLeft, rcLine.top, rcLine.left, rcLine.bottom);
cacheDC->FillSolidRect( &rtTab, m_clrBKSelText );
}
if(m_bShowTab)
{
int nleft = nOldLeft;
int nCenterY = rcLine.top + (rcLine.bottom-rcLine.top)/2;
for(int pos=1; pos<4; pos++)
{
cacheDC->SetPixel( nleft + pos + 0, pos + nCenterY - 4, m_clrTab );
cacheDC->SetPixel( nleft + pos + 3, pos + nCenterY - 4, m_clrTab );
cacheDC->SetPixel( nleft + pos + 0, -pos + nCenterY + 2, m_clrTab );
cacheDC->SetPixel( nleft + pos + 3, -pos + nCenterY + 2, m_clrTab );
}
}
//////////////////////////////////////
str = str.Right(str.GetLength()-nPos-1);
nPos = str.Find(_T("\t"));
//重置输出字符串宽度
sizeContinueStr.cx = 0;
}
JudgeInSeletioAndDrawText( cacheDC, rcLine, str, clrBkColor, clr );
//保存已输出的字符中宽度
CSize sizeTemp = cacheDC->GetTextExtent(str);
sizeContinueStr += sizeTemp;
}
}
if(m_bShowReturnLineToken)
DrawReturnLineToken( m_bRealReturn, cacheDC, rcLine );
//显示下划线
if(m_bShowUnderLine) {
CPen NewPen(PS_SOLID, 1, m_clrUnderLine);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -