项目中要使用代码编辑器,搜索之后,发现了强大的编辑器控件Scintilla。
摘自:http://www.qingfengju.com/article.asp?id=14
1.简介
Scintilla是一款开源的语法高亮编辑器控件,官方网站:http://www.scintilla.org/。Scintilla是最优秀的编辑控件之一,实现了语法高亮,代码折叠,书签,自动完成等等诸多功能,速度快,源代码也比较好理解,易于扩展,易于增加对新语言的支持。 比较著名的scite,Notepad++,Notepad2都是基于Scintilla开发的。
Scintilla提供了Win32版本和Linux版本,在Windows下,它是一个窗体控件,对它的控制都通过发送消息来完成:
LRESULT SendMessage(HWND hWndScintilla,UINT Msg,WPARAM wParam,LPARAM lParam);
Scintilla提供了大量的消息API,每个消息可以带有0个、1个或2个参数。SendMessage函数中的消息,通常带有2个参数:wParam和lParam,没有使用的参数,则设置为0。对于大多数SCI_SETxxxxx设置类消息,都会有一个对应的SCI_GETxxxxx查询消息。
下面的图片是一个数控NC程序编辑器Demo:
2.在MFC中的简单使用
2.1 载入Scintilla链接库
首先,将SciLexer.dll复制到项目中。
a.在CwinApp下添加成员:
HMODULE m_hDll;
并初始化为NULL。
b.在InitInstance中载入DLL:
- m_hDll = LoadLibrary(_T("SciLexer.dll"));
- if (m_hDll==NULL)
- {
- AfxMessageBox("LoadLibrary SciLexer.dll failure...");
- }
c.在ExitInstance中卸载DLL:
- if (m_hDll != NULL)
- {
- FreeLibrary(m_hDll);
- }
2.2 创建一个封装Scintilla的类 — CScintillaWnd
- //-------------------------------------------------------
- // ScintillaWnd.h
- #pragma once
- //注意:这俩文件来自Scintilla的include目录
- #include "Scintilla.h"
- #include "SciLexer.h"
- // CScintillaWnd
- class CScintillaWnd : public CWnd
- {
- DECLARE_DYNAMIC(CScintillaWnd)
- public:
- CScintillaWnd();
- virtual ~CScintillaWnd();
- protected:
- DECLARE_MESSAGE_MAP()
- public:
- virtual BOOL Create(
- DWORD dwExStyle, DWORD dwStyle,const RECT& rect,
- CWnd* pParentWnd, UINT nID);
- };
- //-------------------------------------------------------
- // ScintillaWnd.cpp : 实现文件
- #include "stdafx.h"
- #include "ColorTextBox.h"
- #include "ScintillaWnd.h"
- // CScintillaWnd
- IMPLEMENT_DYNAMIC(CScintillaWnd, CWnd)
- CScintillaWnd::CScintillaWnd()
- {
- }
- CScintillaWnd::~CScintillaWnd(){}
- BEGIN_MESSAGE_MAP(CScintillaWnd, CWnd)
- END_MESSAGE_MAP()
- // CScintillaWnd 消息处理程序
- BOOL CScintillaWnd::Create(DWORD dwExStyle, DWORD dwStyle,const RECT& rect, CWnd* pParentWnd, UINT nID)
- {
- // TODO: 在此添加专用代码和/或调用基类
- return CWnd::CreateEx(dwExStyle,"Scintilla","",dwStyle,rect,pParentWnd,nID);
- }
2.3 CScintillaWnd类的使用举例
将CScintillaWnd类的对象作为目标窗体类的一个成员,比如某个对话框CColorTextBoxDlg 。
- class CColorTextBoxDlg : public CDialog
- {
- // 构造
- public:
- CColorTextBoxDlg(CWnd* pParent = NULL); // 标准构造函数
- // 对话框数据
- enum { IDD = IDD_COLORTEXTBOX_DIALOG };
- protected:
- virtual void DoDataExchange(CDataExchange* pDX);
- //..
- public:
- CScintillaWnd m_ScintillaWnd;
- afx_msg int OnCreate(LPCreateSTRUCT lpCreateStruct);
- afx_msg void OnSize(UINT nType, int cx, int cy);
- };
a.在CcolorTextBoxDlg的OnCreate中创建Scintilla窗口:
- m_ScintillaWnd.Create(
- WS_EX_CLIENTEDGE,
- WS_CHILD | WS_VISIBLE,
- CRect(0,0,lpCreateStruct->cx,lpCreateStruct->cy),
- this,10000);
b.在CcolorTextBoxDlg的OnSize中调整Scintilla窗口的大小:
- m_ScintillaWnd.MoveWindow(0,0,cx,cy);
2.4 接收来自Scintilla控件的通知
当Scintilla控件发生事件时,会用WM_NOTITY消息通知父窗体。
2.4.1在MFC中
- CxxxxWnd::OnNotify(WPARAM wParam, LPARAM lParam, LRESULT* pResult)
- {
- // TODO: 在此添加专用代码和/或调用基类
- LPNMHDR pnmh = (LPNMHDR)lParam;
- struct SCNotification* scn = (struct SCNotification*)lParam;
- switch(pnmh->code)
- {
- case SCN_CHARADDED:
- /* Hey, Scintilla just told me that a new */
- /* character was added to the Edit Control.*/
- /* Now i do something cool with that char. */
- break;
- //case ...
- }
- }
2.4.2在SDK中
- NMHDR *lpnmhdr;
- //[...]
- case WM_NOTIFY:
- lpnmhdr = (LPNMHDR) lParam;
- if(lpnmhdr->hwndFrom==hwndScintilla)
- {
- switch(lpnmhdr->code)
- {
- case SCN_CHARADDED:
- /* Hey, Scintilla just told me that a new */
- /* character was added to the Edit Control.*/
- /* Now i do something cool with that char. */
- break;
- }
- }
- break;
2.5 例:行号的显示(MFC)
要显示行号,首先需要在m_ScintillaWnd创建后立即调用以下函数,并在Scintilla通知主窗口SCN_MODIFIED,SCN_ZOOM时调用:
- void UpdateLineNumberWidth(void)
- {
- char tchLines[32];
- int iLineMarginWidthNow;
- int iLineMarginWidthFit;
- wsprintf(tchLines," %i ",
- m_ScintillaWnd.SendMessage(SCI_GETLINECOUNT,0,0));
- iLineMarginWidthNow = m_ScintillaWnd.SendMessage(
- SCI_GETMARGINWIDTHN,0,0);
- iLineMarginWidthFit = m_ScintillaWnd.SendMessage(
- SCI_TEXTWIDTH,STYLE_LINENUMBER,(LPARAM)tchLines);
- if (iLineMarginWidthNow != iLineMarginWidthFit)
- {
- m_ScintillaWnd.SendMessage(SCI_SETMARGINWIDTHN,0,
- iLineMarginWidthFit);
- }
- }
- BOOL OnNotify(
- WPARAM wParam, LPARAM lParam, LRESULT* pResult)
- {
- LPNMHDR pnmh = (LPNMHDR)lParam;
- struct SCNotification* scn = (struct SCNotification*)lParam;
- switch(pnmh->code)
- {
- case SCN_MODIFIED:
- //This notification is sent when the text or
- //styling of the document changes or is about
- //to change
- case SCN_ZOOM:
- //This notification is generated when the user
- //zooms the display using the keyboard or the
- //SCI_SETZOOM method is called.
- UpdateLineNumberWidth();
- break;
- }
- return CDialog::OnNotify(wParam, lParam, pResult);
- }
2.6 例:设置文本,打开已有文件(MFC)
- void OnMenuFileOpen()
- {
- CFileDialog fDlg(TRUE);
- if (fDlg.DoModal()==IDOK)
- {
- char *pBuffer;
- CString strFilePath=fDlg.GetPathName();
- CStdioFile stdFile(strFilePath,CFile::modeRead);
- UINT nFileLength=stdFile.GetLength();
- pBuffer=new char[nFileLength+1];
- nFileLength=stdFile.Read(pBuffer,nFileLength);
- stdFile.Close();
- if (nFileLength>0)
- {
- if (m_ScintillaWnd.SendMessage(SCI_GETREADONLY,0,0))
- {
- m_ScintillaWnd.SendMessage(SCI_SETREADONLY,FALSE,0);
- }
- m_ScintillaWnd.SendMessage(SCI_CANCEL,0,0);
- m_ScintillaWnd.SendMessage(SCI_SETUNDOCOLLECTION,0,0);
- m_ScintillaWnd.SendMessage(SCI_EMPTYUNDOBUFFER,0,0);
- //如果文本没有只读属性,则清除所有文字。
- m_ScintillaWnd.SendMessage(SCI_CLEARALL,0,0);
- //从所有行中删除标记,若markerNumber=-1,则删除所有标记。
- m_ScintillaWnd.SendMessage(SCI_MARKERDeleteALL,
- (WPARAM)-1,0);
- m_ScintillaWnd.SendMessage(SCI_ADDTEXT,
- nFileLength,(LPARAM)pBuffer);
- m_ScintillaWnd.SendMessage(SCI_SETUNDOCOLLECTION,1,0);
- m_ScintillaWnd.SendMessage(EM_EMPTYUNDOBUFFER,0,0);
- m_ScintillaWnd.SendMessage(SCI_SETSAVEPOINT,0,0);
- m_ScintillaWnd.SendMessage(SCI_GOTOPOS,0,0);
- m_ScintillaWnd.SendMessage(SCI_CHOOSECARETX,0,0);
- UpdateLineNumberWidth();
- }
- delete [] pBuffer;
- }
- }
2.7 例:设置默认字体以及字体大小(MFC)
- void SetDefaultFont(int nSize,const char* face)
- {
- m_ScintillaWnd.SendMessage(SCI_STYLESETFORE,
- STYLE_DEFAULT, RGB(0x00,0x00,0x00));
- m_ScintillaWnd.SendMessage(SCI_STYLESETBACK,
- STYLE_DEFAULT, RGB(0xff,0xff,0xff));
- m_ScintillaWnd.SendMessage(SCI_STYLESETSIZE,
- STYLE_DEFAULT, nSize);
- m_ScintillaWnd.SendMessage(SCI_STYLESETFONT,
- STYLE_DEFAULT, reinterpret_cast<LPARAM>(face));
- }
2.8 Lexer的编写
要实现一个新的语言(比如M语言)的语法高亮,需要在源代码级别对Scintilla做一点修改,并重新编译Scintilla:
2.8.1 加入一个源代码文件:
LexM.cxx
2.8.2 实现如下函数:
- static void ColouriseMDoc (
unsigned int startPos,
int length,
int initStyle,
WordList *keywordlists[],
Accessor &styler);
styler参数是一个Accessor对象,Lexer必须用这个对象来访问将要颜色化的文本。
Lexer用styler.SafeGetCharAt(i)来取得位置在i的字符;
startPos和length参数表示需要颜色化的文本的范围;
Lexer为所有在startPos到startPos+length范围内的字符决定恰当的颜色。
initStyle参数表示最初的状态, 也就是startPos之前一个字符的状态。状态也是表示指定范围内的文本的颜色。
注意: startPos位置的字符被事先假定为一行的开始, 如果新的一行终止了inisStyle状态Lexer应该进入默认状态(或者说任何状态应该在initStyle之后).
keywordlists参数指定Lexer应该识别的关键词,WordList对象包含识别关键词的方法,Present Lexers 用一个classifyWordLLL函数来识别关键词.这些函数展示怎么用keywordlists参数去识别关键词.
2.8.3 通过如下方式调用该函数:
- LexerModule lmM(SCLEX_M, ColouriseMDoc, "m", 0, asmWordListDesc);
2.8.4 在KeyWords.cxx文件中调用LINK_LEXER(lmM)宏。
3.备注
细节请参看Scintilla的帮助文档:http://www.scintilla.org/ScintillaDoc.html。