随笔-341  评论-2670  文章-0  trackbacks-0
    这是GacUI关于文本框高亮的最后一个Demo了。这个Demo是关于XML着色的。XML着色比起C++着色更难,主要是因为在类似<book name="C++ Primer">这样的代码里面,book和name的颜色不一样,<和book的颜色也不一样(参考Visual Studio)。这种时候单纯依靠正则表达式来区分颜色是不够的,我们还需要引入一个新的状态机。这个状态机只有三个状态,用来区分tag name,attribute name和text三种颜色。状态机是手写的,并且GacUI提供了一个回调来写这个只有寥寥几行状态机。先看图:



    跟C++一样,XML着色首先是由正则表达式组成的。XML的正则表达式比较简单,只有符号、name、字符串、CData、注释和其它一些简单的东西:

 1 class XmlColorizer : public GuiTextBoxRegexColorizer
 2 {
 3 public:
 4     XmlColorizer()
 5     {
 6         text::ColorEntry entry=win7::Win7GetTextBoxTextColor();
 7         SetDefaultColor(entry);
 8 
 9         entry.normal.text=Color(01280);
10         AddToken(L"/<!--([^/-]|-[^/-]|--[^>])*--/>", entry);
11 
12         entry.normal.text=Color(1280255);
13         AddToken(L"/<!/[CDATA/[([^/]]|/][^/]]|/]/][^>])*/]/]/>", entry);
14 
15         entry.normal.text=Color(000);
16         AddToken(L"\"[^\"]*\"", entry);
17 
18         entry.normal.text=Color(00255);
19         AddToken(L"[<>=]", entry);
20 
21         entry.normal.text=Color(25500);
22         AddToken(L"[a-zA-Z0-9_/-:]+", entry);
23 
24         entry.normal.text=Color(1632121);
25         AddExtraToken(entry);
26 
27         Setup();
28     }
29 
30     void ColorizeTokenContextSensitive(const wchar_t* text, vint start, vint length, vint& token, int& contextState)override
31     {
32         
33     }
34 
35     int GetContextStartState()override
36     {
37         
38     }
39 };

    其次要对三种地方的[a-zA-Z0-9_/-:]进行着色。Tag的名字用褐色,attribute的名字用红色,而普通文本用黑色。因此我们可以做一个状态机,初始状态为0。如果读进了<,状态变成1。1遇到了一个Tag名字之后变为2。从2开始所有的名字就只能是attribute的名字了。我们只考虑正确的情况,错误的代码着色出了问题不仅没有坏处,还可以提醒程序员有什么地方写错了。之后遇到了>变回0,在0的状态下所有的东西都是普通文本,所以名字就都是黑色的。因此上面ColorizeTokenContextSensitive函数中就需要填入这个逻辑。GetContextStartState返回0,作为第一行的起始状态。代码如下:

    void ColorizeTokenContextSensitive(const wchar_t* text, vint start, vint length, vint& token, int& contextState)override
    {
        
// 0 < 1 name 2 att > 0
        switch(token)
        {
        
case 3:
            
if(length==1)
            {
                
switch(text[start])
                {
                
case '<':
                    contextState
=1;
                    
break;
                
case '>':
                    contextState
=0;
                    
break;
                }
            }
            
break;
        
case 4:
            
switch(contextState)
            {
            
case 0:
                token
=-1;
                
break;
            
case 1:
                token
=5;
                contextState
=2;
                
break;
            }
            
break;
        }
    }

    
int GetContextStartState()override
    {
        
return 0;
    }

    这个函数里面有几个魔法数字,其实都是关于Token的编号的。构造函数里面我们使用AddToken将一个颜色关联到正则表达式上,使用AddExtraToken创建一个没有正则表达式关联的颜色。所以在这个状态机里面,所有的颜色都用Token的序号来表示。无论是使用AddToken还是AddExtraToken,第一个颜色编号为0,第二个颜色编号为1。因此case 3指的是[<>=],而case 4指的是[a-zA-Z0-9_/-:]+。而case 4里面的token=5则表示在状态为1的时候,名字都用AddExtraToken指定的那个褐色进行染色。缺省的名字(也就是id为4的token)是红色,所以不需要对contextState为2的时候进行处理。

    这样我们就完成了XML的着色。GacUI接下来的几个Demo将会是关于ListBox、ListView和TreeView的,敬请期待。
posted on 2012-05-20 00:41 陈梓瀚(vczh) 阅读(2233) 评论(2)  编辑 收藏 引用 所属分类: GacUI

评论:
# re: GacUI Demo:文本框高亮(三,完)上下文相关着色 2012-05-20 01:40 | 陈梓瀚(vczh)
发现xml里面把C++他爹的名字写错了……  回复  更多评论
  
# re: GacUI Demo:文本框高亮(三,完)上下文相关着色 2012-05-20 04:06 | iloveprogramme
严重佩服啊!期待lz完成的那天。  回复  更多评论
  

只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   博问   Chat2DB   管理