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

KDE's Doxygen guidelines are available online.