语法高亮编辑控件Scintilla在MFC中的简单使用

 

项目中要使用代码编辑器,搜索之后,发现了强大的编辑器控件Scintilla。

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个参数:wParamlParam,没有使用的参数,则设置为0。对于大多数SCI_SETxxxxx设置类消息,都会有一个对应的SCI_GETxxxxx查询消息。

下面的图片是一个数控NC程序编辑器Demo:
语法高亮编辑控件Scintilla在MFC中的简单使用_第1张图片

2.在MFC中的简单使用

2.1 载入Scintilla链接库

首先,将SciLexer.dll复制到项目中。

a.CwinApp下添加成员:

HMODULE m_hDll;

并初始化为NULL

b.InitInstance中载入DLL

C++代码
  1. m_hDll = LoadLibrary(_T("SciLexer.dll"));   
  2. if (m_hDll==NULL)   
  3. {   
  4.    AfxMessageBox("LoadLibrary SciLexer.dll failure...");   
  5. }  

c.ExitInstance中卸载DLL

C++代码
  1. if (m_hDll != NULL)   
  2. {      
  3.   FreeLibrary(m_hDll);   
  4. }  

2.2 创建一个封装Scintilla的类 — CScintillaWnd

C++代码
  1. //-------------------------------------------------------   
  2. // ScintillaWnd.h   
  3. #pragma once   
  4.   
  5. //注意:这俩文件来自Scintilla的include目录   
  6. #include "Scintilla.h"   
  7. #include "SciLexer.h"   
  8. // CScintillaWnd   
  9. class CScintillaWnd : public CWnd   
  10. {   
  11.     DECLARE_DYNAMIC(CScintillaWnd)   
  12. public:   
  13.     CScintillaWnd();   
  14.     virtual ~CScintillaWnd();   
  15. protected:   
  16.     DECLARE_MESSAGE_MAP()   
  17. public:   
  18.     virtual BOOL Create(   
  19.         DWORD dwExStyle, DWORD dwStyle,const RECT& rect,    
  20.         CWnd* pParentWnd, UINT nID);   
  21.   
  22. };   
  23.   
  24. //-------------------------------------------------------   
  25.   
  26. // ScintillaWnd.cpp : 实现文件   
  27. #include "stdafx.h"   
  28. #include "ColorTextBox.h"   
  29. #include "ScintillaWnd.h"   
  30.   
  31. // CScintillaWnd   
  32. IMPLEMENT_DYNAMIC(CScintillaWnd, CWnd)   
  33. CScintillaWnd::CScintillaWnd()   
  34. {   
  35. }   
  36.   
  37. CScintillaWnd::~CScintillaWnd(){}   
  38.   
  39. BEGIN_MESSAGE_MAP(CScintillaWnd, CWnd)   
  40. END_MESSAGE_MAP()   
  41.   
  42. // CScintillaWnd 消息处理程序   
  43. BOOL CScintillaWnd::Create(DWORD dwExStyle, DWORD dwStyle,const RECT& rect, CWnd* pParentWnd, UINT nID)   
  44.   
  45. {   
  46.     // TODO: 在此添加专用代码和/或调用基类   
  47.     return CWnd::CreateEx(dwExStyle,"Scintilla","",dwStyle,rect,pParentWnd,nID);   
  48. }   

2.3 CScintillaWnd类的使用举例

   CScintillaWnd类的对象作为目标窗体类的一个成员,比如某个对话框CColorTextBoxDlg 。

C++代码
  1. class CColorTextBoxDlg : public CDialog   
  2. {   
  3. // 构造   
  4. public:   
  5.     CColorTextBoxDlg(CWnd* pParent = NULL); // 标准构造函数   
  6. // 对话框数据   
  7.     enum { IDD = IDD_COLORTEXTBOX_DIALOG };   
  8. protected:   
  9.     virtual void DoDataExchange(CDataExchange* pDX);       
  10.     //..   
  11. public:   
  12.     CScintillaWnd m_ScintillaWnd;   
  13.     afx_msg int OnCreate(LPCreateSTRUCT lpCreateStruct);   
  14.     afx_msg void OnSize(UINT nType, int cx, int cy);   
  15. };  

    a.CcolorTextBoxDlgOnCreate中创建Scintilla窗口:

C++代码
  1. m_ScintillaWnd.Create(   
  2.         WS_EX_CLIENTEDGE,   
  3.         WS_CHILD | WS_VISIBLE,   
  4.         CRect(0,0,lpCreateStruct->cx,lpCreateStruct->cy),   
  5.         this,10000);  

   b.CcolorTextBoxDlgOnSize中调整Scintilla窗口的大小:

