Scintilla在WTL中的簡易使用

Scintilla在WTL中的簡易使用

Scintilla是一個開源的源代碼編輯組件。在這裏記錄下它在WTL中的簡易使用。

在 WTL 中使用 Scintilla 的一個比較成功的案例是pnotepadProgrammer's Notepad Development

大家可以訪問 http://code.google.com/p/pnotepad/ ,不過其現在代碼龐大,閱讀有一定難度。

現提供一玩具級的演示示例,如下:

下面將簡單介紹在WTLSDI框架下如何使用Scintilla。本文使用的是Scintilla 3.2.5。

1.   下載Scintilla源代碼(http://www.scintilla.org/ScintillaDownload.html

編譯參照 Scintilla 3.2.5在VC中的編譯(動態、靜態)

本文使用3.2.5版本

2.新建一WTL SDI項目,如:WtlScintillaSDI


3.(動態、靜態) 使用 Scintilla 代碼初始化。

在 WtlScintillaSDI.cpp 中添加如下代碼:

#ifdef STATIC_BUILD_SCI
#include "Scintilla.h"
#endif
int WINAPI _tWinMain(HINSTANCE hInstance, HINSTANCE /*hPrevInstance*/, LPTSTR lpstrCmdLine, int nCmdShow)
{
#ifdef STATIC_BUILD_SCI
	Scintilla_RegisterClasses( hInstance );
#else
    HMODULE m_hSciLexerDll = NULL;
	m_hSciLexerDll = LoadLibrary(_T("SciLexer.dll"));
	if (NULL == m_hSciLexerDll)
	{
		MessageBox( NULL, _T("LoadLibrary SciLexer.dll failure..."), _T("Error"), MB_ICONSTOP | MB_OK );
		return FALSE;
	}
#endif

	HRESULT hRes = ::CoInitialize(NULL);
// If you are running on NT 4.0 or higher you can use the following call instead to 
// make the EXE free threaded. This means that calls come in on a random RPC thread.
//	HRESULT hRes = ::CoInitializeEx(NULL, COINIT_MULTITHREADED);
	ATLASSERT(SUCCEEDED(hRes));

	// this resolves ATL window thunking problem when Microsoft Layer for Unicode (MSLU) is used
	::DefWindowProc(NULL, 0, 0, 0L);

	AtlInitCommonControls(ICC_COOL_CLASSES | ICC_BAR_CLASSES);	// add flags to support other controls

	hRes = _Module.Init(NULL, hInstance);
	ATLASSERT(SUCCEEDED(hRes));

	int nRet = Run(lpstrCmdLine, nCmdShow);

	_Module.Term();
	::CoUninitialize();

#ifdef STATIC_BUILD_SCI
	Scintilla_ReleaseResources();
#else
	if( m_hSciLexerDll != NULL )
	{
		::FreeLibrary( m_hSciLexerDll );
	}
#endif

	return nRet;
}

4. 在 WtlScintillaSDIView.h 增加一 Scintilla 封裝類,繼承自CWindowImpl,如 CScintillaWnd

#include "Scintilla.h"
#include "SciLexer.h"

class CScintillaWnd : public CWindowImpl<CScintillaWnd>
{
public:
	DECLARE_WND_CLASS(NULL)

	CScintillaWnd()
	{
	}

	BEGIN_MSG_MAP(CScintillaWnd)
	END_MSG_MAP()


	HWND Create( HWND hWndParent, const ATL::_U_RECT& rect, DWORD dwExStyle = 0 )
	{
		// TODO: 在此添加專用代碼和/或調用基類
		return CWindow::Create( _T("Scintilla"), hWndParent, rect, _T(""), WS_CHILD | WS_VISIBLE, dwExStyle );
	}


	/////////////////////////////////////
	// @mfunc init the view with reasonable defaults
	// @rvalue void | not used
	//
	void Init()
	{
		// clear all text styles
		SendMessage(SCI_CLEARDOCUMENTSTYLE, 0, 0);
		// set the number of styling bits to 7 - the asp/html views need a lot of styling - default is 5
		// If you leave the default you will see twiggle lines instead of ASP code
		SendMessage(SCI_SETSTYLEBITS, 7, 0);
		// set the display for indetation guides to on - this displays virtical dotted lines from the beginning of
		// a code block to the end of the block
		SendMessage(SCI_SETINDENTATIONGUIDES, TRUE, 0);
		// set tabwidth to 4
		SendMessage(SCI_SETTABWIDTH,4,0);
		// set indention to 4
		SendMessage(SCI_SETINDENT,4,0);
		// set the caret blinking time to 400 milliseconds
		SendMessage(SCI_SETCARETPERIOD,400,0);
		// display fold margins
		SetFold();
		// hide SelectionMargin
		SendMessage( SCI_SETMARGINWIDTHN, 1, 0 );
		// set markersymbol for marker type 0 - bookmark
		SendMessage(SCI_MARKERDEFINE, 0, SC_MARK_CIRCLE);
		// set the forground color for some styles
		SendMessage(SCI_STYLESETFORE, 0, RGB(0,0,0));
		SendMessage(SCI_STYLESETFORE, 2, RGB(0,64,0));
		SendMessage(SCI_STYLESETFORE, 5, RGB(0,0,255));
		SendMessage(SCI_STYLESETFORE, 6, RGB(200,20,0));
		SendMessage(SCI_STYLESETFORE, 9, RGB(0,0,255));
		SendMessage(SCI_STYLESETFORE, 10, RGB(255,0,64));
		SendMessage(SCI_STYLESETFORE, 11, RGB(0,0,0));
		// set the backgroundcolor of brace highlights
		SendMessage(SCI_STYLESETBACK, STYLE_BRACELIGHT, RGB(0,255,0));
		// set end of line mode to CRLF
		SendMessage(SCI_CONVERTEOLS, 2, 0);
		SendMessage(SCI_SETEOLMODE, 2, 0);
		//   SendMessage(SCI_SETVIEWEOL, TRUE, 0);

		//顯示當前行的淡黃色背景
		SendMessage(SCI_SETCARETLINEVISIBLE,TRUE,0);
		SendMessage(SCI_SETCARETLINEBACK, RGB(255,255,0),0);
		SendMessage(SCI_SETCARETLINEBACKALPHA,100,0);
	}

	void InitScintillaEdit(int nSize,const TCHAR* face)
	{
		Init();
		SetDefaultColorFont(nSize,face);
		UpdateLineNumberWidth();
	}

	void SetDefaultColorFont(int nSize,const TCHAR* face)
	{
		SendMessage(SCI_SETSELFORE,TRUE,RGB(255,255,255));
		//選中行的顏色
		SendMessage(SCI_SETSELBACK,TRUE,RGB(10,36,106));

		//默認文本顏色
		SendMessage(SCI_STYLESETFORE, STYLE_DEFAULT, RGB(0x00,0x00,0x00));
		SendMessage(SCI_STYLESETBACK, STYLE_DEFAULT, RGB(0xff,0xff,0xff));
		SendMessage(SCI_STYLESETSIZE, STYLE_DEFAULT, nSize);
		SendMessage(SCI_STYLESETFONT, STYLE_DEFAULT, reinterpret_cast<LPARAM>(face));
	}

	//自動Fold
	void SetFold( BOOL bFold = TRUE )
	{
		if( bFold )
		{
			// source folding section
			// tell the lexer that we want folding information - the lexer supplies "folding levels"
			SendMessage(SCI_SETPROPERTY, (WPARAM) "fold", (LPARAM) "1" );
			SendMessage(SCI_SETPROPERTY, (WPARAM) "fold.comment", (LPARAM) "1" );
			SendMessage(SCI_SETPROPERTY, (WPARAM) "fold.at.else", (LPARAM) "1" );
			SendMessage(SCI_SETPROPERTY, (WPARAM) "fold.preprocessor", (LPARAM) "1" );
			SendMessage(SCI_SETPROPERTY, (WPARAM) "styling.within.preprocessor", (LPARAM) "1" );

			SendMessage(SCI_SETMARGINTYPEN, 2, SC_MARGIN_SYMBOL);//頁邊類型
			SendMessage(SCI_SETMARGINMASKN, 2, SC_MASK_FOLDERS); //頁邊掩碼
			SetMarginWidthN(2, 16); //SendMessage(SCI_SETMARGINWIDTHN, 2, 16); //頁邊寬度
			SendMessage(SCI_SETMARGINSENSITIVEN, 2, TRUE); //響應鼠標消息

			// 摺疊標籤樣式
			SendMessage(SCI_MARKERDEFINE, SC_MARKNUM_FOLDER, SC_MARK_CIRCLEPLUS);
			SendMessage(SCI_MARKERDEFINE, SC_MARKNUM_FOLDEROPEN, SC_MARK_CIRCLEMINUS);
			SendMessage(SCI_MARKERDEFINE, SC_MARKNUM_FOLDEREND,  SC_MARK_CIRCLEPLUSCONNECTED);
			SendMessage(SCI_MARKERDEFINE, SC_MARKNUM_FOLDEROPENMID, SC_MARK_CIRCLEMINUSCONNECTED);
			SendMessage(SCI_MARKERDEFINE, SC_MARKNUM_FOLDERMIDTAIL, SC_MARK_TCORNERCURVE);

			SendMessage(SCI_MARKERDEFINE, SC_MARKNUM_FOLDERSUB, SC_MARK_VLINE);
			SendMessage(SCI_MARKERDEFINE, SC_MARKNUM_FOLDERTAIL, SC_MARK_LCORNERCURVE);

			// 摺疊標籤線顏色
			SendMessage(SCI_MARKERSETBACK, SC_MARKNUM_FOLDERSUB, 0xff0000); //藍色
			SendMessage(SCI_MARKERSETBACK, SC_MARKNUM_FOLDERMIDTAIL, 0xff0000);
			SendMessage(SCI_MARKERSETBACK, SC_MARKNUM_FOLDERTAIL, 0xff0000);

			SendMessage(SCI_SETFOLDFLAGS, 16, 0); //如果摺疊就在摺疊行的下各畫一條橫線
		}
		else
		{
			SetMarginWidthN(2, 0); //SendMessage( SCI_SETMARGINWIDTHN, 2, 0 ); // FoldMargin
		}
	}

	void ToggleFold(long line)
	{
		SendMessage(SCI_TOGGLEFOLD, static_cast<WPARAM>(line), 0);
	}

	int GetMarginWidthN(int margin)
	{
		return SendMessage(SCI_GETMARGINWIDTHN, static_cast<WPARAM>(margin), 0);
	}

	void SetMarginWidthN(int margin, int mask)
	{
		SendMessage(SCI_SETMARGINWIDTHN, static_cast<WPARAM>(margin), static_cast<LPARAM>(mask));
	}

	void UpdateLineNumberWidth(void)
	{
		//start 顯示行號
		long  iLineMarginWidthNow;
		long  iLineMarginWidthFit;
		long iLineNum = SendMessage( SCI_GETLINECOUNT, 0, 0 );
		long iLineNumCount = 1;
		while( iLineNum != 0 )
		{
			++iLineNumCount;
			iLineNum /= 10;
		}
		iLineMarginWidthNow = SendMessage( SCI_GETMARGINWIDTHN, 0, 0 );
		long charWidth = SendMessage( SCI_TEXTWIDTH, STYLE_LINENUMBER, (LPARAM)("9") );
		iLineMarginWidthFit = charWidth * iLineNumCount;
		if ( iLineMarginWidthNow != iLineMarginWidthFit )
		{
			SendMessage( SCI_SETMARGINWIDTHN, 0, iLineMarginWidthFit );
		}
		//end of 顯示行號
	}

	long LineFromPosition(long pos)
	{
		return SendMessage(SCI_LINEFROMPOSITION, static_cast<WPARAM>(pos), 0);
	}
};
5. 在 CWtlScintillaSDIView 創建 CScintillaWnd 並處理簡單消息
class CWtlScintillaSDIView : public CWindowImpl<CWtlScintillaSDIView>
{
public:
	DECLARE_WND_CLASS(NULL)

	CScintillaWnd * m_pScintillaCtrl;

	~CWtlScintillaSDIView()
	{
		if( NULL != m_pScintillaCtrl )
		{
			delete m_pScintillaCtrl;
		}
	}

	BOOL PreTranslateMessage(MSG* pMsg)
	{
		pMsg;
		return FALSE;
	}

	BEGIN_MSG_MAP(CWtlScintillaSDIView)
		MESSAGE_HANDLER(WM_NOTIFY, OnNotify)
		MESSAGE_HANDLER(WM_SIZE, OnSize)
		MESSAGE_HANDLER(WM_CREATE, OnCreate)
		MESSAGE_HANDLER(WM_PAINT, OnPaint)
	END_MSG_MAP()

// Handler prototypes (uncomment arguments if needed):
//	LRESULT MessageHandler(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
//	LRESULT CommandHandler(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/)
//	LRESULT NotifyHandler(int /*idCtrl*/, LPNMHDR /*pnmh*/, BOOL& /*bHandled*/)

	LRESULT OnPaint(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
	{
		CPaintDC dc(m_hWnd);

		//TODO: Add your drawing code here

		return 0;
	}

	LRESULT OnCreate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
	{
		RECT rcWin;
		GetClientRect( &rcWin );
		m_pScintillaCtrl = new CScintillaWnd();
		m_pScintillaCtrl->Create( m_hWnd, rcWin, 0 ); // cancel WS_EX_CLIENTEDGE ( 3D )
		m_pScintillaCtrl->InitScintillaEdit( 12, _T("Courier New") );
		m_pScintillaCtrl->ShowWindow( SW_SHOW );

		return 0;
	}

	LRESULT OnSize(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
	{
		RECT rcWin;
		GetClientRect( &rcWin );
		m_pScintillaCtrl->MoveWindow( &rcWin );

		return 0;
	}

	LRESULT OnNotify(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
	{
		struct SCNotification* scn = (struct SCNotification*)lParam;
		LPNMHDR pnmh = (LPNMHDR)lParam;
		switch(pnmh->code)
		{
			case SCN_MODIFIED://修改了文件
			case SCN_ZOOM://放大,縮小
				if( m_pScintillaCtrl->GetMarginWidthN(0) != 0 )
					m_pScintillaCtrl->UpdateLineNumberWidth();
				break;
			case SCN_MARGINCLICK://確定是Fold頁邊點擊事件
				{
					long n = m_pScintillaCtrl->LineFromPosition( scn->position );
					m_pScintillaCtrl->ToggleFold( n );
				}
				break;
			case SCN_UPDATEUI://界面更新(單擊鼠標,按下箭頭等)
				break;
		}

		return 0;
	}
};
現在就可以編譯運行,添加 Include 路徑 scintilla\include

A 需要將事先編譯好的 SciLexer.dll放到項目目錄下。

B 靜態鏈接 ScintillaLib.lib 使用 /MT編譯

( 參考Scintilla 3.2.5在VC中的編譯(動態、靜態)

靜態鏈接需在工程屬性,Linker→Input→Additional Dependencies填入ScintillaLib.lib IMM32.lib

並添加C預處理程序定義 STATIC_BUILD_SCI


6.   CScintillaWnd 添加 C++ 語法高亮

//  
const char cppKeyWords[] =  
"and and_eq asm auto bitand bitor bool break "  
"case catch char class compl const const_cast continue "  
"default delete do double dynamic_cast else enum explicit export extern false float for "  
"friend goto if inline int long mutable namespace new not not_eq "  
"operator or or_eq private protected public "  
"register reinterpret_cast return short signed sizeof static static_cast struct switch "  
"template this throw true try typedef typeid typename union unsigned using "  
"virtual void volatile wchar_t while xor xor_eq ";  
    void SetCppSyntax()  
    {  
        SendMessage( SCI_SETLEXER, SCLEX_CPP );  
        SendMessage(SCI_SETKEYWORDS, 0, (LPARAM)cppKeyWords );  
        // 下面設置各種語法元素前景色  
        SendMessage(SCI_STYLESETFORE, SCE_C_WORD, 0x00FF0000);   //關鍵字  
        SendMessage(SCI_STYLESETFORE, SCE_C_STRING, 0x001515A3); //字符串  
        SendMessage(SCI_STYLESETFORE, SCE_C_CHARACTER, 0x001515A3); //字符  
        SendMessage(SCI_STYLESETFORE, SCE_C_PREPROCESSOR, 0x00808080);//預編譯開關  
        SendMessage(SCI_STYLESETFORE, SCE_C_COMMENT, 0x00008000);//塊註釋  
        SendMessage(SCI_STYLESETFORE, SCE_C_COMMENTLINE, 0x00008000);//行註釋  
        SendMessage(SCI_STYLESETFORE, SCE_C_COMMENTDOC, 0x00008000);//文檔註釋(/**開頭)  
        SendMessage(SCI_STYLESETFORE, SCE_C_NUMBER, 0x000010ff);//數字  
        SendMessage(SCI_STYLESETFORE, SCE_C_OPERATOR, 0x0000c0f0);//操作  
    }
修改

	void InitScintillaEdit(int nSize,const TCHAR* face)
	{
		SetCppSyntax();
		Init();
		SetDefaultColorFont(nSize,face);
		UpdateLineNumberWidth();
	}
現在輸入的C++代碼就會有語法高亮,並有Fold


6, 增加OpenFile()

下面演示一簡易版

BOOL OpenFile(LPCTSTR lpFileName)
{
	HANDLE m_hFile = INVALID_HANDLE_VALUE;
	// map modeNoInherit flag
	SECURITY_ATTRIBUTES sa;
	sa.nLength = sizeof(sa);
	sa.lpSecurityDescriptor = NULL;
	sa.bInheritHandle = 0; //modeNoInherit
	m_hFile = ::CreateFile( lpFileName, GENERIC_READ, FILE_SHARE_WRITE | FILE_SHARE_READ, &sa, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL );
	ULONG nFileLength = static_cast<ULONG>( ::GetFileSize( m_hFile, NULL ) );

	char *pBuffer;
	pBuffer=new char[nFileLength+1];

	BOOL bRet = ::ReadFile( m_hFile, pBuffer, nFileLength, &nFileLength, NULL );

	::CloseHandle( m_hFile );

	if (SendMessage(SCI_GETREADONLY,0,0))
	{
		SendMessage(SCI_SETREADONLY,FALSE,0);
	}

	SendMessage(SCI_CANCEL,0,0);
	SendMessage(SCI_SETUNDOCOLLECTION,0,0);
	SendMessage(SCI_EMPTYUNDOBUFFER,0,0);

	//如果文本沒有隻讀屬性,則清除所有文字。
	SendMessage(SCI_CLEARALL,0,0);

	//從所有行中刪除標記,若markerNumber=-1,則刪除所有標記。
	SendMessage(SCI_MARKERDELETEALL,(WPARAM)-1,0);

	SendMessage(SCI_ADDTEXT,nFileLength,(LPARAM)pBuffer); //SetText

	SendMessage(SCI_SETUNDOCOLLECTION,1,0);
	SendMessage(EM_EMPTYUNDOBUFFER,0,0);
	SendMessage(SCI_SETSAVEPOINT,0,0);
	SendMessage(SCI_GOTOPOS,0,0);
	SendMessage(SCI_CHOOSECARETX,0,0);

	UpdateLineNumberWidth();

	delete [] pBuffer;

	return TRUE;
}


VC2005 演示代碼 WtlScintillaSDI-vc2005.7z http://pan.baidu.com/share/link?shareid=386165&uk=201934825

http://blog.csdn.net/gocad/article/details/8826303


參考資料:
1.Scintilla Documentation  http://www.scintilla.org/ScintillaDoc.html

2.Scintilla 在MFC中的簡易使用(動態、靜態)

3.Scintilla的高級技法 http://yp.oss.org.cn/software/show_resource.php?resource_id=854


補充資料,以下爲 Gilad Novik 提供的 AtlScintilla.h an easy wrapper for the Scintilla control, to be used with ATL/WTL projects.

Date: 12/15/2005 http://sourceforge.net/p/scintilla/feature-requests/283/

Date: 06/01/2006 http://code.google.com/p/winx/source/browse/tags/scintilla-1.77/wtl?r=1519

SendMessage(SCI_USEPOPUP,0,0); //可關閉Scintilla 右鍵菜單
visualfc 一個不錯的 WTL 開發輔助工具,提供方便的 Windows消息,菜單/控件消息/通知,DDX/DDV,UpdateUI 增刪函數映射功能。

 大家可從 http://code.google.com/p/visualfc/ 下載, 從 visualfc bolg :http://blog.csdn.net/visualfc/ 得到幫助

下面爲 在Code::Blocks 12.11 中使用的截圖

更新 VisualFC 0.82 使其正確列舉出所有菜單項 ID

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章