九天雁翎的博客
如果你想在软件业获得成功,就使用你知道的最强大的语言,用它解决你知道的最难的问题,并且等待竞争对手的经理做出自甘平庸的选择。 -- Paul Graham

KDE上下文菜单的动态修改

KDE上下文菜单的动态修改

write by 九天雁翎(JTianLing) -- www.jtianling.com

讨论新闻组及文件

一、   提要

事实上静态改变KDE上下文菜单网上还有些资料(以E文为主),但是动态改变的我连E文的资料都没有看到,纯靠自己看代码摸索了。行文会比较乱,但是需要的人应该能找到自己所要的东西。文中复杂的行解释时会与KDE源代码相对应,以做备查。本文操作环境是kubuntu9.04

 

二、   静态修改

KDE中的上下文菜单是以.desktop形式的配置文件方式来管理的。此配置文件强大到甚至支持多语言-_-!牛吧

首先查看系统中此配置文件所在的位置。通过kde4-config –path services命令来查看,下面是我机器中运行的例子:

jtianling@jtianling-laptop:~$ kde4-config --path services

/home/jtianling/.kde/share/kde4/services/:/usr/share/kde4/services/

即有kde4 services配置的地方分别在/home/jtianling/.kde/share/kde4/services/这个个人的目录和/usr/share/kde4/services/这个改变全局配置的目录。这点符合Linux的习惯,在没有root权限或者仅仅想改变自己的操作习惯时,在自己的home目录下就能改变自己的配置,有root权限并且想改变所有用户的配置时,去/usr下面改。此处如是,这里全部以修改全局的为例。想尝试的推荐首先备份。

首先进入services目录下的ServiceMenus这个与上下文菜单相关的配置目录中去。

我们从一个简单的例子开始,下面是Kubuntu 9.04中自带的一个简单菜单项,类似于windows下常用的command prompt Herecygwinbash prompt here,作用是在当前文件夹位置打开一个konsole。对应的文件是konsolehere.desktop

[Desktop Entry]
Type=Service
X-KDE-ServiceTypes=KonqPopupMenu/Plugin,inode/directory
Actions=openTerminalHere;
X-KDE-AuthorizeAction=shell_access

[Desktop Action openTerminalHere]
Name=Open Terminal Here
………
Name[zh_CN]=在此打开终端
Name[zh_TW]=
在這裡開啟終端機
Icon=utilities-terminal
Exec=konsole --workdir %f
X-Ubuntu-Gettext-Domain=desktop_kdebase

 

一个一个解释一下:

配置的方式就与普通的ini文件类似,[]用于分段,xxx=yyy用于配置。这里解释一下配置选项。

 

Type=Service

官网说是类型默认为application,还解释application是有Exec行的东西(原话为:i.e. something with Exec line)需要指定Service表示类似于插件的东西,事实上我在我的机器上,将services目录所有的desktop文件全部统计了一下:

grep –R –e ‘Type=S’ * wc –l

一共有822Service,但是

grep –R –e ‘Type=^S’

一个也没有,所以,直接将此行默认存在就完了。

 

X-KDE-ServiceTypes=KonqPopupMenu/Plugin,inode/directory

还是参考一有说明,KonqPopupMenu/Plugin表示是Konqueror的插件,inode/directory表示在指定目录或目录中的时候也有效。

还可以用all/allall/allfiles表示所有文件,以逗号分隔。

 

Actions=openTerminalHere;

标明动作,由接下来的[Desktop Action openTerminalHere]分段具体说明

 

X-KDE-AuthorizeAction=shell_access

很明显,所要的权限,此处为shell_access

 

[Desktop Action openTerminalHere]

新的分节,后面的openTerminalHere与前面定义的动作一致,整个分节是对此动作的一个详细说明。

 

Name=Open Terminal Here

说明显示的菜单的名字,通过Name[zh_CN]这样的形式支持多语言版本

 

Icon=utilities-terminal

指定图标

 

Exec=konsole --workdir %f

指定菜单命令

上述的东西都不是关键,也不算什么难点,下面真的好戏登场了。

 

三、   动态修改

