Openfetion for ubuntu messaging menu开发手记
自从ubuntu11.04发布之后Openfetion就遇到了一个比较麻烦的问题,把Openfetion飞信最小化到托盘之后就找不到了,没用过ubuntu11.04,不过据说它的unity桌面貌似没有status icon这回事,所以把Openfetion塞进Messaging Menu也成了一个很重要的任务,在这里把开发过程和大家分享。
首先要感谢@YunQiangSu提供的关于ubuntu messaging menu的资料,是在ubuntu wiki上关于Messaging Menu的介绍,链接在此:https://wiki.ubuntu.com/MessagingMenu/,这篇文章是关于Messaging Menu的一个介绍,以及它的行为和样式的一个指南,虽然没有涉及到具体的开发细则,但也不失为一个很重要的参考。很惭愧地说,在这之前我甚至不知道在ubuntu右上角看到的那个信封到底叫什么名字,后来知道它原来是一个Indicator,ubuntu的status icon区域很多软件都是用libappindicator来实现的,所以它们的行为和其它的发行版不太一致,比如Dropbox和Transimission左键点击Status Icon就可以弹出菜单,而在Slackware里面就只能用右键才能弹出菜单,这就是它们的不同,一个是Indicator,一个是普通的Gnome Status Icon,而这里面提到的右上角的那个信封便是Indiactor的一种,名字叫做Messaging Menu。好吧,我相信很多ubuntu用户会过来鄙视我的,写下这些给那些和我一样的小白扫扫盲,有误请指正。
我们可以把软件安装到Messaging Menu里面,这样即使软件没有启动也可以在Messaging Menu里面找到该软件,并可以从那里启动该软件,方法很简单。下面是pidgin的做法:
mkdir -p debian/pidgin/usr/share/indicators/messages/applications echo /usr/share/applications/pidgin.desktop > \ debian/pidgin/usr/share/indicators/messages/applications/pidgin
这两句话的功能一眼就看地出来,无须解释了。
Messaging Menu相关的开发需要用到libindicate这个库,首先得确保在系统里面安装了这个库的开发版本:
sudo apt-get install libindicate-dev
首先我们需要获取默认IndicateServer对象的引用,并对其进行初始化:
IndicateServer *server = indicate_server_ref_default(); /* 这句话给软件分类,主要是保存位置不同 */ indicate_server_set_type(server, "message.openfetion"); /* 这句话比较重要,它会让已安装在Messaging Menu里面的软件项前面带一个箭头, * 表示该软件当前正在运行,效果图见下面 */ indicate_server_set_desktop_file(server, DESKTOP_DIR"openfetion.desktop"); indicate_server_show(server); g_signal_connect(G_OBJECT(server), INDICATE_SERVER_SIGNAL_SERVER_DISPLAY, G_CALLBACK(server_display), fxmain);
当收到好友发送过来的消息时可以在该项后面显示哪个好友发送过来的消息,以及显示未读消息的条数,效果图如下:
这时候就需要给当前的这个IndicateServer对象添加一个IndicateIndicator对象,并为该对象设置相关的属性,如name(即显示Indicator上显示的名字),count(未读消息的数目),time(消息发送的时间),icon(像如发送消息好友的头像),draw-attention(这个属性可以随便设置一个非空字符串,以使Indicator高亮提示用户有消息到达,同理设置空字符串可取消高亮)。
上面的几个属性除了time和icon之外都可以用indicate_indicator_set_property()函数来搞定,该函数设置的属性值都是字符串,time是一个时间值,需要使用indicate_indicator_set_property_time()函数来进行设置,至于icon属性的设置需要用到另外一个库libindicate-gtk,这个库仅提供了两个函数,其中一个便是indicate_indicator_set_proper_icon()用来设置icon属性,属性值是一个GdkPixbuf对象。下面看Openfetion上收到一条消息时Messaging Menu所对的动作:
/* no indicator found, create one :) */ indicator = indicate_indicator_new(); /* add it to the global indicator list */ indicators = g_slist_append(indicators, indicator); indicate_server_add_indicator(server, indicator); indicate_indicator_set_property(indicator, INDICATE_INDICATOR_MESSAGES_PROP_COUNT, "0"); indicate_indicator_set_property(indicator, "sid", sid); /* set icon */ snprintf(portrait, sizeof(portrait) - 1, "%s/%s.jpg", fxmain->user->config->iconPath, sid); pixbuf = gdk_pixbuf_new_from_file(portrait, NULL); indicate_indicator_set_property_icon(indicator, INDICATE_INDICATOR_MESSAGES_PROP_ICON, pixbuf); g_object_unref(pixbuf); g_signal_connect(G_OBJECT(indicator), INDICATE_INDICATOR_SIGNAL_DISPLAY, G_CALLBACK(message_display), NULL); indicate_indicator_show(indicator);
首先我们需要确定在Messaging Menu中是否已经有该用户对应的Indicator存在,如果有就没有必要再创建一个新的造成重复,而检测重复性这个问题费了我不小的力气,libindicate没有提供获取列表的API,但查了一下它的源码,发现它确实实现了一个获取列表的函数,它把它定义为虚函数,推荐用户从这个类继承,我对GTK的C语言面向对象的机制不太了解,不知道该怎么去继承,但既然有这个函数就可以想办法拿来调用,能获取到Indicator列表,但这个列表简直让我抓狂,一个GArray列表,里面存放的是Indicator的id,一个毫无用处的数字,通过它根本获取不到Indicator对象,更不用说Indicator对象的属性了,因此我只能用自己的方法来实现了,其实很简单,就是每创建一个Indicator对象就把它塞到一个GSList里面,创建之前搜索该链表,如果其中有indicator的sid值与要创建的sid相同,则表示已存在,无需再重新创建,把它的count值加1即可。
有一点需要注意的是libindicate-gtk新旧版本的pc文件名是不同的(感谢@riku的测试),在10.04上是旧版pc文件名是indicate-gtk.pc,因此编译选项可以是
pkg-config --cflags --libs indicate-gtk
而在ubuntu 11.04中文件名是indicate-gtk-0.5.pc,编译选项便是:
pkg-config --cflags --libs indicate-gtk-0.5
我在CMakeLists.txt做了一些处理来让它自动识别这两个版本的不同。