C++代码
  1. m_ScintillaWnd.MoveWindow(0,0,cx,cy);  

2.4 接收来自Scintilla控件的通知

    Scintilla控件发生事件时,会用WM_NOTITY消息通知父窗体。

2.4.1MFC

C++代码
  1. CxxxxWnd::OnNotify(WPARAM wParam, LPARAM lParam, LRESULT* pResult)   
  2. {   
  3.     // TODO: 在此添加专用代码和/或调用基类   
  4.     LPNMHDR pnmh = (LPNMHDR)lParam;   
  5.     struct SCNotification* scn = (struct SCNotification*)lParam;   
  6.   
  7.     switch(pnmh->code)   
  8.     {    
  9.         case SCN_CHARADDED:   
  10.   
  11.             /* Hey, Scintilla just told me that a new */  
  12.             /* character was added to the Edit Control.*/  
  13.             /* Now i do something cool with that char. */  
  14.             break;   
  15.   
  16.         //case ...   
  17.     }   
  18. }   

2.4.2SDK

C++代码
  1. NMHDR *lpnmhdr;   
  2. //[...]   
  3. case WM_NOTIFY:   
  4. lpnmhdr = (LPNMHDR) lParam;   
  5. if(lpnmhdr->hwndFrom==hwndScintilla)   
  6. {   
  7.     switch(lpnmhdr->code)   
  8.     {   
  9.         case SCN_CHARADDED:   
  10.             /* Hey, Scintilla just told me that a new */  
  11.             /* character was added to the Edit Control.*/  
  12.             /* Now i do something cool with that char. */  
  13.         break;   
  14.     }   
  15. }   
  16. break;  

2.5 例:行号的显示(MFC)

    要显示行号,首先需要在m_ScintillaWnd创建后立即调用以下函数,并在Scintilla通知主窗口SCN_MODIFIEDSCN_ZOOM时调用:

C++代码
  1. void UpdateLineNumberWidth(void)   
  2. {   
  3.     char tchLines[32];   
  4.     int  iLineMarginWidthNow;   
  5.     int  iLineMarginWidthFit;   
  6.     wsprintf(tchLines," %i ",   
  7. m_ScintillaWnd.SendMessage(SCI_GETLINECOUNT,0,0));   
  8.     iLineMarginWidthNow = m_ScintillaWnd.SendMessage(   
  9. SCI_GETMARGINWIDTHN,0,0);   
  10.     iLineMarginWidthFit = m_ScintillaWnd.SendMessage(   
  11. SCI_TEXTWIDTH,STYLE_LINENUMBER,(LPARAM)tchLines);   
  12.   
  13.     if (iLineMarginWidthNow != iLineMarginWidthFit)    
  14.     {   
  15. m_ScintillaWnd.SendMessage(SCI_SETMARGINWIDTHN,0,   
  16. iLineMarginWidthFit);    
  17.     }   
  18. }   
  19.   
  20. BOOL OnNotify(   
  21. WPARAM wParam, LPARAM lParam, LRESULT* pResult)   
  22. {   
  23.     LPNMHDR pnmh = (LPNMHDR)lParam;   
  24.    struct SCNotification* scn = (struct SCNotification*)lParam;   
  25.   
  26.     switch(pnmh->code)   
  27.     {   
  28.             case SCN_MODIFIED:   
  29.             //This notification is sent when the text or    
  30.             //styling of the document changes or is about    
  31.             //to change   
  32.             case SCN_ZOOM:   
  33.             //This notification is generated when the user   
  34.             //zooms the display using the keyboard or the   
  35.             //SCI_SETZOOM method is called.   
  36.             UpdateLineNumberWidth();   
  37.             break;   
  38.   
  39.     }   
  40.     return CDialog::OnNotify(wParam, lParam, pResult);   
  41. }  

2.6 例:设置文本,打开已有文件(MFC)

