Scintilla控件在Win32环境下的使用(二)

这节的目标就是写出语法高亮,代码折叠这两个效果。

还是首先给大家发个效果图看一下:


主要参考文档:

http://www.cnblogs.com/superanyi/archive/2011/04/07/2008632.html

http://www.cnblogs.com/superanyi/archive/2011/04/07/2008636.html

当然官方文档是必不可少的。

其实有了第一节的工作,如果你玩过vim,那么接下来的工作有点类似于配置.vimrc文件了。

只不过比配置vimrc文件麻烦的是我们要自己确定发送消息的时机。


让Scintilla支持语法高亮

有了前面的SendEditor控制函数,我们就可以配置语法高亮了,下面这段代码可以使我们的Scintilla控件显示C++语法高亮代码:

const char* g_szKeywords=
    "asm auto bool break case catch char class const "
    "const_cast continue default delete do double "
    "dynamic_cast else enum explicit extern false finally "
    "float for friend goto if inline int long mutable "
    "namespace new operator private protected public "
    "register reinterpret_cast register 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";
...
SendEditor(SCI_SETLEXER, SCLEX_CPP); //C++语法解析
SendEditor(SCI_SETKEYWORDS, 0, (sptr_t)g_szKeywords);//设置关键字// 下面设置各种语法元素前景色
SendEditor(SCI_STYLESETFORE, SCE_C_WORD, 0x00FF0000);   //关键字
SendEditor(SCI_STYLESETFORE, SCE_C_STRING, 0x001515A3); //字符串
SendEditor(SCI_STYLESETFORE, SCE_C_CHARACTER, 0x001515A3); //字符
SendEditor(SCI_STYLESETFORE, SCE_C_PREPROCESSOR, 0x00808080);//预编译开关
SendEditor(SCI_STYLESETFORE, SCE_C_COMMENT, 0x00008000);//块注释
SendEditor(SCI_STYLESETFORE, SCE_C_COMMENTLINE, 0x00008000);//行注释
SendEditor(SCI_STYLESETFORE, SCE_C_COMMENTDOC, 0x00008000);//文档注释(/**开头)
上面这段代码自己在c下面还得改一改。在第一篇里面自己定义的SendEditor函数是有四个参数的。所以我在传递参数的时候也必须满足4个参数。

所以所有关于SendEditor的函数就变成了类似下面这样:

SendEditor(ptr,SCI_SETLEXER, SCLEX_CPP, 0);								//C++语法解析

语法解析只负责把代码拆分开,至于哪些是关键字,还得我们来指定。

要支持语法高亮,要做三件事:

一、选定语法解析器设置关键字

语法解析器用于把一大段代码分解成一个个的单词(token),另外还用于代码折叠的控制(后面会说到)。

选定语法解析器的命令是SCI_SETLEXER,如:

SendEditor(SCI_SETLEXER, SCLEX_CPP);

除了SCLEX_CPP以外,还有SCLEX_HTML、SCLEX_PERL、SCLEX_SQL、SCLEX_VB等一大堆,定 义在SciLexer.h里。现代的IDE应该可以定位SCLEX_CPP定义,它周围的SCLEX_XXX就是其它的语法解析器。

另外,也可以用SCI_SETLEXERLANGUAGE命令,如:
SendEditor(SCI_SETLEXERLANGUAGE, 0, (sptr_t)"cpp");

SCI_SETLEXERLANGUAGE接受的是一个字符串参数,这个字符串定义于代码解析器源代码(src\lex*.cxx) 最后面LexerModule开头的那行代码,那里的第三个参数就是。


二、设置关键字

语法解析只负责把代码拆分开,至于哪些是关键字,还得我们来指定。