这在KDE中算是新鲜课题,一则少有人知,甚至没有找到E文资料,二则我看到在KDEchangelog中竟然提到kdesvn的作者发现了其中有一些问题。(见http://www.kde.org/announcements/changelogs/3_5_6/kdelibs.txt)说明平时用的人估计也少。要不是有kdesvn作者走在前面,我连看源代码的头绪都没有,向Rajko Albrecht致敬。

下面是kdesvn的配置片段:

[Desktop Entry]

Type=Service

ServiceTypes=KonqPopupMenu/Plugin,inode/directory,all/all,all/allfiles

X-KDE-Submenu=Subversion (kdesvn)

X-KDE-GetActionMenu=org.kde.kded /modules/kdesvnd org.kde.kdesvnd getActionMenu

Actions=Log;Info;Add;Addnew;Delete;Revert;Rename;Import;Checkout;Switch;Merge;Blame;CreatePatch;Export;Diff;Update;Commit;Checkoutto;Exportto;Tree;

      

其他的都好说了,主要的问题在X-KDE-GetActionMenu=org.kde.kded /modules/kdesvnd org.kde.kdesvnd getActionMenu一句,kdesvn也就是靠此句实现了动态的上下文菜单。

从应用上看,对于非svn工程的目录,在平时显示的是Checkout from a repositoryExport from a subversion repository,而Actions里面也有,而Actions里面还有当目标的确是一个svn工程的目录时的一大堆选项。说明事实上选项是通过某个函数进行筛选然后显示的。看上面的配置,很显然是X-KDE-GetActionMenu配置的getActionMenu函数(从命名上猜的),通过关键字,我定位到了关键的KDE源代码:

kdelibs/kio/kio/kdesktopfileactions.cpp:219开始

QList<KServiceAction> KDesktopFileActions::userDefinedServices( const KService& service, bool bLocalFiles, const KUrl::List & file_list )
{
    QList<KServiceAction> result;

    if (!service.isValid()) // e.g. TryExec failed
        return result;

    QStringList keys;
    const QString actionMenu = service.property("X-KDE-GetActionMenu", QVariant::String).toString();
    if (!actionMenu.isEmpty()) {
        const QStringList dbuscall = actionMenu.split(QChar(' '));
        if (dbuscall.count() >= 4) {
            const QString& app       = dbuscall.at( 0 );
            const QString& object    = dbuscall.at( 1 );
            const QString& interface = dbuscall.at( 2 );
            const QString& function  = dbuscall.at( 3 );

            QDBusInterface remote( app, object, interface );
            // Do NOT use QDBus::BlockWithGui here. It runs a nested event loop,
            // in which timers can fire, leading to crashes like #149736.
            QDBusReply<QStringList> reply = remote.call(function, file_list.toStringList());
            keys = reply;               // ensures that the reply was a QStringList
            if (keys.isEmpty())
                return result;
        } else {
            kWarning(7000) << "The desktop file" << service.entryPath()
                           << "has an invalid X-KDE-GetActionMenu entry."
                           << "Syntax is: app object interface function";
        }
    }

    // Now, either keys is empty (all actions) or it's set to the actions we want

    foreach(const KServiceAction& action, service.actions()) {
        if (keys.isEmpty() || keys.contains(action.name())) {
            const QString exec = action.exec();
            if (bLocalFiles || exec.contains("%U") || exec.contains("%u")) {
                result.append( action );
            }
        }
    }

    return result;
}

 

源码可以看出,其一,我们分析配置文件没有错误,此行的确是关键,其二,获取具体字符串的方式利用了D-bus这个进程间通信手段远程调用了getActionMenu这个函数,然后获取到了一个字符串list,此list就是我们最后看到的菜单项。至此,一切问题已经得到解决,动态菜单的关键就是以X-KDE-GetActionMenu配置然后通过D-bus调用函数。D-bus作为Linux下非常流行的进程间通信手段就远没有KDE动态菜单这么神秘了,资料丰富的多,需要的就自己去查阅吧。

 

四、   参考资料

1.KDE tech baseKonqueror Service Menu相关章节:

http://techbase.kde.org/Development/Tutorials/Creating_Konqueror_Service_Menus

write by 九天雁翎(JTianLing) -- www.jtianling.com

 

 

 



分类:  Linux 
标签:  KDE 

Posted By 九天雁翎 at 九天雁翎的博客 on 2009年10月17日

前一篇: Linux下的blog writer -- bilobo测试 后一篇: Windows Live Writer试用及众多插件试用评测