fix: introduce new DesktopFile class

KeyFile::saveToFile not guaranteed section order, which is not the right
behavior of desktop file. XDG requires the first section of desktop file
always to be "Desktop Entry".

So I write a new DesktopFile class, which override the saveToFile method
of KeyFile, to make it work.

close linuxdeepin/developer-center#3807

Signed-off-by: black-desk <me@black-desk.cn>
This commit is contained in:
Chen Linxuan 2023-03-07 10:52:06 +08:00 committed by deepin-bot[bot]
parent abf322377e
commit accc9c2aac
9 changed files with 127 additions and 47 deletions

42
src/lib/desktopfile.cpp Normal file
View File

@ -0,0 +1,42 @@
// SPDX-FileCopyrightText: 2023 UnionTech Software Technology Co., Ltd.
//
// SPDX-License-Identifier: GPL-3.0-or-later
#include "desktopfile.h"
DesktopFile::DesktopFile(char separtor)
: KeyFile(separtor)
{
}
DesktopFile::~DesktopFile() = default;
bool DesktopFile::saveToFile(const std::string &filePath){
FILE *sfp = fopen(filePath.data(), "w+");
if (!sfp) {
perror("open file failed...");
return false;
}
// NOTE(black_desk): XDG require the first section of desktop file
// is always "Desktop Entry"
auto mainSection = m_mainKeyMap.find("Desktop Entry");
if (mainSection != m_mainKeyMap.end()) {
// FIXME(black_desk): should handle write fail.
writeSectionToFile(mainSection->first, mainSection->second, sfp);
} else {
// FIXME(black_desk): should have some warning.
}
for (const auto &im : m_mainKeyMap) {
if (im.first == "Desktop Entry") {
continue;
}
// FIXME(black_desk): should handle write fail.
writeSectionToFile(im.first, im.second, sfp);
}
fclose(sfp);
return true;
}

24
src/lib/desktopfile.h Normal file
View File

@ -0,0 +1,24 @@
// SPDX-FileCopyrightText: 2023 UnionTech Software Technology Co., Ltd.
//
// SPDX-License-Identifier: GPL-3.0-or-later
#ifndef DESKTOPFILE_H
#define DESKTOPFILE_H
#include "keyfile.h"
#include <string>
#include <map>
#include <vector>
// 解析desktop文件类
class DesktopFile: public KeyFile
{
public:
explicit DesktopFile(char separtor = ';');
virtual ~DesktopFile();
virtual bool saveToFile(const std::string &filePath) override;
};
#endif // DESKTOPFILE_H

View File

