File indexing completed on 2024-04-28 03:53:52
0001 /* 0002 This file is part of the KDE Libraries 0003 0004 SPDX-FileCopyrightText: 2000 Espen Sand <espen@kde.org> 0005 SPDX-FileCopyrightText: 2006 Nicolas GOUTTE <goutte@kde.org> 0006 SPDX-FileCopyrightText: 2008 Friedrich W. H. Kossebau <kossebau@kde.org> 0007 SPDX-FileCopyrightText: 2010 Teo Mrnjavac <teo@kde.org> 0008 SPDX-FileCopyrightText: 2017 Harald Sitter <sitter@kde.org> 0009 SPDX-FileCopyrightText: 2021 Julius Künzel <jk.kdedev@smartlab.uber.space> 0010 0011 SPDX-License-Identifier: LGPL-2.0-or-later 0012 */ 0013 0014 #include "kaboutdata.h" 0015 #include "kjsonutils.h" 0016 0017 #include <QCommandLineOption> 0018 #include <QCommandLineParser> 0019 #include <QCoreApplication> 0020 #include <QFile> 0021 #include <QHash> 0022 #include <QJsonObject> 0023 #include <QList> 0024 #include <QLoggingCategory> 0025 #include <QSharedData> 0026 #include <QStandardPaths> 0027 #include <QTextStream> 0028 #include <QUrl> 0029 0030 #include <algorithm> 0031 0032 Q_DECLARE_LOGGING_CATEGORY(KABOUTDATA) 0033 // logging category for this framework, default: log stuff >= warning 0034 Q_LOGGING_CATEGORY(KABOUTDATA, "kf.coreaddons.kaboutdata", QtWarningMsg) 0035 0036 class KAboutPersonPrivate : public QSharedData 0037 { 0038 public: 0039 QString _name; 0040 QString _task; 0041 QString _emailAddress; 0042 QString _webAddress; 0043 QUrl _avatarUrl; 0044 }; 0045 0046 KAboutPerson::KAboutPerson(const QString &_name, const QString &_task, const QString &_emailAddress, const QString &_webAddress, const QUrl &avatarUrl) 0047 : d(new KAboutPersonPrivate) 0048 { 0049 d->_name = _name; 0050 d->_task = _task; 0051 d->_emailAddress = _emailAddress; 0052 d->_webAddress = _webAddress; 0053 d->_avatarUrl = avatarUrl; 0054 } 0055 0056 KAboutPerson::KAboutPerson(const QString &_name, const QString &_email, bool) 0057 : d(new KAboutPersonPrivate) 0058 { 0059 d->_name = _name; 0060 d->_emailAddress = _email; 0061 } 0062 0063 KAboutPerson::KAboutPerson(const KAboutPerson &other) = default; 0064 0065 KAboutPerson::~KAboutPerson() = default; 0066 0067 QString KAboutPerson::name() const 0068 { 0069 return d->_name; 0070 } 0071 0072 QString KAboutPerson::task() const 0073 { 0074 return d->_task; 0075 } 0076 0077 QString KAboutPerson::emailAddress() const 0078 { 0079 return d->_emailAddress; 0080 } 0081 0082 QString KAboutPerson::webAddress() const 0083 { 0084 return d->_webAddress; 0085 } 0086 0087 QUrl KAboutPerson::avatarUrl() const 0088 { 0089 return d->_avatarUrl; 0090 } 0091 0092 KAboutPerson &KAboutPerson::operator=(const KAboutPerson &other) = default; 0093 0094 KAboutPerson KAboutPerson::fromJSON(const QJsonObject &obj) 0095 { 0096 const QString name = KJsonUtils::readTranslatedString(obj, QStringLiteral("Name")); 0097 const QString task = KJsonUtils::readTranslatedString(obj, QStringLiteral("Task")); 0098 const QString email = obj.value(QLatin1String("Email")).toString(); 0099 const QString website = obj.value(QLatin1String("Website")).toString(); 0100 const QUrl avatarUrl = obj.value(QLatin1String("AvatarUrl")).toVariant().toUrl(); 0101 return KAboutPerson(name, task, email, website, avatarUrl); 0102 } 0103 0104 class KAboutLicensePrivate : public QSharedData 0105 { 0106 public: 0107 KAboutLicensePrivate(KAboutLicense::LicenseKey licenseType, KAboutLicense::VersionRestriction versionRestriction, const KAboutData *aboutData); 0108 KAboutLicensePrivate(const KAboutLicensePrivate &other); 0109 0110 QString spdxID() const; 0111 0112 KAboutLicense::LicenseKey _licenseKey; 0113 QString _licenseText; 0114 QString _pathToLicenseTextFile; 0115 KAboutLicense::VersionRestriction _versionRestriction; 0116 // needed for access to the possibly changing copyrightStatement() 0117 const KAboutData *_aboutData; 0118 }; 0119 0120 KAboutLicensePrivate::KAboutLicensePrivate(KAboutLicense::LicenseKey licenseType, 0121 KAboutLicense::VersionRestriction versionRestriction, 0122 const KAboutData *aboutData) 0123 : QSharedData() 0124 , _licenseKey(licenseType) 0125 , _versionRestriction(versionRestriction) 0126 , _aboutData(aboutData) 0127 { 0128 } 0129 0130 KAboutLicensePrivate::KAboutLicensePrivate(const KAboutLicensePrivate &other) 0131 : QSharedData(other) 0132 , _licenseKey(other._licenseKey) 0133 , _licenseText(other._licenseText) 0134 , _pathToLicenseTextFile(other._pathToLicenseTextFile) 0135 , _versionRestriction(other._versionRestriction) 0136 , _aboutData(other._aboutData) 0137 { 0138 } 0139 0140 QString KAboutLicensePrivate::spdxID() const 0141 { 0142 switch (_licenseKey) { 0143 case KAboutLicense::GPL_V2: 0144 return QStringLiteral("GPL-2.0"); 0145 case KAboutLicense::LGPL_V2: 0146 return QStringLiteral("LGPL-2.0"); 0147 case KAboutLicense::BSDL: 0148 return QStringLiteral("BSD-2-Clause"); 0149 case KAboutLicense::Artistic: 0150 return QStringLiteral("Artistic-1.0"); 0151 case KAboutLicense::GPL_V3: 0152 return QStringLiteral("GPL-3.0"); 0153 case KAboutLicense::LGPL_V3: 0154 return QStringLiteral("LGPL-3.0"); 0155 case KAboutLicense::LGPL_V2_1: 0156 return QStringLiteral("LGPL-2.1"); 0157 case KAboutLicense::MIT: 0158 return QStringLiteral("MIT"); 0159 case KAboutLicense::Custom: 0160 case KAboutLicense::File: 0161 case KAboutLicense::Unknown: 0162 return QString(); 0163 } 0164 return QString(); 0165 } 0166 0167 KAboutLicense::KAboutLicense() 0168 : d(new KAboutLicensePrivate(Unknown, {}, nullptr)) 0169 { 0170 } 0171 0172 KAboutLicense::KAboutLicense(LicenseKey licenseType, VersionRestriction versionRestriction, const KAboutData *aboutData) 0173 : d(new KAboutLicensePrivate(licenseType, versionRestriction, aboutData)) 0174 { 0175 } 0176 0177 KAboutLicense::KAboutLicense(LicenseKey licenseType, const KAboutData *aboutData) 0178 : d(new KAboutLicensePrivate(licenseType, OnlyThisVersion, aboutData)) 0179 { 0180 } 0181 0182 KAboutLicense::KAboutLicense(const KAboutData *aboutData) 0183 : d(new KAboutLicensePrivate(Unknown, OnlyThisVersion, aboutData)) 0184 { 0185 } 0186 0187 KAboutLicense::KAboutLicense(const KAboutLicense &other) 0188 : d(other.d) 0189 { 0190 } 0191 0192 KAboutLicense::~KAboutLicense() 0193 { 0194 } 0195 0196 void KAboutLicense::setLicenseFromPath(const QString &pathToFile) 0197 { 0198 d->_licenseKey = KAboutLicense::File; 0199 d->_pathToLicenseTextFile = pathToFile; 0200 } 0201 0202 void KAboutLicense::setLicenseFromText(const QString &licenseText) 0203 { 0204 d->_licenseKey = KAboutLicense::Custom; 0205 d->_licenseText = licenseText; 0206 } 0207 0208 QString KAboutLicense::text() const 0209 { 0210 QString result; 0211 0212 const QString lineFeed = QStringLiteral("\n\n"); 0213 0214 if (d->_aboutData && !d->_aboutData->copyrightStatement().isEmpty()) { 0215 result = d->_aboutData->copyrightStatement() + lineFeed; 0216 } 0217 0218 bool knownLicense = false; 0219 QString pathToFile; // rel path if known license 0220 switch (d->_licenseKey) { 0221 case KAboutLicense::File: 0222 pathToFile = d->_pathToLicenseTextFile; 0223 break; 0224 case KAboutLicense::GPL_V2: 0225 knownLicense = true; 0226 pathToFile = QStringLiteral("GPL_V2"); 0227 break; 0228 case KAboutLicense::LGPL_V2: 0229 knownLicense = true; 0230 pathToFile = QStringLiteral("LGPL_V2"); 0231 break; 0232 case KAboutLicense::BSDL: 0233 knownLicense = true; 0234 pathToFile = QStringLiteral("BSD"); 0235 break; 0236 case KAboutLicense::Artistic: 0237 knownLicense = true; 0238 pathToFile = QStringLiteral("ARTISTIC"); 0239 break; 0240 case KAboutLicense::GPL_V3: 0241 knownLicense = true; 0242 pathToFile = QStringLiteral("GPL_V3"); 0243 break; 0244 case KAboutLicense::LGPL_V3: 0245 knownLicense = true; 0246 pathToFile = QStringLiteral("LGPL_V3"); 0247 break; 0248 case KAboutLicense::LGPL_V2_1: 0249 knownLicense = true; 0250 pathToFile = QStringLiteral("LGPL_V21"); 0251 break; 0252 case KAboutLicense::MIT: 0253 knownLicense = true; 0254 pathToFile = QStringLiteral("MIT"); 0255 break; 0256 case KAboutLicense::Custom: 0257 if (!d->_licenseText.isEmpty()) { 0258 result = d->_licenseText; 0259 break; 0260 } 0261 Q_FALLTHROUGH(); 0262 // fall through 0263 default: 0264 result += QCoreApplication::translate("KAboutLicense", 0265 "No licensing terms for this program have been specified.\n" 0266 "Please check the documentation or the source for any\n" 0267 "licensing terms.\n"); 0268 } 0269 0270 if (knownLicense) { 0271 #ifndef Q_OS_ANDROID 0272 pathToFile = QStandardPaths::locate(QStandardPaths::GenericDataLocation, QStringLiteral("kf6/licenses/") + pathToFile); 0273 #else 0274 pathToFile = QStringLiteral("assets:/share/kf6/licenses/") + pathToFile; 0275 #endif 0276 result += QCoreApplication::translate("KAboutLicense", "This program is distributed under the terms of the %1.").arg(name(KAboutLicense::ShortName)); 0277 if (!pathToFile.isEmpty()) { 0278 result += lineFeed; 0279 } 0280 } 0281 0282 if (!pathToFile.isEmpty()) { 0283 QFile file(pathToFile); 0284 if (file.open(QIODevice::ReadOnly)) { 0285 QTextStream str(&file); 0286 result += str.readAll(); 0287 } 0288 } 0289 0290 return result; 0291 } 0292 0293 QString KAboutLicense::spdx() const 0294 { 0295 // SPDX licenses are comprised of an identifier (e.g. GPL-2.0), an optional + to denote 'or 0296 // later versions' and optional ' WITH $exception' to denote standardized exceptions from the 0297 // core license. As we do not offer exceptions we effectively only return GPL-2.0 or GPL-2.0+, 0298 // this may change in the future. To that end the documentation makes no assertions about the 0299 // actual content of the SPDX license expression we return. 0300 // Expressions can in theory also contain AND, OR and () to build constructs involving more than 0301 // one license. As this is outside the scope of a single license object we'll ignore this here 0302 // for now. 0303 // The expectation is that the return value is only run through spec-compliant parsers, so this 0304 // can potentially be changed. 0305 0306 auto id = d->spdxID(); 0307 if (id.isNull()) { // Guard against potential future changes which would allow 'Foo+' as input. 0308 return id; 0309 } 0310 return d->_versionRestriction == OrLaterVersions ? id.append(QLatin1Char('+')) : id; 0311 } 0312 0313 QString KAboutLicense::name(KAboutLicense::NameFormat formatName) const 0314 { 0315 QString licenseShort; 0316 QString licenseFull; 0317 0318 switch (d->_licenseKey) { 0319 case KAboutLicense::GPL_V2: 0320 licenseShort = QCoreApplication::translate("KAboutLicense", "GPL v2", "@item license (short name)"); 0321 licenseFull = QCoreApplication::translate("KAboutLicense", "GNU General Public License Version 2", "@item license"); 0322 break; 0323 case KAboutLicense::LGPL_V2: 0324 licenseShort = QCoreApplication::translate("KAboutLicense", "LGPL v2", "@item license (short name)"); 0325 licenseFull = QCoreApplication::translate("KAboutLicense", "GNU Lesser General Public License Version 2", "@item license"); 0326 break; 0327 case KAboutLicense::BSDL: 0328 licenseShort = QCoreApplication::translate("KAboutLicense", "BSD License", "@item license (short name)"); 0329 licenseFull = QCoreApplication::translate("KAboutLicense", "BSD License", "@item license"); 0330 break; 0331 case KAboutLicense::Artistic: 0332 licenseShort = QCoreApplication::translate("KAboutLicense", "Artistic License", "@item license (short name)"); 0333 licenseFull = QCoreApplication::translate("KAboutLicense", "Artistic License", "@item license"); 0334 break; 0335 case KAboutLicense::GPL_V3: 0336 licenseShort = QCoreApplication::translate("KAboutLicense", "GPL v3", "@item license (short name)"); 0337 licenseFull = QCoreApplication::translate("KAboutLicense", "GNU General Public License Version 3", "@item license"); 0338 break; 0339 case KAboutLicense::LGPL_V3: 0340 licenseShort = QCoreApplication::translate("KAboutLicense", "LGPL v3", "@item license (short name)"); 0341 licenseFull = QCoreApplication::translate("KAboutLicense", "GNU Lesser General Public License Version 3", "@item license"); 0342 break; 0343 case KAboutLicense::LGPL_V2_1: 0344 licenseShort = QCoreApplication::translate("KAboutLicense", "LGPL v2.1", "@item license (short name)"); 0345 licenseFull = QCoreApplication::translate("KAboutLicense", "GNU Lesser General Public License Version 2.1", "@item license"); 0346 break; 0347 case KAboutLicense::MIT: 0348 licenseShort = QCoreApplication::translate("KAboutLicense", "MIT License", "@item license (short name)"); 0349 licenseFull = QCoreApplication::translate("KAboutLicense", "MIT License", "@item license"); 0350 break; 0351 case KAboutLicense::Custom: 0352 case KAboutLicense::File: 0353 licenseShort = licenseFull = QCoreApplication::translate("KAboutLicense", "Custom", "@item license"); 0354 break; 0355 default: 0356 licenseShort = licenseFull = QCoreApplication::translate("KAboutLicense", "Not specified", "@item license"); 0357 } 0358 0359 const QString result = (formatName == KAboutLicense::ShortName) ? licenseShort : (formatName == KAboutLicense::FullName) ? licenseFull : QString(); 0360 0361 return result; 0362 } 0363 0364 KAboutLicense &KAboutLicense::operator=(const KAboutLicense &other) 0365 { 0366 d = other.d; 0367 return *this; 0368 } 0369 0370 KAboutLicense::LicenseKey KAboutLicense::key() const 0371 { 0372 return d->_licenseKey; 0373 } 0374 0375 KAboutLicense KAboutLicense::byKeyword(const QString &rawKeyword) 0376 { 0377 // Setup keyword->enum dictionary on first call. 0378 // Use normalized keywords, by the algorithm below. 0379 static const QHash<QByteArray, KAboutLicense::LicenseKey> licenseDict{ 0380 {"gpl", KAboutLicense::GPL}, {"gplv2", KAboutLicense::GPL_V2}, 0381 {"gplv2+", KAboutLicense::GPL_V2}, {"gpl20", KAboutLicense::GPL_V2}, 0382 {"gpl20+", KAboutLicense::GPL_V2}, {"lgpl", KAboutLicense::LGPL}, 0383 {"lgplv2", KAboutLicense::LGPL_V2}, {"lgplv2+", KAboutLicense::LGPL_V2}, 0384 {"lgpl20", KAboutLicense::LGPL_V2}, {"lgpl20+", KAboutLicense::LGPL_V2}, 0385 {"bsd", KAboutLicense::BSDL}, {"bsd2clause", KAboutLicense::BSDL}, 0386 {"artistic", KAboutLicense::Artistic}, {"artistic10", KAboutLicense::Artistic}, 0387 {"gplv3", KAboutLicense::GPL_V3}, {"gplv3+", KAboutLicense::GPL_V3}, 0388 {"gpl30", KAboutLicense::GPL_V3}, {"gpl30+", KAboutLicense::GPL_V3}, 0389 {"lgplv3", KAboutLicense::LGPL_V3}, {"lgplv3+", KAboutLicense::LGPL_V3}, 0390 {"lgpl30", KAboutLicense::LGPL_V3}, {"lgpl30+", KAboutLicense::LGPL_V3}, 0391 {"lgplv21", KAboutLicense::LGPL_V2_1}, {"lgplv21+", KAboutLicense::LGPL_V2_1}, 0392 {"lgpl21", KAboutLicense::LGPL_V2_1}, {"lgpl21+", KAboutLicense::LGPL_V2_1}, 0393 {"mit", KAboutLicense::MIT}, 0394 }; 0395 0396 // Normalize keyword. 0397 QString keyword = rawKeyword; 0398 keyword = keyword.toLower(); 0399 keyword.replace(QLatin1StringView("-or-later"), QLatin1StringView("+")); 0400 keyword.remove(QLatin1Char(' ')); 0401 keyword.remove(QLatin1Char('.')); 0402 keyword.remove(QLatin1Char('-')); 0403 0404 LicenseKey license = licenseDict.value(keyword.toLatin1(), KAboutLicense::Custom); 0405 auto restriction = keyword.endsWith(QLatin1Char('+')) ? OrLaterVersions : OnlyThisVersion; 0406 return KAboutLicense(license, restriction, nullptr); 0407 } 0408 0409 class KAboutComponentPrivate : public QSharedData 0410 { 0411 public: 0412 QString _name; 0413 QString _description; 0414 QString _version; 0415 QString _webAddress; 0416 KAboutLicense _license; 0417 }; 0418 0419 KAboutComponent::KAboutComponent(const QString &_name, 0420 const QString &_description, 0421 const QString &_version, 0422 const QString &_webAddress, 0423 enum KAboutLicense::LicenseKey licenseType) 0424 : d(new KAboutComponentPrivate) 0425 { 0426 d->_name = _name; 0427 d->_description = _description; 0428 d->_version = _version; 0429 d->_webAddress = _webAddress; 0430 d->_license = KAboutLicense(licenseType, nullptr); 0431 } 0432 0433 KAboutComponent::KAboutComponent(const QString &_name, 0434 const QString &_description, 0435 const QString &_version, 0436 const QString &_webAddress, 0437 const QString &pathToLicenseFile) 0438 : d(new KAboutComponentPrivate) 0439 { 0440 d->_name = _name; 0441 d->_description = _description; 0442 d->_version = _version; 0443 d->_webAddress = _webAddress; 0444 d->_license = KAboutLicense(); 0445 d->_license.setLicenseFromPath(pathToLicenseFile); 0446 } 0447 0448 KAboutComponent::KAboutComponent(const KAboutComponent &other) = default; 0449 0450 KAboutComponent::~KAboutComponent() = default; 0451 0452 QString KAboutComponent::name() const 0453 { 0454 return d->_name; 0455 } 0456 0457 QString KAboutComponent::description() const 0458 { 0459 return d->_description; 0460 } 0461 0462 QString KAboutComponent::version() const 0463 { 0464 return d->_version; 0465 } 0466 0467 QString KAboutComponent::webAddress() const 0468 { 0469 return d->_webAddress; 0470 } 0471 0472 KAboutLicense KAboutComponent::license() const 0473 { 0474 return d->_license; 0475 } 0476 0477 KAboutComponent &KAboutComponent::operator=(const KAboutComponent &other) = default; 0478 0479 class KAboutDataPrivate 0480 { 0481 public: 0482 KAboutDataPrivate() 0483 : customAuthorTextEnabled(false) 0484 { 0485 } 0486 QString _componentName; 0487 QString _displayName; 0488 QString _shortDescription; 0489 QString _copyrightStatement; 0490 QString _otherText; 0491 QString _homepageAddress; 0492 QList<KAboutPerson> _authorList; 0493 QList<KAboutPerson> _creditList; 0494 QList<KAboutPerson> _translatorList; 0495 QList<KAboutComponent> _componentList; 0496 QList<KAboutLicense> _licenseList; 0497 QVariant programLogo; 0498 QString customAuthorPlainText, customAuthorRichText; 0499 bool customAuthorTextEnabled; 0500 0501 QString organizationDomain; 0502 QString desktopFileName; 0503 0504 // Everything dr.konqi needs, we store as utf-8, so we 0505 // can just give it a pointer, w/o any allocations. 0506 QByteArray _internalProgramName; 0507 QByteArray _version; 0508 QByteArray _bugAddress; 0509 QByteArray productName; 0510 0511 static QList<KAboutPerson> parseTranslators(const QString &translatorName, const QString &translatorEmail); 0512 }; 0513 0514 KAboutData::KAboutData(const QString &_componentName, 0515 const QString &_displayName, 0516 const QString &_version, 0517 const QString &_shortDescription, 0518 enum KAboutLicense::LicenseKey licenseType, 0519 const QString &_copyrightStatement, 0520 const QString &text, 0521 const QString &homePageAddress, 0522 const QString &bugAddress) 0523 : d(new KAboutDataPrivate) 0524 { 0525 d->_componentName = _componentName; 0526 int p = d->_componentName.indexOf(QLatin1Char('/')); 0527 if (p >= 0) { 0528 d->_componentName = d->_componentName.mid(p + 1); 0529 } 0530 0531 d->_displayName = _displayName; 0532 if (!d->_displayName.isEmpty()) { // KComponentData("klauncher") gives empty program name 0533 d->_internalProgramName = _displayName.toUtf8(); 0534 } 0535 d->_version = _version.toUtf8(); 0536 d->_shortDescription = _shortDescription; 0537 d->_licenseList.append(KAboutLicense(licenseType, this)); 0538 d->_copyrightStatement = _copyrightStatement; 0539 d->_otherText = text; 0540 d->_homepageAddress = homePageAddress; 0541 d->_bugAddress = bugAddress.toUtf8(); 0542 0543 QUrl homePageUrl(homePageAddress); 0544 if (!homePageUrl.isValid() || homePageUrl.scheme().isEmpty()) { 0545 // Default domain if nothing else is better 0546 homePageUrl.setUrl(QStringLiteral("https://kde.org/")); 0547 } 0548 0549 const QChar dotChar(QLatin1Char('.')); 0550 QStringList hostComponents = homePageUrl.host().split(dotChar); 0551 0552 // Remove leading component unless 2 (or less) components are present 0553 if (hostComponents.size() > 2) { 0554 hostComponents.removeFirst(); 0555 } 0556 0557 d->organizationDomain = hostComponents.join(dotChar); 0558 0559 // KF6: do not set a default desktopFileName value here, but remove this code and leave it empty 0560 // see KAboutData::desktopFileName() for details 0561 0562 // desktop file name is reverse domain name 0563 std::reverse(hostComponents.begin(), hostComponents.end()); 0564 hostComponents.append(_componentName); 0565 0566 d->desktopFileName = hostComponents.join(dotChar); 0567 } 0568 0569 KAboutData::KAboutData(const QString &_componentName, const QString &_displayName, const QString &_version) 0570 : d(new KAboutDataPrivate) 0571 { 0572 d->_componentName = _componentName; 0573 int p = d->_componentName.indexOf(QLatin1Char('/')); 0574 if (p >= 0) { 0575 d->_componentName = d->_componentName.mid(p + 1); 0576 } 0577 0578 d->_displayName = _displayName; 0579 if (!d->_displayName.isEmpty()) { // KComponentData("klauncher") gives empty program name 0580 d->_internalProgramName = _displayName.toUtf8(); 0581 } 0582 d->_version = _version.toUtf8(); 0583 0584 // match behaviour of other constructors 0585 d->_licenseList.append(KAboutLicense(KAboutLicense::Unknown, this)); 0586 d->_bugAddress = "submit@bugs.kde.org"; 0587 d->organizationDomain = QStringLiteral("kde.org"); 0588 // KF6: do not set a default desktopFileName value here, but remove this code and leave it empty 0589 // see KAboutData::desktopFileName() for details 0590 d->desktopFileName = QLatin1String("org.kde.") + d->_componentName; 0591 } 0592 0593 KAboutData::~KAboutData() = default; 0594 0595 KAboutData::KAboutData(const KAboutData &other) 0596 : d(new KAboutDataPrivate) 0597 { 0598 *d = *other.d; 0599 for (KAboutLicense &al : d->_licenseList) { 0600 al.d.detach(); 0601 al.d->_aboutData = this; 0602 } 0603 } 0604 0605 KAboutData &KAboutData::operator=(const KAboutData &other) 0606 { 0607 if (this != &other) { 0608 *d = *other.d; 0609 for (KAboutLicense &al : d->_licenseList) { 0610 al.d.detach(); 0611 al.d->_aboutData = this; 0612 } 0613 } 0614 return *this; 0615 } 0616 0617 KAboutData &KAboutData::addAuthor(const QString &name, const QString &task, const QString &emailAddress, const QString &webAddress, const QUrl &avatarUrl) 0618 { 0619 d->_authorList.append(KAboutPerson(name, task, emailAddress, webAddress, avatarUrl)); 0620 return *this; 0621 } 0622 0623 KAboutData &KAboutData::addCredit(const QString &name, const QString &task, const QString &emailAddress, const QString &webAddress, const QUrl &avatarUrl) 0624 { 0625 d->_creditList.append(KAboutPerson(name, task, emailAddress, webAddress, avatarUrl)); 0626 return *this; 0627 } 0628 0629 KAboutData &KAboutData::setTranslator(const QString &name, const QString &emailAddress) 0630 { 0631 d->_translatorList = KAboutDataPrivate::parseTranslators(name, emailAddress); 0632 return *this; 0633 } 0634 0635 KAboutData &KAboutData::addComponent(const QString &name, 0636 const QString &description, 0637 const QString &version, 0638 const QString &webAddress, 0639 KAboutLicense::LicenseKey licenseKey) 0640 { 0641 d->_componentList.append(KAboutComponent(name, description, version, webAddress, licenseKey)); 0642 return *this; 0643 } 0644 0645 KAboutData & 0646 KAboutData::addComponent(const QString &name, const QString &description, const QString &version, const QString &webAddress, const QString &pathToLicenseFile) 0647 { 0648 d->_componentList.append(KAboutComponent(name, description, version, webAddress, pathToLicenseFile)); 0649 return *this; 0650 } 0651 0652 KAboutData &KAboutData::setLicenseText(const QString &licenseText) 0653 { 0654 d->_licenseList[0] = KAboutLicense(this); 0655 d->_licenseList[0].setLicenseFromText(licenseText); 0656 return *this; 0657 } 0658 0659 KAboutData &KAboutData::addLicenseText(const QString &licenseText) 0660 { 0661 // if the default license is unknown, overwrite instead of append 0662 KAboutLicense &firstLicense = d->_licenseList[0]; 0663 KAboutLicense newLicense(this); 0664 newLicense.setLicenseFromText(licenseText); 0665 if (d->_licenseList.count() == 1 && firstLicense.d->_licenseKey == KAboutLicense::Unknown) { 0666 firstLicense = newLicense; 0667 } else { 0668 d->_licenseList.append(newLicense); 0669 } 0670 0671 return *this; 0672 } 0673 0674 KAboutData &KAboutData::setLicenseTextFile(const QString &pathToFile) 0675 { 0676 d->_licenseList[0] = KAboutLicense(this); 0677 d->_licenseList[0].setLicenseFromPath(pathToFile); 0678 return *this; 0679 } 0680 0681 KAboutData &KAboutData::addLicenseTextFile(const QString &pathToFile) 0682 { 0683 // if the default license is unknown, overwrite instead of append 0684 KAboutLicense &firstLicense = d->_licenseList[0]; 0685 KAboutLicense newLicense(this); 0686 newLicense.setLicenseFromPath(pathToFile); 0687 if (d->_licenseList.count() == 1 && firstLicense.d->_licenseKey == KAboutLicense::Unknown) { 0688 firstLicense = newLicense; 0689 } else { 0690 d->_licenseList.append(newLicense); 0691 } 0692 return *this; 0693 } 0694 0695 KAboutData &KAboutData::setComponentName(const QString &componentName) 0696 { 0697 d->_componentName = componentName; 0698 return *this; 0699 } 0700 0701 KAboutData &KAboutData::setDisplayName(const QString &_displayName) 0702 { 0703 d->_displayName = _displayName; 0704 d->_internalProgramName = _displayName.toUtf8(); 0705 return *this; 0706 } 0707 0708 KAboutData &KAboutData::setVersion(const QByteArray &_version) 0709 { 0710 d->_version = _version; 0711 return *this; 0712 } 0713 0714 KAboutData &KAboutData::setShortDescription(const QString &_shortDescription) 0715 { 0716 d->_shortDescription = _shortDescription; 0717 return *this; 0718 } 0719 0720 KAboutData &KAboutData::setLicense(KAboutLicense::LicenseKey licenseKey) 0721 { 0722 return setLicense(licenseKey, KAboutLicense::OnlyThisVersion); 0723 } 0724 0725 KAboutData &KAboutData::setLicense(KAboutLicense::LicenseKey licenseKey, KAboutLicense::VersionRestriction versionRestriction) 0726 { 0727 d->_licenseList[0] = KAboutLicense(licenseKey, versionRestriction, this); 0728 return *this; 0729 } 0730 0731 KAboutData &KAboutData::addLicense(KAboutLicense::LicenseKey licenseKey) 0732 { 0733 return addLicense(licenseKey, KAboutLicense::OnlyThisVersion); 0734 } 0735 0736 KAboutData &KAboutData::addLicense(KAboutLicense::LicenseKey licenseKey, KAboutLicense::VersionRestriction versionRestriction) 0737 { 0738 // if the default license is unknown, overwrite instead of append 0739 KAboutLicense &firstLicense = d->_licenseList[0]; 0740 if (d->_licenseList.count() == 1 && firstLicense.d->_licenseKey == KAboutLicense::Unknown) { 0741 firstLicense = KAboutLicense(licenseKey, versionRestriction, this); 0742 } else { 0743 d->_licenseList.append(KAboutLicense(licenseKey, versionRestriction, this)); 0744 } 0745 return *this; 0746 } 0747 0748 KAboutData &KAboutData::setCopyrightStatement(const QString &_copyrightStatement) 0749 { 0750 d->_copyrightStatement = _copyrightStatement; 0751 return *this; 0752 } 0753 0754 KAboutData &KAboutData::setOtherText(const QString &_otherText) 0755 { 0756 d->_otherText = _otherText; 0757 return *this; 0758 } 0759 0760 KAboutData &KAboutData::setHomepage(const QString &homepage) 0761 { 0762 d->_homepageAddress = homepage; 0763 return *this; 0764 } 0765 0766 KAboutData &KAboutData::setBugAddress(const QByteArray &_bugAddress) 0767 { 0768 d->_bugAddress = _bugAddress; 0769 return *this; 0770 } 0771 0772 KAboutData &KAboutData::setOrganizationDomain(const QByteArray &domain) 0773 { 0774 d->organizationDomain = QString::fromLatin1(domain.data()); 0775 return *this; 0776 } 0777 0778 KAboutData &KAboutData::setProductName(const QByteArray &_productName) 0779 { 0780 d->productName = _productName; 0781 return *this; 0782 } 0783 0784 QString KAboutData::componentName() const 0785 { 0786 return d->_componentName; 0787 } 0788 0789 QString KAboutData::productName() const 0790 { 0791 if (!d->productName.isEmpty()) { 0792 return QString::fromUtf8(d->productName); 0793 } 0794 return componentName(); 0795 } 0796 0797 const char *KAboutData::internalProductName() const 0798 { 0799 return d->productName.isEmpty() ? nullptr : d->productName.constData(); 0800 } 0801 0802 QString KAboutData::displayName() const 0803 { 0804 return d->_displayName; 0805 } 0806 0807 /// @internal 0808 /// Return the program name. It is always pre-allocated. 0809 /// Needed for KCrash in particular. 0810 const char *KAboutData::internalProgramName() const 0811 { 0812 return d->_internalProgramName.constData(); 0813 } 0814 0815 QVariant KAboutData::programLogo() const 0816 { 0817 return d->programLogo; 0818 } 0819 0820 KAboutData &KAboutData::setProgramLogo(const QVariant &image) 0821 { 0822 d->programLogo = image; 0823 return *this; 0824 } 0825 0826 QString KAboutData::version() const 0827 { 0828 return QString::fromUtf8(d->_version.data()); 0829 } 0830 0831 /// @internal 0832 /// Return the untranslated and uninterpreted (to UTF8) string 0833 /// for the version information. Used in particular for KCrash. 0834 const char *KAboutData::internalVersion() const 0835 { 0836 return d->_version.constData(); 0837 } 0838 0839 QString KAboutData::shortDescription() const 0840 { 0841 return d->_shortDescription; 0842 } 0843 0844 QString KAboutData::homepage() const 0845 { 0846 return d->_homepageAddress; 0847 } 0848 0849 QString KAboutData::bugAddress() const 0850 { 0851 return QString::fromUtf8(d->_bugAddress.constData()); 0852 } 0853 0854 QString KAboutData::organizationDomain() const 0855 { 0856 return d->organizationDomain; 0857 } 0858 0859 /// @internal 0860 /// Return the untranslated and uninterpreted (to UTF8) string 0861 /// for the bug mail address. Used in particular for KCrash. 0862 const char *KAboutData::internalBugAddress() const 0863 { 0864 if (d->_bugAddress.isEmpty()) { 0865 return nullptr; 0866 } 0867 return d->_bugAddress.constData(); 0868 } 0869 0870 QList<KAboutPerson> KAboutData::authors() const 0871 { 0872 return d->_authorList; 0873 } 0874 0875 QList<KAboutPerson> KAboutData::credits() const 0876 { 0877 return d->_creditList; 0878 } 0879 0880 QList<KAboutPerson> KAboutDataPrivate::parseTranslators(const QString &translatorName, const QString &translatorEmail) 0881 { 0882 if (translatorName.isEmpty() || translatorName == QLatin1String("Your names")) { 0883 return {}; 0884 } 0885 0886 // use list of string views to delay creating new QString instances after the white-space trimming 0887 const QList<QStringView> nameList = QStringView(translatorName).split(QLatin1Char(',')); 0888 0889 QList<QStringView> emailList; 0890 if (!translatorEmail.isEmpty() && translatorEmail != QLatin1String("Your emails")) { 0891 emailList = QStringView(translatorEmail).split(QLatin1Char(','), Qt::KeepEmptyParts); 0892 } 0893 0894 QList<KAboutPerson> personList; 0895 personList.reserve(nameList.size()); 0896 0897 auto eit = emailList.constBegin(); 0898 0899 for (const QStringView &name : nameList) { 0900 QStringView email; 0901 if (eit != emailList.constEnd()) { 0902 email = *eit; 0903 ++eit; 0904 } 0905 0906 personList.append(KAboutPerson(name.trimmed().toString(), email.trimmed().toString(), true)); 0907 } 0908 0909 return personList; 0910 } 0911 0912 QList<KAboutPerson> KAboutData::translators() const 0913 { 0914 return d->_translatorList; 0915 } 0916 0917 QString KAboutData::aboutTranslationTeam() 0918 { 0919 return QCoreApplication::translate("KAboutData", 0920 "<p>KDE is translated into many languages thanks to the work " 0921 "of the translation teams all over the world.</p>" 0922 "<p>For more information on KDE internationalization " 0923 "visit <a href=\"https://l10n.kde.org\">https://l10n.kde.org</a></p>", 0924 "replace this with information about your translation team"); 0925 } 0926 0927 QString KAboutData::otherText() const 0928 { 0929 return d->_otherText; 0930 } 0931 0932 QList<KAboutComponent> KAboutData::components() const 0933 { 0934 return d->_componentList; 0935 } 0936 0937 QList<KAboutLicense> KAboutData::licenses() const 0938 { 0939 return d->_licenseList; 0940 } 0941 0942 QString KAboutData::copyrightStatement() const 0943 { 0944 return d->_copyrightStatement; 0945 } 0946 0947 QString KAboutData::customAuthorPlainText() const 0948 { 0949 return d->customAuthorPlainText; 0950 } 0951 0952 QString KAboutData::customAuthorRichText() const 0953 { 0954 return d->customAuthorRichText; 0955 } 0956 0957 bool KAboutData::customAuthorTextEnabled() const 0958 { 0959 return d->customAuthorTextEnabled; 0960 } 0961 0962 KAboutData &KAboutData::setCustomAuthorText(const QString &plainText, const QString &richText) 0963 { 0964 d->customAuthorPlainText = plainText; 0965 d->customAuthorRichText = richText; 0966 0967 d->customAuthorTextEnabled = true; 0968 0969 return *this; 0970 } 0971 0972 KAboutData &KAboutData::unsetCustomAuthorText() 0973 { 0974 d->customAuthorPlainText = QString(); 0975 d->customAuthorRichText = QString(); 0976 0977 d->customAuthorTextEnabled = false; 0978 0979 return *this; 0980 } 0981 0982 KAboutData &KAboutData::setDesktopFileName(const QString &desktopFileName) 0983 { 0984 d->desktopFileName = desktopFileName; 0985 0986 return *this; 0987 } 0988 0989 QString KAboutData::desktopFileName() const 0990 { 0991 return d->desktopFileName; 0992 // KF6: switch to this code and adapt API dox 0993 #if 0 0994 // if desktopFileName has been explicitly set, use that value 0995 if (!d->desktopFileName.isEmpty()) { 0996 return d->desktopFileName; 0997 } 0998 0999 // return a string calculated on-the-fly from the current org domain & component name 1000 const QChar dotChar(QLatin1Char('.')); 1001 QStringList hostComponents = d->organizationDomain.split(dotChar); 1002 1003 // desktop file name is reverse domain name 1004 std::reverse(hostComponents.begin(), hostComponents.end()); 1005 hostComponents.append(componentName()); 1006 1007 return hostComponents.join(dotChar); 1008 #endif 1009 } 1010 1011 class KAboutDataRegistry 1012 { 1013 public: 1014 KAboutDataRegistry() 1015 : m_appData(nullptr) 1016 { 1017 } 1018 ~KAboutDataRegistry() 1019 { 1020 delete m_appData; 1021 } 1022 KAboutDataRegistry(const KAboutDataRegistry &) = delete; 1023 KAboutDataRegistry &operator=(const KAboutDataRegistry &) = delete; 1024 1025 KAboutData *m_appData; 1026 }; 1027 1028 Q_GLOBAL_STATIC(KAboutDataRegistry, s_registry) 1029 1030 namespace 1031 { 1032 void warnIfOutOfSync(const char *aboutDataString, const QString &aboutDataValue, const char *appDataString, const QString &appDataValue) 1033 { 1034 if (aboutDataValue != appDataValue) { 1035 qCWarning(KABOUTDATA) << appDataString << appDataValue << "is out-of-sync with" << aboutDataString << aboutDataValue; 1036 } 1037 } 1038 1039 } 1040 1041 KAboutData KAboutData::applicationData() 1042 { 1043 QCoreApplication *app = QCoreApplication::instance(); 1044 1045 KAboutData *aboutData = s_registry->m_appData; 1046 1047 // not yet existing 1048 if (!aboutData) { 1049 // init from current Q*Application data 1050 aboutData = new KAboutData(QCoreApplication::applicationName(), QString(), QString()); 1051 // Unset the default (KDE) bug address, this is likely a third-party app. https://bugs.kde.org/show_bug.cgi?id=473517 1052 aboutData->setBugAddress(QByteArray()); 1053 // For applicationDisplayName & desktopFileName, which are only properties of QGuiApplication, 1054 // we have to try to get them via the property system, as the static getter methods are 1055 // part of QtGui only. Disadvantage: requires an app instance. 1056 // Either get all or none of the properties & warn about it 1057 if (app) { 1058 aboutData->setOrganizationDomain(QCoreApplication::organizationDomain().toUtf8()); 1059 aboutData->setVersion(QCoreApplication::applicationVersion().toUtf8()); 1060 aboutData->setDisplayName(app->property("applicationDisplayName").toString()); 1061 aboutData->setDesktopFileName(app->property("desktopFileName").toString()); 1062 } else { 1063 qCWarning(KABOUTDATA) << "Could not initialize the properties of KAboutData::applicationData by the equivalent properties from Q*Application: no " 1064 "app instance (yet) existing."; 1065 } 1066 1067 s_registry->m_appData = aboutData; 1068 } else { 1069 // check if in-sync with Q*Application metadata, as their setters could have been called 1070 // after the last KAboutData::setApplicationData, with different values 1071 warnIfOutOfSync("KAboutData::applicationData().componentName", 1072 aboutData->componentName(), 1073 "QCoreApplication::applicationName", 1074 QCoreApplication::applicationName()); 1075 warnIfOutOfSync("KAboutData::applicationData().version", 1076 aboutData->version(), 1077 "QCoreApplication::applicationVersion", 1078 QCoreApplication::applicationVersion()); 1079 warnIfOutOfSync("KAboutData::applicationData().organizationDomain", 1080 aboutData->organizationDomain(), 1081 "QCoreApplication::organizationDomain", 1082 QCoreApplication::organizationDomain()); 1083 if (app) { 1084 warnIfOutOfSync("KAboutData::applicationData().displayName", 1085 aboutData->displayName(), 1086 "QGuiApplication::applicationDisplayName", 1087 app->property("applicationDisplayName").toString()); 1088 warnIfOutOfSync("KAboutData::applicationData().desktopFileName", 1089 aboutData->desktopFileName(), 1090 "QGuiApplication::desktopFileName", 1091 app->property("desktopFileName").toString()); 1092 } 1093 } 1094 1095 return *aboutData; 1096 } 1097 1098 void KAboutData::setApplicationData(const KAboutData &aboutData) 1099 { 1100 if (s_registry->m_appData) { 1101 *s_registry->m_appData = aboutData; 1102 } else { 1103 s_registry->m_appData = new KAboutData(aboutData); 1104 } 1105 1106 // For applicationDisplayName & desktopFileName, which are only properties of QGuiApplication, 1107 // we have to try to set them via the property system, as the static getter methods are 1108 // part of QtGui only. Disadvantage: requires an app instance. 1109 // So set either all or none of the properties & warn about it 1110 QCoreApplication *app = QCoreApplication::instance(); 1111 if (app) { 1112 app->setApplicationVersion(aboutData.version()); 1113 app->setApplicationName(aboutData.componentName()); 1114 app->setOrganizationDomain(aboutData.organizationDomain()); 1115 app->setProperty("applicationDisplayName", aboutData.displayName()); 1116 app->setProperty("desktopFileName", aboutData.desktopFileName()); 1117 } else { 1118 qCWarning(KABOUTDATA) << "Could not initialize the equivalent properties of Q*Application: no instance (yet) existing."; 1119 } 1120 1121 // KF6: Rethink the current relation between KAboutData::applicationData and the Q*Application metadata 1122 // Always overwriting the Q*Application metadata here, but not updating back the KAboutData 1123 // in applicationData() is unbalanced and can result in out-of-sync data if the Q*Application 1124 // setters have been called meanwhile 1125 // Options are to remove the overlapping properties of KAboutData for cleancode, or making the 1126 // overlapping properties official shadow properties of their Q*Application countparts, though 1127 // that increases behavioural complexity a little. 1128 } 1129 1130 // only for KCrash (no memory allocation allowed) 1131 const KAboutData *KAboutData::applicationDataPointer() 1132 { 1133 if (s_registry.exists()) { 1134 return s_registry->m_appData; 1135 } 1136 return nullptr; 1137 } 1138 1139 bool KAboutData::setupCommandLine(QCommandLineParser *parser) 1140 { 1141 if (!d->_shortDescription.isEmpty()) { 1142 parser->setApplicationDescription(d->_shortDescription); 1143 } 1144 1145 parser->addHelpOption(); 1146 1147 QCoreApplication *app = QCoreApplication::instance(); 1148 if (app && !app->applicationVersion().isEmpty()) { 1149 parser->addVersionOption(); 1150 } 1151 1152 return parser->addOption(QCommandLineOption(QStringLiteral("author"), QCoreApplication::translate("KAboutData CLI", "Show author information."))) 1153 && parser->addOption(QCommandLineOption(QStringLiteral("license"), QCoreApplication::translate("KAboutData CLI", "Show license information."))) 1154 && parser->addOption(QCommandLineOption(QStringLiteral("desktopfile"), 1155 QCoreApplication::translate("KAboutData CLI", "The base file name of the desktop entry for this application."), 1156 QCoreApplication::translate("KAboutData CLI", "file name"))); 1157 } 1158 1159 void KAboutData::processCommandLine(QCommandLineParser *parser) 1160 { 1161 bool foundArgument = false; 1162 if (parser->isSet(QStringLiteral("author"))) { 1163 foundArgument = true; 1164 if (d->_authorList.isEmpty()) { 1165 printf("%s\n", 1166 qPrintable(QCoreApplication::translate("KAboutData CLI", "This application was written by somebody who wants to remain anonymous."))); 1167 } else { 1168 printf("%s\n", qPrintable(QCoreApplication::translate("KAboutData CLI", "%1 was written by:").arg(qAppName()))); 1169 for (const KAboutPerson &person : std::as_const(d->_authorList)) { 1170 QString authorData = QLatin1String(" ") + person.name(); 1171 if (!person.emailAddress().isEmpty()) { 1172 authorData.append(QLatin1String(" <") + person.emailAddress() + QLatin1Char('>')); 1173 } 1174 printf("%s\n", qPrintable(authorData)); 1175 } 1176 } 1177 if (!customAuthorTextEnabled()) { 1178 if (bugAddress() == QLatin1String("submit@bugs.kde.org")) { 1179 printf("%s\n", qPrintable(QCoreApplication::translate("KAboutData CLI", "Please use https://bugs.kde.org to report bugs."))); 1180 } else if (!bugAddress().isEmpty()) { 1181 printf("%s\n", qPrintable(QCoreApplication::translate("KAboutData CLI", "Please report bugs to %1.").arg(bugAddress()))); 1182 } 1183 } else { 1184 printf("%s\n", qPrintable(customAuthorPlainText())); 1185 } 1186 } else if (parser->isSet(QStringLiteral("license"))) { 1187 foundArgument = true; 1188 for (const KAboutLicense &license : std::as_const(d->_licenseList)) { 1189 printf("%s\n", qPrintable(license.text())); 1190 } 1191 } 1192 1193 const QString desktopFileName = parser->value(QStringLiteral("desktopfile")); 1194 if (!desktopFileName.isEmpty()) { 1195 d->desktopFileName = desktopFileName; 1196 } 1197 1198 if (foundArgument) { 1199 ::exit(EXIT_SUCCESS); 1200 } 1201 } 1202 1203 #include "moc_kaboutdata.cpp"