Nemo 插件开发机制
这篇文档介绍文件管理器三种插件开发机制,并提供对应的简单示例。
插件分类
右键菜单
我们右键某个文件,弹出的菜单项有 nemo 自定义的也有插件开发的。比如文件共享、文件压缩的功能都是通过插件机制嵌入到文件管理器的菜单。
这类插件最重要的接口就是 NemoMenuProviderIface,必须实现这个接口的 iface->get_file_items 函数。函数定义:
static GList *sendto_extension_get_file_items (NemoMenuProvider *provider,
GtkWidget *window,
GList *files)
其中 files 列表就是选择的文件清单,可以只选中一个文件,也可以多个。列表成员 NemoFileInfo,许多工作都是围绕这个文件清单来做的。
主要工作:创建右键菜单项 nemo_menu_item_new,这个菜单可能需要子菜单,比如示例 test-sendto 是一个右键发送到可移动设备的原型程序,所以需要把系统中的可移动设备列表放在子菜单中。nemo_menu_new 创建子菜单,nemo_menu_item_set_submenu 把它添加到菜单项。
文件属性页
右键文件,在菜单中选择属性,弹出属性窗口,默认有三个tab页,“基本”,“权限”,“打开方式”,在这三个之外,我们也可以根据文件类型、内容的不同自定义属性页。
这类插件最重要的接口是 NemoPropertyPageProviderIface,必须实现这个接口的 iface->get_pages 函数,函数定义:
static GList *
get_property_pages (NemoPropertyPageProvider *provider,
GList *files)
这里的 files 文件也是我们选中的文件清单,列表成员类型NemoFileInfo,比如示例中对于所有 mimetype 是文本文件类型的都新增属性页“Text File”,就是通过检查这个文件的 nemo_file_info_get_mime_type 来做的。
主要工作:调用 nemo_property_page_new 创建属性页。属性页是个 Gtk 控件,所以具体内容就是使用 gtk 创建就可以了。
详细信息列
这个插件与上面两个略有不同,上面那两个都是选择若干文件后右键触发的,所以都能拿到一个文件清单列表,这个列表里就是一个个 NemoFileInfo,我们可以对这些文件做处理。但是这个插件比较不一样,没有选择的文件。我们打开某个目录,在列表视图下所有文件都会新增一列,比如我们示例的文本类型都会增加一个“test column”的属性,其他文件类型没有新增属性这列就为空。
这个插件除了要实现 NemoColumnProviderIface 接口,还得实现 NemoInfoProviderIface 接口,这个接口就可以帮助我们拿到所有文件的信息,根据自己的需要来设置文件属性。
代码示例
每个插件都需要实现几个固定的接口,编译生成动态链接库,安装到 nemo 的 extensions 目录,这样 nemo 启动时才能找到他们。
1、插件加载等接口
/* Extension initialization */
void nemo_module_initialize (GTypeModule *module)
{
sendto_extension_register_type(module);
provider_types[0] = sendto_extension_get_type();
}
void nemo_module_shutdown(void)
{
/* Any module-specific shutdown */
}
void nemo_module_list_types (const GType **types, int *num_types)
{
*types = provider_types;
*num_types = G_N_ELEMENTS (provider_types);
}
2、接口实现: 以属性页为例
/* 我们这个插件实现了什么接口就在这个函数中注册
* 这里只实现了属性页,所以只有一个 interface
*/
static void
nemo_txt_properties_page_register_type (GTypeModule *module)
{
GType nemo_txt_type = nemo_txt_properties_page_get_type ();
static const GInterfaceInfo property_page_provider_iface_info = {
(GInterfaceInitFunc) property_page_provider_iface_init,
NULL,
NULL
};
g_type_module_add_interface (module,
nemo_txt_type,
NEMO_TYPE_PROPERTY_PAGE_PROVIDER,
&property_page_provider_iface_info);
}
右键菜单
按照上面介绍的,右键菜单最重要的接口就是 get_file_items,在接口注册中添加接口 sendto_extension_menu_provider_iface_init,接口中定义我们要实现的函数:
iface->get_file_items = sendto_extension_get_file_items;
函数实现:
/* 右键菜单实现最重要的就是这个函数 */
static GList *sendto_extension_get_file_items (NemoMenuProvider *provider,
GtkWidget *window,
GList *files)
{
GList *items, *iterate;
NemoMenuItem *separator;
NemoMenuItem *item;
show_menu = TRUE;
gchar *uri = NULL;
GFile *object = NULL;
if (!files) {
return NULL;
}
/* 这个 files 就是我们右键时选中的文件清单,可以单个也可以多个 */
iterate = files;
while (iterate != NULL) {
NemoFileInfo *file = iterate->data;
uri = nemo_file_info_get_uri (file);
object = g_file_new_for_uri (uri);
gchar *path = g_file_get_path (object);
if (path == NULL) // show_menu 用来控制是否显示自定义的菜单,这个例子中不存在可移动设备就不用显示。
{
show_menu = FALSE;
} else {
g_free (path);
}
g_free (uri);
iterate = iterate->next;
}
item = nemo_menu_item_new ("SendtoExtension::SendtoData",
"NFS Sendto",
"Sendto Data",
"Sendto Description");
g_object_set (G_OBJECT(item), "sensitive", FALSE, NULL);
NemoMenu *sendtoMenu = nemo_menu_new();
nemo_menu_item_set_submenu(item, sendtoMenu);
create_sendto_target (sendtoMenu, files, item);
separator = nemo_menu_item_new_separator ("Sendto Separator");
items = g_list_append (NULL, separator);
items = g_list_append(items, item);
return items;
}
文件属性页
属性页最重要的接口是 get_pages,接口注册函数中定义我们的实现函数
static void
property_page_provider_iface_init (NemoPropertyPageProviderIface *iface)
{
iface->get_pages = get_property_pages;
}
函数实现:
/* 这个函数最重要,nemo 加载属性页时就会调用这里 */
static GList *
get_property_pages (NemoPropertyPageProvider *provider,
GList *files)
{
GList *pages;
NemoFileInfo *file;
char *mime_type;
/* Only show the property page if 1 file is selected */
if (!files || files->next != NULL) {
return NULL;
}
pages = NULL;
file = NEMO_FILE_INFO (files->data);
mime_type = nemo_file_info_get_mime_type (file);
g_print ("%s %d: mime type %s\n", __func__, __LINE__, mime_type);
if (mime_type != NULL && is_mime_type_supported (mime_type)) {
NemoTxtPropertiesPage *page;
NemoPropertyPage *real_page;
page = g_object_new (nemo_txt_properties_page_get_type (), NULL);
load_location (page, file);
real_page = nemo_property_page_new ("NemoTxtPropertiesPage::property_page",
gtk_label_new (_("Text File")),
GTK_WIDGET (page));
pages = g_list_append (pages, real_page);
}
g_free (mime_type);
return pages;
}
详细信息列
这个插件最特殊的就是需要修改文件信息,因为我们要定制文件列信息,为了实现这个功能,必须修改文件属性。这个插件的注册接口:
static void
nemo_txt_column_register_type (GTypeModule *module)
{
GType nemo_txt_type = nemo_txt_column_get_type ();
static const GInterfaceInfo column_provider_iface_info = {
(GInterfaceInitFunc) nemo_txt_column_provider_iface_init,
NULL,
NULL
};
static const GInterfaceInfo info_provider_iface_info = {
(GInterfaceInitFunc) nemo_txt_info_provider_iface_init,
NULL,
NULL
};
g_type_module_add_interface (module,
nemo_txt_type,
NEMO_TYPE_COLUMN_PROVIDER,
&column_provider_iface_info);
g_type_module_add_interface (module,
nemo_txt_type,
NEMO_TYPE_INFO_PROVIDER,
&info_provider_iface_info);
}
首先介绍 column provider,这个很简单,我们需要实现 get_columns接口,想增加几列就在这个函数中调用 nemo_column_new 新建列,比较重要的是有个属性信息,比如例子中自定义 title 属性,我们在 info provider 中需要用到。
NemoInfoProviderIface 的定义如下:
static void
nemo_txt_info_provider_iface_init (NemoInfoProviderIface *iface) {
iface->update_file_info = nemo_txt_update_file_info;
return;
}
在这个函数中,我们为了方便只是简单的判断如果是文本文件,就设置属性 title 的值为 “test column”
/* Info interfaces */
static NemoOperationResult
nemo_txt_update_file_info (NemoInfoProvider *provider,
NemoFileInfo *file,
GClosure *update_complete,
NemoOperationHandle **handle)
{
char *mime_type;
g_print ("%s %d\n", __func__, __LINE__);
mime_type = nemo_file_info_get_mime_type (file);
g_print ("%s %d: mime type %s\n", __func__, __LINE__, mime_type);
if (mime_type != NULL && is_mime_type_supported (mime_type)) {
nemo_file_info_add_string_attribute (file,
"title", // 对应上面的 title
"test column");
}
return NEMO_OPERATION_IN_PROGRESS;
}
插件验证
这三个示例程序,分别编译成动态链接库文件,安装到插件目录 /usr/lib/x86_64-linux-gnu/nemo/extensions-3.0/,重启 nemo。
1、如果外接了U盘,右键某个文件,就会出现 NFS Sendto 菜单,hover 上去出现 U 盘子菜单,点击子菜单触发信号,示例程序弹一个 gtk 对话框。
2、如果选择某个文本文件,右键属性,在属性页会新增一个文本文件页,显示“文本类型” 和 “File uri” 这两条信息.
3、进入某个空文件目录,选择详细信息,新建一个文件文档。右键在顶部列表栏中选择我们自定义的 “Title”,就能看到测试值 “test column”
注意:
测试代码,没有任何保护措施,仅供参考。