@ -20,7 +20,7 @@ std::vector<std::string> DesktopInfo::currentDesktops;
DesktopInfo::DesktopInfo(const std::string &_fileName)
: m_isValid(true)
, m_keyFile(KeyFile())
, m_desktopFile()
{
std::string fileNameWithSuffix(_fileName);
if (!DString::endWith(_fileName, ".desktop"))
@ -45,10 +45,10 @@ DesktopInfo::DesktopInfo(const std::string &_fileName)
}
}
m_keyFile.loadFile(m_fileName);
m_desktopFile.loadFile(m_fileName);
// check DesktopInfo valid
std::vector<std::string> mainKeys = m_keyFile.getMainKeys();
std::vector<std::string> mainKeys = m_desktopFile.getMainKeys();
if (mainKeys.size() == 0)
m_isValid = false;
@ -58,11 +58,11 @@ DesktopInfo::DesktopInfo(const std::string &_fileName)
if (!found)
m_isValid = false;
if (m_keyFile.getStr(MainSection, KeyType) != TypeApplication)
if (m_desktopFile.getStr(MainSection, KeyType) != TypeApplication)
m_isValid = false;
m_name = m_keyFile.getLocaleStr(MainSection, KeyName, "");
m_icon = m_keyFile.getStr(MainSection, KeyIcon);
m_name = m_desktopFile.getLocaleStr(MainSection, KeyName, "");
m_icon = m_desktopFile.getStr(MainSection, KeyIcon);
m_id = getId();
}
@ -98,12 +98,12 @@ bool DesktopInfo::shouldShow()
bool DesktopInfo::getNoDisplay()
{
return m_keyFile.getBool(MainSection, KeyNoDisplay);
return m_desktopFile.getBool(MainSection, KeyNoDisplay);
}
bool DesktopInfo::getIsHidden()
{
return m_keyFile.getBool(MainSection, KeyHidden);
return m_desktopFile.getBool(MainSection, KeyHidden);
}
bool DesktopInfo::getShowIn(std::vector<std::string> desktopEnvs)
@ -119,8 +119,8 @@ bool DesktopInfo::getShowIn(std::vector<std::string> desktopEnvs)
desktopEnvs.assign(currentDesktops.begin(), currentDesktops.end());
}
std::vector<std::string> onlyShowIn = m_keyFile.getStrList(MainSection, KeyOnlyShowIn);
std::vector<std::string> notShowIn = m_keyFile.getStrList(MainSection, KeyNotShowIn);
std::vector<std::string> onlyShowIn = m_desktopFile.getStrList(MainSection, KeyOnlyShowIn);
std::vector<std::string> notShowIn = m_desktopFile.getStrList(MainSection, KeyNotShowIn);
#ifdef QT_DEBUG
auto strVector2qstrVector = [](const std::vector<std::string> &vector) {
@ -159,7 +159,7 @@ bool DesktopInfo::getShowIn(std::vector<std::string> desktopEnvs)
std::string DesktopInfo::getExecutable()
{
return m_keyFile.getStr(MainSection, KeyExec);
return m_desktopFile.getStr(MainSection, KeyExec);
}
bool DesktopInfo::isExecutableOk()
@ -213,12 +213,12 @@ bool DesktopInfo::isDesktopAction(std::string name)
std::vector<DesktopAction> DesktopInfo::getActions()
{
std::vector<DesktopAction> actions;
for (const auto &mainKey : m_keyFile.getMainKeys()) {
for (const auto &mainKey : m_desktopFile.getMainKeys()) {
if (DString::startWith(mainKey, "Desktop Action")
|| DString::endWith(mainKey, "Shortcut Group")) {
DesktopAction action;
action.name = m_keyFile.getLocaleStr(mainKey, KeyName, "");
action.exec = m_keyFile.getStr(mainKey, KeyExec);
action.name = m_desktopFile.getLocaleStr(mainKey, KeyName, "");
action.exec = m_desktopFile.getStr(mainKey, KeyExec);
action.section = mainKey;
actions.push_back(action);
}
@ -246,13 +246,13 @@ DesktopInfo DesktopInfo::getDesktopInfoById(std::string appId)
bool DesktopInfo::getTerminal()
{
return m_keyFile.getBool(MainSection, KeyTerminal);
return m_desktopFile.getBool(MainSection, KeyTerminal);
}
// TryExec is Path to an executable file on disk used to determine if the program is actually installed
std::string DesktopInfo::getTryExec()
{
return m_keyFile.getStr(MainSection, KeyTryExec);
return m_desktopFile.getStr(MainSection, KeyTryExec);
}
// 按$PATH路径查找执行文件
@ -301,7 +301,7 @@ std::string DesktopInfo::getId()
std::string DesktopInfo::getGenericName()
{
return m_keyFile.getLocaleStr(MainSection, KeyGenericName, "");
return m_desktopFile.getLocaleStr(MainSection, KeyGenericName, "");
}
std::string DesktopInfo::getName()
@ -316,17 +316,17 @@ std::string DesktopInfo::getIcon()
std::string DesktopInfo::getCommandLine()
{
return m_keyFile.getStr(MainSection, KeyExec);
return m_desktopFile.getStr(MainSection, KeyExec);
}
std::vector<std::string> DesktopInfo::getKeywords()
{
return m_keyFile.getLocaleStrList(MainSection, KeyKeywords, "");
return m_desktopFile.getLocaleStrList(MainSection, KeyKeywords, "");
}
std::vector<std::string> DesktopInfo::getCategories()
{
return m_keyFile.getStrList(MainSection, KeyCategories);
return m_desktopFile.getStrList(MainSection, KeyCategories);
}
void DesktopInfo::setDesktopOverrideExec(const std::string &execStr)
@ -334,9 +334,9 @@ void DesktopInfo::setDesktopOverrideExec(const std::string &execStr)
m_overRideExec = execStr;
}
KeyFile *DesktopInfo::getKeyFile()
DesktopFile *DesktopInfo::getDesktopFile()
{
return &m_keyFile;
return &m_desktopFile;
}
// class AppsDir

View File

@ -5,7 +5,7 @@
#ifndef DESKTOPINFO_H
#define DESKTOPINFO_H
#include "keyfile.h"
#include "desktopfile.h"
#include <string>
#include <vector>
@ -82,7 +82,7 @@ public:
std::vector<std::string> getCategories();
void setDesktopOverrideExec(const std::string &execStr);
KeyFile *getKeyFile();
DesktopFile *getDesktopFile();
private:
std::string getTryExec();
@ -96,7 +96,7 @@ private:
std::string m_icon;
std::string m_overRideExec;
bool m_isValid;
KeyFile m_keyFile;
DesktopFile m_desktopFile;
};
// 应用目录类

View File

@ -177,12 +177,8 @@ bool KeyFile::saveToFile(const std::string &filePath)
}
for (const auto &im : m_mainKeyMap) {
const auto &keyMap = im.second;
std::string section = "[" + im.first + "]\n";
fputs(section.c_str(), sfp);
for (const auto &ik : keyMap) {
std::string kv = ik.first + "=" + ik.second + "\n";
fputs(kv.c_str(), sfp);
if (!writeSectionToFile(im.first, im.second,sfp)){
return false;
}
}
@ -190,6 +186,22 @@ bool KeyFile::saveToFile(const std::string &filePath)
return true;
}
bool KeyFile::writeSectionToFile(const std::string& sectionName, const KeyMap& keyMap, FILE * file){
if (file == nullptr) {
return false;
}
std::string section = "[" + sectionName + "]\n";
fputs(section.c_str(), file);
for (const auto &ik : keyMap) {
std::string kv = ik.first + "=" + ik.second + "\n";
fputs(kv.c_str(), file);
}
// FIXME(black_desk): should handle fputs error
return true;
}
bool KeyFile::loadFile(const std::string &filePath)
{
m_mainKeyMap.clear();

View File

@ -12,12 +12,12 @@
typedef std::map<std::string, std::string> KeyMap;
typedef std::map<std::string, KeyMap> MainKeyMap;
// 解析ini、desktop文件类
// 解析ini文件类
class KeyFile
{
public:
explicit KeyFile(char separtor = ';');
~KeyFile();
virtual ~KeyFile();
bool getBool(const std::string &section, const std::string &key, bool defaultValue = false);
void setBool(const std::string &section, const std::string &key, const std::string &defaultValue = "false");
@ -34,7 +34,7 @@ public:
std::vector<std::string> getLocaleStrList(const std::string &section, const std::string &key, std::string defaultLocale = "");
void setKey(const std::string &section, const std::string &key, const std::string &value);
bool saveToFile(const std::string &filePath);
virtual bool saveToFile(const std::string &filePath);
bool loadFile(const std::string &filePath);
std::vector<std::string> getMainKeys();
std::string getFilePath()
@ -45,12 +45,14 @@ public:
// for test
void print();
private:
protected:
MainKeyMap m_mainKeyMap; // section -> key : value
std::string m_filePath;
FILE *m_fp;
bool m_modified;
char m_listSeparator;
static bool writeSectionToFile(const std::string& sectionName, const KeyMap& keyMap, FILE * file);
};
#endif // KEYFILE_H

View File

@ -29,7 +29,7 @@ void AppInfo::init(DesktopInfo &info)
return;
}
std::string xDeepinVendor= info.getKeyFile()->getStr(MainSection, "X-Deepin-Vendor");
std::string xDeepinVendor= info.getDesktopFile()->getStr(MainSection, "X-Deepin-Vendor");
if (xDeepinVendor == "deepin") {
m_name = info.getGenericName().c_str();
if (m_name.isEmpty())

View File

@ -247,9 +247,9 @@ bool Launcher::requestSendToDesktop(QString appId)
// 创建桌面快捷方式文件
DesktopInfo dinfo(itemsMap[appId].info.path.toStdString());
dinfo.getKeyFile()->setKey(MainSection, dbusService.toStdString(), "X-Deepin-CreatedBy");
dinfo.getKeyFile()->setKey(MainSection, appId.toStdString(), "X-Deepin-AppID");
if (!dinfo.getKeyFile()->saveToFile(filePath.toStdString()))
dinfo.getDesktopFile()->setKey(MainSection, dbusService.toStdString(), "X-Deepin-CreatedBy");
dinfo.getDesktopFile()->setKey(MainSection, appId.toStdString(), "X-Deepin-AppID");
if (!dinfo.getDesktopFile()->saveToFile(filePath.toStdString()))
return false;
// 播放音频
@ -907,7 +907,7 @@ AppType Launcher::getAppType(DesktopInfo &info, const Item &item)
QFileInfo fileInfo;
// 判断是否为flatpak应用
do {
if (!info.getKeyFile()->containKey(MainSection, "X-Flatpak"))
if (!info.getDesktopFile()->containKey(MainSection, "X-Flatpak"))
break;
std::vector<std::string> parts = DString::splitStr(info.getCommandLine(), ' ');
@ -946,7 +946,7 @@ AppType Launcher::getAppType(DesktopInfo &info, const Item &item)
// 判断是否为wineApp
do {
std::string createdBy = info.getKeyFile()->getStr(MainSection, "X-Created-By");
std::string createdBy = info.getDesktopFile()->getStr(MainSection, "X-Created-By");
if (DString::startWith(createdBy, "cxoffice-") || strstr(info.getCommandLine().c_str(), "env WINEPREFIX=")) {
ty = AppType::WineApp;
goto end;
@ -1184,10 +1184,10 @@ bool Launcher::isDeepinCustomDesktopFile(QString fileName)
Item Launcher::NewItemWithDesktopInfo(DesktopInfo &info)
{
QString enName(info.getKeyFile()->getStr(MainSection, KeyName).c_str());
QString enComment(info.getKeyFile()->getStr(MainSection, KeyComment).c_str());
QString xDeepinCategory(info.getKeyFile()->getStr(MainSection, "X-Deepin-Category").c_str());
QString xDeepinVendor(info.getKeyFile()->getStr(MainSection, "X-Deepin-Vendor").c_str());
QString enName(info.getDesktopFile()->getStr(MainSection, KeyName).c_str());
QString enComment(info.getDesktopFile()->getStr(MainSection, KeyComment).c_str());
QString xDeepinCategory(info.getDesktopFile()->getStr(MainSection, "X-Deepin-Category").c_str());
QString xDeepinVendor(info.getDesktopFile()->getStr(MainSection, "X-Deepin-Vendor").c_str());
QString appName;
if (xDeepinVendor == "deepin")

View File

@ -343,7 +343,7 @@ bool StartManager::doLaunchAppWithOptions(QString desktopFile, uint32_t timestam
}
if (options.find("path") != options.end()) {
info.getKeyFile()->setKey(MainSection, KeyPath, options["path"].toString().toStdString());
info.getDesktopFile()->setKey(MainSection, KeyPath, options["path"].toString().toStdString());
}
if (options.find("desktop-override-exec") != options.end()) {
@ -433,7 +433,7 @@ bool StartManager::launch(DesktopInfo *info, QString cmdLine, uint32_t timestamp
exeArgs.insert(0, SETTING->getDefaultTerminalExec());
}
std::string workingDir = info->getKeyFile()->getStr(MainSection, KeyPath);
std::string workingDir = info->getDesktopFile()->getStr(MainSection, KeyPath);
if (workingDir.empty()) {
workingDir = BaseDir::homeDir();
}