From 3deb287238e448757e7a6131717b0f3a6aad14bf Mon Sep 17 00:00:00 2001 From: Alf Gaida Date: Sun, 22 Oct 2017 00:01:49 +0200 Subject: [PATCH] Adding upstream version 0.3.1. Signed-off-by: Alf Gaida --- .github/ISSUE_TEMPLATE.md | 34 --------- CHANGELOG | 14 +++- CMakeLists.txt | 2 +- qtxdg/xdgdesktopfile.cpp | 153 ++++++++++++++++++++++++++++++-------- qtxdg/xdgdesktopfile.h | 22 ++++++ 5 files changed, 157 insertions(+), 68 deletions(-) delete mode 100644 .github/ISSUE_TEMPLATE.md diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md deleted file mode 100644 index 81cf0bb..0000000 --- a/.github/ISSUE_TEMPLATE.md +++ /dev/null @@ -1,34 +0,0 @@ - - - -##### Expected Behavior - - - -##### Current Behavior - - - -##### Possible Solution - - - -##### Steps to Reproduce (for bugs) - - -1. -2. -3. -4. - -##### Context - - - -##### System Information - -* Distribution & Version: -* Kernel: -* Qt Version: -* cmake Version: -* Package version: diff --git a/CHANGELOG b/CHANGELOG index 06c61f5..a70cca9 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,7 +1,19 @@ -libqtxdg-3.0.0 / 2017-09-22 +libqtxdg-3.1.0 / 2017-10-21 =========================== + * Bump version to 3.1.0 + * xdgdesktopfile: Add API for getting actions info + * xdgdesktopfile: Add support for activating actions + * xdgdesktopfile: Add getting actions + * Check $XDG_CONFIG_HOME and $XDG_CONFIG_DIRS for mimeapps.list first. + * Fix reading and writing mimeapps.list file. + * Don't export github templates + +3.0.0 / 2017-09-22 +================== + + * Release 3.0.0: Update changelog * Backport support for Scale directory key according to Icon Theme spec * Bump Major to 3 * test: Drop Q_FOREACH diff --git a/CMakeLists.txt b/CMakeLists.txt index 6af52bb..9145c34 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -26,7 +26,7 @@ else() endif() set(QTXDG_MAJOR_VERSION 3) -set(QTXDG_MINOR_VERSION 0) +set(QTXDG_MINOR_VERSION 1) set(QTXDG_PATCH_VERSION 0) set(QTXDG_VERSION_STRING ${QTXDG_MAJOR_VERSION}.${QTXDG_MINOR_VERSION}.${QTXDG_PATCH_VERSION}) diff --git a/qtxdg/xdgdesktopfile.cpp b/qtxdg/xdgdesktopfile.cpp index d21e2df..cde6388 100644 --- a/qtxdg/xdgdesktopfile.cpp +++ b/qtxdg/xdgdesktopfile.cpp @@ -66,6 +66,7 @@ static const QStringList nonDetachExecs = QStringList() static const QLatin1String onlyShowInKey("OnlyShowIn"); static const QLatin1String notShowInKey("NotShowIn"); static const QLatin1String categoriesKey("Categories"); +static const QLatin1String actionsKey("Actions"); static const QLatin1String extendPrefixKey("X-"); static const QLatin1String mimeTypeKey("MimeType"); static const QLatin1String applicationsStr("applications"); @@ -274,14 +275,37 @@ QString &unEscapeExec(QString& str) return doUnEscape(str, repl); } +namespace +{ + /*! + * Helper class for getting the keys for "Additional applications actions" + * ([Desktop Action %s] sections) + */ + class XdgDesktopAction : public XdgDesktopFile + { + public: + XdgDesktopAction(const XdgDesktopFile & parent, const QString & action) + : XdgDesktopFile(parent) + , m_prefix(QString{QLatin1String("Desktop Action %1")}.arg(action)) + {} + + protected: + virtual QString prefix() const { return m_prefix; } + + private: + const QString m_prefix; + }; +} + class XdgDesktopFileData: public QSharedData { public: XdgDesktopFileData(); bool read(const QString &prefix); XdgDesktopFile::Type detectType(XdgDesktopFile *q) const; - bool startApplicationDetached(const XdgDesktopFile *q, const QStringList& urls) const; + bool startApplicationDetached(const XdgDesktopFile *q, const QString & action, const QStringList& urls) const; bool startLinkDetached(const XdgDesktopFile *q) const; - bool startByDBus(const QStringList& urls) const; + bool startByDBus(const QString & action, const QStringList& urls) const; + QStringList getListValue(const XdgDesktopFile * q, const QString & key, bool tryExtendPrefix) const; QString mFileName; bool mIsValid; @@ -365,7 +389,7 @@ XdgDesktopFile::Type XdgDesktopFileData::detectType(XdgDesktopFile *q) const return XdgDesktopFile::UnknownType; } -bool XdgDesktopFileData::startApplicationDetached(const XdgDesktopFile *q, const QStringList& urls) const +bool XdgDesktopFileData::startApplicationDetached(const XdgDesktopFile *q, const QString & action, const QStringList& urls) const { //DBusActivatable handling if (q->value(QLatin1String("DBusActivatable"), false).toBool()) { @@ -390,10 +414,12 @@ bool XdgDesktopFileData::startApplicationDetached(const XdgDesktopFile *q, const * We consider that this violation is more acceptable than an failure * in launching an application. */ - if (startByDBus(urls)) + if (startByDBus(action, urls)) return true; } - QStringList args = q->expandExecString(urls); + QStringList args = action.isEmpty() + ? q->expandExecString(urls) + : XdgDesktopAction{*q, action}.expandExecString(urls); if (args.isEmpty()) return false; @@ -481,8 +507,7 @@ bool XdgDesktopFileData::startLinkDetached(const XdgDesktopFile *q) const return false; } -// TODO: Handle ActivateAction -bool XdgDesktopFileData::startByDBus(const QStringList& urls) const +bool XdgDesktopFileData::startByDBus(const QString & action, const QStringList& urls) const { QFileInfo f(mFileName); QString path(f.completeBaseName()); @@ -509,7 +534,13 @@ bool XdgDesktopFileData::startByDBus(const QStringList& urls) const << ", but trying to continue..."; } QDBusMessage reply; - if (urls.isEmpty()) + if (!action.isEmpty()) + { + QList v_urls; + for (const auto & url : urls) + v_urls.append(url); + reply = app.call(QLatin1String("ActivateAction"), action, v_urls, platformData); + } else if (urls.isEmpty()) reply = app.call(QLatin1String("Activate"), platformData); else reply = app.call(QLatin1String("Open"), urls, platformData); @@ -517,6 +548,19 @@ bool XdgDesktopFileData::startByDBus(const QStringList& urls) const return QDBusMessage::ErrorMessage != reply.type(); } +QStringList XdgDesktopFileData::getListValue(const XdgDesktopFile * q, const QString & key, bool tryExtendPrefix) const +{ + QString used_key = key; + if (!q->contains(used_key) && tryExtendPrefix) + { + used_key = extendPrefixKey + key; + if (!q->contains(used_key)) + return QStringList(); + } + + return q->value(key).toString().split(QLatin1Char(';'), QString::SkipEmptyParts); +} + XdgDesktopFile::XdgDesktopFile(): d(new XdgDesktopFileData) @@ -750,22 +794,13 @@ QVariant XdgDesktopFile::localizedValue(const QString& key, const QVariant& defa QStringList XdgDesktopFile::categories() const { - QString key; - if (contains(categoriesKey)) - { - key = categoriesKey; - } - else - { - key = extendPrefixKey + categoriesKey; - if (!contains(key)) - return QStringList(); - } - - QStringList cats = value(key).toString().split(QLatin1Char(';')); - return cats; + return d->getListValue(this, categoriesKey, true); } +QStringList XdgDesktopFile::actions() const +{ + return d->getListValue(this, actionsKey, false); +} void XdgDesktopFile::removeEntry(const QString& key) { @@ -806,18 +841,41 @@ QIcon const XdgDesktopFile::icon(const QIcon& fallback) const } +QIcon const XdgDesktopFile::actionIcon(const QString & action, const QIcon& fallback) const +{ + return d->mType == ApplicationType + ? XdgDesktopAction{*this, action}.icon(icon(fallback)) + : fallback; +} + + QString const XdgDesktopFile::iconName() const { return value(iconKey).toString(); } +QString const XdgDesktopFile::actionIconName(const QString & action) const +{ + return d->mType == ApplicationType + ? XdgDesktopAction{*this, action}.iconName() + : QString{}; +} + + QStringList XdgDesktopFile::mimeTypes() const { return value(mimeTypeKey).toString().split(QLatin1Char(';'), QString::SkipEmptyParts); } +QString XdgDesktopFile::actionName(const QString & action) const +{ + return d->mType == ApplicationType + ? XdgDesktopAction{*this, action}.name() + : QString{}; +} + XdgDesktopFile::Type XdgDesktopFile::type() const { return d->mType; @@ -839,7 +897,7 @@ bool XdgDesktopFile::startDetached(const QStringList& urls) const switch(d->mType) { case ApplicationType: - return d->startApplicationDetached(this, urls); + return d->startApplicationDetached(this, QString{}, urls); break; case LinkType: @@ -851,6 +909,11 @@ bool XdgDesktopFile::startDetached(const QStringList& urls) const } } +bool XdgDesktopFile::actionActivate(const QString & action, const QStringList& urls) const +{ + return d->mType == ApplicationType ? d->startApplicationDetached(this, action, urls) : false; +} + /************************************************ This is an overloaded function. @@ -1396,7 +1459,7 @@ bool readDesktopFile(QIODevice & device, QSettings::SettingsMap & map) if (value.contains(QLatin1Char(';'))) { - map.insert(key, value.split(QLatin1Char(';'))); + map.insert(key, value.split(QLatin1Char(';'), QString::SkipEmptyParts)); } else { @@ -1418,7 +1481,10 @@ bool writeDesktopFile(QIODevice & device, const QSettings::SettingsMap & map) for (auto it = map.constBegin(); it != map.constEnd(); ++it) { - if (! it.value().canConvert()) + bool isString = it.value().canConvert(); + bool isStringList = (it.value().type() == QVariant::StringList); + + if ((! isString) && (! isStringList)) { return false; } @@ -1444,7 +1510,21 @@ bool writeDesktopFile(QIODevice & device, const QSettings::SettingsMap & map) return false; } - stream << remainingKey << QLatin1Char('=') << it.value().toString() << QLatin1Char('\n'); + stream << remainingKey << QLatin1Char('='); + + if (isString) + { + stream << it.value().toString() << QLatin1Char(';'); + } + else /* if (isStringList) */ + { + for (const QString &value: it.value().toStringList()) + { + stream << value << QLatin1Char(';'); + } + } + + stream << QLatin1Char('\n'); } @@ -1612,13 +1692,22 @@ QList XdgDesktopFileCache::getApps(const QString& mimetype) XdgDesktopFile* XdgDesktopFileCache::getDefaultApp(const QString& mimetype) { - // First, we look in ~/.local/share/applications/mimeapps.list, /usr/local/share/applications/mimeapps.list and - // /usr/share/applications/mimeapps.list (in that order) for a default. - QStringList dataDirs = XdgDirs::dataDirs(); - dataDirs.prepend(XdgDirs::dataHome(false)); - for (const QString &dataDir : const_cast(dataDirs)) + // First, we look in following places for a default in specified order: + // ~/.config/mimeapps.list + // /etc/xdg/mimeapps.list + // ~/.local/share/applications/mimeapps.list + // /usr/local/share/applications/mimeapps.list + // /usr/share/applications/mimeapps.list + QStringList mimeDirsList; + + mimeDirsList.append(XdgDirs::configHome(false)); + mimeDirsList.append(XdgDirs::configDirs()); + mimeDirsList.append(XdgDirs::dataHome(false) + QLatin1String("/applications")); + mimeDirsList.append(XdgDirs::dataDirs(QLatin1String("/applications"))); + + for (const QString &mimeDir : const_cast(mimeDirsList)) { - QString defaultsListPath = dataDir + QLatin1String("/applications/mimeapps.list"); + QString defaultsListPath = mimeDir + QLatin1String("/mimeapps.list"); if (QFileInfo::exists(defaultsListPath)) { QSettings defaults(defaultsListPath, desktopFileSettingsFormat()); diff --git a/qtxdg/xdgdesktopfile.h b/qtxdg/xdgdesktopfile.h index 63c0994..a1d7533 100644 --- a/qtxdg/xdgdesktopfile.h +++ b/qtxdg/xdgdesktopfile.h @@ -130,6 +130,9 @@ public: //! Returns the entry Categories. It supports X-Categories extensions. QStringList categories() const; + //! Returns list of values in entry Actions. + QStringList actions() const; + //! Returns true if there exists a setting called key; returns false otherwise. bool contains(const QString& key) const; @@ -142,9 +145,13 @@ public: //! Returns an icon specified in this file. QIcon const icon(const QIcon& fallback = QIcon()) const; + //! Returns an icon for application action \param action. + QIcon const actionIcon(const QString & action, const QIcon& fallback = QIcon()) const; //! Returns an icon name specified in this file. QString const iconName() const; + //! Returns an icon name for application action \param action. + QString const actionIconName(const QString & action) const; //! Returns an list of mimetypes specified in this file. /*! @return Returns a list of the "MimeType=" entries. @@ -155,6 +162,8 @@ public: //! This function is provided for convenience. It's equivalent to calling localizedValue("Name").toString(). QString name() const { return localizedValue(QLatin1String("Name")).toString(); } + //! Returns an (localized) name for application action \param action. + QString actionName(const QString & action) const; //! This function is provided for convenience. It's equivalent to calling localizedValue("Comment").toString(). QString comment() const { return localizedValue(QLatin1String("Comment")).toString(); } @@ -175,6 +184,19 @@ public: //! This function is provided for convenience. It's equivalent to calling startDetached(QStringList(url)). bool startDetached(const QString& url = QString()) const; + /*! For file with Application type. Activates action defined by the \param action. Action is activated + * either with the [Desktop Action %s]/Exec or by the D-Bus if the [Desktop Entry]/DBusActivatable is set. + * \note Starting is done the same way as \sa startDetached() + * + * \return true on success; otherwise returns false. + * \param urls - A list of files or URLS. Each file is passed as a separate argument to the executable program. + * + * For file with Link type, do nothing. + * + * For file with Directory type, do nothing. + */ + bool actionActivate(const QString & action, const QStringList & urls) const; + /*! A Exec value consists of an executable program optionally followed by one or more arguments. This function expands this arguments and returns command line string parts. Note this method make sense only for Application type.