2020-10-13 开发文件管理器插件

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”

注意:
测试代码,没有任何保护措施,仅供参考。

你可能感兴趣的:(2020-10-13 开发文件管理器插件)