昨晚将phoneyc检测网页的结果写入数据库时,想起可以顺手加个md5值作为每个url的签名,于是在终端里敲下echo "www.godorz.cn" | md5sum ,在这之前,一切都是美好的,可当我要拿出计算出的md5值时,才知道原来在终端里复制文本是这么繁琐,首先要选定文本,然后右键+c,后一步是很容易的,但是前一步选定文本对于鼠标不灵视力不好脾气火爆的我来说,就真的是个杯具了..正好最近在学gtk,干脆就写个计算md5的gui,然后将结果默认选定,酱紫就可以直接ctrl+c复制啦,呵呵..

md5算法是在这里找的,说来惭愧的是,,作为计算机系的学生,生平第一次真正见到request for comment文档,还是21页的文档,很长很恐怖,我马上把它关了..

有了md5源码,其余的就很简单了,打开glade,设计好界面如下:

然后新建一个c文件,用GtkBuilder *builder将glade设计好的界面引用下来,然后逐个传给GtkWidget组件,剩下需要作的就是捣鼓捣鼓每个组件回调函数了. 代码如下,注释已经很明了了:


#include<gtk/gtk.h>

GtkWidget *resultentry;
GtkWidget *filew;
GtkWidget *entry;
gchar filepath[512];
.....

void cb_okbutton(GtkWidget *widget, GtkWidget *entry)
{
//得到输入控件entry值,传入filename
const gchar *tmp;
tmp = gtk_entry_get_text(GTK_ENTRY(entry));
char filename[512];
sprintf(filename, "%s", tmp);

if (filename[0]==34) filename[strlen(filename)-1]=0,strcpy(filename,filename+1);  //支持文件拖曳,但会多出双引号,这里是处理多余的双引号
if (!(fp=fopen(filename,"rb"))) {printf("Can not open this file!\n"); return;}  //以二进制打开文件
fseek(fp, 0, SEEK_END);  //文件指针转到文件末尾
if((len=ftell(fp))==-1) {printf("Sorry! Can not calculate files which larger than 2 GB!\n");fclose(fp); return ;}  //ftell函数返回long,最大为2GB,超出返回-1
rewind(fp);  //文件指针复位到文件头
A=0x67452301,B=0xefcdab89,C=0x98badcfe,D=0x10325476; //初始化链接变量
flen[1]=len/0x20000000;     //flen单位是bit
flen[0]=(len%0x20000000)*8;
memset(x,0,64);   //初始化x数组为0
fread(&x,4,16,fp);  //以4字节为一组,读取16组数据
for(i=0;i<len/64;i++)
{    //循环运算直至文件结束
md5();
memset(x,0,64);
fread(&x,4,16,fp);
}
((char*)x)[len%64]=128;  //文件结束补1,补0操作,128二进制即10000000
if(len%64>55) md5(),memset(x,0,64);
memcpy(x+14,flen,8);    //文件末尾加入原文件的bit长度
md5();
fclose(fp);

// 将结果高低位逆反输出到buf
char buf[512];
sprintf(buf, "%08x%08x%08x%08x",PP(A),PP(B),PP(C),PP(D));  //高低位逆反输出

//printf("%08x%08x%08x%08x\n",PP(A),PP(B),PP(C),PP(D));  //高低位逆反输出

// printf("%s\n", buf);

// 将buf内容显示在输入控件resultentry上
gtk_entry_set_text(GTK_ENTRY(resultentry),buf);

// 输入控件resultentry内容默认选定,方便用户复制
gtk_editable_select_region(GTK_EDITABLE(resultentry), 0, GTK_ENTRY(resultentry)->text_length);

}

// 获得文件名,并将它打印到控制台(console)上
void file_ok_sel( GtkWidget *w, GtkFileSelection *fs )
{
// 将文件选择控件所选择的文件路径传给filepath
sprintf(filepath, "%s", gtk_file_selection_get_filename (GTK_FILE_SELECTION (fs)));

//printf("%s\n", filepath);

// 将文件选择控件所选择的文件路径显示在输入空间entry上
gtk_entry_set_text (GTK_ENTRY (entry), filepath);

//const gchar *tmp;
//tmp = gtk_entry_get_text(GTK_ENTRY(entry));
//printf("<<<<<<<<<< %s\n", tmp);

// 文件已选择完毕,隐藏文件选择控件,貌似不能直接destroy掉..
//gtk_widget_destroy(GTK_WIDGET(fs));
gtk_widget_hide(GTK_WIDGET(fs));
}

void cb_button(GtkWidget *widget, GtkWidget *entry)
{

// 创建一个新的文件选择构件
filew = gtk_file_selection_new ("File selection");

g_signal_connect (G_OBJECT (filew), "destroy", G_CALLBACK (gtk_widget_destroy), NULL);
// 为 ok_button 按钮设置回调函数,连接到 file_ok_sel function 函数
g_signal_connect (G_OBJECT (GTK_FILE_SELECTION (filew)->ok_button), "clicked", G_CALLBACK (file_ok_sel), filew);

// 为 cancel_button 设置回调函数,销毁构件
g_signal_connect_swapped (G_OBJECT (GTK_FILE_SELECTION (filew)->cancel_button), "clicked", G_CALLBACK (gtk_widget_destroy), filew);

// 设置文件名,比如这个一个文件保存对话框,我们给了一个缺省文件名
gtk_file_selection_set_filename (GTK_FILE_SELECTION(filew), "hello.cmp");

gtk_widget_show (filew);

}

....

编译命令为:  gcc -o gtkmd5 gtkmd5.c `pkg-config --cflags --libs gtk+-2.0` -export-dynamic

比较恶心的是文件选择控件filew,当点击button时它被new出来,但是选择好文件之后,filew应该是消失的,我理所当然的调用 gtk_widget_destroy(GTK_WIDGET(fs)); 将之destroy掉了,伴随而来的副作用是主窗口也将退出,于是搞了个很猥琐的方法,直接用gtk_widget_hide(GTK_WIDGET(fs)); 将它隐藏起来了..不过,现在想想这种做法是不错的,因为用户可能重新需要选择文件,其实也就没有必要将之destroy掉了,隐藏起来正好是歪打正着..

最后是测试,居然很傻的总是先用md5sum算出gtkmd5.c的md5值,然后在gtkmd5.c中这修改那调整一点,然后compile+link,用生成的程序再次计算gtkme5.c的值,然后目光呆滞的看着两个不同的结果,抱着脑袋纠结了半天不明所以..真成杯具批发商了..最后,怨妇般的在这里对着个helloworld般的小程序吐槽.莫非更年期到了?

完整代码和glade文件在这..

最最后: 程序写得仔细点的话,应该把计算文件夹里所有文件md5值的功能加上的,这样,就可以把图中蹩脚的result entry换成GTK_TEXT_VIEW了..HoHo~~其实还应该加上个选择按钮,计算文件或者字符串的..