?? 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間隔
PARAFORMAT2 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 + -