KCoreAddons

kaboutdata.cpp
1/*
2 This file is part of the KDE Libraries
3
4 SPDX-FileCopyrightText: 2000 Espen Sand <espen@kde.org>
5 SPDX-FileCopyrightText: 2006 Nicolas GOUTTE <goutte@kde.org>
6 SPDX-FileCopyrightText: 2008 Friedrich W. H. Kossebau <kossebau@kde.org>
7 SPDX-FileCopyrightText: 2010 Teo Mrnjavac <teo@kde.org>
8 SPDX-FileCopyrightText: 2017 Harald Sitter <sitter@kde.org>
9 SPDX-FileCopyrightText: 2021 Julius Künzel <jk.kdedev@smartlab.uber.space>
10
11 SPDX-License-Identifier: LGPL-2.0-or-later
12*/
13
14#include "kaboutdata.h"
15#include "kjsonutils.h"
16
17#include <QCommandLineOption>
18#include <QCommandLineParser>
19#include <QCoreApplication>
20#include <QFile>
21#include <QHash>
22#include <QJsonObject>
23#include <QList>
24#include <QLoggingCategory>
25#include <QSharedData>
26#include <QStandardPaths>
27#include <QTextStream>
28#include <QUrl>
29
30#include <algorithm>
31
32Q_DECLARE_LOGGING_CATEGORY(KABOUTDATA)
33// logging category for this framework, default: log stuff >= warning
34Q_LOGGING_CATEGORY(KABOUTDATA, "kf.coreaddons.kaboutdata", QtWarningMsg)
35
36class KAboutPersonPrivate : public QSharedData
37{
38public:
39 QString _name;
40 QString _task;
41 QString _emailAddress;
42 QString _webAddress;
43 QUrl _avatarUrl;
44};
45
46KAboutPerson::KAboutPerson(const QString &_name, const QString &_task, const QString &_emailAddress, const QString &_webAddress, const QUrl &avatarUrl)
47 : d(new KAboutPersonPrivate)
48{
49 d->_name = _name;
50 d->_task = _task;
51 d->_emailAddress = _emailAddress;
52 d->_webAddress = _webAddress;
53 d->_avatarUrl = avatarUrl;
54}
55
56KAboutPerson::KAboutPerson(const QString &_name, const QString &_email, bool)
57 : d(new KAboutPersonPrivate)
58{
59 d->_name = _name;
60 d->_emailAddress = _email;
61}
62
63KAboutPerson::KAboutPerson(const KAboutPerson &other) = default;
64
65KAboutPerson::~KAboutPerson() = default;
66
67QString KAboutPerson::name() const
68{
69 return d->_name;
70}
71
72QString KAboutPerson::task() const
73{
74 return d->_task;
75}
76
77QString KAboutPerson::emailAddress() const
78{
79 return d->_emailAddress;
80}
81
82QString KAboutPerson::webAddress() const
83{
84 return d->_webAddress;
85}
86
87QUrl KAboutPerson::avatarUrl() const
88{
89 return d->_avatarUrl;
90}
91
93
95{
96 const QString name = KJsonUtils::readTranslatedString(obj, QStringLiteral("Name"));
97 const QString task = KJsonUtils::readTranslatedString(obj, QStringLiteral("Task"));
98 const QString email = obj.value(QLatin1String("Email")).toString();
99 const QString website = obj.value(QLatin1String("Website")).toString();
100 const QUrl avatarUrl = obj.value(QLatin1String("AvatarUrl")).toVariant().toUrl();
101 return KAboutPerson(name, task, email, website, avatarUrl);
102}
103
104class KAboutLicensePrivate : public QSharedData
105{
106public:
107 KAboutLicensePrivate(KAboutLicense::LicenseKey licenseType, KAboutLicense::VersionRestriction versionRestriction, const KAboutData *aboutData);
108 KAboutLicensePrivate(const KAboutLicensePrivate &other);
109
110 QString spdxID() const;
111
112 KAboutLicense::LicenseKey _licenseKey;
113 QString _licenseText;
114 QString _pathToLicenseTextFile;
115 KAboutLicense::VersionRestriction _versionRestriction;
116 // needed for access to the possibly changing copyrightStatement()
117 const KAboutData *_aboutData;
118};
119
120KAboutLicensePrivate::KAboutLicensePrivate(KAboutLicense::LicenseKey licenseType,
121 KAboutLicense::VersionRestriction versionRestriction,
122 const KAboutData *aboutData)
123 : QSharedData()
124 , _licenseKey(licenseType)
125 , _versionRestriction(versionRestriction)
126 , _aboutData(aboutData)
127{
128}
129
130KAboutLicensePrivate::KAboutLicensePrivate(const KAboutLicensePrivate &other)
131 : QSharedData(other)
132 , _licenseKey(other._licenseKey)
133 , _licenseText(other._licenseText)
134 , _pathToLicenseTextFile(other._pathToLicenseTextFile)
135 , _versionRestriction(other._versionRestriction)
136 , _aboutData(other._aboutData)
137{
138}
139
140QString KAboutLicensePrivate::spdxID() const
141{
142 switch (_licenseKey) {
144 return QStringLiteral("GPL-2.0");
146 return QStringLiteral("LGPL-2.0");
148 return QStringLiteral("BSD-2-Clause");
150 return QStringLiteral("Artistic-1.0");
152 return QStringLiteral("GPL-3.0");
154 return QStringLiteral("LGPL-3.0");
156 return QStringLiteral("LGPL-2.1");
158 return QStringLiteral("MIT");
162 return QString();
163 }
164 return QString();
165}
166
168 : d(new KAboutLicensePrivate(Unknown, {}, nullptr))
169{
170}
171
172KAboutLicense::KAboutLicense(LicenseKey licenseType, VersionRestriction versionRestriction, const KAboutData *aboutData)
173 : d(new KAboutLicensePrivate(licenseType, versionRestriction, aboutData))
174{
175}
176
177KAboutLicense::KAboutLicense(LicenseKey licenseType, const KAboutData *aboutData)
178 : d(new KAboutLicensePrivate(licenseType, OnlyThisVersion, aboutData))
179{
180}
181
183 : d(new KAboutLicensePrivate(Unknown, OnlyThisVersion, aboutData))
184{
185}
186
188 : d(other.d)
189{
190}
191
192KAboutLicense::~KAboutLicense()
193{
194}
195
196void KAboutLicense::setLicenseFromPath(const QString &pathToFile)
197{
198 d->_licenseKey = KAboutLicense::File;
199 d->_pathToLicenseTextFile = pathToFile;
200}
201
202void KAboutLicense::setLicenseFromText(const QString &licenseText)
203{
204 d->_licenseKey = KAboutLicense::Custom;
205 d->_licenseText = licenseText;
206}
207
208QString KAboutLicense::text() const
209{
210 QString result;
211
212 const QString lineFeed = QStringLiteral("\n\n");
213
214 if (d->_aboutData && !d->_aboutData->copyrightStatement().isEmpty()) {
215 result = d->_aboutData->copyrightStatement() + lineFeed;
216 }
217
218 bool knownLicense = false;
219 QString pathToFile; // rel path if known license
220 switch (d->_licenseKey) {
222 pathToFile = d->_pathToLicenseTextFile;
223 break;
225 knownLicense = true;
226 pathToFile = QStringLiteral("GPL_V2");
227 break;
229 knownLicense = true;
230 pathToFile = QStringLiteral("LGPL_V2");
231 break;
233 knownLicense = true;
234 pathToFile = QStringLiteral("BSD");
235 break;
237 knownLicense = true;
238 pathToFile = QStringLiteral("ARTISTIC");
239 break;
241 knownLicense = true;
242 pathToFile = QStringLiteral("GPL_V3");
243 break;
245 knownLicense = true;
246 pathToFile = QStringLiteral("LGPL_V3");
247 break;
249 knownLicense = true;
250 pathToFile = QStringLiteral("LGPL_V21");
251 break;
253 knownLicense = true;
254 pathToFile = QStringLiteral("MIT");
255 break;
257 if (!d->_licenseText.isEmpty()) {
258 result = d->_licenseText;
259 break;
260 }
261 Q_FALLTHROUGH();
262 // fall through
263 default:
264 result += QCoreApplication::translate("KAboutLicense",
265 "No licensing terms for this program have been specified.\n"
266 "Please check the documentation or the source for any\n"
267 "licensing terms.\n");
268 }
269
270 if (knownLicense) {
271#ifndef Q_OS_ANDROID
272 pathToFile = QStandardPaths::locate(QStandardPaths::GenericDataLocation, QStringLiteral("kf6/licenses/") + pathToFile);
273#else
274 pathToFile = QStringLiteral("assets:/share/kf6/licenses/") + pathToFile;
275#endif
276 result += QCoreApplication::translate("KAboutLicense", "This program is distributed under the terms of the %1.").arg(name(KAboutLicense::ShortName));
277 if (!pathToFile.isEmpty()) {
278 result += lineFeed;
279 }
280 }
281
282 if (!pathToFile.isEmpty()) {
283 QFile file(pathToFile);
284 if (file.open(QIODevice::ReadOnly)) {
285 QTextStream str(&file);
286 result += str.readAll();
287 }
288 }
289
290 return result;
291}
292
293QString KAboutLicense::spdx() const
294{
295 // SPDX licenses are comprised of an identifier (e.g. GPL-2.0), an optional + to denote 'or
296 // later versions' and optional ' WITH $exception' to denote standardized exceptions from the
297 // core license. As we do not offer exceptions we effectively only return GPL-2.0 or GPL-2.0+,
298 // this may change in the future. To that end the documentation makes no assertions about the
299 // actual content of the SPDX license expression we return.
300 // Expressions can in theory also contain AND, OR and () to build constructs involving more than
301 // one license. As this is outside the scope of a single license object we'll ignore this here
302 // for now.
303 // The expectation is that the return value is only run through spec-compliant parsers, so this
304 // can potentially be changed.
305
306 auto id = d->spdxID();
307 if (id.isNull()) { // Guard against potential future changes which would allow 'Foo+' as input.
308 return id;
309 }
310 return d->_versionRestriction == OrLaterVersions ? id.append(QLatin1Char('+')) : id;
311}
312
313QString KAboutLicense::name(KAboutLicense::NameFormat formatName) const
314{
315 QString licenseShort;
316 QString licenseFull;
317
318 switch (d->_licenseKey) {
320 licenseShort = QCoreApplication::translate("KAboutLicense", "GPL v2", "@item license (short name)");
321 licenseFull = QCoreApplication::translate("KAboutLicense", "GNU General Public License Version 2", "@item license");
322 break;
324 licenseShort = QCoreApplication::translate("KAboutLicense", "LGPL v2", "@item license (short name)");
325 licenseFull = QCoreApplication::translate("KAboutLicense", "GNU Lesser General Public License Version 2", "@item license");
326 break;
328 licenseShort = QCoreApplication::translate("KAboutLicense", "BSD License", "@item license (short name)");
329 licenseFull = QCoreApplication::translate("KAboutLicense", "BSD License", "@item license");
330 break;
332 licenseShort = QCoreApplication::translate("KAboutLicense", "Artistic License", "@item license (short name)");
333 licenseFull = QCoreApplication::translate("KAboutLicense", "Artistic License", "@item license");
334 break;
336 licenseShort = QCoreApplication::translate("KAboutLicense", "GPL v3", "@item license (short name)");
337 licenseFull = QCoreApplication::translate("KAboutLicense", "GNU General Public License Version 3", "@item license");
338 break;
340 licenseShort = QCoreApplication::translate("KAboutLicense", "LGPL v3", "@item license (short name)");
341 licenseFull = QCoreApplication::translate("KAboutLicense", "GNU Lesser General Public License Version 3", "@item license");
342 break;
344 licenseShort = QCoreApplication::translate("KAboutLicense", "LGPL v2.1", "@item license (short name)");
345 licenseFull = QCoreApplication::translate("KAboutLicense", "GNU Lesser General Public License Version 2.1", "@item license");
346 break;
348 licenseShort = QCoreApplication::translate("KAboutLicense", "MIT License", "@item license (short name)");
349 licenseFull = QCoreApplication::translate("KAboutLicense", "MIT License", "@item license");
350 break;
353 licenseShort = licenseFull = QCoreApplication::translate("KAboutLicense", "Custom", "@item license");
354 break;
355 default:
356 licenseShort = licenseFull = QCoreApplication::translate("KAboutLicense", "Not specified", "@item license");
357 }
358
359 const QString result = (formatName == KAboutLicense::ShortName) ? licenseShort : (formatName == KAboutLicense::FullName) ? licenseFull : QString();
360
361 return result;
362}
363
365{
366 d = other.d;
367 return *this;
368}
369
370KAboutLicense::LicenseKey KAboutLicense::key() const
371{
372 return d->_licenseKey;
373}
374
376{
377 // Setup keyword->enum dictionary on first call.
378 // Use normalized keywords, by the algorithm below.
379 static const QHash<QByteArray, KAboutLicense::LicenseKey> licenseDict{
380 {"gpl", KAboutLicense::GPL}, {"gplv2", KAboutLicense::GPL_V2},
381 {"gplv2+", KAboutLicense::GPL_V2}, {"gpl20", KAboutLicense::GPL_V2},
382 {"gpl20+", KAboutLicense::GPL_V2}, {"lgpl", KAboutLicense::LGPL},
383 {"lgplv2", KAboutLicense::LGPL_V2}, {"lgplv2+", KAboutLicense::LGPL_V2},
384 {"lgpl20", KAboutLicense::LGPL_V2}, {"lgpl20+", KAboutLicense::LGPL_V2},
385 {"bsd", KAboutLicense::BSDL}, {"bsd2clause", KAboutLicense::BSDL},
386 {"artistic", KAboutLicense::Artistic}, {"artistic10", KAboutLicense::Artistic},
387 {"gplv3", KAboutLicense::GPL_V3}, {"gplv3+", KAboutLicense::GPL_V3},
388 {"gpl30", KAboutLicense::GPL_V3}, {"gpl30+", KAboutLicense::GPL_V3},
389 {"lgplv3", KAboutLicense::LGPL_V3}, {"lgplv3+", KAboutLicense::LGPL_V3},
390 {"lgpl30", KAboutLicense::LGPL_V3}, {"lgpl30+", KAboutLicense::LGPL_V3},
391 {"lgplv21", KAboutLicense::LGPL_V2_1}, {"lgplv21+", KAboutLicense::LGPL_V2_1},
392 {"lgpl21", KAboutLicense::LGPL_V2_1}, {"lgpl21+", KAboutLicense::LGPL_V2_1},
393 {"mit", KAboutLicense::MIT},
394 };
395
396 // Normalize keyword.
397 QString keyword = rawKeyword;
398 keyword = keyword.toLower();
399 keyword.replace(QLatin1StringView("-or-later"), QLatin1StringView("+"));
400 keyword.remove(QLatin1Char(' '));
401 keyword.remove(QLatin1Char('.'));
402 keyword.remove(QLatin1Char('-'));
403
404 LicenseKey license = licenseDict.value(keyword.toLatin1(), KAboutLicense::Custom);
405 auto restriction = keyword.endsWith(QLatin1Char('+')) ? OrLaterVersions : OnlyThisVersion;
406 return KAboutLicense(license, restriction, nullptr);
407}
408
409class KAboutComponentPrivate : public QSharedData
410{
411public:
412 QString _name;
413 QString _description;
414 QString _version;
415 QString _webAddress;
416 KAboutLicense _license;
417};
418
420 const QString &_description,
421 const QString &_version,
422 const QString &_webAddress,
423 enum KAboutLicense::LicenseKey licenseType)
424 : d(new KAboutComponentPrivate)
425{
426 d->_name = _name;
427 d->_description = _description;
428 d->_version = _version;
429 d->_webAddress = _webAddress;
430 d->_license = KAboutLicense(licenseType, nullptr);
431}
432
434 const QString &_description,
435 const QString &_version,
436 const QString &_webAddress,
437 const QString &pathToLicenseFile)
438 : d(new KAboutComponentPrivate)
439{
440 d->_name = _name;
441 d->_description = _description;
442 d->_version = _version;
443 d->_webAddress = _webAddress;
444 d->_license = KAboutLicense();
445 d->_license.setLicenseFromPath(pathToLicenseFile);
446}
447
449
450KAboutComponent::~KAboutComponent() = default;
451
452QString KAboutComponent::name() const
453{
454 return d->_name;
455}
456
457QString KAboutComponent::description() const
458{
459 return d->_description;
460}
461
462QString KAboutComponent::version() const
463{
464 return d->_version;
465}
466
467QString KAboutComponent::webAddress() const
468{
469 return d->_webAddress;
470}
471
473{
474 return d->_license;
475}
476
478
479class KAboutDataPrivate
480{
481public:
482 KAboutDataPrivate()
483 : customAuthorTextEnabled(false)
484 {
485 }
486 QString _componentName;
487 QString _displayName;
488 QString _shortDescription;
489 QString _copyrightStatement;
490 QString _otherText;
491 QString _homepageAddress;
492 QList<KAboutPerson> _authorList;
493 QList<KAboutPerson> _creditList;
494 QList<KAboutPerson> _translatorList;
495 QList<KAboutComponent> _componentList;
496 QList<KAboutLicense> _licenseList;
497 QVariant programLogo;
498 QString customAuthorPlainText, customAuthorRichText;
499 bool customAuthorTextEnabled;
500
501 QString organizationDomain;
502 QString desktopFileName;
503
504 // Everything dr.konqi needs, we store as utf-8, so we
505 // can just give it a pointer, w/o any allocations.
506 QByteArray _internalProgramName;
507 QByteArray _version;
508 QByteArray _bugAddress;
509 QByteArray productName;
510
511 static QList<KAboutPerson> parseTranslators(const QString &translatorName, const QString &translatorEmail);
512};
513
514KAboutData::KAboutData(const QString &_componentName,
515 const QString &_displayName,
516 const QString &_version,
517 const QString &_shortDescription,
518 enum KAboutLicense::LicenseKey licenseType,
519 const QString &_copyrightStatement,
520 const QString &text,
521 const QString &homePageAddress,
522 const QString &bugAddress)
523 : d(new KAboutDataPrivate)
524{
525 d->_componentName = _componentName;
526 int p = d->_componentName.indexOf(QLatin1Char('/'));
527 if (p >= 0) {
528 d->_componentName = d->_componentName.mid(p + 1);
529 }
530
531 d->_displayName = _displayName;
532 if (!d->_displayName.isEmpty()) { // KComponentData("klauncher") gives empty program name
533 d->_internalProgramName = _displayName.toUtf8();
534 }
535 d->_version = _version.toUtf8();
536 d->_shortDescription = _shortDescription;
537 d->_licenseList.append(KAboutLicense(licenseType, this));
538 d->_copyrightStatement = _copyrightStatement;
539 d->_otherText = text;
540 d->_homepageAddress = homePageAddress;
541 d->_bugAddress = bugAddress.toUtf8();
542
543 QUrl homePageUrl(homePageAddress);
544 if (!homePageUrl.isValid() || homePageUrl.scheme().isEmpty()) {
545 // Default domain if nothing else is better
546 homePageUrl.setUrl(QStringLiteral("https://kde.org/"));
547 }
548
549 const QChar dotChar(QLatin1Char('.'));
550 QStringList hostComponents = homePageUrl.host().split(dotChar);
551
552 // Remove leading component unless 2 (or less) components are present
553 if (hostComponents.size() > 2) {
554 hostComponents.removeFirst();
555 }
556
557 d->organizationDomain = hostComponents.join(dotChar);
558
559 // KF6: do not set a default desktopFileName value here, but remove this code and leave it empty
560 // see KAboutData::desktopFileName() for details
561
562 // desktop file name is reverse domain name
563 std::reverse(hostComponents.begin(), hostComponents.end());
564 hostComponents.append(_componentName);
565
566 d->desktopFileName = hostComponents.join(dotChar);
567}
568
569KAboutData::KAboutData(const QString &_componentName, const QString &_displayName, const QString &_version)
570 : d(new KAboutDataPrivate)
571{
572 d->_componentName = _componentName;
573 int p = d->_componentName.indexOf(QLatin1Char('/'));
574 if (p >= 0) {
575 d->_componentName = d->_componentName.mid(p + 1);
576 }
577
578 d->_displayName = _displayName;
579 if (!d->_displayName.isEmpty()) { // KComponentData("klauncher") gives empty program name
580 d->_internalProgramName = _displayName.toUtf8();
581 }
582 d->_version = _version.toUtf8();
583
584 // match behaviour of other constructors
585 d->_licenseList.append(KAboutLicense(KAboutLicense::Unknown, this));
586 d->_bugAddress = "submit@bugs.kde.org";
587 d->organizationDomain = QStringLiteral("kde.org");
588 // KF6: do not set a default desktopFileName value here, but remove this code and leave it empty
589 // see KAboutData::desktopFileName() for details
590 d->desktopFileName = QLatin1String("org.kde.") + d->_componentName;
591}
592
593KAboutData::~KAboutData() = default;
594
596 : d(new KAboutDataPrivate)
597{
598 *d = *other.d;
599 for (KAboutLicense &al : d->_licenseList) {
600 al.d.detach();
601 al.d->_aboutData = this;
602 }
603}
604
606{
607 if (this != &other) {
608 *d = *other.d;
609 for (KAboutLicense &al : d->_licenseList) {
610 al.d.detach();
611 al.d->_aboutData = this;
612 }
613 }
614 return *this;
615}
616
617KAboutData &KAboutData::addAuthor(const QString &name, const QString &task, const QString &emailAddress, const QString &webAddress, const QUrl &avatarUrl)
618{
619 d->_authorList.append(KAboutPerson(name, task, emailAddress, webAddress, avatarUrl));
620 return *this;
621}
622
623KAboutData &KAboutData::addCredit(const QString &name, const QString &task, const QString &emailAddress, const QString &webAddress, const QUrl &avatarUrl)
624{
625 d->_creditList.append(KAboutPerson(name, task, emailAddress, webAddress, avatarUrl));
626 return *this;
627}
628
629KAboutData &KAboutData::setTranslator(const QString &name, const QString &emailAddress)
630{
631 d->_translatorList = KAboutDataPrivate::parseTranslators(name, emailAddress);
632 return *this;
633}
634
636 const QString &description,
637 const QString &version,
638 const QString &webAddress,
639 KAboutLicense::LicenseKey licenseKey)
640{
641 d->_componentList.append(KAboutComponent(name, description, version, webAddress, licenseKey));
642 return *this;
643}
644
646KAboutData::addComponent(const QString &name, const QString &description, const QString &version, const QString &webAddress, const QString &pathToLicenseFile)
647{
648 d->_componentList.append(KAboutComponent(name, description, version, webAddress, pathToLicenseFile));
649 return *this;
650}
651
653{
654 d->_licenseList[0] = KAboutLicense(this);
655 d->_licenseList[0].setLicenseFromText(licenseText);
656 return *this;
657}
658
660{
661 // if the default license is unknown, overwrite instead of append
662 KAboutLicense &firstLicense = d->_licenseList[0];
663 KAboutLicense newLicense(this);
664 newLicense.setLicenseFromText(licenseText);
665 if (d->_licenseList.count() == 1 && firstLicense.d->_licenseKey == KAboutLicense::Unknown) {
666 firstLicense = newLicense;
667 } else {
668 d->_licenseList.append(newLicense);
669 }
670
671 return *this;
672}
673
675{
676 d->_licenseList[0] = KAboutLicense(this);
677 d->_licenseList[0].setLicenseFromPath(pathToFile);
678 return *this;
679}
680
682{
683 // if the default license is unknown, overwrite instead of append
684 KAboutLicense &firstLicense = d->_licenseList[0];
685 KAboutLicense newLicense(this);
686 newLicense.setLicenseFromPath(pathToFile);
687 if (d->_licenseList.count() == 1 && firstLicense.d->_licenseKey == KAboutLicense::Unknown) {
688 firstLicense = newLicense;
689 } else {
690 d->_licenseList.append(newLicense);
691 }
692 return *this;
693}
694
696{
697 d->_componentName = componentName;
698 return *this;
699}
700
702{
703 d->_displayName = _displayName;
704 d->_internalProgramName = _displayName.toUtf8();
705 return *this;
706}
707
709{
710 d->_version = _version;
711 return *this;
712}
713
715{
716 d->_shortDescription = _shortDescription;
717 return *this;
718}
719
721{
722 return setLicense(licenseKey, KAboutLicense::OnlyThisVersion);
723}
724
726{
727 d->_licenseList[0] = KAboutLicense(licenseKey, versionRestriction, this);
728 return *this;
729}
730
732{
733 return addLicense(licenseKey, KAboutLicense::OnlyThisVersion);
734}
735
737{
738 // if the default license is unknown, overwrite instead of append
739 KAboutLicense &firstLicense = d->_licenseList[0];
740 if (d->_licenseList.count() == 1 && firstLicense.d->_licenseKey == KAboutLicense::Unknown) {
741 firstLicense = KAboutLicense(licenseKey, versionRestriction, this);
742 } else {
743 d->_licenseList.append(KAboutLicense(licenseKey, versionRestriction, this));
744 }
745 return *this;
746}
747
749{
750 d->_copyrightStatement = _copyrightStatement;
751 return *this;
752}
753
755{
756 d->_otherText = _otherText;
757 return *this;
758}
759
761{
762 d->_homepageAddress = homepage;
763 return *this;
764}
765
767{
768 d->_bugAddress = _bugAddress;
769 return *this;
770}
771
773{
774 d->organizationDomain = QString::fromLatin1(domain.data());
775 return *this;
776}
777
779{
780 d->productName = _productName;
781 return *this;
782}
783
784QString KAboutData::componentName() const
785{
786 return d->_componentName;
787}
788
789QString KAboutData::productName() const
790{
791 if (!d->productName.isEmpty()) {
792 return QString::fromUtf8(d->productName);
793 }
794 return componentName();
795}
796
798{
799 return d->productName.isEmpty() ? nullptr : d->productName.constData();
800}
801
802QString KAboutData::displayName() const
803{
804 return d->_displayName;
805}
806
807/// @internal
808/// Return the program name. It is always pre-allocated.
809/// Needed for KCrash in particular.
811{
812 return d->_internalProgramName.constData();
813}
814
815QVariant KAboutData::programLogo() const
816{
817 return d->programLogo;
818}
819
821{
822 d->programLogo = image;
823 return *this;
824}
825
826QString KAboutData::version() const
827{
828 return QString::fromUtf8(d->_version.data());
829}
830
831/// @internal
832/// Return the untranslated and uninterpreted (to UTF8) string
833/// for the version information. Used in particular for KCrash.
835{
836 return d->_version.constData();
837}
838
839QString KAboutData::shortDescription() const
840{
841 return d->_shortDescription;
842}
843
844QString KAboutData::homepage() const
845{
846 return d->_homepageAddress;
847}
848
849QString KAboutData::bugAddress() const
850{
851 return QString::fromUtf8(d->_bugAddress.constData());
852}
853
855{
856 return d->organizationDomain;
857}
858
859/// @internal
860/// Return the untranslated and uninterpreted (to UTF8) string
861/// for the bug mail address. Used in particular for KCrash.
863{
864 if (d->_bugAddress.isEmpty()) {
865 return nullptr;
866 }
867 return d->_bugAddress.constData();
868}
869
870QList<KAboutPerson> KAboutData::authors() const
871{
872 return d->_authorList;
873}
874
875QList<KAboutPerson> KAboutData::credits() const
876{
877 return d->_creditList;
878}
879
880QList<KAboutPerson> KAboutDataPrivate::parseTranslators(const QString &translatorName, const QString &translatorEmail)
881{
882 if (translatorName.isEmpty() || translatorName == QLatin1String("Your names")) {
883 return {};
884 }
885
886 // use list of string views to delay creating new QString instances after the white-space trimming
887 const QList<QStringView> nameList = QStringView(translatorName).split(QLatin1Char(','));
888
889 QList<QStringView> emailList;
890 if (!translatorEmail.isEmpty() && translatorEmail != QLatin1String("Your emails")) {
891 emailList = QStringView(translatorEmail).split(QLatin1Char(','), Qt::KeepEmptyParts);
892 }
893
894 QList<KAboutPerson> personList;
895 personList.reserve(nameList.size());
896
897 auto eit = emailList.constBegin();
898
899 for (const QStringView &name : nameList) {
900 QStringView email;
901 if (eit != emailList.constEnd()) {
902 email = *eit;
903 ++eit;
904 }
905
906 personList.append(KAboutPerson(name.trimmed().toString(), email.trimmed().toString(), true));
907 }
908
909 return personList;
910}
911
912QList<KAboutPerson> KAboutData::translators() const
913{
914 return d->_translatorList;
915}
916
918{
919 return QCoreApplication::translate("KAboutData",
920 "<p>KDE is translated into many languages thanks to the work "
921 "of the translation teams all over the world.</p>"
922 "<p>For more information on KDE internationalization "
923 "visit <a href=\"https://l10n.kde.org\">https://l10n.kde.org</a></p>",
924 "replace this with information about your translation team");
925}
926
927QString KAboutData::otherText() const
928{
929 return d->_otherText;
930}
931
932QList<KAboutComponent> KAboutData::components() const
933{
934 return d->_componentList;
935}
936
937QList<KAboutLicense> KAboutData::licenses() const
938{
939 return d->_licenseList;
940}
941
942QString KAboutData::copyrightStatement() const
943{
944 return d->_copyrightStatement;
945}
946
948{
949 return d->customAuthorPlainText;
950}
951
953{
954 return d->customAuthorRichText;
955}
956
958{
959 return d->customAuthorTextEnabled;
960}
961
963{
964 d->customAuthorPlainText = plainText;
965 d->customAuthorRichText = richText;
966
967 d->customAuthorTextEnabled = true;
968
969 return *this;
970}
971
973{
974 d->customAuthorPlainText = QString();
975 d->customAuthorRichText = QString();
976
977 d->customAuthorTextEnabled = false;
978
979 return *this;
980}
981
983{
984 d->desktopFileName = desktopFileName;
985
986 return *this;
987}
988
989QString KAboutData::desktopFileName() const
990{
991 return d->desktopFileName;
992 // KF6: switch to this code and adapt API dox
993#if 0
994 // if desktopFileName has been explicitly set, use that value
995 if (!d->desktopFileName.isEmpty()) {
996 return d->desktopFileName;
997 }
998
999 // 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
1011class KAboutDataRegistry
1012{
1013public:
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
1028Q_GLOBAL_STATIC(KAboutDataRegistry, s_registry)
1029
1030namespace
1031{
1032void 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
1042{
1044
1045 KAboutData *aboutData = s_registry->m_appData;
1046
1047 // not yet existing
1048 if (!aboutData) {
1049 // init from current Q*Application data
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) {
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",
1075 warnIfOutOfSync("KAboutData::applicationData().version",
1076 aboutData->version(),
1077 "QCoreApplication::applicationVersion",
1079 warnIfOutOfSync("KAboutData::applicationData().organizationDomain",
1080 aboutData->organizationDomain(),
1081 "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
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
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)
1131const KAboutData *KAboutData::applicationDataPointer()
1132{
1133 if (s_registry.exists()) {
1134 return s_registry->m_appData;
1135 }
1136 return nullptr;
1137}
1138
1140{
1141 if (!d->_shortDescription.isEmpty()) {
1142 parser->setApplicationDescription(d->_shortDescription);
1143 }
1144
1145 parser->addHelpOption();
1146
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
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"
This class is used to store information about a third party component.
Definition kaboutdata.h:381
KAboutLicense license() const
The component's license.
KAboutComponent & operator=(const KAboutComponent &other)
Assignment operator.
KAboutComponent(const QString &name=QString(), const QString &description=QString(), const QString &version=QString(), const QString &webAddress=QString(), enum KAboutLicense::LicenseKey licenseType=KAboutLicense::Unknown)
Convenience constructor.
This class is used to store information about a program or plugin.
Definition kaboutdata.h:548
KAboutData & setProductName(const QByteArray &name)
Defines the product name which will be used in the KBugReport dialog.
KAboutData & setLicenseText(const QString &license)
Defines a license text, which is translated.
KAboutData & setShortDescription(const QString &shortDescription)
Defines a short description of what the program does.
KAboutData & setHomepage(const QString &homepage)
Defines the program homepage.
KAboutData & setDesktopFileName(const QString &desktopFileName)
Sets the base name of the desktop entry for this application.
static QString aboutTranslationTeam()
Returns a message about the translation team.
KAboutData & addLicense(KAboutLicense::LicenseKey licenseKey)
Adds a license identifier.
const char * internalBugAddress() const
KAboutData & setLicenseTextFile(const QString &file)
Defines a license text by pointing to a file where it resides.
const char * internalProductName() const
QString customAuthorRichText() const
Returns the rich text displayed around the list of authors instead of the default message telling use...
KAboutData & setCopyrightStatement(const QString &copyrightStatement)
Defines the copyright statement to show when displaying the license.
const char * internalVersion() const
bool customAuthorTextEnabled() const
Returns whether custom text should be displayed around the list of authors.
const char * internalProgramName() const
KAboutData & setProgramLogo(const QVariant &image)
Defines the program logo.
KAboutData & addCredit(const QString &name, const QString &task=QString(), const QString &emailAddress=QString(), const QString &webAddress=QString(), const QUrl &avatarUrl=QUrl())
Defines a person that deserves credit.
QString customAuthorPlainText() const
Returns the plain text displayed around the list of authors instead of the default message telling us...
KAboutData & setBugAddress(const QByteArray &bugAddress)
Defines the address where bug reports should be sent.
KAboutData & operator=(const KAboutData &other)
Assignment operator.
KAboutData & setTranslator(const QString &name, const QString &emailAddress)
Sets the name(s) of the translator(s) of the GUI.
QString organizationDomain() const
Returns the domain name of the organization that wrote this application.
KAboutData & addLicenseText(const QString &license)
Adds a license text, which is translated.
KAboutData & setDisplayName(const QString &displayName)
Defines the displayable component name string.
KAboutData & addAuthor(const QString &name, const QString &task=QString(), const QString &emailAddress=QString(), const QString &webAddress=QString(), const QUrl &avatarUrl=QUrl())
Defines an author.
static void setApplicationData(const KAboutData &aboutData)
Sets the application data for this application.
KAboutData & setVersion(const QByteArray &version)
Defines the program version string.
KAboutData & setOtherText(const QString &otherText)
Defines the additional text to show in the about dialog.
KAboutData & setCustomAuthorText(const QString &plainText, const QString &richText)
Sets the custom text displayed around the list of authors instead of the default message telling user...
KAboutData & setOrganizationDomain(const QByteArray &domain)
Defines the domain of the organization that wrote this application.
KAboutData(const QString &componentName, const QString &displayName, const QString &version, const QString &shortDescription, enum KAboutLicense::LicenseKey licenseType, const QString &copyrightStatement=QString(), const QString &otherText=QString(), const QString &homePageAddress=QString(), const QString &bugAddress=QStringLiteral("submit@bugs.kde.org"))
Constructor.
void processCommandLine(QCommandLineParser *parser)
Reads the processed parser and sees if any of the arguments are the ones set up from setupCommandLine...
KAboutData & unsetCustomAuthorText()
Clears any custom text displayed around the list of authors and falls back to the default message tel...
KAboutData & addLicenseTextFile(const QString &file)
Adds a license text by pointing to a file where it resides.
KAboutData & addComponent(const QString &name, const QString &description=QString(), const QString &version=QString(), const QString &webAddress=QString(), KAboutLicense::LicenseKey licenseKey=KAboutLicense::Unknown)
Defines a component that is used by the application.
KAboutData & setLicense(KAboutLicense::LicenseKey licenseKey)
Defines the license identifier.
static KAboutData applicationData()
Returns the KAboutData for the application.
bool setupCommandLine(QCommandLineParser *parser)
Configures the parser command line parser to provide an authors entry with information about the deve...
KAboutData & setComponentName(const QString &componentName)
Defines the component name used internally.
This class is used to store information about a license.
Definition kaboutdata.h:187
LicenseKey
Describes the license of the software; for more information see: https://spdx.org/licenses/.
Definition kaboutdata.h:200
@ GPL_V3
GPL_V3, see https://spdx.org/licenses/GPL-3.0.html.
Definition kaboutdata.h:210
@ BSDL
BSDL, see https://spdx.org/licenses/BSD-2-Clause.html.
Definition kaboutdata.h:208
@ Artistic
Artistic, see https://spdx.org/licenses/Artistic-2.0.html.
Definition kaboutdata.h:209
@ LGPL_V2_1
LGPL_V2_1.
Definition kaboutdata.h:212
@ Unknown
Unknown license.
Definition kaboutdata.h:203
@ Custom
Custom license.
Definition kaboutdata.h:201
@ LGPL_V2
LGPL_V2, this has the same value as LicenseKey::LGPL, see https://spdx.org/licenses/LGPL-2....
Definition kaboutdata.h:207
@ GPL_V2
GPL_V2, this has the same value as LicenseKey::GPL, see https://spdx.org/licenses/GPL-2....
Definition kaboutdata.h:205
@ File
License set from text file, see setLicenseFromPath()
Definition kaboutdata.h:202
@ LGPL_V3
LGPL_V3, see https://spdx.org/licenses/LGPL-3.0-only.html.
Definition kaboutdata.h:211
VersionRestriction
Whether later versions of the license are allowed.
Definition kaboutdata.h:229
static KAboutLicense byKeyword(const QString &keyword)
Fetch a known license by a keyword/spdx ID.
NameFormat
Format of the license name.
Definition kaboutdata.h:220
KAboutLicense & operator=(const KAboutLicense &other)
Assignment operator.
This class is used to store information about a person or developer.
Definition kaboutdata.h:64
static KAboutPerson fromJSON(const QJsonObject &obj)
Creates a KAboutPerson from a JSON object with the following structure:
KAboutPerson(const QString &name=QString(), const QString &task=QString(), const QString &emailAddress=QString(), const QString &webAddress=QString(), const QUrl &avatarUrl=QUrl())
Convenience constructor.
KAboutPerson & operator=(const KAboutPerson &other)
Assignment operator.
QString name(StandardShortcut id)
char * data()
QCommandLineOption addHelpOption()
bool addOption(const QCommandLineOption &option)
QCommandLineOption addVersionOption()
bool isSet(const QCommandLineOption &option) const const
void setApplicationDescription(const QString &description)
QString value(const QCommandLineOption &option) const const
QCoreApplication * instance()
QString translate(const char *context, const char *sourceText, const char *disambiguation, int n)
bool open(FILE *fh, OpenMode mode, FileHandleFlags handleFlags)
QJsonValue value(QLatin1StringView key) const const
QString toString() const const
QVariant toVariant() const const
void append(QList< T > &&value)
iterator begin()
const_iterator constBegin() const const
const_iterator constEnd() const const
iterator end()
void removeFirst()
void reserve(qsizetype size)
qsizetype size() const const
QVariant property(const char *name) const const
bool setProperty(const char *name, QVariant &&value)
QString locate(StandardLocation type, const QString &fileName, LocateOptions options)
QString & append(QChar ch)
QString arg(Args &&... args) const const
bool endsWith(QChar c, Qt::CaseSensitivity cs) const const
QString fromLatin1(QByteArrayView str)
QString fromUtf8(QByteArrayView str)
bool isEmpty() const const
QString & remove(QChar ch, Qt::CaseSensitivity cs)
QString & replace(QChar before, QChar after, Qt::CaseSensitivity cs)
QStringList split(QChar sep, Qt::SplitBehavior behavior, Qt::CaseSensitivity cs) const const
QByteArray toLatin1() const const
QString toLower() const const
QByteArray toUtf8() const const
QString trimmed() const const
QString join(QChar separator) const const
QList< QStringView > split(QChar sep, Qt::SplitBehavior behavior, Qt::CaseSensitivity cs) const const
QString toString() const const
QStringView trimmed() const const
KeepEmptyParts
QString readAll()
QString host(ComponentFormattingOptions options) const const
bool isValid() const const
QString scheme() const const
void setUrl(const QString &url, ParsingMode parsingMode)
QString toString() const const
QUrl toUrl() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Tue Mar 26 2024 11:13:31 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.