fix(applications): apply XDG display rules to desktop entries

main
reionwong 3 weeks ago
parent ce28822adc
commit dbb514c648

@ -79,27 +79,6 @@ QString normalized(const QString &value)
return value.trimmed().toLower();
}
bool desktopMatches(const DesktopEntryData &entry, const QByteArray &desktop)
{
const QStringList currentDesktops = QString::fromLocal8Bit(desktop)
.split(QLatin1Char(':'), Qt::SkipEmptyParts);
auto containsDesktop = [&currentDesktops](const QStringList &desktops) {
for (const QString &desktop : desktops) {
for (const QString &current : currentDesktops) {
if (desktop.compare(current, Qt::CaseInsensitive) == 0)
return true;
}
}
return false;
};
if (!entry.onlyShowIn.isEmpty() && !containsDesktop(entry.onlyShowIn))
return false;
if (containsDesktop(entry.notShowIn))
return false;
return true;
}
} // namespace
DesktopEntryModel::DesktopEntryModel(QObject *parent)
@ -365,7 +344,7 @@ void ApplicationRegistry::applyScan(const QList<DesktopEntryData> &results)
QHash<QString, DesktopEntry *> newEntriesByPath;
QList<DesktopEntry *> allEntries;
QList<DesktopEntry *> visibleEntries;
const QByteArray currentDesktop = qgetenv("XDG_CURRENT_DESKTOP");
const QStringList currentDesktops = DesktopEntry::currentDesktops();
for (const DesktopEntryData &data : results) {
DesktopEntry *entry = oldEntries.take(data.id);
@ -377,7 +356,7 @@ void ApplicationRegistry::applyScan(const QList<DesktopEntryData> &results)
newEntriesByPath.insert(QFileInfo(data.path).absoluteFilePath(), entry);
allEntries.append(entry);
if (!data.hidden && !data.noDisplay && desktopMatches(data, currentDesktop))
if (entry->shouldShow(currentDesktops))
visibleEntries.append(entry);
}

