标签:tin tip 不能 大小 class ipa 安装 wchar_t http
Scintilla是一个免费、跨平台、支持语法高亮的编辑控件。它完整支持源代码的编辑和调试,包括语法高亮、错误指示、代码完成 (code completion)和调用提示(call tips)。能包含标记(marker)的页边(margin)可用于标记断点、折叠和高亮当前行。
可以从这里下载Scintilla库:http://scintilla.sourceforge.net/ScintillaDownload.html
这里有Scinilla相关的库下载,比如wxScintilla就是Scintilla的wxWidgets移植版。http://www.scintilla.org/ScintillaRelated.html
另外,Scintilla的作者为了演示这个东东的功能,编写了一个叫SciTE的演示程序。不过这个程序的功能已经强大到足以作为我 们的常用代码编辑器,很值得下载下来学习学习。
偶只在Windows下编译过,所以只好说说Windows环境下的编译方法。对于Linux,没试过(丢人-_-)
下载、解压略过不提
首先进入scintilla的win32目录:
cd scintilla\win32
mingw32-make
nmake -f scintilla.mak
nmake -f scintilla_vc6.mak
make -fscintilla.mak
编译完成后,在bin目录里会得到Scintilla.dll和SciLexer.dll文件,SciLexer.dll是包含了语法解析器 (Lexer)的Scintilla控件,一般来说我们只要用它就可以了。
需要说明的是,不管是用什么编译器生成的DLL文件,都可以供给其它编译器使用(就象系统DLL一样,任何编译器都能使用),所以不用为各种编译器都编译 一份。
如果觉得生成的SciLexer.dll太大的话,可以考虑去除自带的部分语法解析器。比如你打算只用它来高亮C++代码的话,可以:
要启用Scintilla,首先当然是要加载之前编译的DLL文件啦~~
::LoadLibrary(_T("SciLexer.dll"));
SciLexer.dll加载后会自动以"Scintilla"作为类名注册一个窗体类,我们只要直接用这个类名建立窗体就可以了:
::CreateWindow(_T("Scintilla"),...);
由于Scintilla主要是窗体操作,为了减少不必要的窗体代码(主要是偷懒外加推广一下C++Builder,呵呵),这里使用C++Builder 来写演示程序。对于一些C++Builder的VCL库特有的东东,后面会有解释的。
首先新建一个窗体应用程序,
然后在WinMain里载入SciLexer.dll:
WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int) { LoadLibrary(_T("SciLexer.dll")); ...
最后,在TForm1的构造里建立Scintilla窗体:
#define SCINT_ID 1010 __fastcall TForm1::TForm1(TComponent* Owner) : TForm(Owner) { ::CreateWindow(_T("Scintilla"), NULL, WS_CHILD|WS_CLIPCHILDREN|WS_CLIPSIBLINGS|WS_VISIBLE, 0,0,ClientWidth,ClientHeight, Handle, (HMENU)SCINT_ID, HInstance, NULL); }
很简单,是吧?对于Scintilla来说,没什么好解释的了。
这里主要给不了解C++Builder的童鞋介绍一下VCL的东东,以便于接下来的讲解和代码阅读(以及移植到其它编译器中)。
现在,我们的成果是这样的:
现在,看上去还比较土,接下来我们开始配置它,为使它成为可与VS媲美的代码编程器而战!
配置Scintilla控件是通过向该控件发送配置命令实现的,各种配置命令可以在doc目录下找到(或者是这里http://scintilla.sourceforge.net/ScintillaDoc.html),后面的大部分事情都是在介绍这些配置命令。
有两种方法来发送配置命令,一种是直接使用SendMessage API。另一种是取得直接控制函数,通过函数来执行配置命令。
在Windows下,第二种方法要比第一种快得多。
直接控制函数的定义为:
typedef sptr_t (*SciFnDirect)(sptr_t ptr, unsigned int iMessage, uptr_t wParam, sptr_t lParam);
后三个参数和SendMessage的后三个参数一样。
SciFnDirect的第一个参数用于指定具体的Scintilla窗体,它类似于窗体的HWND又不完全相同,姑且也称之为句柄吧。它是用一个配置命 令取得的,下面马上就要讲到。
取得直接控制函数和句柄的方法是:
SciFnDirect fnDirect = (SciFnDirect)SendMessage(hwndEditor,SCI_GETDIRECTFUNCTION,0,0); sptr_t ptrDirect = (sptr_t)SendMessage(hwndEditor,SCI_GETDIRECTPOINTER,0,0);
取得这两样东西以后,就可以直接执行配置命令了,如:
m_fnDirect(fnDirect, SCI_CLEARALL, 0, 0);
演示代码:编写成员函数SendEditor,用于配置之前建立的Scintilla控件。
#include <Scintilla.h> #include <SciLexer.h> class TForm1 : public TForm { __published: // IDE-managed Components private : // User declarations SciFnDirect m_fnDirect; sptr_t m_ptrDirect; public : // User declarations __fastcall TForm1(TComponent* Owner); sptr_t SendEditor(unsigned int iMessage, uptr_t wParam = 0, sptr_t lParam = 0) { return m_fnDirect(m_ptrDirect, iMessage, wParam, lParam); } }; #define SCINT_ID 1010 __fastcall TForm1::TForm1(TComponent* Owner) : TForm(Owner) { HWND hwndEditor = ::CreateWindow(_T( "Scintilla" ), NULL, WS_CHILD|WS_CLIPCHILDREN|WS_CLIPSIBLINGS|WS_VISIBLE, 0,0,ClientWidth,ClientHeight, Handle, ( HMENU )SCINT_ID, HInstance, NULL); m_fnDirect = (SciFnDirect)SendMessage(hwndEditor,SCI_GETDIRECTFUNCTION,0,0); m_ptrDirect = (sptr_t)SendMessage(hwndEditor,SCI_GETDIRECTPOINTER,0,0); } |
有了前面的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);//文档注释(/**开头) |
语法解析器用于把一大段代码分解成一个个的单词(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_STYLECLEARALL //把所有文本元素设置成与STYLE_DEFAULT相同的风格 |
Scintilla文档建议的顺序是先向STYLE_DEFAULT设置一些通用风格,然后再用SCI_STYLECLEARALL 把所有元素风格重置成与STYLE_DEFAULT一致,最后单独设置其它元素。
#include <Scintilla.h> #include <SciLexer.h> ... void TForm1::setCppStyle() { const char * szKeywords1= "asm auto break case catch class const " "const_cast continue default delete do double " "dynamic_cast else enum explicit extern false " "for friend goto if inline mutable " "namespace new operator private protected public " "register reinterpret_cast return signed " "sizeof static static_cast struct switch template " "this throw true try typedef typeid typename " "union unsigned using virtual volatile while" ; const char * szKeywords2= "bool char float int long short void wchar_t" ; // 设置全局风格 SendEditor(SCI_STYLESETFONT, STYLE_DEFAULT,(sptr_t) "Courier New" ); SendEditor(SCI_STYLESETSIZE, STYLE_DEFAULT,10); SendEditor(SCI_STYLECLEARALL); //C++语法解析 SendEditor(SCI_SETLEXER, SCLEX_CPP); SendEditor(SCI_SETKEYWORDS, 0, (sptr_t)szKeywords1); //设置关键字 SendEditor(SCI_SETKEYWORDS, 1, (sptr_t)szKeywords2); //设置关键字 // 下面设置各种语法元素风格 SendEditor(SCI_STYLESETFORE, SCE_C_WORD, 0x00FF0000); //关键字 SendEditor(SCI_STYLESETFORE, SCE_C_WORD2, 0x00800080); //关键字 SendEditor(SCI_STYLESETBOLD, SCE_C_WORD2, TRUE); //关键字 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); //文档注释(/**开头) SendEditor(SCI_SETCARETLINEVISIBLE, TRUE); SendEditor(SCI_SETCARETLINEBACK, 0xb0ffff); } __fastcall TForm1::TForm1(TComponent* Owner) : TForm(Owner) { ... setCppStyle(); } |
看上去不错,如果你愿意,还可以加上当前行高亮功能:
SendEditor(SCI_SETCARETLINEVISIBLE, TRUE); SendEditor(SCI_SETCARETLINEBACK, 0xb0ffff); |
SendEditor(SCI_SETTABWIDTH, 4); |
这里继续学习Scintilla更多的控制命令和实现细节,完善我们的编辑器;
代码折叠是现代IDE和代码编辑器的必备功能,如果现在推出一个不支持折叠的编辑器,那是要被BS地~~。为了不被BS,很有必要先“研究”一下Scintilla的页边(Margins)和标记(Markers)功能。
当页边不是设定为显示行号时(由SCI_SETMARGINTYPEN命令设置),那么它就会显示标记。刚才说过Scintilla有32种标记,一般来说不会让一个页边来显示所有的标记,而是只显示部分标记。
在一个页边里可以显示哪几种标记由SCI_SETMARGINMASKN命令设置,它的参数是一个32位掩码(mask)值,掩码值的第n位为1时表示该页边可显示n号标记。
所有页边相关的命令以SCI_SETMARGIN或SCI_GETMARGIN作为前缀,如:
所有标记相关的命令以SCI_MARKER作为前缀,如:
// 标记和页边演示 void TForm1::example() { // 先写10行文本上去 for ( int i=0; i<10; i++) SendEditor(SCI_APPENDTEXT, 12, (sptr_t) "hello world " ); // 0号页边,宽度为9,显示0号标记(0..0001B) SendEditor(SCI_SETMARGINTYPEN,0,SC_MARGIN_SYMBOL); SendEditor(SCI_SETMARGINWIDTHN,0, 9); SendEditor(SCI_SETMARGINMASKN,0, 0x01); // 1号页边,宽度为9,显示1,2号标记(0..0110B) SendEditor(SCI_SETMARGINTYPEN,1, SC_MARGIN_SYMBOL); SendEditor(SCI_SETMARGINWIDTHN,1, 9); SendEditor(SCI_SETMARGINMASKN,1, 0x06); // 2号页边,宽度为20,显示行号 SendEditor(SCI_SETMARGINTYPEN,2, SC_MARGIN_NUMBER); SendEditor(SCI_SETMARGINWIDTHN,2, 20); for ( int i=0; i<10; i++) { // 前10行分别加入0~2号标记 SendEditor(SCI_MARKERADD, i, i%3); } // 设置标记的前景色 SendEditor(SCI_MARKERSETFORE,0,0x0000ff); //0-红色 SendEditor(SCI_MARKERSETFORE,1,0x00ff00); //1-绿色 SendEditor(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");
#define MARGIN_FOLD_INDEX 2 void TForm1::setFold() { SendEditor(SCI_SETPROPERTY,(sptr_t) "fold" ,(sptr_t) "1" ); SendEditor(SCI_SETMARGINTYPEN, MARGIN_FOLD_INDEX, SC_MARGIN_SYMBOL); //页边类型 SendEditor(SCI_SETMARGINMASKN, MARGIN_FOLD_INDEX, SC_MASK_FOLDERS); //页边掩码 SendEditor(SCI_SETMARGINWIDTHN, MARGIN_FOLD_INDEX, 11); //页边宽度 SendEditor(SCI_SETMARGINSENSITIVEN, MARGIN_FOLD_INDEX, TRUE); //响应鼠标消息 // 折叠标签样式 SendEditor(SCI_MARKERDEFINE, SC_MARKNUM_FOLDER, SC_MARK_CIRCLEPLUS); SendEditor(SCI_MARKERDEFINE, SC_MARKNUM_FOLDEROPEN, SC_MARK_CIRCLEMINUS); SendEditor(SCI_MARKERDEFINE, SC_MARKNUM_FOLDEREND, SC_MARK_CIRCLEPLUSCONNECTED); SendEditor(SCI_MARKERDEFINE, SC_MARKNUM_FOLDEROPENMID, SC_MARK_CIRCLEMINUSCONNECTED); SendEditor(SCI_MARKERDEFINE, SC_MARKNUM_FOLDERMIDTAIL, SC_MARK_TCORNERCURVE); SendEditor(SCI_MARKERDEFINE, SC_MARKNUM_FOLDERSUB, SC_MARK_VLINE); SendEditor(SCI_MARKERDEFINE, SC_MARKNUM_FOLDERTAIL, SC_MARK_LCORNERCURVE); // 折叠标签颜色 SendEditor(SCI_MARKERSETBACK, SC_MARKNUM_FOLDERSUB, 0xa0a0a0); SendEditor(SCI_MARKERSETBACK, SC_MARKNUM_FOLDERMIDTAIL, 0xa0a0a0); SendEditor(SCI_MARKERSETBACK, SC_MARKNUM_FOLDERTAIL, 0xa0a0a0); SendEditor(SCI_SETFOLDFLAGS, 16|4, 0); //如果折叠就在折叠行的上下各画一条横线 } __fastcall TForm1::TForm1(TComponent* Owner) : TForm(Owner) { ... setFold(); } void __fastcall TForm1::WndProc(Messages::TMessage &Message) { TForm::WndProc(Message); if (Message.Msg == WM_NOTIFY){ SCNotification* notify = (SCNotification*)Message.LParam; if (notify->nmhdr.code == SCN_MARGINCLICK && notify->nmhdr.idFrom == SCINT_ID){ // 确定是页边点击事件 const int line_number = SendEditor(SCI_LINEFROMPOSITION,notify->position); SendEditor(SCI_TOGGLEFOLD, line_number); } } } |
这里TForm1::WndProc方法是Scintilla父窗体即我们的TForm1的窗口处理函数。
代码折叠以后我们要通过点击页边上的+和-标记来打开和折叠代码,所以需要页边接收鼠标点击事件:
这里TForm1::WndProc方法是Scintilla父窗体即我们的TForm1的窗口处理函数。
代码折叠以后我们要通过点击页边上的+和-标记来打开和折叠代码,所以需要页边接收鼠标点击事件:
SendEditor(SCI_SETMARGINSENSITIVEN, MARGIN_FOLD_INDEX, TRUE); //响应鼠标消息
这样,当有鼠标点击该页边后,Scintilla就会向它的父窗体发送代码为SCN_MARGINCLICK的WM_NOTIFY消息,其中的LParam为SCNotification*类型。关于SCNotification的详细信息请参考这里。SCNotification的position成员指出了点击位置对应的行号,最后我们用SCI_TOGGLEFOLD命令折叠或展开代码。
Scintilla自带的标记样式和VS比起来还有差距,反正偶是怎么调都觉得有点土。Scintilla允许我们自己定义标记的样式,方法是:
xpm在linux系统下用得比较多,它和BMP、jpg一样也是一种图片格式,有不少工具可以把图片转换成xpm格式的,比如我喜欢的免费看图软件XnView就可以,想必ACDSee也行吧。
xpm比较特殊的地方是它可以作为头文件直接被C语言调用,吃惊吧?用文本编辑器打开它看看,呵呵,其实它就是一个数组定义。
如下面这个数据(代码)就是后面马上就要用到的minus.xpm和plus.xpm图片文件的内容(从eclipse里挖出来的):
/* XPM */ static const char *minus_xpm[] = { /* width height num_colors chars_per_pixel */ " 9 9 16 1" , /* colors */ "` c #8c96ac" , ". c #c4d0da" , "# c #daecf4" , "a c #ccdeec" , "b c #eceef4" , "c c #e0e5eb" , "d c #a7b7c7" , "e c #e4ecf0" , "f c #d0d8e2" , "g c #b7c5d4" , "h c #fafdfc" , "i c #b4becc" , "j c #d1e6f1" , "k c #e4f2fb" , "l c #ecf6fc" , "m c #d4dfe7" , /* pixels */ "hbc.i.cbh" , "bffeheffb" , "mfllkllfm" , "gjkkkkkji" , "da`````jd" , "ga#j##jai" , "f.k##k#.a" , "#..jkj..#" , "hemgdgc#h" }; |
/* XPM */ static const char *plus_xpm[] = { /* width height num_colors chars_per_pixel */ " 9 9 16 1" , /* colors */ "` c #242e44" , ". c #8ea0b5" , "# c #b7d5e4" , "a c #dcf2fc" , "b c #a2b8c8" , "c c #ccd2dc" , "d c #b8c6d4" , "e c #f4f4f4" , "f c #accadc" , "g c #798fa4" , "h c #a4b0c0" , "i c #cde5f0" , "j c #bcdeec" , "k c #ecf1f6" , "l c #acbccc" , "m c #fcfefc" , /* pixels */ "mech.hcem" , "eldikille" , "dlaa`akld" , ".#ii`ii#." , "g#`````fg" , ".fjj`jjf." , "lbji`ijbd" , "khb#idlhk" , "mkd.ghdkm" }; |
#include "minus.xpm" #include "plus.xpm" void TForm1::setFold() { ... // 折叠标签样式 SendEditor(SCI_MARKERDEFINE, SC_MARKNUM_FOLDER, SC_MARK_PIXMAP); SendEditor(SCI_MARKERDEFINE, SC_MARKNUM_FOLDEROPEN, SC_MARK_PIXMAP); SendEditor(SCI_MARKERDEFINE, SC_MARKNUM_FOLDEREND, SC_MARK_PIXMAP); SendEditor(SCI_MARKERDEFINE, SC_MARKNUM_FOLDEROPENMID, SC_MARK_PIXMAP); SendEditor(SCI_MARKERDEFINE, SC_MARKNUM_FOLDERMIDTAIL, SC_MARK_TCORNERCURVE); SendEditor(SCI_MARKERDEFINE, SC_MARKNUM_FOLDERSUB, SC_MARK_VLINE); SendEditor(SCI_MARKERDEFINE, SC_MARKNUM_FOLDERTAIL, SC_MARK_LCORNERCURVE); // SendEditor(SCI_MARKERDEFINEPIXMAP, SC_MARKNUM_FOLDER, (sptr_t)plus_xpm); SendEditor(SCI_MARKERDEFINEPIXMAP, SC_MARKNUM_FOLDEROPEN, (sptr_t)minus_xpm); SendEditor(SCI_MARKERDEFINEPIXMAP, SC_MARKNUM_FOLDEREND, (sptr_t)plus_xpm); SendEditor(SCI_MARKERDEFINEPIXMAP, SC_MARKNUM_FOLDEROPENMID, (sptr_t)minus_xpm); ... } |
在VS里编写C++代码时,输入回车换行后会保持和上一行的缩进一致,输入"{‘字符后回车还会帮我们多缩进一次,输入‘}‘后又能自动退回。我们的编辑器也要实现这个功能。
现在再仔细了解一下Scintilla的通知消息(http://scintilla.sourceforge.net/ScintillaDoc.html#Notifications),除了前面用到的页边点击事件外,还有很多事件非常有用。
实现自动缩进功能我们要关心的事件通知是SCN_CHARADDED和SCN_UPDATEUI。
改写TForm1::WndProc,处理这两个事件,我们的编辑器支持自动缩进啦
void __fastcall TForm1::WndProc(Messages::TMessage &Message) { TForm::WndProc(Message); if (Message.Msg == WM_NOTIFY) { ... // 处理自动缩进 static int LastProcessedChar = 0; //在CharAdded事件中记录最后输入的字符 if (notify->nmhdr.code == SCN_CHARADDED) { LastProcessedChar = notify->ch; } // 在UpdateUI事件中处理缩进 if (notify->nmhdr.code == SCN_UPDATEUI && LastProcessedChar!=0) { int pos = SendEditor(SCI_GETCURRENTPOS); //取得当前位置 int line = SendEditor(SCI_LINEFROMPOSITION,pos); //取得当前行 //如果最后输入的字符是右括号的话就自动让当前行缩进和它匹配的左括号所在行一致 if ( strchr ( "})>]" ,LastProcessedChar) && isspace (SendEditor(SCI_GETCHARAT,pos-2)) && //要求右括号左边是空白字符 LastProcessedChar!=0) { //找前一个单词起始位置,这里用它来确定右括号左边是否全是空白字符 int startpos = SendEditor(SCI_WORDSTARTPOSITION,pos-1, false ); int linepos = SendEditor(SCI_POSITIONFROMLINE,line); //当前行起始位置 if (startpos == linepos) //这样相当于判断右括号左边是否全是空白字符 { int othpos = SendEditor(SCI_BRACEMATCH,pos-1); //得到对应的左括号所在的位置 int othline = SendEditor(SCI_LINEFROMPOSITION,othpos); //左括号所在行 int nIndent = SendEditor(SCI_GETLINEINDENTATION,othline); //左括号所在行的缩进值 // 替换右括号前面的空白字符,使之与左括号缩进一致 char space[1024]; memset (space, ‘ ‘ ,1024); SendEditor(SCI_SETTARGETSTART, startpos); SendEditor(SCI_SETTARGETEND, pos-1); SendEditor(SCI_REPLACETARGET,nIndent,(sptr_t)space); } } // 如果输入的是回车,则保持与上一行缩进一致 // 如果上一行最后有效字符为左括号,就多缩进四个空格 if (LastProcessedChar == ‘ ‘ ) { if (line > 0) { // 得到上一行缩进设置 int nIndent = SendEditor(SCI_GETLINEINDENTATION,line-1); // 查找上一行最后一个有效字符(非空白字符) int nPrevLinePos = SendEditor(SCI_POSITIONFROMLINE,line-1); int c = ‘ ‘ ; for ( int p = pos-2; p>=nPrevLinePos && isspace (c); p--, c=SendEditor(SCI_GETCHARAT,p)); // 如果是左括号,就多缩进四格 if (c && strchr ( "{([<" ,c)) nIndent+=4; // 缩进... char space[1024]; memset (space, ‘ ‘ ,1024); space[nIndent] = 0; SendEditor(SCI_REPLACESEL, 0, (sptr_t)space); } } LastProcessedChar = 0; } } } |
VS的代码完成和函数提示功能是很值得称道的,它们可以极大地提高我们的编程效率(造成我现在写代码时往往只记住前四个字母,如果在对象后面点了小数点后不出现提示就会心慌意乱的说-_-),尽管有时也会失效。
做为IDE这个功能是绝对不能少D。即使你只打算做个编辑器,如果有这个功能那也是一大亮点啊~~(目前很多代码编辑器都没这个功能的说)。
关于函数提示的几个命令以SCI_CALLTIP作为前缀,这里只介绍我们即将使用的几个命令(更多命令见:http://scintilla.sourceforge.net/ScintillaDoc.html#CallTips)
在我们程序中加入提示的最佳时机是SCN_CHARADDED(见上一节)事件。当用户输入左圆括号‘(‘时,取得括号左边的函数名,然后显示出该函数的完整定义。
下面的代码实现了CreateWindow和MoveWindow两个API的函数提示
//我们要高亮的两个函数 const size_t FUNCSIZE=2; char * g_szFuncList[FUNCSIZE]={ //函数名 "CreateWindow(" , "MoveWindow(" }; char * g_szFuncDesc[FUNCSIZE]={ //函数信息 "HWND CreateWindow(" "LPCTSTR lpClassName," " LPCTSTR lpWindowName," " DWORD dwStyle, " " int x," " int y," " int nWidth," " int nHeight, " " HWND hWndParent," " HMENU hMenu," " HANDLE hInstance," " PVOID lpParam" ")" , "BOOL MoveWindow(" "HWND hWnd," " int X," " int Y," " int nWidth," " int nHeight," " BOOL bRepaint" ")" }; void __fastcall TForm1::WndProc(Messages::TMessage &Message) { TForm::WndProc(Message); if (Message.Msg == WM_NOTIFY) { SCNotification* notify = (SCNotification*)Message.LParam; ... if (notify->nmhdr.code == SCN_CHARADDED) { ... // 函数提示功能 static const char * pCallTipNextWord = NULL; //下一个高亮位置 static const char * pCallTipCurDesc = NULL; //当前提示的函数信息 if (notify->ch == ‘(‘ ) //如果输入了左括号,显示函数提示 { char word[1000]; //保存当前光标下的单词(函数名) TextRange tr; //用于SCI_GETTEXTRANGE命令 int pos = SendEditor(SCI_GETCURRENTPOS); //取得当前位置(括号的位置) int startpos = SendEditor(SCI_WORDSTARTPOSITION,pos-1); //当前单词起始位置 int endpos = SendEditor(SCI_WORDENDPOSITION,pos-1); //当前单词终止位置 tr.chrg.cpMin = startpos; //设定单词区间,取出单词 tr.chrg.cpMax = endpos; tr.lpstrText = word; SendEditor(SCI_GETTEXTRANGE,0, sptr_t(&tr)); for ( size_t i=0; i<FUNCSIZE; i++) //找找有没有我们认识的函数? { if ( memcmp (g_szFuncList[i],word, sizeof (g_szFuncList[i])) == 0) { //找到啦,那么显示提示吧 pCallTipCurDesc = g_szFuncDesc[i]; //当前提示的函数信息 SendEditor(SCI_CALLTIPSHOW,pos,sptr_t(pCallTipCurDesc)); //显示这个提示 const char *pStart = strchr (pCallTipCurDesc, ‘(‘ )+1; //高亮第一个参数 const char *pEnd = strchr (pStart, ‘,‘ ); //参数列表以逗号分隔 if (pEnd == NULL) pEnd = strchr (pStart, ‘)‘ ); //若是最后一个参数,后面是右括号 SendEditor(SCI_CALLTIPSETHLT, pStart-pCallTipCurDesc, pEnd-pCallTipCurDesc); pCallTipNextWord = pEnd+1; //指向下一参数位置 break ; } } } else if (notify->ch == ‘)‘ ) //如果输入右括号,就关闭函数提示 { SendEditor(SCI_CALLTIPCANCEL); pCallTipCurDesc = NULL; pCallTipNextWord = NULL; } else if (notify->ch == ‘,‘ && SendEditor(SCI_CALLTIPACTIVE) && pCallTipCurDesc) { //输入的是逗号,高亮下一个参数 const char *pStart = pCallTipNextWord; const char *pEnd =
再分享一下我老师大神的人工智能教程吧。零基础!通俗易懂!风趣幽默!还带黄段子!希望你也加入到我们人工智能的队伍中来!https://blog.csdn.net/jiangjunshow |
标签:tin tip 不能 大小 class ipa 安装 wchar_t http
原文地址:https://www.cnblogs.com/skiwnchiwns/p/10343533.html