这种方式带来了些许的灵活性,比如我们要高亮一种自定义的语言,这种语言的风格与C++类似(如Java、C#、php等),我们也 可以选定SCLEX_CPP作为语法解析器,然后定义自己的关键字。(所以不需要把各种解析器都编译进DLL文件里)

设置关键字的命令是SCI_SETKEYWORDS。它的wParam用于指定关键字种类,可以是0~8即9种类型,这样我们可以做 更细致的区分,如把关键字for if和int bool区分显示。lParam指定关键字,以空格分隔。    
三、设置文本元素对应的字体风格

即字体、前景色、背景色、斜体粗体等

设置字体风格的命令以SCI_STYLE作为前缀,这组命令比较多,为了不浪费篇幅,偶这里只列举几个,其它的可以参考这里

(http://scintilla.sourceforge.net/ScintillaDoc.html#StyleDefinition)

SCI_STYLESETBACK(int styleNumber, int colour) //设置背景色

SCI_STYLESETFORE(int styleNumber, int colour) //设置前景色

SCI_STYLESETFONT(int styleNumber, char *fontName) //设置字体

SCI_STYLESETSIZE(int styleNumber, int sizeInPoints)//设置字号

SCI_STYLESETBOLD(int styleNumber, bool bold) //设置粗体

SCI_STYLESETBACK(int styleNumber, int colour) //设置背景色

SCI_STYLESETFORE(int styleNumber, int colour) //设置前景色

SCI_STYLESETFONT(int styleNumber, char *fontName) //设置字体

SCI_STYLESETSIZE(int styleNumber, int sizeInPoints)//设置字号

SCI_STYLESETBOLD(int styleNumber, bool bold) //设置粗体

这里的styleNumber是指文本元素,如关键字、行号、控制字串等。前面代码中的SCE_C_XXXX是C++解析器分解出的 语法相关的元素。另外还有

STYLE_DEFAULT(默认)

STYLE_LINENUMBER(行号)

STYLE_BRACELIGHT(括号匹 配)

STYLE_BRACEBAD(括号失配)

STYLE_CONTROLCHAR(控制字符)

STYLE_INDENTGUIDE(缩进线)

STYLE_CALLTIP(调用提示)

SCI_STYLECLEARALL //把所有文本元素设置成与STYLE_DEFAULT相同的风格

Scintilla文档建议的顺序是先向STYLE_DEFAULT设置一些通用风格,然后再用SCI_STYLECLEARALL 把所有元素风格重置成与STYLE_DEFAULT一致,最后单独设置其它元素。

加上当前行高亮功能:

SendEditor(SCI_SETCARETLINEVISIBLE, TRUE);
SendEditor(SCI_SETCARETLINEBACK, 0xb0ffff);
把TAB宽度由默认的8改为4(依个人习惯~~)
SendEditor(SCI_SETTABWIDTH, 4);

让Scintilla实现代码折叠

页边(Margins)和标记(Markers)

代码折叠是现代IDE和代码编辑器的必备功能,如果现在推出一个不支持折叠的编辑器,那是要被BS地~~。为了不被BS,很有必要先“研究”一下Scintilla的页边(Margins)和标记(Markers)功能。

  • 页 边(Margins):页边是位于文本显示区左边的一竖条区域,它可以用于显示行号、书签、断点标记等东东。Scintilla最多可以有5个页边(从左 到右的编号为0~4),每个页边可以使用SCI_SETMARGINTYPEN命令确定是用于显示行号还是符号。我们可以用 SCI_SETMARGINWIDTHN命令控制一个页边的宽度,如果设置为0,则表示不显示该页边。默认是只显示宽度为16的1号页边。
  • 标 记(Markers):标记,不用说也知道是用来标记文本位置(确切地说,是文本行)的。我们可以使用32种标记(编号0~31),我们可以自由决定这 32种标记的意义,如标记0用来表示断点、标记1~10表示书签、标记20表示语法错误行等等。不过,如果编辑器要支持代码折叠功能,我们得把标记 25~31留出来,把这7个标记作为代码折叠专用标记(后面还会讲到)。

告诉页边显示哪些标记

当页边不是设定为显示行号时(由SCI_SETMARGINTYPEN命令设置),那么它就会显示标记。刚才说过Scintilla有32种标记,一般来说不会让一个页边来显示所有的标记,而是只显示部分标记。

在一个页边里可以显示哪几种标记由SCI_SETMARGINMASKN命令设置,它的参数是一个32位掩码(mask)值,掩码值的第n位为1时表示该页边可显示n号标记。

所有页边相关的命令以SCI_SETMARGIN或SCI_GETMARGIN作为前缀,如:

  • SCI_SETMARGINTYPEN(int margin, int type)  设置页边显示行号还是符号,type可以是SC_MARGIN_SYMBOL或SC_MARGIN_NUMBER
  • SCI_SETMARGINWIDTHN(int margin, int pixelWidth)  设置页边宽度
  • SCI_SETMARGINMASKN(int margin, int mask)  设置页边掩码
  • SCI_SETMARGINSENSITIVEN(int margin, bool sensitive)  设置页边是否接受鼠标点击事件

所有标记相关的命令以SCI_MARKER作为前缀,如:

  • SCI_MARKERADD(int line, int markerNumber)  在指定行加入一个markerNumber号标记
  • SCI_MARKERDEFINE(int markerNumber, int markerSymbols)  定义markerNumber号标记的样式
  • SCI_MARKERDELETE(int line, int markerNumber) 在指定行上的删除markerNumber号标记
  • SCI_MARKERDELETEALL(int markerNumber) 删除文本中所有markerNumber号标记
  • SCI_MARKERSETFORE(int markerNumber, int colour) 为markerNumber号标记指定前景色
  • SCI_MARKERSETBACK(int markerNumber, int colour) 为markerNumber号标记指定背景色

                // 先写10行文本上去
		for(int i=0; i<10; i++)
	        	SendEditor(ptr,SCI_APPENDTEXT, 12, (sptr_t)"hello world\n");


		// 1号页边,宽度为20,显示行号
		SendEditor(ptr,SCI_SETMARGINTYPEN,1, SC_MARGIN_NUMBER);
		SendEditor(ptr,SCI_SETMARGINWIDTHN,1, 20);

		for(int i=0; i<10; i++)
		{
			// 前10行分别加入0~2号标记
			SendEditor(ptr,SCI_MARKERADD, i, i%3);
		}

		// 设置标记的前景色
		SendEditor(ptr,SCI_MARKERSETFORE,0,0x0000ff);//0-红色
		SendEditor(ptr,SCI_MARKERSETFORE,1,0x00ff00);//1-绿色
		SendEditor(ptr,SCI_MARKERSETFORE,2,0xff0000);//2-蓝色 


		

如果你不喜欢这些圆圈,可以用SCI_MARKERDEFINE命令改变标记的样式,可选的有:

SC_MARK_CIRCLE, SC_MARK_ROUNDRECT, SC_MARK_ARROW, SC_MARK_SMALLRECT,
SC_MARK_SHORTARROW, SC_MARK_EMPTY, SC_MARK_ARROWDOWN, SC_MARK_MINUS,
SC_MARK_PLUS, SC_MARK_VLINE, SC_MARK_LCORNER, SC_MARK_TCORNER, SC_MARK_BOXPLUS,
SC_MARK_BOXPLUSCONNECTED, SC_MARK_BOXMINUS, SC_MARK_BOXMINUSCONNECTED,
SC_MARK_LCORNERCURVE, SC_MARK_TCORNERCURVE, SC_MARK_CIRCLEPLUS,
SC_MARK_CIRCLEPLUSCONNECTED, SC_MARK_CIRCLEMINUS, SC_MARK_CIRCLEMINUSCONNECTED,
SC_MARK_BACKGROUND, SC_MARK_DOTDOTDOT, SC_MARK_ARROWS, SC_MARK_PIXMAP,
SC_MARK_FULLRECT, SC_MARK_LEFTRECT, SC_MARK_CHARACTER

默认是SC_MARK_CIRCLE,小圆圈。你可以试试其它的。(注意SC_MARK_CHARACTER比较特殊,它和一个ASCII码加起来决定标记显示为一个对应的ASCII字符)

有了这些基础,我们可以动手为Scintilla加入代码折叠功能了...


前面曾说过当编辑器有代码折叠功能时,25号到31号这7个标记是作为代码折叠专用标记的。在scintilla.h中,我们可以找到它们的定义:

#define SC_MARKNUM_FOLDEREND 25  //折叠状态(多级中间)
#define SC_MARKNUM_FOLDEROPENMID 26  //展开状态(多级中间)
#define SC_MARKNUM_FOLDERMIDTAIL 27  //被折叠代码块尾部(多级中间)
#define SC_MARKNUM_FOLDERTAIL 28  //被折叠代码块尾部
#define SC_MARKNUM_FOLDERSUB 29   //被折叠的代码块
#define SC_MARKNUM_FOLDER 30     //折叠状态
#define SC_MARKNUM_FOLDEROPEN 31 //展开状态
显示这些标记的掩码是0xFE000000,同样头文件里已经定义好了
#define SC_MASK_FOLDERS 0xFE000000
要加入代码折叠功能,还有一个最最关键的事情,就是要得到语法解析器(Lexer)的支持,上面的这些标记都是由语法解析器自动添加删除的。一般来说,只要用下面这条命令就可以了让语法解析器支持代码折叠了:
SendEditor(SCI_SETPROPERTY,(sptr_t)"fold",(sptr_t)"1");

然后设置一下折叠属性样式:

SendEditor(ptr,SCI_SETPROPERTY,(sptr_t)"fold",(sptr_t)"1");

		SendEditor(ptr,SCI_SETMARGINTYPEN, MARGIN_FOLD_INDEX, SC_MARGIN_SYMBOL);//页边类型
		SendEditor(ptr,SCI_SETMARGINMASKN, MARGIN_FOLD_INDEX, SC_MASK_FOLDERS); //页边掩码
		SendEditor(ptr,SCI_SETMARGINWIDTHN, MARGIN_FOLD_INDEX, 11); //页边宽度
		SendEditor(ptr,SCI_SETMARGINSENSITIVEN, MARGIN_FOLD_INDEX, TRUE); //响应鼠标消息

		// 折叠标签样式
		SendEditor(ptr,SCI_MARKERDEFINE, SC_MARKNUM_FOLDER, SC_MARK_CIRCLEPLUS); 
		SendEditor(ptr,SCI_MARKERDEFINE, SC_MARKNUM_FOLDEROPEN, SC_MARK_CIRCLEMINUS); 
		SendEditor(ptr,SCI_MARKERDEFINE, SC_MARKNUM_FOLDEREND,  SC_MARK_CIRCLEPLUSCONNECTED);
		SendEditor(ptr,SCI_MARKERDEFINE, SC_MARKNUM_FOLDEROPENMID, SC_MARK_CIRCLEMINUSCONNECTED);
		SendEditor(ptr,SCI_MARKERDEFINE, SC_MARKNUM_FOLDERMIDTAIL, SC_MARK_TCORNERCURVE);
		SendEditor(ptr,SCI_MARKERDEFINE, SC_MARKNUM_FOLDERSUB, SC_MARK_VLINE); 
		SendEditor(ptr,SCI_MARKERDEFINE, SC_MARKNUM_FOLDERTAIL, SC_MARK_LCORNERCURVE);

		// 折叠标签颜色
		SendEditor(ptr,SCI_MARKERSETBACK, SC_MARKNUM_FOLDERSUB, 0xa0a0a0);
		SendEditor(ptr,SCI_MARKERSETBACK, SC_MARKNUM_FOLDERMIDTAIL, 0xa0a0a0);
		SendEditor(ptr,SCI_MARKERSETBACK, SC_MARKNUM_FOLDERTAIL, 0xa0a0a0);

		SendEditor(ptr,SCI_SETFOLDFLAGS, 16|4, 0); //如果折叠就在折叠行的上下各画一条横线 

当然还有最最关键的一步是什么时候要知道什么时候折叠。

我们在点击页边的时候(这里我用了自己的方法,根据margin页码数目来判断的)

      #define MARGIN_FOLD_INDEX 2 
             
      if(lpnmhdr->hwndFrom==hwndScintilla)
			{
				switch(lpnmhdr->code)
				{
				case SCN_MARGINCLICK:
					if (notify->margin == MARGIN_FOLD_INDEX)
					{
						line_number = SendEditor(ptr,SCI_LINEFROMPOSITION,notify->position,0);
						SendEditor(ptr,SCI_TOGGLEFOLD, line_number,0);
					}
				break;
				}
			}
好啦。这样一来我们就实现了代码高亮和代码折叠这两个功能了。

如果想继续深入学习更多scintilla的功能的话,就只能啃读官方文档了。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值