为Scintilla加入代码折叠功能

为Scintilla加入代码折叠功能

来自http://www.cppprog.com/2009/1111/176_2.html

前面曾说过当编辑器有代码折叠功能时,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");

是时候上代码了:

  1. #define MARGIN_FOLD_INDEX 2
  2. void TForm1::setFold()
  3. {
  4.     SendEditor(SCI_SETPROPERTY,(sptr_t)"fold",(sptr_t)"1");
  5.  
  6.     SendEditor(SCI_SETMARGINTYPEN, MARGIN_FOLD_INDEX, SC_MARGIN_SYMBOL);//页边类型
  7.     SendEditor(SCI_SETMARGINMASKN, MARGIN_FOLD_INDEX, SC_MASK_FOLDERS); //页边掩码
  8.     SendEditor(SCI_SETMARGINWIDTHN, MARGIN_FOLD_INDEX, 11); //页边宽度
  9.     SendEditor(SCI_SETMARGINSENSITIVEN, MARGIN_FOLD_INDEX, TRUE); //响应鼠标消息
  10.  
  11.     // 折叠标签样式
  12.     SendEditor(SCI_MARKERDEFINE, SC_MARKNUM_FOLDER, SC_MARK_CIRCLEPLUS); 
  13.     SendEditor(SCI_MARKERDEFINE, SC_MARKNUM_FOLDEROPEN, SC_MARK_CIRCLEMINUS); 
  14.     SendEditor(SCI_MARKERDEFINE, SC_MARKNUM_FOLDEREND,  SC_MARK_CIRCLEPLUSCONNECTED);
  15.     SendEditor(SCI_MARKERDEFINE, SC_MARKNUM_FOLDEROPENMID, SC_MARK_CIRCLEMINUSCONNECTED);
  16.     SendEditor(SCI_MARKERDEFINE, SC_MARKNUM_FOLDERMIDTAIL, SC_MARK_TCORNERCURVE);
  17.     SendEditor(SCI_MARKERDEFINE, SC_MARKNUM_FOLDERSUB, SC_MARK_VLINE); 
  18.     SendEditor(SCI_MARKERDEFINE, SC_MARKNUM_FOLDERTAIL, SC_MARK_LCORNERCURVE);
  19.  
  20.     // 折叠标签颜色
  21.     SendEditor(SCI_MARKERSETBACK, SC_MARKNUM_FOLDERSUB, 0xa0a0a0);
  22.     SendEditor(SCI_MARKERSETBACK, SC_MARKNUM_FOLDERMIDTAIL, 0xa0a0a0);
  23.     SendEditor(SCI_MARKERSETBACK, SC_MARKNUM_FOLDERTAIL, 0xa0a0a0);
  24.  
  25.     SendEditor(SCI_SETFOLDFLAGS, 16|4, 0); //如果折叠就在折叠行的上下各画一条横线
  26. }
  27. __fastcall TForm1::TForm1(TComponent* Owner)
  28.     : TForm(Owner)
  29. {
  30.     ...
  31.     setFold();
  32. }
  33. void __fastcall TForm1::WndProc(Messages::TMessage &Message)
  34. {
  35.     TForm::WndProc(Message);
  36.  
  37.     if(Message.Msg == WM_NOTIFY){
  38.         SCNotification* notify = (SCNotification*)Message.LParam;
  39.         if(notify->nmhdr.code == SCN_MARGINCLICK &&
  40.             notify->nmhdr.idFrom == SCINT_ID){
  41.             // 确定是页边点击事件
  42.             const int line_number = SendEditor(SCI_LINEFROMPOSITION,notify->position);
  43.             SendEditor(SCI_TOGGLEFOLD, line_number);
  44.         }
  45.     }
  46. }
现在的效果

\

这里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允许我们自己定义标记的样式,方法是:

  1. 用SCI_MARKERDEFINE命令设置标记的样式为SC_MARK_PIXMAP
  2. 用SCI_MARKERDEFINEPIXMAP命令设置标记使用的图形,这里的图形要求是xpm格式。

怎样得到xpm格式图形

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"
};

演示,使用自定义图形

  1. #include "minus.xpm"
  2. #include "plus.xpm"
  3. void TForm1::setFold()
  4. {
  5.     ...
  6.     // 折叠标签样式
  7.     SendEditor(SCI_MARKERDEFINE, SC_MARKNUM_FOLDER, SC_MARK_PIXMAP);
  8.     SendEditor(SCI_MARKERDEFINE, SC_MARKNUM_FOLDEROPEN, SC_MARK_PIXMAP);
  9.     SendEditor(SCI_MARKERDEFINE, SC_MARKNUM_FOLDEREND,  SC_MARK_PIXMAP);
  10.     SendEditor(SCI_MARKERDEFINE, SC_MARKNUM_FOLDEROPENMID, SC_MARK_PIXMAP);
  11.     SendEditor(SCI_MARKERDEFINE, SC_MARKNUM_FOLDERMIDTAIL, SC_MARK_TCORNERCURVE);
  12.     SendEditor(SCI_MARKERDEFINE, SC_MARKNUM_FOLDERSUB, SC_MARK_VLINE);
  13.     SendEditor(SCI_MARKERDEFINE, SC_MARKNUM_FOLDERTAIL, SC_MARK_LCORNERCURVE);
  14.  
  15.     //
  16.     SendEditor(SCI_MARKERDEFINEPIXMAP, SC_MARKNUM_FOLDER, (sptr_t)plus_xpm);
  17.     SendEditor(SCI_MARKERDEFINEPIXMAP, SC_MARKNUM_FOLDEROPEN, (sptr_t)minus_xpm);
  18.     SendEditor(SCI_MARKERDEFINEPIXMAP, SC_MARKNUM_FOLDEREND, (sptr_t)plus_xpm);
  19.     SendEditor(SCI_MARKERDEFINEPIXMAP, SC_MARKNUM_FOLDEROPENMID, (sptr_t)minus_xpm);
  20.     ...
  21. }
现在,我们的成果是这样的(与VS有一拼了吧^_^):

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值