项目中要使用代码编辑器,搜索之后,发现了强大的编辑器控件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:

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.CcolorTextBoxDlgOnCreate中创建Scintilla窗口:

m_ScintillaWnd.Create(   
WS_EX_CLIENTEDGE,   
WS_CHILD | WS_VISIBLE,   
CRect(0,0,lpCreateStruct->cx,lpCreateStruct->cy),   
this,10000);

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

m_ScintillaWnd.MoveWindow(0,0,cx,cy);

2.4 接收来自Scintilla控件的通知

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

2.4.1MFC

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.2SDK

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_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中的简单使用
https://blog.qingfengju.com/index.asp?id=14

上一篇: 汶川大地震1周年祭
下一篇: 语法高亮编辑控件Scintilla在MFC中的简单使用之完整示例

分类:Win32/C++ 查看次数:23267 发布时间:2009/5/12 18:45:52