Skip to content

Commit

Permalink
chore: [example] add example for dfm-extension
Browse files Browse the repository at this point in the history
add example for dfm-extension

Log: add example for dfm-extension
  • Loading branch information
Johnson-zs committed Jan 3, 2024
1 parent 9cad874 commit e4b6222
Show file tree
Hide file tree
Showing 8 changed files with 386 additions and 1 deletion.
2 changes: 1 addition & 1 deletion .reuse/dep5
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ Copyright: None
License: CC0-1.0

# ignore files
Files: src/*.json src/*.xml src/*.policy src/*/pinyin.dict src/*.js src/*.ini src/*.qss src/*.theme src/*/templates/* src/*.psd src/*/com.deepin.filemanager.daemon.conf src/dfm-base/qrc/configure/*.cpp
Files: src/*.json src/*.xml src/*.policy src/*/pinyin.dict src/*.js src/*.ini src/*.qss src/*.theme src/*/templates/* src/*.psd src/*/com.deepin.filemanager.daemon.conf src/dfm-base/qrc/configure/*.cpp examples/*
Copyright: None
License: CC0-1.0

Expand Down
37 changes: 37 additions & 0 deletions examples/dfm-extension-example/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
cmake_minimum_required(VERSION 2.8)

# 设置项目名称
project(dfm-extension-example)

set(CMAKE_CXX_STANDARD 17)

# 只需要依赖 dfm-extension
find_package(dfm-extension REQUIRED)


# 扩展插件源码
file(GLOB_RECURSE SRCS CONFIGURE_DEPENDS
"./*.h"
"./*.cpp"
)

# 生成共享库
add_library(${PROJECT_NAME} SHARED ${SRCS})

# 链接 dfm-extension
target_link_libraries(${PROJECT_NAME}
PUBLIC ${dfm-extension_LIBRARIES}
)

# 安裝配置
include(GNUInstallDirs)
if(NOT DEFINED LIB_INSTALL_DIR)
set(LIB_INSTALL_DIR ${CMAKE_INSTALL_FULL_LIBDIR})
endif()

if(NOT DEFINED DFM_EXT_PLUGIN_DIR)
set(DFM_EXT_PLUGIN_DIR ${LIB_INSTALL_DIR}/dde-file-manager/plugins/extensions)
endif()

# 安裝插件
install(TARGETS ${PROJECT_NAME} LIBRARY DESTINATION ${DFM_EXT_PLUGIN_DIR})
19 changes: 19 additions & 0 deletions examples/dfm-extension-example/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# dfm-extension-example

一个基于文件管理器(dde-file-manager)的扩展开发库(dfm-extension)的示例程序。

## 依赖

```shell
$ sudo apt install libdfm-extension-dev
```

## 安装

```shell
$ cmake -B build -DCMAKE_INSTALL_PREFIX=/usr
$ cmake --build build
$ sudo cmake --build build --target install
```

安装后,重启生效。
31 changes: 31 additions & 0 deletions examples/dfm-extension-example/dfm-extension-example.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
#include <dfm-extension/dfm-extension.h>

#include "mymenuplugin.h"
#include "myemblemiconplugin.h"

// 右键菜单的扩展
static DFMEXT::DFMExtMenuPlugin *myMenu { nullptr };
// 角标的扩展
static DFMEXT::DFMExtEmblemIconPlugin *myEmblemIcon { nullptr };

extern "C" void dfm_extension_initiliaze()
{
myMenu = new Exapmle::MyMenuPlugin;
myEmblemIcon = new Exapmle::MyEmblemIconPlugin;
}

extern "C" void dfm_extension_shutdown()
{
delete myMenu;
delete myEmblemIcon;
}

extern "C" DFMEXT::DFMExtMenuPlugin *dfm_extension_menu()
{
return myMenu;
}

extern "C" DFMEXT::DFMExtEmblemIconPlugin *dfm_extension_emblem()
{
return myEmblemIcon;
}
54 changes: 54 additions & 0 deletions examples/dfm-extension-example/myemblemiconplugin.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// SPDX-FileCopyrightText: 2023 UnionTech Software Technology Co., Ltd.
//
// SPDX-License-Identifier: GPL-3.0-or-later

#include "myemblemiconplugin.h"

#include <sys/types.h>
#include <sys/xattr.h>
#include <ulimit.h>

namespace Exapmle {

USING_DFMEXT_NAMESPACE

MyEmblemIconPlugin::MyEmblemIconPlugin()
: DFMEXT::DFMExtEmblemIconPlugin()
{
registerLocationEmblemIcons([this](const std::string &filePath, int systemIconCount) {
return locationEmblemIcons(filePath, systemIconCount);
});
}

MyEmblemIconPlugin::~MyEmblemIconPlugin()
{
}

DFMExtEmblem MyEmblemIconPlugin::locationEmblemIcons(const std::string &filePath, int systemIconCount) const
{
DFMExtEmblem emblem;

// 一个文件的角标最多只有 4 个,当系统角标的数量已经达到 4 个时则无法添加扩展角标了
// 此外,如果扩展角标添加的位置被系统角标占用,那么扩展角标将无法被显示
if (systemIconCount >= 4)
return emblem;

// 从文件扩展属性中获取右键菜单扩展添加的角标属性
char buffer[FILENAME_MAX] { 0 };
ssize_t result = getxattr(filePath.c_str(), "user.icon", buffer, FILENAME_MAX);
if (result == -1)
return emblem;

// 添加角标 icon 到文件图标的左下角
std::string strBuffer { buffer };
if (!strBuffer.empty()) {
std::vector<DFMExtEmblemIconLayout> layouts;
DFMExtEmblemIconLayout iconLayout { DFMExtEmblemIconLayout::LocationType::BottomLeft, strBuffer };
layouts.push_back(iconLayout);
emblem.setEmblem(layouts);
}

return emblem;
}

} // namespace Exapmle
23 changes: 23 additions & 0 deletions examples/dfm-extension-example/myemblemiconplugin.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// SPDX-FileCopyrightText: 2023 UnionTech Software Technology Co., Ltd.
//
// SPDX-License-Identifier: GPL-3.0-or-later

#ifndef MYEMBLEMICONPLUGIN_H
#define MYEMBLEMICONPLUGIN_H

#include <dfm-extension/emblemicon/dfmextemblemiconplugin.h>

namespace Exapmle {

class MyEmblemIconPlugin : public DFMEXT::DFMExtEmblemIconPlugin
{
public:
MyEmblemIconPlugin();
~MyEmblemIconPlugin();

DFMEXT::DFMExtEmblem locationEmblemIcons(const std::string &filePath, int systemIconCount) const DFM_FAKE_OVERRIDE;
};

} // namespace Exapmle

#endif // MYEMBLEMICONPLUGIN_H
184 changes: 184 additions & 0 deletions examples/dfm-extension-example/mymenuplugin.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
// SPDX-FileCopyrightText: 2023 UnionTech Software Technology Co., Ltd.
//
// SPDX-License-Identifier: GPL-3.0-or-later

#include "mymenuplugin.h"

#include <sys/types.h>
#include <sys/xattr.h>
#include <sys/wait.h>
#include <unistd.h>
#include <cassert>
#include <iostream>

#include <dfm-extension/menu/dfmextmenu.h>
#include <dfm-extension/menu/dfmextmenuproxy.h>
#include <dfm-extension/menu/dfmextaction.h>

namespace Exapmle {
USING_DFMEXT_NAMESPACE

MyMenuPlugin::MyMenuPlugin()
: DFMEXT::DFMExtMenuPlugin()
{
registerInitialize([this](DFMEXT::DFMExtMenuProxy *proxy) {
initialize(proxy);
});
registerBuildNormalMenu([this](DFMExtMenu *main, const std::string &currentPath,
const std::string &focusPath, const std::list<std::string> &pathList,
bool onDesktop) {
return buildNormalMenu(main, currentPath, focusPath, pathList, onDesktop);
});
registerBuildEmptyAreaMenu([this](DFMExtMenu *main, const std::string &currentPath, bool onDesktop) {
return buildEmptyAreaMenu(main, currentPath, onDesktop);
});
}

MyMenuPlugin::~MyMenuPlugin()
{
}

void MyMenuPlugin::initialize(DFMExtMenuProxy *proxy)
{
m_proxy = proxy;
}

bool MyMenuPlugin::buildNormalMenu(DFMExtMenu *main, const std::string &currentPath,
const std::string &focusPath, const std::list<std::string> &pathList,
bool onDesktop)
{
// 可使用此方法管理自己开辟的内存,菜单关闭时被调用
// 但是并不需要使用代理 m_proxy->deleteMenu / delteAction
// 因为只要创建的 menu 和 action 被加入了菜单 main,那么
// 它们的内存会被自动释放
auto memTest { new int };
main->registerDeleted([memTest](DFMExtMenu *self) {
delete memTest;
});

(void)onDesktop;
(void)currentPath;
(void)focusPath;

// 通过代理创建 action,此 action 在堆区分配,不自行释放将内存泄露!
auto rootAction { m_proxy->createAction() };
rootAction->setText("角标管理");

// 通过代理创建 menu,此 menu 在堆区分配,不自行释放将内存泄露!
auto menu { m_proxy->createMenu() };

// 二级菜单在 Hover 中创建,以减少一级菜单显示的性能开销
rootAction->setMenu(menu);
rootAction->registerHovered([this, pathList](DFMExtAction *action) {
if (!action->menu()->actions().empty())
return;
auto favoriteEmblemAct { m_proxy->createAction() };
favoriteEmblemAct->setText("角标设置为favorite");
favoriteEmblemAct->setIcon("emblem-favorite");
favoriteEmblemAct->registerTriggered([this, pathList](DFMExtAction *, bool) {
std::for_each(pathList.begin(), pathList.end(), [this](const std::string &path) {
setEmblemIcon(path, "emblem-favorite");
});
});

auto defaultEmblemAct { m_proxy->createAction() };
defaultEmblemAct->setIcon("emblem-default");
defaultEmblemAct->setText("角标设置为default");
defaultEmblemAct->registerTriggered([this, pathList](DFMExtAction *, bool) {
std::for_each(pathList.begin(), pathList.end(), [this](const std::string &path) {
setEmblemIcon(path, "emblem-default");
});
});

auto clearEmbelmAct { m_proxy->createAction() };
clearEmbelmAct->setIcon("emblem-important");
clearEmbelmAct->setText("清除角标");
clearEmbelmAct->registerTriggered([this, pathList](DFMExtAction *, bool) {
std::for_each(pathList.begin(), pathList.end(), [this](const std::string &path) {
clearEmblemIcon(path);
});
});

action->menu()->addAction(favoriteEmblemAct);
action->menu()->addAction(defaultEmblemAct);
action->menu()->addAction(clearEmbelmAct);
});

main->addAction(rootAction);
return true;
}

bool MyMenuPlugin::buildEmptyAreaMenu(DFMExtMenu *main, const std::string &currentPath, bool onDesktop)
{
assert(main);

// 通过代理创建 action,此 action 在堆区分配,不自行释放将内存泄露!
auto action { m_proxy->createAction() };

// 通过 onDesktop 区分业务在桌面还是文管
if (onDesktop)
action->setText("从文件管理器打开桌面");
else
action->setText("打开当前路径");
// 添加图标,也支持图片的文件绝对路径
// 例如:/usr/share/icons/Adwaita/16x16/emblems/emblem-generic.png
action->setIcon("emblem-generic");

// action 被点击触发的业务处理
action->registerTriggered([currentPath](DFMExtAction *self, bool checked) {
(void)self;
(void)checked;
pid_t pid = fork();
if (pid == 0) {
// 子进程中调用 execvp 函数来执行 dde-file-manager 进程,并传递参数
char *argv[] { "/usr/bin/dde-file-manager", "-n", const_cast<char *>(currentPath.c_str()), NULL };
execvp(argv[0], argv);
} else if (pid > 0) {
// 父进程等待子进程结束
int status;
waitpid(-1, &status, WNOHANG);
} else {
perror("fork failed");
}
});

main->addAction(action);
return true;
}

void MyMenuPlugin::setEmblemIcon(const std::string &filePath, const std::string &iconName)
{
std::cout << "set emblem icon " << iconName << " for " << filePath << std::endl;
const std::string path { removeScheme(filePath) };
int result = setxattr(path.c_str(), "user.icon", iconName.c_str(), iconName.size(), 0);
if (result == -1)
perror("setxattr");
}

void MyMenuPlugin::clearEmblemIcon(const std::string &filePath)
{
std::cout << "clear emblem icon for " << filePath;
const std::string path { removeScheme(filePath) };
int result = removexattr(path.c_str(), "user.icon");
if (result == -1)
perror("removexattr");
}

// 在 V5 版本的文管中,menu 接收到的是文件 url 的字符串,而 V6 则直接是文件路径
std::string MyMenuPlugin::removeScheme(const std::string &url)
{
std::string result = url;

// 查找第一个冒号后的斜杠位置
size_t startPos = result.find("://");
if (startPos != std::string::npos) {
startPos = result.find('/', startPos + 3); // 跳过冒号和两个斜杠
if (startPos != std::string::npos) {
result = result.substr(startPos);
}
}

return result;
}

} // namespace Exapmle
Loading

0 comments on commit e4b6222

Please sign in to comment.