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 pathToFile = QStringLiteral(":/org.kde.kcoreaddons/licenses/") + pathToFile;
272 result += QCoreApplication::translate("KAboutLicense", "This program is distributed under the terms of the %1.").arg(name(KAboutLicense::ShortName));
273 if (!pathToFile.isEmpty()) {
274 result += lineFeed;
275 }
276 }
277
278 if (!pathToFile.isEmpty()) {
279 QFile file(pathToFile);
280 if (file.open(QIODevice::ReadOnly)) {
281 QTextStream str(&file);
282 result += str.readAll();
283 }
284 }
285
286 return result;
287}
288
289QString KAboutLicense::spdx() const
290{
291 // SPDX licenses are comprised of an identifier (e.g. GPL-2.0), an optional + to denote 'or
292 // later versions' and optional ' WITH $exception' to denote standardized exceptions from the
293 // core license. As we do not offer exceptions we effectively only return GPL-2.0 or GPL-2.0+,
294 // this may change in the future. To that end the documentation makes no assertions about the
295 // actual content of the SPDX license expression we return.
296 // Expressions can in theory also contain AND, OR and () to build constructs involving more than
297 // one license. As this is outside the scope of a single license object we'll ignore this here
298 // for now.
299 // The expectation is that the return value is only run through spec-compliant parsers, so this
300 // can potentially be changed.
301
302 auto id = d->spdxID();
303 if (id.isNull()) { // Guard against potential future changes which would allow 'Foo+' as input.
304 return id;
305 }
306 return d->_versionRestriction == OrLaterVersions ? id.append(QLatin1Char('+')) : id;
307}
308
309QString KAboutLicense::name(KAboutLicense::NameFormat formatName) const
310{
311 QString licenseShort;
312 QString licenseFull;
313
314 switch (d->_licenseKey) {
316 licenseShort = QCoreApplication::translate("KAboutLicense", "GPL v2", "@item license (short name)");
317 licenseFull = QCoreApplication::translate("KAboutLicense", "GNU General Public License Version 2", "@item license");
318 break;
320 licenseShort = QCoreApplication::translate("KAboutLicense", "LGPL v2", "@item license (short name)");
321 licenseFull = QCoreApplication::translate("KAboutLicense", "GNU Lesser General Public License Version 2", "@item license");
322 break;
324 licenseShort = QCoreApplication::translate("KAboutLicense", "BSD License", "@item license (short name)");
325 licenseFull = QCoreApplication::translate("KAboutLicense", "BSD License", "@item license");
326 break;
328 licenseShort = QCoreApplication::translate("KAboutLicense", "Artistic License", "@item license (short name)");
329 licenseFull = QCoreApplication::translate("KAboutLicense", "Artistic License", "@item license");
330 break;
332 licenseShort = QCoreApplication::translate("KAboutLicense", "GPL v3", "@item license (short name)");
333 licenseFull = QCoreApplication::translate("KAboutLicense", "GNU General Public License Version 3", "@item license");
334 break;
336 licenseShort = QCoreApplication::translate("KAboutLicense", "LGPL v3", "@item license (short name)");
337 licenseFull = QCoreApplication::translate("KAboutLicense", "GNU Lesser General Public License Version 3", "@item license");
338 break;
340 licenseShort = QCoreApplication::translate("KAboutLicense", "LGPL v2.1", "@item license (short name)");
341 licenseFull = QCoreApplication::translate("KAboutLicense", "GNU Lesser General Public License Version 2.1", "@item license");
342 break;
344 licenseShort = QCoreApplication::translate("KAboutLicense", "MIT License", "@item license (short name)");
345 licenseFull = QCoreApplication::translate("KAboutLicense", "MIT License", "@item license");
346 break;
349 licenseShort = licenseFull = QCoreApplication::translate("KAboutLicense", "Custom", "@item license");
350 break;
351 default:
352 licenseShort = licenseFull = QCoreApplication::translate("KAboutLicense", "Not specified", "@item license");
353 }
354
355 const QString result = (formatName == KAboutLicense::ShortName) ? licenseShort : (formatName == KAboutLicense::FullName) ? licenseFull : QString();
356
357 return result;
358}
359
361{
362 d = other.d;
363 return *this;
364}
365
366KAboutLicense::LicenseKey KAboutLicense::key() const
367{
368 return d->_licenseKey;
369}
370
372{
373 // Setup keyword->enum dictionary on first call.
374 // Use normalized keywords, by the algorithm below.
375 static const QHash<QByteArray, KAboutLicense::LicenseKey> licenseDict{
376 {"gpl", KAboutLicense::GPL}, {"gplv2", KAboutLicense::GPL_V2},
377 {"gplv2+", KAboutLicense::GPL_V2}, {"gpl20", KAboutLicense::GPL_V2},
378 {"gpl20+", KAboutLicense::GPL_V2}, {"lgpl", KAboutLicense::LGPL},
379 {"lgplv2", KAboutLicense::LGPL_V2}, {"lgplv2+", KAboutLicense::LGPL_V2},
380 {"lgpl20", KAboutLicense::LGPL_V2}, {"lgpl20+", KAboutLicense::LGPL_V2},
381 {"bsd", KAboutLicense::BSDL}, {"bsd2clause", KAboutLicense::BSDL},
382 {"artistic", KAboutLicense::Artistic}, {"artistic10", KAboutLicense::Artistic},
383 {"gplv3", KAboutLicense::GPL_V3}, {"gplv3+", KAboutLicense::GPL_V3},
384 {"gpl30", KAboutLicense::GPL_V3}, {"gpl30+", KAboutLicense::GPL_V3},
385 {"lgplv3", KAboutLicense::LGPL_V3}, {"lgplv3+", KAboutLicense::LGPL_V3},
386 {"lgpl30", KAboutLicense::LGPL_V3}, {"lgpl30+", KAboutLicense::LGPL_V3},
387 {"lgplv21", KAboutLicense::LGPL_V2_1}, {"lgplv21+", KAboutLicense::LGPL_V2_1},
388 {"lgpl21", KAboutLicense::LGPL_V2_1}, {"lgpl21+", KAboutLicense::LGPL_V2_1},
389 {"mit", KAboutLicense::MIT},
390 };
391
392 // Normalize keyword.
393 QString keyword = rawKeyword;
394 keyword = keyword.toLower();
395 keyword.replace(QLatin1StringView("-or-later"), QLatin1StringView("+"));
396 keyword.remove(QLatin1Char(' '));
397 keyword.remove(QLatin1Char('.'));
398 keyword.remove(QLatin1Char('-'));
399
400 LicenseKey license = licenseDict.value(keyword.toLatin1(), KAboutLicense::Custom);
401 auto restriction = keyword.endsWith(QLatin1Char('+')) ? OrLaterVersions : OnlyThisVersion;
402 return KAboutLicense(license, restriction, nullptr);
403}
404
405class KAboutComponentPrivate : public QSharedData
406{
407public:
408 QString _name;
409 QString _description;
410 QString _version;
411 QString _webAddress;
412 KAboutLicense _license;
413};
414
416 const QString &_description,
417 const QString &_version,
418 const QString &_webAddress,
419 enum KAboutLicense::LicenseKey licenseType)
420 : d(new KAboutComponentPrivate)
421{
422 d->_name = _name;
423 d->_description = _description;
424 d->_version = _version;
425 d->_webAddress = _webAddress;
426 d->_license = KAboutLicense(licenseType, nullptr);
427}
428
430 const QString &_description,
431 const QString &_version,
432 const QString &_webAddress,
433 const QString &pathToLicenseFile)
434 : d(new KAboutComponentPrivate)
435{
436 d->_name = _name;
437 d->_description = _description;
438 d->_version = _version;
439 d->_webAddress = _webAddress;
440 d->_license = KAboutLicense();
441 d->_license.setLicenseFromPath(pathToLicenseFile);
442}
443
445
446KAboutComponent::~KAboutComponent() = default;
447
448QString KAboutComponent::name() const
449{
450 return d->_name;
451}
452
453QString KAboutComponent::description() const
454{
455 return d->_description;
456}
457
458QString KAboutComponent::version() const
459{
460 return d->_version;
461}
462
463QString KAboutComponent::webAddress() const
464{
465 return d->_webAddress;
466}
467
469{
470 return d->_license;
471}
472
474
475class KAboutDataPrivate
476{
477public:
478 KAboutDataPrivate()
479 : customAuthorTextEnabled(false)
480 {
481 }
482 QString _componentName;
483 QString _displayName;
484 QString _shortDescription;
485 QString _copyrightStatement;
486 QString _otherText;
487 QString _homepageAddress;
488 QList<KAboutPerson> _authorList;
489 QList<KAboutPerson> _creditList;
490 QList<KAboutPerson> _translatorList;
491 QList<KAboutComponent> _componentList;
492 QList<KAboutLicense> _licenseList;
493 QVariant programLogo;
494 QString customAuthorPlainText, customAuthorRichText;
495 bool customAuthorTextEnabled;
496
497 QString organizationDomain;
498 QString desktopFileName;
499
500 // Everything dr.konqi needs, we store as utf-8, so we
501 // can just give it a pointer, w/o any allocations.
502 QByteArray _internalProgramName;
503 QByteArray _version;
504 QByteArray _bugAddress;
505 QByteArray productName;
506
507 static QList<KAboutPerson> parseTranslators(const QString &translatorName, const QString &translatorEmail);
508};
509
510KAboutData::KAboutData(const QString &_componentName,
511 const QString &_displayName,
512 const QString &_version,
513 const QString &_shortDescription,
514 enum KAboutLicense::LicenseKey licenseType,
515 const QString &_copyrightStatement,
516 const QString &text,
517 const QString &homePageAddress,
518 const QString &bugAddress)
519 : d(new KAboutDataPrivate)
520{
521 d->_componentName = _componentName;
522 int p = d->_componentName.indexOf(QLatin1Char('/'));
523 if (p >= 0) {
524 d->_componentName = d->_componentName.mid(p + 1);
525 }
526
527 d->_displayName = _displayName;
528 if (!d->_displayName.isEmpty()) { // KComponentData("klauncher") gives empty program name
529 d->_internalProgramName = _displayName.toUtf8();
530 }
531 d->_version = _version.toUtf8();
532 d->_shortDescription = _shortDescription;
533 d->_licenseList.append(KAboutLicense(licenseType, this));
534 d->_copyrightStatement = _copyrightStatement;
535 d->_otherText = text;
536 d->_homepageAddress = homePageAddress;
537 d->_bugAddress = bugAddress.toUtf8();
538
539 QUrl homePageUrl(homePageAddress);
540 if (!homePageUrl.isValid() || homePageUrl.scheme().isEmpty()) {
541 // Default domain if nothing else is better
542 homePageUrl.setUrl(QStringLiteral("https://kde.org/"));
543 }
544
545 const QChar dotChar(QLatin1Char('.'));
546 QStringList hostComponents = homePageUrl.host().split(dotChar);
547
548 // Remove leading component unless 2 (or less) components are present
549 if (hostComponents.size() > 2) {
550 hostComponents.removeFirst();
551 }
552
553 d->organizationDomain = hostComponents.join(dotChar);
554
555 // KF6: do not set a default desktopFileName value here, but remove this code and leave it empty
556 // see KAboutData::desktopFileName() for details
557
558 // desktop file name is reverse domain name
559 std::reverse(hostComponents.begin(), hostComponents.end());
560 hostComponents.append(_componentName);
561
562 d->desktopFileName = hostComponents.join(dotChar);
563}
564
565KAboutData::KAboutData(const QString &_componentName, const QString &_displayName, const QString &_version)
566 : d(new KAboutDataPrivate)
567{
568 d->_componentName = _componentName;
569 int p = d->_componentName.indexOf(QLatin1Char('/'));
570 if (p >= 0) {
571 d->_componentName = d->_componentName.mid(p + 1);
572 }
573
574 d->_displayName = _displayName;
575 if (!d->_displayName.isEmpty()) { // KComponentData("klauncher") gives empty program name
576 d->_internalProgramName = _displayName.toUtf8();
577 }
578 d->_version = _version.toUtf8();
579
580 // match behaviour of other constructors
581 d->_licenseList.append(KAboutLicense(KAboutLicense::Unknown, this));
582 d->_bugAddress = "submit@bugs.kde.org";
583 d->organizationDomain = QStringLiteral("kde.org");
584 // KF6: do not set a default desktopFileName value here, but remove this code and leave it empty
585 // see KAboutData::desktopFileName() for details
586 d->desktopFileName = QLatin1String("org.kde.") + d->_componentName;
587}
588
589KAboutData::~KAboutData() = default;
590
592 : d(new KAboutDataPrivate)
593{
594 *d = *other.d;
595 for (KAboutLicense &al : d->_licenseList) {
596 al.d.detach();
597 al.d->_aboutData = this;
598 }
599}
600
602{
603 if (this != &other) {
604 *d = *other.d;
605 for (KAboutLicense &al : d->_licenseList) {
606 al.d.detach();
607 al.d->_aboutData = this;
608 }
609 }
610 return *this;
611}
612
613KAboutData &KAboutData::addAuthor(const QString &name, const QString &task, const QString &emailAddress, const QString &webAddress, const QUrl &avatarUrl)
614{
615 d->_authorList.append(KAboutPerson(name, task, emailAddress, webAddress, avatarUrl));
616 return *this;
617}
618
619KAboutData &KAboutData::addCredit(const QString &name, const QString &task, const QString &emailAddress, const QString &webAddress, const QUrl &avatarUrl)
620{
621 d->_creditList.append(KAboutPerson(name, task, emailAddress, webAddress, avatarUrl));
622 return *this;
623}
624
625KAboutData &KAboutData::setTranslator(const QString &name, const QString &emailAddress)
626{
627 d->_translatorList = KAboutDataPrivate::parseTranslators(name, emailAddress);
628 return *this;
629}
630
632 const QString &description,
633 const QString &version,
634 const QString &webAddress,
635 KAboutLicense::LicenseKey licenseKey)
636{
637 d->_componentList.append(KAboutComponent(name, description, version, webAddress, licenseKey));
638 return *this;
639}
640
642KAboutData::addComponent(const QString &name, const QString &description, const QString &version, const QString &webAddress, const QString &pathToLicenseFile)
643{
644 d->_componentList.append(KAboutComponent(name, description, version, webAddress, pathToLicenseFile));
645 return *this;
646}
647
649{
650 d->_licenseList[0] = KAboutLicense(this);
651 d->_licenseList[0].setLicenseFromText(licenseText);
652 return *this;
653}
654
656{
657 // if the default license is unknown, overwrite instead of append
658 KAboutLicense &firstLicense = d->_licenseList[0];
659 KAboutLicense newLicense(this);
660 newLicense.setLicenseFromText(licenseText);
661 if (d->_licenseList.count() == 1 && firstLicense.d->_licenseKey == KAboutLicense::Unknown) {
662 firstLicense = newLicense;
663 } else {
664 d->_licenseList.append(newLicense);
665 }
666
667 return *this;
668}
669
671{
672 d->_licenseList[0] = KAboutLicense(this);
673 d->_licenseList[0].setLicenseFromPath(pathToFile);
674 return *this;
675}
676
678{
679 // if the default license is unknown, overwrite instead of append
680 KAboutLicense &firstLicense = d->_licenseList[0];
681 KAboutLicense newLicense(this);
682 newLicense.setLicenseFromPath(pathToFile);
683 if (d->_licenseList.count() == 1 && firstLicense.d->_licenseKey == KAboutLicense::Unknown) {
684 firstLicense = newLicense;
685 } else {
686 d->_licenseList.append(newLicense);
687 }
688 return *this;
689}
690
692{
693 d->_componentName = componentName;
694 return *this;
695}
696
698{
699 d->_displayName = _displayName;
700 d->_internalProgramName = _displayName.toUtf8();
701 return *this;
702}
703
705{
706 d->_version = _version;
707 return *this;
708}
709
711{
712 d->_shortDescription = _shortDescription;
713 return *this;
714}
715
717{
718 return setLicense(licenseKey, KAboutLicense::OnlyThisVersion);
719}
720
722{
723 d->_licenseList[0] = KAboutLicense(licenseKey, versionRestriction, this);
724 return *this;
725}
726
728{
729 return addLicense(licenseKey, KAboutLicense::OnlyThisVersion);
730}
731
733{
734 // if the default license is unknown, overwrite instead of append
735 KAboutLicense &firstLicense = d->_licenseList[0];
736 if (d->_licenseList.count() == 1 && firstLicense.d->_licenseKey == KAboutLicense::Unknown) {
737 firstLicense = KAboutLicense(licenseKey, versionRestriction, this);
738 } else {
739 d->_licenseList.append(KAboutLicense(licenseKey, versionRestriction, this));
740 }
741 return *this;
742}
743
745{
746 d->_copyrightStatement = _copyrightStatement;
747 return *this;
748}
749
751{
752 d->_otherText = _otherText;
753 return *this;
754}
755
757{
758 d->_homepageAddress = homepage;
759 return *this;
760}
761
763{
764 d->_bugAddress = _bugAddress;
765 return *this;
766}
767
769{
770 d->organizationDomain = QString::fromLatin1(domain.data());
771 return *this;
772}
773
775{
776 d->productName = _productName;
777 return *this;
778}
779
780QString KAboutData::componentName() const
781{
782 return d->_componentName;
783}
784
785QString KAboutData::productName() const
786{
787 if (!d->productName.isEmpty()) {
788 return QString::fromUtf8(d->productName);
789 }
790 return componentName();
791}
792
794{
795 return d->productName.isEmpty() ? nullptr : d->productName.constData();
796}
797
798QString KAboutData::displayName() const
799{
800 return d->_displayName;
801}
802
803/// @internal
804/// Return the program name. It is always pre-allocated.
805/// Needed for KCrash in particular.
807{
808 return d->_internalProgramName.constData();
809}
810
811QVariant KAboutData::programLogo() const
812{
813 return d->programLogo;
814}
815
817{
818 d->programLogo = image;
819 return *this;
820}
821
822QString KAboutData::version() const
823{
824 return QString::fromUtf8(d->_version.data());
825}
826
827/// @internal
828/// Return the untranslated and uninterpreted (to UTF8) string
829/// for the version information. Used in particular for KCrash.
831{
832 return d->_version.constData();
833}
834
835QString KAboutData::shortDescription() const
836{
837 return d->_shortDescription;
838}
839
840QString KAboutData::homepage() const
841{
842 return d->_homepageAddress;
843}
844
845QString KAboutData::bugAddress() const
846{
847 return QString::fromUtf8(d->_bugAddress.constData());
848}
849
851{
852 return d->organizationDomain;
853}
854
855/// @internal
856/// Return the untranslated and uninterpreted (to UTF8) string
857/// for the bug mail address. Used in particular for KCrash.
859{
860 if (d->_bugAddress.isEmpty()) {
861 return nullptr;
862 }
863 return d->_bugAddress.constData();
864}
865
866QList<KAboutPerson> KAboutData::authors() const
867{
868 return d->_authorList;
869}
870
871QList<KAboutPerson> KAboutData::credits() const
872{
873 return d->_creditList;
874}
875
876QList<KAboutPerson> KAboutDataPrivate::parseTranslators(const QString &translatorName, const QString &translatorEmail)
877{
878 if (translatorName.isEmpty() || translatorName == QLatin1String("Your names")) {
879 return {};
880 }
881
882 // use list of string views to delay creating new QString instances after the white-space trimming
883 const QList<QStringView> nameList = QStringView(translatorName).split(QLatin1Char(','));
884
885 QList<QStringView> emailList;
886 if (!translatorEmail.isEmpty() && translatorEmail != QLatin1String("Your emails")) {
887 emailList = QStringView(translatorEmail).split(QLatin1Char(','), Qt::KeepEmptyParts);
888 }
889
890 QList<KAboutPerson> personList;
891 personList.reserve(nameList.size());
892
893 auto eit = emailList.constBegin();
894
895 for (const QStringView &name : nameList) {
896 QStringView email;
897 if (eit != emailList.constEnd()) {
898 email = *eit;
899 ++eit;
900 }
901
902 personList.append(KAboutPerson(name.trimmed().toString(), email.trimmed().toString(), true));
903 }
904
905 return personList;
906}
907
908QList<KAboutPerson> KAboutData::translators() const
909{
910 return d->_translatorList;
911}
912
914{
915 return QCoreApplication::translate("KAboutData",
916 "<p>KDE is translated into many languages thanks to the work "
917 "of the translation teams all over the world.</p>"
918 "<p>For more information on KDE internationalization "
919 "visit <a href=\"https://l10n.kde.org\">https://l10n.kde.org</a></p>",
920 "replace this with information about your translation team");
921}
922
923QString KAboutData::otherText() const
924{
925 return d->_otherText;
926}
927
928QList<KAboutComponent> KAboutData::components() const
929{
930 return d->_componentList;
931}
932
933QList<KAboutLicense> KAboutData::licenses() const
934{
935 return d->_licenseList;
936}
937
938QString KAboutData::copyrightStatement() const
939{
940 return d->_copyrightStatement;
941}
942
944{
945 return d->customAuthorPlainText;
946}
947
949{
950 return d->customAuthorRichText;
951}
952
954{
955 return d->customAuthorTextEnabled;
956}
957
959{
960 d->customAuthorPlainText = plainText;
961 d->customAuthorRichText = richText;
962
963 d->customAuthorTextEnabled = true;
964
965 return *this;
966}
967
969{
970 d->customAuthorPlainText = QString();
971 d->customAuthorRichText = QString();
972
973 d->customAuthorTextEnabled = false;
974
975 return *this;
976}
977
979{
980 d->desktopFileName = desktopFileName;
981
982 return *this;
983}
984
985QString KAboutData::desktopFileName() const
986{
987 return d->desktopFileName;
988 // KF6: switch to this code and adapt API dox
989#if 0
990 // if desktopFileName has been explicitly set, use that value
991 if (!d->desktopFileName.isEmpty()) {
992 return d->desktopFileName;
993 }
994
995 // return a string calculated on-the-fly from the current org domain & component name
996 const QChar dotChar(QLatin1Char('.'));
997 QStringList hostComponents = d->organizationDomain.split(dotChar);
998
999 // desktop file name is reverse domain name
1000 std::reverse(hostComponents.begin(), hostComponents.end());
1001 hostComponents.append(componentName());
1002
1003 return hostComponents.join(dotChar);
1004#endif
1005}
1006
1007class KAboutDataRegistry
1008{
1009public:
1010 KAboutDataRegistry()
1011 : m_appData(nullptr)
1012 {
1013 }
1014 ~KAboutDataRegistry()
1015 {
1016 delete m_appData;
1017 }
1018 KAboutDataRegistry(const KAboutDataRegistry &) = delete;
1019 KAboutDataRegistry &operator=(const KAboutDataRegistry &) = delete;
1020
1021 KAboutData *m_appData;
1022};
1023
1024Q_GLOBAL_STATIC(KAboutDataRegistry, s_registry)
1025
1026namespace
1027{
1028void warnIfOutOfSync(const char *aboutDataString, const QString &aboutDataValue, const char *appDataString, const QString &appDataValue)
1029{
1030 if (aboutDataValue != appDataValue) {
1031 qCWarning(KABOUTDATA) << appDataString << appDataValue << "is out-of-sync with" << aboutDataString << aboutDataValue;
1032 }
1033}
1034
1035}
1036
1038{
1040
1041 KAboutData *aboutData = s_registry->m_appData;
1042
1043 // not yet existing
1044 if (!aboutData) {
1045 // init from current Q*Application data
1047 // Unset the default (KDE) bug address, this is likely a third-party app. https://bugs.kde.org/show_bug.cgi?id=473517
1048 aboutData->setBugAddress(QByteArray());
1049 // For applicationDisplayName & desktopFileName, which are only properties of QGuiApplication,
1050 // we have to try to get them via the property system, as the static getter methods are
1051 // part of QtGui only. Disadvantage: requires an app instance.
1052 // Either get all or none of the properties & warn about it
1053 if (app) {
1055 aboutData->setVersion(QCoreApplication::applicationVersion().toUtf8());
1056 aboutData->setDisplayName(app->property("applicationDisplayName").toString());
1057 aboutData->setDesktopFileName(app->property("desktopFileName").toString());
1058 } else {
1059 qCWarning(KABOUTDATA) << "Could not initialize the properties of KAboutData::applicationData by the equivalent properties from Q*Application: no "
1060 "app instance (yet) existing.";
1061 }
1062
1063 s_registry->m_appData = aboutData;
1064 } else {
1065 // check if in-sync with Q*Application metadata, as their setters could have been called
1066 // after the last KAboutData::setApplicationData, with different values
1067 warnIfOutOfSync("KAboutData::applicationData().componentName",
1068 aboutData->componentName(),
1069 "QCoreApplication::applicationName",
1071 warnIfOutOfSync("KAboutData::applicationData().version",
1072 aboutData->version(),
1073 "QCoreApplication::applicationVersion",
1075 warnIfOutOfSync("KAboutData::applicationData().organizationDomain",
1076 aboutData->organizationDomain(),
1077 "QCoreApplication::organizationDomain",
1079 if (app) {
1080 warnIfOutOfSync("KAboutData::applicationData().displayName",
1081 aboutData->displayName(),
1082 "QGuiApplication::applicationDisplayName",
1083 app->property("applicationDisplayName").toString());
1084 warnIfOutOfSync("KAboutData::applicationData().desktopFileName",
1085 aboutData->desktopFileName(),
1086 "QGuiApplication::desktopFileName",
1087 app->property("desktopFileName").toString());
1088 }
1089 }
1090
1091 return *aboutData;
1092}
1093
1095{
1096 if (s_registry->m_appData) {
1097 *s_registry->m_appData = aboutData;
1098 } else {
1099 s_registry->m_appData = new KAboutData(aboutData);
1100 }
1101
1102 // For applicationDisplayName & desktopFileName, which are only properties of QGuiApplication,
1103 // we have to try to set them via the property system, as the static getter methods are
1104 // part of QtGui only. Disadvantage: requires an app instance.
1105 // So set either all or none of the properties & warn about it
1107 if (app) {
1108 app->setApplicationVersion(aboutData.version());
1109 app->setApplicationName(aboutData.componentName());
1110 app->setOrganizationDomain(aboutData.organizationDomain());
1111 app->setProperty("applicationDisplayName", aboutData.displayName());
1112 app->setProperty("desktopFileName", aboutData.desktopFileName());
1113 } else {
1114 qCWarning(KABOUTDATA) << "Could not initialize the equivalent properties of Q*Application: no instance (yet) existing.";
1115 }
1116
1117 // KF6: Rethink the current relation between KAboutData::applicationData and the Q*Application metadata
1118 // Always overwriting the Q*Application metadata here, but not updating back the KAboutData
1119 // in applicationData() is unbalanced and can result in out-of-sync data if the Q*Application
1120 // setters have been called meanwhile
1121 // Options are to remove the overlapping properties of KAboutData for cleancode, or making the
1122 // overlapping properties official shadow properties of their Q*Application countparts, though
1123 // that increases behavioural complexity a little.
1124}
1125
1126// only for KCrash (no memory allocation allowed)
1127const KAboutData *KAboutData::applicationDataPointer()
1128{
1129 if (s_registry.exists()) {
1130 return s_registry->m_appData;
1131 }
1132 return nullptr;
1133}
1134
1136{
1137 if (!d->_shortDescription.isEmpty()) {
1138 parser->setApplicationDescription(d->_shortDescription);
1139 }
1140
1141 parser->addHelpOption();
1142
1144 if (app && !app->applicationVersion().isEmpty()) {
1145 parser->addVersionOption();
1146 }
1147
1148 return parser->addOption(QCommandLineOption(QStringLiteral("author"), QCoreApplication::translate("KAboutData CLI", "Show author information.")))
1149 && parser->addOption(QCommandLineOption(QStringLiteral("license"), QCoreApplication::translate("KAboutData CLI", "Show license information.")))
1150 && parser->addOption(QCommandLineOption(QStringLiteral("desktopfile"),
1151 QCoreApplication::translate("KAboutData CLI", "The base file name of the desktop entry for this application."),
1152 QCoreApplication::translate("KAboutData CLI", "file name")));
1153}
1154
1156{
1157 bool foundArgument = false;
1158 if (parser->isSet(QStringLiteral("author"))) {
1159 foundArgument = true;
1160 if (d->_authorList.isEmpty()) {
1161 printf("%s\n",
1162 qPrintable(QCoreApplication::translate("KAboutData CLI", "This application was written by somebody who wants to remain anonymous.")));
1163 } else {
1164 printf("%s\n", qPrintable(QCoreApplication::translate("KAboutData CLI", "%1 was written by:").arg(qAppName())));
1165 for (const KAboutPerson &person : std::as_const(d->_authorList)) {
1166 QString authorData = QLatin1String(" ") + person.name();
1167 if (!person.emailAddress().isEmpty()) {
1168 authorData.append(QLatin1String(" <") + person.emailAddress() + QLatin1Char('>'));
1169 }
1170 printf("%s\n", qPrintable(authorData));
1171 }
1172 }
1173 if (!customAuthorTextEnabled()) {
1174 if (bugAddress() == QLatin1String("submit@bugs.kde.org")) {
1175 printf("%s\n", qPrintable(QCoreApplication::translate("KAboutData CLI", "Please use https://bugs.kde.org to report bugs.")));
1176 } else if (!bugAddress().isEmpty()) {
1177 printf("%s\n", qPrintable(QCoreApplication::translate("KAboutData CLI", "Please report bugs to %1.").arg(bugAddress())));
1178 }
1179 } else {
1180 printf("%s\n", qPrintable(customAuthorPlainText()));
1181 }
1182 } else if (parser->isSet(QStringLiteral("license"))) {
1183 foundArgument = true;
1184 for (const KAboutLicense &license : std::as_const(d->_licenseList)) {
1185 printf("%s\n", qPrintable(license.text()));
1186 }
1187 }
1188
1189 const QString desktopFileName = parser->value(QStringLiteral("desktopfile"));
1190 if (!desktopFileName.isEmpty()) {
1191 d->desktopFileName = desktopFileName;
1192 }
1193
1194 if (foundArgument) {
1195 ::exit(EXIT_SUCCESS);
1196 }
1197}
1198
1199#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(GameStandardAction 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 & 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 Fri Jul 26 2024 11:56:13 by doxygen 1.11.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.