C++代码
  1. void OnMenuFileOpen()   
  2. {   
  3.     CFileDialog fDlg(TRUE);   
  4.     if (fDlg.DoModal()==IDOK)   
  5.     {   
  6.         char *pBuffer;   
  7.         CString strFilePath=fDlg.GetPathName();   
  8.         CStdioFile stdFile(strFilePath,CFile::modeRead);   
  9.         UINT nFileLength=stdFile.GetLength();   
  10.         pBuffer=new char[nFileLength+1];   
  11.         nFileLength=stdFile.Read(pBuffer,nFileLength);   
  12.         stdFile.Close();   
  13.   
  14.         if (nFileLength>0)   
  15.         {   
  16.             if (m_ScintillaWnd.SendMessage(SCI_GETREADONLY,0,0))   
  17.             {      
  18.                 m_ScintillaWnd.SendMessage(SCI_SETREADONLY,FALSE,0);   
  19.             }   
  20.             m_ScintillaWnd.SendMessage(SCI_CANCEL,0,0);   
  21.             m_ScintillaWnd.SendMessage(SCI_SETUNDOCOLLECTION,0,0);   
  22.             m_ScintillaWnd.SendMessage(SCI_EMPTYUNDOBUFFER,0,0);        
  23.   
  24. //如果文本没有只读属性,则清除所有文字。   
  25. m_ScintillaWnd.SendMessage(SCI_CLEARALL,0,0);   
  26.   
  27. //从所有行中删除标记,若markerNumber=-1,则删除所有标记。   
  28. m_ScintillaWnd.SendMessage(SCI_MARKERDeleteALL,   
  29. (WPARAM)-1,0);   
  30.             m_ScintillaWnd.SendMessage(SCI_ADDTEXT,   
  31. nFileLength,(LPARAM)pBuffer);   
  32.             m_ScintillaWnd.SendMessage(SCI_SETUNDOCOLLECTION,1,0);   
  33.             m_ScintillaWnd.SendMessage(EM_EMPTYUNDOBUFFER,0,0);   
  34.             m_ScintillaWnd.SendMessage(SCI_SETSAVEPOINT,0,0);   
  35.             m_ScintillaWnd.SendMessage(SCI_GOTOPOS,0,0);   
  36.             m_ScintillaWnd.SendMessage(SCI_CHOOSECARETX,0,0);   
  37.             UpdateLineNumberWidth();   
  38.         }     
  39.         delete [] pBuffer;   
  40.     }   
  41. }   

2.7 例:设置默认字体以及字体大小(MFC)

C++代码
  1. void SetDefaultFont(int nSize,const char* face)   
  2. {   
  3.     m_ScintillaWnd.SendMessage(SCI_STYLESETFORE,    
  4. STYLE_DEFAULT, RGB(0x00,0x00,0x00));   
  5.     m_ScintillaWnd.SendMessage(SCI_STYLESETBACK,    
  6. STYLE_DEFAULT, RGB(0xff,0xff,0xff));   
  7.     m_ScintillaWnd.SendMessage(SCI_STYLESETSIZE,    
  8. STYLE_DEFAULT, nSize);   
  9.     m_ScintillaWnd.SendMessage(SCI_STYLESETFONT,    
  10. STYLE_DEFAULT, reinterpret_cast<LPARAM>(face));   
  11. }  

2.8 Lexer的编写

要实现一个新的语言(比如M语言)的语法高亮,需要在源代码级别对Scintilla做一点修改,并重新编译Scintilla

2.8.1 加入一个源代码文件:

LexM.cxx

2.8.2 实现如下函数:

C++代码
  1. static void ColouriseMDoc (
    unsigned 
    int startPos, 
    int length,
    int initStyle,
    WordList *keywordlists[],
    Accessor &styler);  

styler参数是一个Accessor对象,Lexer必须用这个对象来访问将要颜色化的文本。
Lexerstyler.SafeGetCharAt(i)来取得位置在i的字符;
startPoslength参数表示需要颜色化的文本的范围;
Lexer
为所有在startPosstartPos+length范围内的字符决定恰当的颜色。
initStyle参数表示最初的状态, 也就是startPos之前一个字符的状态。状态也是表示指定范围内的文本的颜色。

注意:  startPos位置的字符被事先假定为一行的开始, 如果新的一行终止了inisStyle状态Lexer应该进入默认状态(或者说任何状态应该在initStyle之后).

keywordlists参数指定Lexer应该识别的关键词,WordList对象包含识别关键词的方法,Present Lexers 用一个classifyWordLLL函数来识别关键词.这些函数展示怎么用keywordlists参数去识别关键词.

2.8.3 通过如下方式调用该函数:

C++代码
  1. LexerModule lmM(SCLEX_M, ColouriseMDoc, "m", 0, asmWordListDesc);  

2.8.4 KeyWords.cxx文件中调用LINK_LEXER(lmM)宏。

3.备注

细节请参看Scintilla的帮助文档:http://www.scintilla.org/ScintillaDoc.html。

你可能感兴趣的:(语法高亮编辑控件Scintilla在MFC中的简单使用)