(1) interface.c,interface.h:用于界面的初始化和定制。
主要的函数说明:
void a_Interface_init(void);界面初始化的主函数
void a_Interface_status(BrowserWindow *bw, const char *format, ... );
设置状态栏内容。
void a_Interface_openfile_dialog(BrowserWindow *bw);文件打开对话框
void a_Interface_set_Page_title(BrowserWindow *bw, char *title);
设置窗口标题。
void a_Interface_entry_open_url(GtkWidget *widget, BrowserWindow *bw);
打开网址输入栏输入的网址。
BrowserWindow *a_Interface_new_browser_window(gint width, gint height);
新建浏览窗口,是很重要的函数,在其中创建了所有的界面控件,并对按钮等进行了消息绑定。
(2) menu.c,menu.h
主要函数:GtkWidget *a_Menu_mainbar_new (BrowserWindow *bw);
定义了主菜单项,并进行了消息绑定
(3) browser.h
定义了重要的窗口结构如下:
struct _BrowserWindow
{
/* 主窗口的widgets */
GtkWidget *main_window;
GtkWidget *back_button;
GtkWidget *forw_button;
GtkWidget *stop_button;
GtkWidget *location;
GtkWidget *location_button;
GtkWidget *status;
/* 键盘控制表*/
GtkAccelGroup *accel_group;
/* 工具条按钮*/
GtkWidget *back_menuitem;
GtkWidget *forw_menuitem;
GtkWidget *stop_menuitem;
/* 主文档widget. (用于绘制HTML或其它) */
GtkWidget *layout;
/* 当前光标类型*/
GdkCursorType CursorType;
/* 对话框widgets*/
GtkWidget *open_dialog_window;
GtkWidget *open_dialog_entry;
GtkWidget *openfile_dialog_window;
GtkWidget *quit_dialog_window;
/* 指向保存词法分析结果的数据结构*/
BitTokenContext *global_cx;
/* 文件类型:html or plain text*/
gint file_type;
};
5.4控制模块
(1)command.c,command.h
主要的函数说明:
void a_Commands_openfile_callback (GtkWidget *widget, gpointer client_data);打开文件的对话框
void a_Commands_openurl_callback (GtkWidget *widget, gpointer client_data);打开URL
void a_Commands_close_callback(GtkWidget * widget, gpointer client_data);关闭窗口
void a_Commands_exit_callback (GtkWidget *widget, gpointer client_data);退出程序
void a_Commands_viewsource_callback (GtkWidget *widget, gpointer client_data);查看HTML源码
void a_Commands_reload_callback (GtkWidget *widget, gpointer client_data);刷新当前网页
void a_Commands_home_callback (GtkWidget *widget, gpointer client_data);显示主页
void a_Commands_helphome_callback (GtkWidget *widget, gpointer client_data); 显示帮助
(2)nav.h,nav.c:是命令对应的与网页操作有关的具体实施
主要的函数说明:
void a_Nav_push(BrowserWindow *bw, const char*);按URL打开一个网址或文件,具有对不完整URL的兼容性。
void a_Nav_reload(BrowserWindow *bw);刷新当前网页
void a_Nav_open_splash(BrowserWindow *bw,char *str);打开起始页(内置页面)
5.5词法分析模块
词法分析的原理和算法在前面已有详述。
(1) BitToken.c,BitToken.h
主要的函数说明:
BitTokenContext * Bit_NewContext(); 创建新的全局结构
int Bit_Tokenize(BitTokenContext *global_cx); 局部词法分析
void Bit_BeginToken(BitTokenContext *global_cx); 全局词法分析
int Bit_DestroyToken(BitTokenContext *global_cx); 释放内存
char *Token_ReadUntil(BitTokenContext *global_cx,char *sUntil);重要的字符处理函数,读取到指定字符后结束
char *Token_GetAttribute(BitTokenContext *global_cx); 取元素属性
void Token_ConvertIfNeed(char * aString); 转义字串的处理
int Token_ConsumTag(BitTokenContext *global_cx);处理元素
int Token_Consum_PlainText(BitTokenContext *global_cx); 处理文本
void Bit_ShowTokenResult(BitTokenContext *global_cx);显示分析结果
void Bit_SaveTokenResult(BitTokenContext *global_cx,char * filename); 保存分析结果
(2) BitHtmlDtd.h,BitHtmlDtd.c
用于存储HTML4.0元素的名称和属性。
(3) BitTokenList.h,BitTokenList.c
元素链表相关
(4) BitTokenAttrList.h,BitTokenAttrList.c
元素属性链表相关
(5) BitStr.h,BitStr.c
字符串处理函数
5.6使用PIXMAP的画图模块
因本部份是HTML文件的显示模块、文本文件的显示模块的基础,所以先予说明。
paint.c,paint.h
主要的函数说明:
gint pixmap_new(GtkWidget *widget,int width,int height);
在此函数中使用
pixmap = gdk_pixmap_new(widget->window,width+30,height+30,-1);来新建一个pixmap。
gint expose_event (GtkWidget *widget, GdkEventExpose *event); 在expose消息到来时,即若界面被破坏需重画时,使用
gdk_draw_pixmap(widget->window,widget->style->fg_gc[GTK_WIDGET_STATE(widget)],pixmap,event->area.x,event->area.y,event->area.x,ent->area.y, event->area.width, event->area.height);来重画。
gint pixmap_repaint(GtkWidget *widget);用于提供手动重画。
gint Browser_Paint(BrowserWindow *bw);layout的主函数,用来根据文件类型来调用HTML文件的显示模块或文本文件的显示,同时初始化滚动条。
5.7文本文件的显示模块
plain.c,plain.h
char *Plain_handle_tabs(const char *str)将TAB转为空格。
void a_Plain_write(GtkLayout *display,char *Buf1, gint BufSize)主要函数
下面介绍一下文本显示的算法。
指定默认字体
font=gdk_font_load("-adobe-helvetica-medium-r-normal--14-*-*-*-*-*-iso8859-1");
通过预布局来计算页面的长度:
while(i<BufSize)
{ j=0;
while(line_size<SCREEN_WIDTH-20 && Buf[i]!='\n')
{str[j]=Buf[i];
line_size+=gdk_char_width(font,str[j]);
j++;
i++;
}
str[j]='\0';
if(Buf[i]=='\n')i++;
x=X_START;
line_size=x;
y+=16;
}
创建PIXMAP
pixmap_new(drawing_area,SCREEN_WIDTH,y);
gc = gdk_gc_new(drawing_area->window);
进行真实的画图。
pixmap_repaint(drawing_area);
输出到PIXMAP并显示
5.8 HTML文件的显示模块
这部份是整个浏览器最重要的部份之一,综合了语法分析与HTML的布局、输出,其算法的好坏直接关系到网页的显示效果。
主要流程:
while(pTtokenList!=NULL)
{…………
switch(pTtokenList->token->type)
{
case HTML_TITLE:
…………
break;
case HTML_TEXT:
…………
break;
…………
…………
default:
………
break;
} //switch
pTtokenList=pTtokenList->next;
} //while
可以看到,这部份与语词分析结合的十分紧密,利用词法分析的结果,遍历各元素节点,取出其元素属性,根据一定的布局算法来进行布局。
例如:当遇到title元素时,就使用gtk函数来设定窗口标题为指定标题
gtk_window_set_title(GTK_WINDOW(bw->main_window),pTtokenList->token->pData);
其中pTtokenList->token->pData即为词法分析分析出的标题内容。
由于程序结构十分简单清晰,大部份元素的处理都简单易懂,参考源程序即可,下面主要针对<font>和相关标记对字体的设置阐述其算法。
由于<font>标记允许嵌套,所以使用了栈来对font元素进行管理,例如以下的HTML代码:
<font size=4 color=#0000FF>
This program is not<b>free software</b>; you can redistribute it and/or
modify it under the terms of the<font size=5 color=FF0000>GNU General
Public License</font>as published by the Free Software Foundation;
either version 2 of the License, or (at your option) any later version.
</font>
显示的效果应为GNU General Public License的字号为5,颜色为FF0000,即红色;free software应为粗体,受首尾两个呼应的font标记约束,其它字字号均为4,颜色为0000FF,由于free software只被<b></b>这一对加粗符号约束,所以其颜色应受首尾的font标记的约束,即应为0000FF。
这种嵌套的约束方式带来了HTML元素管理的混乱,也容易产生冗余的HTML代码,但既然标准是这么定的,也只能想办法加以解决,固然现在随着样式表的广泛采用,font已面临寿终正寝,但仍然大量存在,特别在对字体的颜色的设置,使用font标记很方便。
栈式管理的主要算法详解:
void html_open_font(GtkWidget *widget,char * style_str,char *color_str,char *size_str,int html_element,int insert_to_list);
该函数用于指定当前的字体属性,其参数包括style,color,size,以及改变字体属性的元素的名称,int insert_to_list用于标记此字体属性是否入栈,通常是入栈的。
这样,在出现font或相关元素的首标记时,我们将词法分析的结果提取出来,即将其元素属性提取出来,作为参数传递给html_open_font函数,该函数将这些属性进行组合,设置成为当前字体属性,并入栈保存;在出现font或相关元素的尾标记时,出于保险(因为存在交错包含关系的元素),首先检验栈顶元素与正在处理的元素尾标记是否匹配(名称相同),如相同则出栈,并将栈内下一字体属性设为系统的当前字体属性。
出栈函数为void html_close_font(GtkWidget *widget,int html_element)
需要注意的是由于并不是所有的font元素都指定所有的属性,可能只指定其中的一个或一部份属性,因此在入栈时必须做这样的处理,即首先获取当前的字体属性,根据哪些属性发生了变化来组合新的字体属性,然后入栈。
所使用的栈的结构很简单,如下。
typedef struct _font_list{
int html_element;
char color_str[15];
char size_str[15];
char style_str[15];
}font_list;
此为font_list的类型定义,描述了字体属性的结构
font_list font_opening[50]; 定义一个数组作为栈的存储形式
int current_font=0; 定义一个整型变量,作为栈顶指针
如此,一个简单的数组就发挥了巨大的作用,配以一点点算法,就带来了丰富多彩的界面效果。
5.9 Netbit实际应用效果及比较
下图为Netbit browser运行时的界面,所打开的页面源代码如下:
<html>
<body>
<h1>
<font color=#FF00FF><b>Netbit Browser Version 0.0.1 Demo</b></font></h1>
<hr>
<h4>License</h4>
<p>
<font size=4 color=#0000FF>
This program is not <b>free software</b>; you can redistribute it and/or
modify it under the terms of the <font size=5 color=FF0000>GNU General
Public License </font>as published by the Free Software Foundation;
either version 2 of the License, or (at your option) any later version.
</font>
<hr>
<h3>Design based on GTK, by sogo and ce!</h3>
</body>
</html>