@ -7,6 +7,7 @@
#include <QLocale>
#include <QMap>
#include <QRegularExpression>
#include <QStandardPaths>
namespace {
@ -34,6 +35,27 @@ bool parseBool(const QString &value)
return value.compare(QStringLiteral("true"), Qt::CaseInsensitive) == 0;
}
// TryExec may be a bare name to look up in PATH or an absolute path.
bool executableExists(const QString &program)
{
if (program.contains(QLatin1Char('/'))) {
const QFileInfo info(program);
return info.exists() && info.isExecutable();
}
return !QStandardPaths::findExecutable(program).isEmpty();
}
bool listContainsDesktop(const QStringList &list, const QStringList &desktops)
{
for (const QString &wanted : list) {
for (const QString &current : desktops) {
if (wanted.compare(current, Qt::CaseInsensitive) == 0)
return true;
}
}
return false;
}
QString localizedValue(const QMap<QString, QString> &values,
const QMap<QString, QString> &localized,
const QString &key)
@ -148,15 +170,48 @@ QString DesktopEntry::genericName() const { return m_genericName; }
QString DesktopEntry::comment() const { return m_comment; }
QString DesktopEntry::icon() const { return m_icon; }
QString DesktopEntry::exec() const { return m_exec; }
QString DesktopEntry::tryExec() const { return m_tryExec; }
QStringList DesktopEntry::command() const { return m_command; }
QString DesktopEntry::workingDirectory() const { return m_workingDirectory; }
QString DesktopEntry::startupWMClass() const { return m_startupWMClass; }
QStringList DesktopEntry::categories() const { return m_categories; }
QStringList DesktopEntry::keywords() const { return m_keywords; }
QStringList DesktopEntry::mimeTypes() const { return m_mimeTypes; }
QStringList DesktopEntry::onlyShowIn() const { return m_onlyShowIn; }
QStringList DesktopEntry::notShowIn() const { return m_notShowIn; }
bool DesktopEntry::terminal() const { return m_terminal; }
bool DesktopEntry::noDisplay() const { return m_noDisplay; }
bool DesktopEntry::hidden() const { return m_hidden; }
bool DesktopEntry::dbusActivatable() const { return m_dbusActivatable; }
QStringList DesktopEntry::currentDesktops()
{
QString value = qEnvironmentVariable("XDG_CURRENT_DESKTOP");
if (value.isEmpty())
value = QStringLiteral("Cutefish");
return value.split(QLatin1Char(':'), Qt::SkipEmptyParts);
}
bool DesktopEntry::shouldShow() const
{
return shouldShow(currentDesktops());
}
bool DesktopEntry::shouldShow(const QStringList &desktops) const
{
// Hidden means the entry is deleted; it only exists to mask a system one.
if (m_hidden || m_noDisplay)
return false;
if (m_exec.isEmpty() && !m_dbusActivatable)
return false;
if (!m_tryExec.isEmpty() && !executableExists(m_tryExec))
return false;
if (!m_onlyShowIn.isEmpty() && !listContainsDesktop(m_onlyShowIn, desktops))
return false;
if (listContainsDesktop(m_notShowIn, desktops))
return false;
return true;
}
QStringList DesktopEntry::commandForArguments(const QStringList &arguments) const
{
@ -227,6 +282,7 @@ bool DesktopEntry::parse(const QString &id, const QString &path,
data.comment = localizedValue(values, localized, QStringLiteral("Comment"));
data.icon = values.value(QStringLiteral("Icon"));
data.exec = values.value(QStringLiteral("Exec"));
data.tryExec = values.value(QStringLiteral("TryExec"));
data.workingDirectory = values.value(QStringLiteral("Path"));
data.startupWMClass = values.value(QStringLiteral("StartupWMClass"));
data.categories = splitList(values.value(QStringLiteral("Categories")));
@ -237,6 +293,7 @@ bool DesktopEntry::parse(const QString &id, const QString &path,
data.terminal = parseBool(values.value(QStringLiteral("Terminal")));
data.noDisplay = parseBool(values.value(QStringLiteral("NoDisplay")));
data.hidden = parseBool(values.value(QStringLiteral("Hidden")));
data.dbusActivatable = parseBool(values.value(QStringLiteral("DBusActivatable")));
} else if (group.startsWith(QStringLiteral("Desktop Action "))) {
const QString actionId = group.mid(QStringLiteral("Desktop Action ").size());
const auto action = actionValues.value(actionId);
@ -287,7 +344,11 @@ bool DesktopEntry::parse(const QString &id, const QString &path,
}
finishGroup();
if (data.type != QStringLiteral("Application") || data.name.isEmpty())
// A Hidden entry is kept without a Name so it still masks the system entry
// of the same id further down XDG_DATA_DIRS.
if (data.type != QStringLiteral("Application"))
return false;
if (data.name.isEmpty() && !data.hidden)
return false;
*result = data;
@ -316,6 +377,10 @@ void DesktopEntry::update(const DesktopEntryData &data)
m_icon = data.icon;
emit iconChanged();
}
if (m_tryExec != data.tryExec) {
m_tryExec = data.tryExec;
emit tryExecChanged();
}
const bool hasExecChanged = m_exec != data.exec;
if (hasExecChanged) {
m_exec = data.exec;
@ -341,6 +406,14 @@ void DesktopEntry::update(const DesktopEntryData &data)
m_mimeTypes = data.mimeTypes;
emit mimeTypesChanged();
}
if (m_onlyShowIn != data.onlyShowIn) {
m_onlyShowIn = data.onlyShowIn;
emit onlyShowInChanged();
}
if (m_notShowIn != data.notShowIn) {
m_notShowIn = data.notShowIn;
emit notShowInChanged();
}
if (m_terminal != data.terminal) {
m_terminal = data.terminal;
emit terminalChanged();
@ -353,6 +426,10 @@ void DesktopEntry::update(const DesktopEntryData &data)
m_hidden = data.hidden;
emit hiddenChanged();
}
if (m_dbusActivatable != data.dbusActivatable) {
m_dbusActivatable = data.dbusActivatable;
emit dbusActivatableChanged();
}
const QStringList command = commandForArguments(QStringList());
if (m_command != command) {

@ -22,6 +22,7 @@ struct DesktopEntryData
QString comment;
QString icon;
QString exec;
QString tryExec;
QString workingDirectory;
QString startupWMClass;
QStringList categories;
@ -33,6 +34,7 @@ struct DesktopEntryData
bool terminal = false;
bool noDisplay = false;
bool hidden = false;
bool dbusActivatable = false;
};
Q_DECLARE_METATYPE(DesktopEntryData)
@ -48,15 +50,20 @@ class DesktopEntry : public QObject
Q_PROPERTY(QString comment READ comment NOTIFY commentChanged)
Q_PROPERTY(QString icon READ icon NOTIFY iconChanged)
Q_PROPERTY(QString exec READ exec NOTIFY execChanged)
Q_PROPERTY(QString tryExec READ tryExec NOTIFY tryExecChanged)
Q_PROPERTY(QStringList command READ command NOTIFY commandChanged)
Q_PROPERTY(QString workingDirectory READ workingDirectory NOTIFY workingDirectoryChanged)
Q_PROPERTY(QString startupWMClass READ startupWMClass NOTIFY startupWMClassChanged)
Q_PROPERTY(QStringList categories READ categories NOTIFY categoriesChanged)
Q_PROPERTY(QStringList keywords READ keywords NOTIFY keywordsChanged)
Q_PROPERTY(QStringList mimeTypes READ mimeTypes NOTIFY mimeTypesChanged)
Q_PROPERTY(QStringList onlyShowIn READ onlyShowIn NOTIFY onlyShowInChanged)
Q_PROPERTY(QStringList notShowIn READ notShowIn NOTIFY notShowInChanged)
Q_PROPERTY(bool terminal READ terminal NOTIFY terminalChanged)
Q_PROPERTY(bool noDisplay READ noDisplay NOTIFY noDisplayChanged)
Q_PROPERTY(bool hidden READ hidden NOTIFY hiddenChanged)
Q_PROPERTY(bool dbusActivatable READ dbusActivatable NOTIFY dbusActivatableChanged)
Q_PROPERTY(bool shouldShow READ shouldShow NOTIFY changed)
public:
explicit DesktopEntry(const QString &id, QObject *parent = nullptr);
@ -68,15 +75,26 @@ public:
QString comment() const;
QString icon() const;
QString exec() const;
QString tryExec() const;
QStringList command() const;
QString workingDirectory() const;
QString startupWMClass() const;
QStringList categories() const;
QStringList keywords() const;
QStringList mimeTypes() const;
QStringList onlyShowIn() const;
QStringList notShowIn() const;
bool terminal() const;
bool noDisplay() const;
bool hidden() const;
bool dbusActivatable() const;
// XDG display rules: Hidden, NoDisplay, TryExec, OnlyShowIn/NotShowIn.
bool shouldShow() const;
bool shouldShow(const QStringList &desktops) const;
// XDG_CURRENT_DESKTOP split on ':', defaulting to this session.
static QStringList currentDesktops();
Q_INVOKABLE bool launch(const QStringList &arguments = QStringList()) const;
@ -95,15 +113,19 @@ signals:
void commentChanged();
void iconChanged();
void execChanged();
void tryExecChanged();
void commandChanged();
void workingDirectoryChanged();
void startupWMClassChanged();
void categoriesChanged();
void keywordsChanged();
void mimeTypesChanged();
void onlyShowInChanged();
void notShowInChanged();
void terminalChanged();
void noDisplayChanged();
void hiddenChanged();
void dbusActivatableChanged();
void changed();
private:
@ -114,13 +136,17 @@ private:
QString m_comment;
QString m_icon;
QString m_exec;
QString m_tryExec;
QStringList m_command;
QString m_workingDirectory;
QString m_startupWMClass;
QStringList m_categories;
QStringList m_keywords;
QStringList m_mimeTypes;
QStringList m_onlyShowIn;
QStringList m_notShowIn;
bool m_terminal = false;
bool m_noDisplay = false;
bool m_hidden = false;
bool m_dbusActivatable = false;
};

Loading…
Cancel
Save