diff --git a/src/libhawaiishell/HF1HawaiiShellMacros.cmake b/src/libhawaiishell/HF1HawaiiShellMacros.cmake index 41045480..54b58dd5 100644 --- a/src/libhawaiishell/HF1HawaiiShellMacros.cmake +++ b/src/libhawaiishell/HF1HawaiiShellMacros.cmake @@ -37,10 +37,10 @@ # # \param type the package type, one of the following: element, shell, # lookandfeel, containment -# \param name the package name, same as the X-Hawaii-PluginInfo-Name -# key value from metadata.desktop +# \param name the package name, same as the "name" key value +# from metadata.json # \param srcpath the source path to install from, it's the -# directory that contains metadata.desktop +# directory that contains metadata.json # macro(hawaiishell_install_package type name srcpath) if("${type}" STREQUAL "element") diff --git a/src/libhawaiishell/containment.cpp b/src/libhawaiishell/containment.cpp index f847edad..e83aa70a 100644 --- a/src/libhawaiishell/containment.cpp +++ b/src/libhawaiishell/containment.cpp @@ -171,10 +171,10 @@ void Containment::setPackage(const Package &package) // Read the containment type QString containmentType = package.metadata().property( - QStringLiteral("X-Hawaii-ContainmentType")).toString(); - if (containmentType == QStringLiteral("Desktop")) + QStringLiteral("containment-type")).toString(); + if (containmentType == QStringLiteral("desktop")) d->type = Types::DesktopContainment; - else if (containmentType == QStringLiteral("Panel")) + else if (containmentType == QStringLiteral("panel")) d->type = Types::PanelContainment; else qFatal("Containment \"%s\" has an invalid type \"%s\", aborting...", diff --git a/src/libhawaiishell/package.cpp b/src/libhawaiishell/package.cpp index ac70e456..3d231347 100644 --- a/src/libhawaiishell/package.cpp +++ b/src/libhawaiishell/package.cpp @@ -141,7 +141,7 @@ void PackagePrivate::createPackageMetadata(const QString &path) { delete metadata; - QString metadataPath(path + "/metadata.desktop"); + QString metadataPath(path + "/metadata.json"); if (!QFile::exists(metadataPath)) { qWarning() << "No metadata file in the package, expected it at:" << metadataPath; metadataPath.clear(); @@ -602,7 +602,7 @@ QString Package::contentsHash() const } QCryptographicHash hash(QCryptographicHash::Sha1); - QString metadataPath = d->path + "metadata.desktop"; + QString metadataPath = d->path + "metadata.json"; if (QFile::exists(metadataPath)) { QFile f(metadataPath); if (f.open(QIODevice::ReadOnly)) { diff --git a/src/libhawaiishell/packages_p.cpp b/src/libhawaiishell/packages_p.cpp index 60e0c416..5466a167 100644 --- a/src/libhawaiishell/packages_p.cpp +++ b/src/libhawaiishell/packages_p.cpp @@ -35,7 +35,7 @@ void BasePackage::initializePackage(Package *package) { // Metadata package->addFileDefinition("metadata", - QStringLiteral("metadata.desktop"), + QStringLiteral("metadata.json"), tr("Metadata file")); package->setRequired("metadata", true); } @@ -53,8 +53,8 @@ void MainScriptPackage::pathChanged(Package *package) if (package->path().isEmpty()) return; - PluginMetadata metadata(package->path() + "/metadata.desktop"); - const QString mainScript = metadata.property(QStringLiteral("X-Hawaii-MainScript")).toString(); + PluginMetadata metadata(package->path() + "/metadata.json"); + const QString mainScript = metadata.mainScript(); if (!mainScript.isEmpty()) package->addFileDefinition("mainscript", mainScript, tr("Main script file")); } diff --git a/src/libhawaiishell/packagestructure.cpp b/src/libhawaiishell/packagestructure.cpp index e829720c..a58184ef 100644 --- a/src/libhawaiishell/packagestructure.cpp +++ b/src/libhawaiishell/packagestructure.cpp @@ -46,8 +46,8 @@ PackageStructure::~PackageStructure() Called when a the PackageStructure should initialize a Package with the initial structure. This allows setting paths before setPath is called. - Note: one special value is "metadata" which can be set to the location of KPluginInfo - compatible .desktop file within the package. If not defined, it is assumed that this file + Note: one special value is "metadata" which can be set to the location of PluginMetadata + compatible .json file within the package. If not defined, it is assumed that this file exists under the top level directory of the package. \param package the Package to set up. The object is empty of all definition when diff --git a/src/libhawaiishell/pluginmetadata.cpp b/src/libhawaiishell/pluginmetadata.cpp index e237a55e..1a90a695 100644 --- a/src/libhawaiishell/pluginmetadata.cpp +++ b/src/libhawaiishell/pluginmetadata.cpp @@ -24,8 +24,11 @@ * $END_LICENSE$ ***************************************************************************/ +#include +#include +#include + #include "pluginmetadata.h" -#include "xdgdesktopfile.h" namespace Hawaii { @@ -39,23 +42,87 @@ class PluginMetadataPrivate { public: PluginMetadataPrivate(); - ~PluginMetadataPrivate(); - XdgDesktopFile *entry; + /************************************************ + * LC_MESSAGES value Possible keys in order of matching + * lang_COUNTRY@MODIFIER lang_COUNTRY@MODIFIER, lang_COUNTRY, lang@MODIFIER, lang, + * default value + * lang_COUNTRY lang_COUNTRY, lang, default value + * lang@MODIFIER lang@MODIFIER, lang, default value + * lang lang, default value + * + * This function is derivate from libqtxdg code. + * Copyright (C) 2010-2011 Razor team + * Author: Alexander Sokoloff + ************************************************/ + QStringList currentLanguage() const + { + QStringList list; + QString lang = QString::fromUtf8(getenv("LC_MESSAGES")); + + if (lang.isEmpty()) + lang = QString::fromUtf8(getenv("LC_ALL")); + if (lang.isEmpty()) + lang = QString::fromUtf8(getenv("LANG")); + + QString modifier = lang.section('@', 1); + if (!modifier.isEmpty()) + lang.truncate(lang.length() - modifier.length() - 1); + + QString encoding = lang.section('.', 1); + if (!encoding.isEmpty()) + lang.truncate(lang.length() - encoding.length() - 1); + + + QString country = lang.section('_', 1); + if (!country.isEmpty()) + lang.truncate(lang.length() - country.length() - 1); + + if (!modifier.isEmpty() && !country.isEmpty()) + list.append(QString("%1_%2@%3").arg(lang, country, modifier)); + + if (!country.isEmpty()) + list.append(QString("%1_%2").arg(lang, country)); + + if (!modifier.isEmpty()) + list.append(QString("%1@%2").arg(lang, modifier)); + + list.append(lang); + + return list; + } + + QVariant localizedValue(const QString &key) const + { + if (!valid) + return QVariant(); + + QVariantMap translations = json.value(QStringLiteral("translations")).toObject().toVariantMap(); + QStringList langs = currentLanguage(); + for (QString lang: translations.keys()) { + if (langs.contains(lang)) { + QMap map = translations[lang].toMap(); + if (map.contains(key)) + return translations[lang].toMap()[key]; + return json.value(key); + } + } + + return json.value(key); + } + + bool valid; + QFile file; + QJsonObject json; PluginMetadata::Type type; }; PluginMetadataPrivate::PluginMetadataPrivate() - : entry(nullptr) + : valid(false) , type(PluginMetadata::InvalidType) { } -PluginMetadataPrivate::~PluginMetadataPrivate() -{ - delete entry; -} - /* * PluginMetadata */ @@ -73,49 +140,62 @@ PluginMetadata::PluginMetadata() PluginMetadata::~PluginMetadata() { - //delete d_ptr; + delete d_ptr; } bool PluginMetadata::load(const QString &fileName) { Q_D(PluginMetadata); - if (!d->entry) - d->entry = new XdgDesktopFile(); - if (d->entry->load(fileName)) { - if (d->entry->value(QStringLiteral("Type")).toString() != QStringLiteral("Service")) { - // Unload metadata if it's not the right type - delete d->entry; - d->entry = nullptr; - } else { - // Determine the plugin type - QString type = d->entry->value(QStringLiteral("X-Hawaii-ServiceType")).toString(); - if (type.startsWith(QStringLiteral("Hawaii/Shell"))) { - if (type.endsWith(QStringLiteral("/Element"))) - d->type = PluginMetadata::ElementType; - else if (type.endsWith(QStringLiteral("/Containment"))) - d->type = PluginMetadata::ContainmentType; - else if (type.endsWith(QStringLiteral("/Shell"))) - d->type = PluginMetadata::ShellType; - else if (type.endsWith(QStringLiteral("/LookAndFeel"))) - d->type = PluginMetadata::LookAndFeelType; - else - d->type = PluginMetadata::InvalidType; - } else { - d->type = PluginMetadata::InvalidType; - } - } + // Open the file + d->file.setFileName(fileName); + if (!d->file.open(QIODevice::ReadOnly | QIODevice::Text)) { + qWarning("Couldn't open \"%s\": %s", + qPrintable(fileName), + qPrintable(d->file.errorString())); + d->valid = false; + return false; + } - return true; + QByteArray data = d->file.readAll(); + d->file.close(); + + // Load the JSON document + QJsonParseError error; + QJsonDocument doc = QJsonDocument::fromJson(data, &error); + if (error.error != QJsonParseError::NoError) { + qWarning("Error parsing \"%s\" at offset %d: %s", + qPrintable(fileName), + error.offset, + qPrintable(error.errorString())); + d->valid = false; + return false; } - return false; + // Retrieve the JSON object + d->json = doc.object(); + + // Determine the plugin type + QString type = d->json.value(QStringLiteral("type")).toString(); + if (type == QStringLiteral("element")) + d->type = PluginMetadata::ElementType; + else if (type == QStringLiteral("containment")) + d->type = PluginMetadata::ContainmentType; + else if (type == QStringLiteral("shell")) + d->type = PluginMetadata::ShellType; + else if (type == QStringLiteral("lookandfeel")) + d->type = PluginMetadata::LookAndFeelType; + else + d->type = PluginMetadata::InvalidType; + + d->valid = d->type != PluginMetadata::InvalidType; + return true; } bool PluginMetadata::isValid() const { Q_D(const PluginMetadata); - return d->entry && d->entry->isValid(); + return d->valid; } PluginMetadata::Type PluginMetadata::type() const @@ -127,81 +207,89 @@ PluginMetadata::Type PluginMetadata::type() const QString PluginMetadata::name() const { Q_D(const PluginMetadata); - if (!d->entry) + if (!d->valid) return QString(); - return d->entry->name(); + return d->localizedValue(QStringLiteral("title")).toString(); } QString PluginMetadata::comment() const { Q_D(const PluginMetadata); - if (!d->entry) + if (!d->valid) return QString(); - return d->entry->comment(); + return d->localizedValue(QStringLiteral("description")).toString(); } QString PluginMetadata::iconName() const { Q_D(const PluginMetadata); - if (!d->entry) + if (!d->valid) return QString(); - return d->entry->iconName(); + return d->json.value(QStringLiteral("icon-name")).toString(); } QString PluginMetadata::internalName() const { Q_D(const PluginMetadata); - if (!d->entry) + if (!d->valid) return QString(); - return d->entry->value(QStringLiteral("X-Hawaii-PluginInfo-Name")).toString(); + return d->json.value(QStringLiteral("name")).toString(); } QString PluginMetadata::version() const { Q_D(const PluginMetadata); - if (!d->entry) + if (!d->valid) return QString(); - return d->entry->value(QStringLiteral("X-Hawaii-PluginInfo-Version")).toString(); + return d->json.value(QStringLiteral("version")).toString(); } QString PluginMetadata::license() const { Q_D(const PluginMetadata); - if (!d->entry) + if (!d->valid) return QString(); - return d->entry->value(QStringLiteral("X-Hawaii-PluginInfo-License")).toString(); + return d->json.value(QStringLiteral("license")).toString(); } QString PluginMetadata::authorName() const { Q_D(const PluginMetadata); - if (!d->entry) + if (!d->valid) return QString(); - return d->entry->value(QStringLiteral("X-Hawaii-PluginInfo-AuthorName")).toString(); + return d->json.value(QStringLiteral("author-name")).toString(); } QString PluginMetadata::authorEmail() const { Q_D(const PluginMetadata); - if (!d->entry) + if (!d->valid) return QString(); - return d->entry->value(QStringLiteral("X-Hawaii-PluginInfo-AuthorEmail")).toString(); + return d->json.value(QStringLiteral("author-email")).toString(); } QString PluginMetadata::webSite() const { Q_D(const PluginMetadata); - if (!d->entry) + if (!d->valid) + return QString(); + return d->json.value(QStringLiteral("website")).toString(); +} + +QString PluginMetadata::mainScript() const +{ + Q_D(const PluginMetadata); + if (!d->valid) return QString(); - return d->entry->value(QStringLiteral("X-Hawaii-PluginInfo-WebSite")).toString(); + return d->json.value(QStringLiteral("mainscript")).toString(); } QVariant PluginMetadata::property(const QString &key) const { Q_D(const PluginMetadata); - if (!d->entry) + if (!d->valid) return QVariant(); - return d->entry->localizedValue(key); + return d->localizedValue(key); } } // namespace Shell diff --git a/src/libhawaiishell/pluginmetadata.h b/src/libhawaiishell/pluginmetadata.h index d040ab54..d6d29b0d 100644 --- a/src/libhawaiishell/pluginmetadata.h +++ b/src/libhawaiishell/pluginmetadata.h @@ -57,39 +57,48 @@ class HAWAIISHELL_EXPORT PluginMetadata /*! * Read plugin metadata from \a fileName. * - * Plugin metadata are basically desktop entries and should be in - * the following form: + * Plugin metadata are JSON documents and should be in the following form: * * \code - * [DesktopEntry] - * Name=User Visible Name - * Comment=Description of what the plugin does. - * Type=Service - * - * X-Hawaii-ServiceType=... - * X-Hawaii-PluginInfo-Name=internalname - * X-Hawaii-PluginInfo-Version=Version number - * X-Hawaii-PluginInfo-License=License name - * X-Hawaii-PluginInfo-AuthorName=Author's Name - * X-Hawaii-PluginInfo-AuthorEmail=Author's Email - * X-Hawaii-PluginInfo-WebSite=http://plugin.org + * { + * "name": "internal name", + * "type": "...", + * "version": "version number", + * "license": "license name", + * "title": "user visible name"; + * "description": "description of what the plugin does", + * "icon-name": "icon name from Freedesktop.org Icon Naming Specification", + * "author-name": "author's name", + * "author-email": "author's email address", + * "website": "http://plugin.org", + * "mainscript": "path/to/mainfile.ext" + * } * \endcode * - * The X-Hawaii-PluginInfo-ServiceType key can only contain - * one of the following values: + * The "type" key can only contain one of the following values: * \list - * \listitem Hawaii/Shell/Element - * \listitem Hawaii/Shell/Containment - * \listitem Hawaii/Shell/Shell - * \listitem Hawaii/Shell/LookAndFeel + * \listitem element + * \listitem containment + * \listitem shell + * \listitem lookandfeel * \endlist * - * Use a descriptive name for X-Hawaii-PluginInfo-License such as GPL, - * GPLv2, or GPLv2+ if the plugin is licensed under the terms of the + * The "icon-name" key is not mandatory, especially for non visual + * items such as containments. It's generally used only for elements. + * + * Use a descriptive name for "license" such as GPL, GPLv2, or GPLv2+ + * if the plugin is licensed under the terms of the * GNU General Public License v2 or (at your option) any later version. * - * Don't use nick names for X-Hawaii-PluginInfo-Author, please use - * your first and last name. + * Don't use nick names for "author-name", please use your first and + * last name. + * + * If "type" is "containment" you must add the "containment-type" key + * with one of the following values: + * \list + * \listitem desktop + * \listitem panel + * \endlist * * \param fileName name with full path of the plugin metadata */ @@ -170,6 +179,11 @@ class HAWAIISHELL_EXPORT PluginMetadata */ QString webSite() const; + /*! + * \return the main script. + */ + QString mainScript() const; + /*! * Returns the value associated with \a key. * You can use this method if you want to read custom values.