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
29#include <algorithm>
30
31using namespace Qt::StringLiterals;
32
33Q_DECLARE_LOGGING_CATEGORY(KABOUTDATA)
34// logging category for this framework, default: log stuff >= warning
35Q_LOGGING_CATEGORY(KABOUTDATA, "kf.coreaddons.kaboutdata", QtWarningMsg)
36
37class KAboutPersonPrivate : public QSharedData
38{
39public:
40 QString _name;
41 QString _task;
42 QString _emailAddress;
43 QString _webAddress;
44 QUrl _avatarUrl;
45};
46
47KAboutPerson::KAboutPerson(const QString &_name, const QString &_task, const QString &_emailAddress, const QString &_webAddress, const QUrl &avatarUrl)
48 : d(new KAboutPersonPrivate)
49{
50 d->_name = _name;
51 d->_task = _task;
52 d->_emailAddress = _emailAddress;
53 d->_webAddress = _webAddress;
54 d->_avatarUrl = avatarUrl;
55}
56
57KAboutPerson::KAboutPerson(const QString &_name, const QString &_email, bool)
58 : d(new KAboutPersonPrivate)
59{
60 d->_name = _name;
61 d->_emailAddress = _email;
62}
63
64KAboutPerson::KAboutPerson(const KAboutPerson &other) = default;
65
66KAboutPerson::~KAboutPerson() = default;
67
68QString KAboutPerson::name() const
69{
70 return d->_name;
71}
72
73QString KAboutPerson::task() const
74{
75 return d->_task;
76}
77
78QString KAboutPerson::emailAddress() const
79{
80 return d->_emailAddress;
81}
82
83QString KAboutPerson::webAddress() const
84{
85 return d->_webAddress;
86}
87
88QUrl KAboutPerson::avatarUrl() const
89{
90 return d->_avatarUrl;
91}
92
93KAboutPerson &KAboutPerson::operator=(const KAboutPerson &other) = default;
94
95KAboutPerson KAboutPerson::fromJSON(const QJsonObject &obj)
96{
97 const QString name = KJsonUtils::readTranslatedString(jo: obj, QStringLiteral("Name"));
98 const QString task = KJsonUtils::readTranslatedString(jo: obj, QStringLiteral("Task"));
99 const QString email = obj.value(key: QLatin1String("Email")).toString();
100 const QString website = obj.value(key: QLatin1String("Website")).toString();
101 const QUrl avatarUrl = obj.value(key: QLatin1String("AvatarUrl")).toVariant().toUrl();
102 return KAboutPerson(name, task, email, website, avatarUrl);
103}
104
105class KAboutLicensePrivate : public QSharedData
106{
107public:
108 KAboutLicensePrivate(KAboutLicense::LicenseKey licenseType, KAboutLicense::VersionRestriction versionRestriction, const KAboutData *aboutData);
109 KAboutLicensePrivate(const KAboutLicensePrivate &other);
110
111 QString spdxID() const;
112
113 KAboutLicense::LicenseKey _licenseKey;
114 QString _licenseText;
115 QString _pathToLicenseTextFile;
116 KAboutLicense::VersionRestriction _versionRestriction;
117 // needed for access to the possibly changing copyrightStatement()
118 const KAboutData *_aboutData;
119};
120
121KAboutLicensePrivate::KAboutLicensePrivate(KAboutLicense::LicenseKey licenseType,
122 KAboutLicense::VersionRestriction versionRestriction,
123 const KAboutData *aboutData)
124 : QSharedData()
125 , _licenseKey(licenseType)
126 , _versionRestriction(versionRestriction)
127 , _aboutData(aboutData)
128{
129}
130
131KAboutLicensePrivate::KAboutLicensePrivate(const KAboutLicensePrivate &other)
132 : QSharedData(other)
133 , _licenseKey(other._licenseKey)
134 , _licenseText(other._licenseText)
135 , _pathToLicenseTextFile(other._pathToLicenseTextFile)
136 , _versionRestriction(other._versionRestriction)
137 , _aboutData(other._aboutData)
138{
139}
140
141QString KAboutLicensePrivate::spdxID() const
142{
143 switch (_licenseKey) {
144 case KAboutLicense::GPL_V2:
145 return QStringLiteral("GPL-2.0");
146 case KAboutLicense::LGPL_V2:
147 return QStringLiteral("LGPL-2.0");
148 case KAboutLicense::BSD_2_Clause:
149 return QStringLiteral("BSD-2-Clause");
150 case KAboutLicense::BSD_3_Clause:
151 return QStringLiteral("BSD-3-Clause");
152 case KAboutLicense::Artistic:
153 return QStringLiteral("Artistic-1.0");
154 case KAboutLicense::GPL_V3:
155 return QStringLiteral("GPL-3.0");
156 case KAboutLicense::LGPL_V3:
157 return QStringLiteral("LGPL-3.0");
158 case KAboutLicense::LGPL_V2_1:
159 return QStringLiteral("LGPL-2.1");
160 case KAboutLicense::MIT:
161 return QStringLiteral("MIT");
162 case KAboutLicense::ODbL_V1:
163 return QStringLiteral("ODbL-1.0");
164 case KAboutLicense::Apache_V2:
165 return QStringLiteral("Apache-2.0");
166 case KAboutLicense::FTL:
167 return QStringLiteral("FTL");
168 case KAboutLicense::BSL_V1:
169 return QStringLiteral("BSL-1.0");
170 case KAboutLicense::CC0_V1:
171 return QStringLiteral("CC0-1.0");
172 case KAboutLicense::MPL_V2:
173 return QStringLiteral("MPL-2.0");
174 case KAboutLicense::Custom:
175 case KAboutLicense::File:
176 case KAboutLicense::Unknown:
177 return QString();
178 }
179 return QString();
180}
181
182KAboutLicense::KAboutLicense()
183 : d(new KAboutLicensePrivate(Unknown, {}, nullptr))
184{
185}
186
187KAboutLicense::KAboutLicense(LicenseKey licenseType, VersionRestriction versionRestriction, const KAboutData *aboutData)
188 : d(new KAboutLicensePrivate(licenseType, versionRestriction, aboutData))
189{
190}
191
192KAboutLicense::KAboutLicense(LicenseKey licenseType, const KAboutData *aboutData)
193 : d(new KAboutLicensePrivate(licenseType, OnlyThisVersion, aboutData))
194{
195}
196
197KAboutLicense::KAboutLicense(const KAboutData *aboutData)
198 : d(new KAboutLicensePrivate(Unknown, OnlyThisVersion, aboutData))
199{
200}
201
202KAboutLicense::KAboutLicense(const KAboutLicense &other)
203 : d(other.d)
204{
205}
206
207KAboutLicense::~KAboutLicense()
208{
209}
210
211void KAboutLicense::setLicenseFromPath(const QString &pathToFile)
212{
213 d->_licenseKey = KAboutLicense::File;
214 d->_pathToLicenseTextFile = pathToFile;
215}
216
217void KAboutLicense::setLicenseFromText(const QString &licenseText)
218{
219 d->_licenseKey = KAboutLicense::Custom;
220 d->_licenseText = licenseText;
221}
222
223QString KAboutLicense::text() const
224{
225 QString result;
226
227 const QString lineFeed = QStringLiteral("\n\n");
228
229 if (d->_aboutData && !d->_aboutData->copyrightStatement().isEmpty()
230 && (d->_licenseKey == KAboutLicense::BSD_2_Clause || d->_licenseKey == KAboutLicense::BSD_3_Clause || d->_licenseKey == KAboutLicense::MIT
231 || d->_licenseKey == KAboutLicense::Artistic)) {
232 result = d->_aboutData->copyrightStatement() + lineFeed;
233 }
234
235 bool knownLicense = false;
236 QString pathToFile; // rel path if known license
237 switch (d->_licenseKey) {
238 case KAboutLicense::File:
239 pathToFile = d->_pathToLicenseTextFile;
240 break;
241 case KAboutLicense::GPL_V2:
242 knownLicense = true;
243 pathToFile = QStringLiteral("GPL_V2");
244 break;
245 case KAboutLicense::LGPL_V2:
246 knownLicense = true;
247 pathToFile = QStringLiteral("LGPL_V2");
248 break;
249 case KAboutLicense::BSD_2_Clause:
250 knownLicense = true;
251 pathToFile = QStringLiteral("BSD");
252 break;
253 case KAboutLicense::Artistic:
254 knownLicense = true;
255 pathToFile = QStringLiteral("ARTISTIC");
256 break;
257 case KAboutLicense::GPL_V3:
258 knownLicense = true;
259 pathToFile = QStringLiteral("GPL_V3");
260 break;
261 case KAboutLicense::LGPL_V3:
262 knownLicense = true;
263 pathToFile = QStringLiteral("LGPL_V3");
264 break;
265 case KAboutLicense::LGPL_V2_1:
266 knownLicense = true;
267 pathToFile = QStringLiteral("LGPL_V21");
268 break;
269 case KAboutLicense::MIT:
270 knownLicense = true;
271 pathToFile = QStringLiteral("MIT");
272 break;
273 case KAboutLicense::ODbL_V1:
274 case KAboutLicense::Apache_V2:
275 case KAboutLicense::FTL:
276 case KAboutLicense::BSL_V1:
277 case KAboutLicense::BSD_3_Clause:
278 case KAboutLicense::CC0_V1:
279 case KAboutLicense::MPL_V2:
280 result +=
281 QCoreApplication::translate(context: "KAboutLicense", key: "<p>This program is distributed under the terms of the %1.</p>").arg(a: name(formatName: KAboutLicense::ShortName))
282 + QCoreApplication::translate(context: "KAboutLicense",
283 key: "<p>You can find the full terms on <a href=\"https://spdx.org/licenses/%1.html\">the SPDX website</a>.</p>")
284 .arg(a: d->spdxID());
285 break;
286 case KAboutLicense::Custom:
287 if (!d->_licenseText.isEmpty()) {
288 result = d->_licenseText;
289 break;
290 }
291 Q_FALLTHROUGH();
292 // fall through
293 default:
294 result += QCoreApplication::translate(context: "KAboutLicense",
295 key: "No licensing terms for this program have been specified.\n"
296 "Please check the documentation or the source for any\n"
297 "licensing terms.\n");
298 }
299
300 if (knownLicense) {
301 pathToFile = QStringLiteral(":/org.kde.kcoreaddons/licenses/") + pathToFile;
302 result += QCoreApplication::translate(context: "KAboutLicense", key: "This program is distributed under the terms of the %1.").arg(a: name(formatName: KAboutLicense::ShortName));
303 if (!pathToFile.isEmpty()) {
304 result += lineFeed;
305 }
306 }
307
308 if (!pathToFile.isEmpty()) {
309 QFile file(pathToFile);
310 if (file.open(flags: QIODevice::ReadOnly)) {
311 QTextStream str(&file);
312 result += str.readAll();
313 }
314 }
315
316 return result;
317}
318
319QString KAboutLicense::spdx() const
320{
321 // SPDX licenses are comprised of an identifier (e.g. GPL-2.0), an optional + to denote 'or
322 // later versions' and optional ' WITH $exception' to denote standardized exceptions from the
323 // core license. As we do not offer exceptions we effectively only return GPL-2.0 or GPL-2.0+,
324 // this may change in the future. To that end the documentation makes no assertions about the
325 // actual content of the SPDX license expression we return.
326 // Expressions can in theory also contain AND, OR and () to build constructs involving more than
327 // one license. As this is outside the scope of a single license object we'll ignore this here
328 // for now.
329 // The expectation is that the return value is only run through spec-compliant parsers, so this
330 // can potentially be changed.
331
332 auto id = d->spdxID();
333 if (id.isNull()) { // Guard against potential future changes which would allow 'Foo+' as input.
334 return id;
335 }
336 return d->_versionRestriction == OrLaterVersions ? id.append(c: QLatin1Char('+')) : id;
337}
338
339QString KAboutLicense::name(KAboutLicense::NameFormat formatName) const
340{
341 QString licenseShort;
342 QString licenseFull;
343
344 switch (d->_licenseKey) {
345 case KAboutLicense::GPL_V2:
346 licenseShort = QCoreApplication::translate(context: "KAboutLicense", key: "GPL v2", disambiguation: "@item license (short name)");
347 licenseFull = QCoreApplication::translate(context: "KAboutLicense", key: "GNU General Public License Version 2", disambiguation: "@item license");
348 break;
349 case KAboutLicense::LGPL_V2:
350 licenseShort = QCoreApplication::translate(context: "KAboutLicense", key: "LGPL v2", disambiguation: "@item license (short name)");
351 licenseFull = QCoreApplication::translate(context: "KAboutLicense", key: "GNU Lesser General Public License Version 2", disambiguation: "@item license");
352 break;
353 case KAboutLicense::BSD_2_Clause:
354 licenseShort = QCoreApplication::translate(context: "KAboutLicense", key: "BSD License", disambiguation: "@item license (short name)");
355 licenseFull = QCoreApplication::translate(context: "KAboutLicense", key: "BSD License", disambiguation: "@item license");
356 break;
357 case KAboutLicense::Artistic:
358 licenseShort = QCoreApplication::translate(context: "KAboutLicense", key: "Artistic License", disambiguation: "@item license (short name)");
359 licenseFull = QCoreApplication::translate(context: "KAboutLicense", key: "Artistic License", disambiguation: "@item license");
360 break;
361 case KAboutLicense::GPL_V3:
362 licenseShort = QCoreApplication::translate(context: "KAboutLicense", key: "GPL v3", disambiguation: "@item license (short name)");
363 licenseFull = QCoreApplication::translate(context: "KAboutLicense", key: "GNU General Public License Version 3", disambiguation: "@item license");
364 break;
365 case KAboutLicense::LGPL_V3:
366 licenseShort = QCoreApplication::translate(context: "KAboutLicense", key: "LGPL v3", disambiguation: "@item license (short name)");
367 licenseFull = QCoreApplication::translate(context: "KAboutLicense", key: "GNU Lesser General Public License Version 3", disambiguation: "@item license");
368 break;
369 case KAboutLicense::LGPL_V2_1:
370 licenseShort = QCoreApplication::translate(context: "KAboutLicense", key: "LGPL v2.1", disambiguation: "@item license (short name)");
371 licenseFull = QCoreApplication::translate(context: "KAboutLicense", key: "GNU Lesser General Public License Version 2.1", disambiguation: "@item license");
372 break;
373 case KAboutLicense::MIT:
374 licenseShort = QCoreApplication::translate(context: "KAboutLicense", key: "MIT License", disambiguation: "@item license (short name)");
375 licenseFull = QCoreApplication::translate(context: "KAboutLicense", key: "MIT License", disambiguation: "@item license");
376 break;
377 case KAboutLicense::CC0_V1:
378 licenseShort = QCoreApplication::translate(context: "KAboutLicense", key: "CC0", disambiguation: "@item license (short name)");
379 licenseFull = QCoreApplication::translate(context: "KAboutLicense", key: "Creative Commons Zero", disambiguation: "@item license");
380 break;
381 case KAboutLicense::ODbL_V1:
382 licenseShort = QCoreApplication::translate(context: "KAboutLicense", key: "ODbL v1.0", disambiguation: "@item license (short name)");
383 licenseFull = QCoreApplication::translate(context: "KAboutLicense", key: "Open Data Commons Open Database License v1.0", disambiguation: "@item license");
384 break;
385 case KAboutLicense::Apache_V2:
386 licenseShort = QCoreApplication::translate(context: "KAboutLicense", key: "Apache 2.0", disambiguation: "@item license (short name)");
387 licenseFull = QCoreApplication::translate(context: "KAboutLicense", key: "Apache License 2.0", disambiguation: "@item license");
388 break;
389 case KAboutLicense::FTL:
390 licenseShort = QCoreApplication::translate(context: "KAboutLicense", key: "FTL", disambiguation: "@item license (short name)");
391 licenseFull = QCoreApplication::translate(context: "KAboutLicense", key: "Freetype Project License", disambiguation: "@item license");
392 break;
393 case KAboutLicense::BSL_V1:
394 licenseShort = QCoreApplication::translate(context: "KAboutLicense", key: "Boost License", disambiguation: "@item license (short name)");
395 licenseFull = QCoreApplication::translate(context: "KAboutLicense", key: "Boost Software License 1.0", disambiguation: "@item license");
396 break;
397 case KAboutLicense::BSD_3_Clause:
398 licenseShort = QCoreApplication::translate(context: "KAboutLicense", key: "BSD-3-Clause", disambiguation: "@item license (short name)");
399 licenseFull = QCoreApplication::translate(context: "KAboutLicense", key: "BSD 3-Clause \"New\" or \"Revised\" License", disambiguation: "@item license");
400 break;
401 case KAboutLicense::MPL_V2:
402 licenseShort = QCoreApplication::translate(context: "KAboutLicense", key: "MPL 2.0", disambiguation: "@item license (short name)");
403 licenseFull = QCoreApplication::translate(context: "KAboutLicense", key: "Mozilla Public License 2.0", disambiguation: "@item license");
404 break;
405 case KAboutLicense::Custom:
406 case KAboutLicense::File:
407 licenseShort = licenseFull = QCoreApplication::translate(context: "KAboutLicense", key: "Custom", disambiguation: "@item license");
408 break;
409 default:
410 licenseShort = licenseFull = QCoreApplication::translate(context: "KAboutLicense", key: "Not specified", disambiguation: "@item license");
411 }
412
413 const QString result = (formatName == KAboutLicense::ShortName) ? licenseShort : (formatName == KAboutLicense::FullName) ? licenseFull : QString();
414
415 return result;
416}
417
418KAboutLicense &KAboutLicense::operator=(const KAboutLicense &other)
419{
420 d = other.d;
421 return *this;
422}
423
424KAboutLicense::LicenseKey KAboutLicense::key() const
425{
426 return d->_licenseKey;
427}
428
429KAboutLicense KAboutLicense::byKeyword(const QString &rawKeyword)
430{
431 // Setup keyword->enum dictionary on first call.
432 // Use normalized keywords, by the algorithm below.
433 static const QHash<QByteArray, KAboutLicense::LicenseKey> licenseDict{
434 {"gpl", KAboutLicense::GPL}, {"gplv2", KAboutLicense::GPL_V2},
435 {"gplv2+", KAboutLicense::GPL_V2}, {"gpl20", KAboutLicense::GPL_V2},
436 {"gpl20+", KAboutLicense::GPL_V2}, {"lgpl", KAboutLicense::LGPL},
437 {"lgplv2", KAboutLicense::LGPL_V2}, {"lgplv2+", KAboutLicense::LGPL_V2},
438 {"lgpl20", KAboutLicense::LGPL_V2}, {"lgpl20+", KAboutLicense::LGPL_V2},
439 {"bsd", KAboutLicense::BSD_2_Clause}, {"bsd2clause", KAboutLicense::BSD_2_Clause},
440 {"apache", KAboutLicense::Apache_V2}, {"bsd3clause", KAboutLicense::BSD_3_Clause},
441 {"artistic", KAboutLicense::Artistic}, {"artistic10", KAboutLicense::Artistic},
442 {"gplv3", KAboutLicense::GPL_V3}, {"gplv3+", KAboutLicense::GPL_V3},
443 {"gpl30", KAboutLicense::GPL_V3}, {"gpl30+", KAboutLicense::GPL_V3},
444 {"lgplv3", KAboutLicense::LGPL_V3}, {"lgplv3+", KAboutLicense::LGPL_V3},
445 {"lgpl30", KAboutLicense::LGPL_V3}, {"lgpl30+", KAboutLicense::LGPL_V3},
446 {"lgplv21", KAboutLicense::LGPL_V2_1}, {"lgplv21+", KAboutLicense::LGPL_V2_1},
447 {"lgpl21", KAboutLicense::LGPL_V2_1}, {"lgpl21+", KAboutLicense::LGPL_V2_1},
448 {"mit", KAboutLicense::MIT},
449 };
450
451 // Normalize keyword.
452 QString keyword = rawKeyword;
453 keyword = keyword.toLower();
454 keyword.replace(before: QLatin1StringView("-or-later"), after: QLatin1StringView("+"));
455 keyword.remove(c: QLatin1Char(' '));
456 keyword.remove(c: QLatin1Char('.'));
457 keyword.remove(c: QLatin1Char('-'));
458
459 LicenseKey license = licenseDict.value(key: keyword.toLatin1(), defaultValue: KAboutLicense::Custom);
460 auto restriction = keyword.endsWith(c: QLatin1Char('+')) ? OrLaterVersions : OnlyThisVersion;
461 return KAboutLicense(license, restriction, nullptr);
462}
463
464class KAboutComponentPrivate : public QSharedData
465{
466public:
467 QString _name;
468 QString _description;
469 QString _version;
470 QString _webAddress;
471 KAboutLicense _license;
472};
473
474KAboutComponent::KAboutComponent(const QString &_name,
475 const QString &_description,
476 const QString &_version,
477 const QString &_webAddress,
478 enum KAboutLicense::LicenseKey licenseType)
479 : d(new KAboutComponentPrivate)
480{
481 d->_name = _name;
482 d->_description = _description;
483 d->_version = _version;
484 d->_webAddress = _webAddress;
485 d->_license = KAboutLicense(licenseType, nullptr);
486}
487
488KAboutComponent::KAboutComponent(const QString &_name,
489 const QString &_description,
490 const QString &_version,
491 const QString &_webAddress,
492 const QString &pathToLicenseFile)
493 : d(new KAboutComponentPrivate)
494{
495 d->_name = _name;
496 d->_description = _description;
497 d->_version = _version;
498 d->_webAddress = _webAddress;
499 d->_license = KAboutLicense();
500 d->_license.setLicenseFromPath(pathToLicenseFile);
501}
502
503KAboutComponent::KAboutComponent(const KAboutComponent &other) = default;
504
505KAboutComponent::~KAboutComponent() = default;
506
507QString KAboutComponent::name() const
508{
509 return d->_name;
510}
511
512QString KAboutComponent::description() const
513{
514 return d->_description;
515}
516
517QString KAboutComponent::version() const
518{
519 return d->_version;
520}
521
522QString KAboutComponent::webAddress() const
523{
524 return d->_webAddress;
525}
526
527KAboutLicense KAboutComponent::license() const
528{
529 return d->_license;
530}
531
532KAboutComponent &KAboutComponent::operator=(const KAboutComponent &other) = default;
533
534class KAboutDataPrivate
535{
536public:
537 KAboutDataPrivate()
538 : customAuthorTextEnabled(false)
539 {
540 }
541 QString _componentName;
542 QString _displayName;
543 QString _shortDescription;
544 QString _copyrightStatement;
545 QString _otherText;
546 QString _homepageAddress;
547 QList<KAboutPerson> _authorList;
548 QList<KAboutPerson> _creditList;
549 QList<KAboutPerson> _translatorList;
550 QList<KAboutComponent> _componentList;
551 QList<KAboutLicense> _licenseList;
552 QVariant ;
553 QString customAuthorPlainText, customAuthorRichText;
554 bool customAuthorTextEnabled;
555
556 QString organizationDomain;
557 QString desktopFileName;
558
559 // Everything dr.konqi needs, we store as utf-8, so we
560 // can just give it a pointer, w/o any allocations.
561 QByteArray _internalProgramName;
562 QByteArray _version;
563 QByteArray _bugAddress;
564 QByteArray productName;
565
566 static QList<KAboutPerson> parseTranslators(const QString &translatorName, const QString &translatorEmail);
567};
568
569KAboutData::KAboutData(const QString &_componentName,
570 const QString &_displayName,
571 const QString &_version,
572 const QString &_shortDescription,
573 enum KAboutLicense::LicenseKey licenseType,
574 const QString &_copyrightStatement,
575 const QString &text,
576 const QString &homePageAddress,
577 const QString &bugAddress)
578 : d(new KAboutDataPrivate)
579{
580 d->_componentName = _componentName;
581 int p = d->_componentName.indexOf(ch: QLatin1Char('/'));
582 if (p >= 0) {
583 d->_componentName = d->_componentName.mid(position: p + 1);
584 }
585
586 d->_displayName = _displayName;
587 if (!d->_displayName.isEmpty()) { // KComponentData("klauncher") gives empty program name
588 d->_internalProgramName = _displayName.toUtf8();
589 }
590 d->_version = _version.toUtf8();
591 d->_shortDescription = _shortDescription;
592 d->_licenseList.append(t: KAboutLicense(licenseType, this));
593 d->_copyrightStatement = _copyrightStatement;
594 d->_otherText = text;
595 d->_homepageAddress = homePageAddress;
596 d->_bugAddress = bugAddress.toUtf8();
597
598 QUrl homePageUrl(homePageAddress);
599 if (!homePageUrl.isValid() || homePageUrl.scheme().isEmpty()) {
600 // Default domain if nothing else is better
601 homePageUrl.setUrl(QStringLiteral("https://kde.org/"));
602 }
603
604 const QChar dotChar(QLatin1Char('.'));
605 QStringList hostComponents = homePageUrl.host().split(sep: dotChar);
606
607 // Remove leading component unless 2 (or less) components are present
608 if (hostComponents.size() > 2) {
609 hostComponents.removeFirst();
610 }
611
612 d->organizationDomain = hostComponents.join(sep: dotChar);
613
614 // KF6: do not set a default desktopFileName value here, but remove this code and leave it empty
615 // see KAboutData::desktopFileName() for details
616
617 // desktop file name is reverse domain name
618 std::reverse(first: hostComponents.begin(), last: hostComponents.end());
619 hostComponents.append(t: _componentName);
620
621 d->desktopFileName = hostComponents.join(sep: dotChar);
622}
623
624KAboutData::KAboutData(const QString &_componentName, const QString &_displayName, const QString &_version)
625 : d(new KAboutDataPrivate)
626{
627 d->_componentName = _componentName;
628 int p = d->_componentName.indexOf(ch: QLatin1Char('/'));
629 if (p >= 0) {
630 d->_componentName = d->_componentName.mid(position: p + 1);
631 }
632
633 d->_displayName = _displayName;
634 if (!d->_displayName.isEmpty()) { // KComponentData("klauncher") gives empty program name
635 d->_internalProgramName = _displayName.toUtf8();
636 }
637 d->_version = _version.toUtf8();
638
639 // match behaviour of other constructors
640 d->_licenseList.append(t: KAboutLicense(KAboutLicense::Unknown, this));
641 d->_bugAddress = "submit@bugs.kde.org";
642 d->organizationDomain = QStringLiteral("kde.org");
643 // KF6: do not set a default desktopFileName value here, but remove this code and leave it empty
644 // see KAboutData::desktopFileName() for details
645 d->desktopFileName = QLatin1String("org.kde.") + d->_componentName;
646}
647
648KAboutData::~KAboutData() = default;
649
650KAboutData::KAboutData(const KAboutData &other)
651 : d(new KAboutDataPrivate)
652{
653 *d = *other.d;
654 for (KAboutLicense &al : d->_licenseList) {
655 al.d.detach();
656 al.d->_aboutData = this;
657 }
658}
659
660KAboutData &KAboutData::operator=(const KAboutData &other)
661{
662 if (this != &other) {
663 *d = *other.d;
664 for (KAboutLicense &al : d->_licenseList) {
665 al.d.detach();
666 al.d->_aboutData = this;
667 }
668 }
669 return *this;
670}
671
672KAboutData &KAboutData::addAuthor(const QString &name, const QString &task, const QString &emailAddress, const QString &webAddress, const QUrl &avatarUrl)
673{
674 d->_authorList.append(t: KAboutPerson(name, task, emailAddress, webAddress, avatarUrl));
675 return *this;
676}
677
678KAboutData &KAboutData::addAuthor(const KAboutPerson &author)
679{
680 d->_authorList.append(t: author);
681 return *this;
682}
683
684KAboutData &KAboutData::addCredit(const KAboutPerson &person)
685{
686 d->_creditList.append(t: person);
687 return *this;
688}
689
690KAboutData &KAboutData::addCredit(const QString &name, const QString &task, const QString &emailAddress, const QString &webAddress, const QUrl &avatarUrl)
691{
692 d->_creditList.append(t: KAboutPerson(name, task, emailAddress, webAddress, avatarUrl));
693 return *this;
694}
695
696KAboutData &KAboutData::setTranslator(const QString &name, const QString &emailAddress)
697{
698 d->_translatorList = KAboutDataPrivate::parseTranslators(translatorName: name, translatorEmail: emailAddress);
699 return *this;
700}
701
702KAboutData &KAboutData::addComponent(const KAboutComponent &component)
703{
704 d->_componentList.append(t: component);
705 return *this;
706}
707
708KAboutData &KAboutData::addComponent(const QString &name,
709 const QString &description,
710 const QString &version,
711 const QString &webAddress,
712 KAboutLicense::LicenseKey licenseKey)
713{
714 d->_componentList.append(t: KAboutComponent(name, description, version, webAddress, licenseKey));
715 return *this;
716}
717
718KAboutData &
719KAboutData::addComponent(const QString &name, const QString &description, const QString &version, const QString &webAddress, const QString &pathToLicenseFile)
720{
721 d->_componentList.append(t: KAboutComponent(name, description, version, webAddress, pathToLicenseFile));
722 return *this;
723}
724
725KAboutData &KAboutData::setLicenseText(const QString &licenseText)
726{
727 d->_licenseList[0] = KAboutLicense(this);
728 d->_licenseList[0].setLicenseFromText(licenseText);
729 return *this;
730}
731
732KAboutData &KAboutData::addLicenseText(const QString &licenseText)
733{
734 // if the default license is unknown, overwrite instead of append
735 KAboutLicense &firstLicense = d->_licenseList[0];
736 KAboutLicense newLicense(this);
737 newLicense.setLicenseFromText(licenseText);
738 if (d->_licenseList.count() == 1 && firstLicense.d->_licenseKey == KAboutLicense::Unknown) {
739 firstLicense = newLicense;
740 } else {
741 d->_licenseList.append(t: newLicense);
742 }
743
744 return *this;
745}
746
747KAboutData &KAboutData::setLicenseTextFile(const QString &pathToFile)
748{
749 d->_licenseList[0] = KAboutLicense(this);
750 d->_licenseList[0].setLicenseFromPath(pathToFile);
751 return *this;
752}
753
754KAboutData &KAboutData::addLicenseTextFile(const QString &pathToFile)
755{
756 // if the default license is unknown, overwrite instead of append
757 KAboutLicense &firstLicense = d->_licenseList[0];
758 KAboutLicense newLicense(this);
759 newLicense.setLicenseFromPath(pathToFile);
760 if (d->_licenseList.count() == 1 && firstLicense.d->_licenseKey == KAboutLicense::Unknown) {
761 firstLicense = newLicense;
762 } else {
763 d->_licenseList.append(t: newLicense);
764 }
765 return *this;
766}
767
768KAboutData &KAboutData::setComponentName(const QString &componentName)
769{
770 d->_componentName = componentName;
771 return *this;
772}
773
774KAboutData &KAboutData::setDisplayName(const QString &_displayName)
775{
776 d->_displayName = _displayName;
777 d->_internalProgramName = _displayName.toUtf8();
778 return *this;
779}
780
781KAboutData &KAboutData::setVersion(const QByteArray &_version)
782{
783 d->_version = _version;
784 return *this;
785}
786
787KAboutData &KAboutData::setShortDescription(const QString &_shortDescription)
788{
789 d->_shortDescription = _shortDescription;
790 return *this;
791}
792
793KAboutData &KAboutData::setLicense(KAboutLicense::LicenseKey licenseKey)
794{
795 return setLicense(licenseKey, versionRestriction: KAboutLicense::OnlyThisVersion);
796}
797
798KAboutData &KAboutData::setLicense(KAboutLicense::LicenseKey licenseKey, KAboutLicense::VersionRestriction versionRestriction)
799{
800 d->_licenseList[0] = KAboutLicense(licenseKey, versionRestriction, this);
801 return *this;
802}
803
804KAboutData &KAboutData::addLicense(KAboutLicense::LicenseKey licenseKey)
805{
806 return addLicense(licenseKey, versionRestriction: KAboutLicense::OnlyThisVersion);
807}
808
809KAboutData &KAboutData::addLicense(KAboutLicense::LicenseKey licenseKey, KAboutLicense::VersionRestriction versionRestriction)
810{
811 // if the default license is unknown, overwrite instead of append
812 KAboutLicense &firstLicense = d->_licenseList[0];
813 if (d->_licenseList.count() == 1 && firstLicense.d->_licenseKey == KAboutLicense::Unknown) {
814 firstLicense = KAboutLicense(licenseKey, versionRestriction, this);
815 } else {
816 d->_licenseList.append(t: KAboutLicense(licenseKey, versionRestriction, this));
817 }
818 return *this;
819}
820
821KAboutData &KAboutData::setCopyrightStatement(const QString &_copyrightStatement)
822{
823 d->_copyrightStatement = _copyrightStatement;
824 return *this;
825}
826
827KAboutData &KAboutData::setOtherText(const QString &_otherText)
828{
829 d->_otherText = _otherText;
830 return *this;
831}
832
833KAboutData &KAboutData::setHomepage(const QString &homepage)
834{
835 d->_homepageAddress = homepage;
836 return *this;
837}
838
839KAboutData &KAboutData::setBugAddress(const QByteArray &_bugAddress)
840{
841 d->_bugAddress = _bugAddress;
842 return *this;
843}
844
845KAboutData &KAboutData::setOrganizationDomain(const QByteArray &domain)
846{
847 d->organizationDomain = QString::fromLatin1(ba: domain.data());
848 return *this;
849}
850
851KAboutData &KAboutData::setProductName(const QByteArray &_productName)
852{
853 d->productName = _productName;
854 return *this;
855}
856
857QString KAboutData::componentName() const
858{
859 return d->_componentName;
860}
861
862QString KAboutData::productName() const
863{
864 if (!d->productName.isEmpty()) {
865 return QString::fromUtf8(ba: d->productName);
866 }
867 return componentName();
868}
869
870const char *KAboutData::internalProductName() const
871{
872 return d->productName.isEmpty() ? nullptr : d->productName.constData();
873}
874
875QString KAboutData::displayName() const
876{
877 return d->_displayName;
878}
879
880/// @internal
881/// Return the program name. It is always pre-allocated.
882/// Needed for KCrash in particular.
883const char *KAboutData::internalProgramName() const
884{
885 return d->_internalProgramName.constData();
886}
887
888QVariant KAboutData::programLogo() const
889{
890 return d->programLogo;
891}
892
893KAboutData &KAboutData::setProgramLogo(const QVariant &image)
894{
895 d->programLogo = image;
896 return *this;
897}
898
899QString KAboutData::version() const
900{
901 return QString::fromUtf8(utf8: d->_version.data());
902}
903
904/// @internal
905/// Return the untranslated and uninterpreted (to UTF8) string
906/// for the version information. Used in particular for KCrash.
907const char *KAboutData::internalVersion() const
908{
909 return d->_version.constData();
910}
911
912QString KAboutData::shortDescription() const
913{
914 return d->_shortDescription;
915}
916
917QString KAboutData::homepage() const
918{
919 return d->_homepageAddress;
920}
921
922QString KAboutData::bugAddress() const
923{
924 return QString::fromUtf8(utf8: d->_bugAddress.constData());
925}
926
927QString KAboutData::organizationDomain() const
928{
929 return d->organizationDomain;
930}
931
932/// @internal
933/// Return the untranslated and uninterpreted (to UTF8) string
934/// for the bug mail address. Used in particular for KCrash.
935const char *KAboutData::internalBugAddress() const
936{
937 if (d->_bugAddress.isEmpty()) {
938 return nullptr;
939 }
940 return d->_bugAddress.constData();
941}
942
943QList<KAboutPerson> KAboutData::authors() const
944{
945 return d->_authorList;
946}
947
948QList<KAboutPerson> KAboutData::credits() const
949{
950 return d->_creditList;
951}
952
953QList<KAboutPerson> KAboutDataPrivate::parseTranslators(const QString &translatorName, const QString &translatorEmail)
954{
955 if (translatorName.isEmpty() || translatorName == QLatin1String("Your names")) {
956 return {};
957 }
958
959 // use list of string views to delay creating new QString instances after the white-space trimming
960 const QList<QStringView> nameList = QStringView(translatorName).split(sep: QLatin1Char(','));
961
962 QList<QStringView> emailList;
963 if (!translatorEmail.isEmpty() && translatorEmail != QLatin1String("Your emails")) {
964 emailList = QStringView(translatorEmail).split(sep: QLatin1Char(','), behavior: Qt::KeepEmptyParts);
965 }
966
967 QList<KAboutPerson> personList;
968 personList.reserve(asize: nameList.size());
969
970 auto eit = emailList.constBegin();
971
972 for (const QStringView &name : nameList) {
973 QStringView email;
974 if (eit != emailList.constEnd()) {
975 email = *eit;
976 ++eit;
977 }
978
979 personList.append(t: KAboutPerson(name.trimmed().toString(), email.trimmed().toString(), true));
980 }
981
982 return personList;
983}
984
985QList<KAboutPerson> KAboutData::translators() const
986{
987 return d->_translatorList;
988}
989
990QString KAboutData::aboutTranslationTeam()
991{
992 return QCoreApplication::translate(context: "KAboutData",
993 key: "<p>KDE is translated into many languages thanks to the work "
994 "of the translation teams all over the world.</p>"
995 "<p>For more information on KDE internationalization "
996 "visit <a href=\"https://l10n.kde.org\">https://l10n.kde.org</a></p>",
997 disambiguation: "replace this with information about your translation team");
998}
999
1000QString KAboutData::otherText() const
1001{
1002 return d->_otherText;
1003}
1004
1005QList<KAboutComponent> KAboutData::components() const
1006{
1007 return d->_componentList;
1008}
1009
1010QList<KAboutLicense> KAboutData::licenses() const
1011{
1012 return d->_licenseList;
1013}
1014
1015QString KAboutData::copyrightStatement() const
1016{
1017 return d->_copyrightStatement;
1018}
1019
1020QString KAboutData::customAuthorPlainText() const
1021{
1022 return d->customAuthorPlainText;
1023}
1024
1025QString KAboutData::customAuthorRichText() const
1026{
1027 return d->customAuthorRichText;
1028}
1029
1030bool KAboutData::customAuthorTextEnabled() const
1031{
1032 return d->customAuthorTextEnabled;
1033}
1034
1035KAboutData &KAboutData::setCustomAuthorText(const QString &plainText, const QString &richText)
1036{
1037 d->customAuthorPlainText = plainText;
1038 d->customAuthorRichText = richText;
1039
1040 d->customAuthorTextEnabled = true;
1041
1042 return *this;
1043}
1044
1045KAboutData &KAboutData::unsetCustomAuthorText()
1046{
1047 d->customAuthorPlainText = QString();
1048 d->customAuthorRichText = QString();
1049
1050 d->customAuthorTextEnabled = false;
1051
1052 return *this;
1053}
1054
1055KAboutData &KAboutData::setDesktopFileName(const QString &desktopFileName)
1056{
1057 d->desktopFileName = desktopFileName;
1058
1059 return *this;
1060}
1061
1062QString KAboutData::desktopFileName() const
1063{
1064 return d->desktopFileName;
1065 // KF6: switch to this code and adapt API dox
1066#if 0
1067 // if desktopFileName has been explicitly set, use that value
1068 if (!d->desktopFileName.isEmpty()) {
1069 return d->desktopFileName;
1070 }
1071
1072 // return a string calculated on-the-fly from the current org domain & component name
1073 const QChar dotChar(QLatin1Char('.'));
1074 QStringList hostComponents = d->organizationDomain.split(dotChar);
1075
1076 // desktop file name is reverse domain name
1077 std::reverse(hostComponents.begin(), hostComponents.end());
1078 hostComponents.append(componentName());
1079
1080 return hostComponents.join(dotChar);
1081#endif
1082}
1083
1084class KAboutDataRegistry
1085{
1086public:
1087 KAboutDataRegistry()
1088 : m_appData(nullptr)
1089 {
1090 }
1091 ~KAboutDataRegistry()
1092 {
1093 delete m_appData;
1094 }
1095 KAboutDataRegistry(const KAboutDataRegistry &) = delete;
1096 KAboutDataRegistry &operator=(const KAboutDataRegistry &) = delete;
1097
1098 KAboutData *m_appData;
1099};
1100
1101Q_GLOBAL_STATIC(KAboutDataRegistry, s_registry)
1102
1103namespace
1104{
1105void warnIfOutOfSync(const char *aboutDataString, const QString &aboutDataValue, const char *appDataString, const QString &appDataValue)
1106{
1107 if (aboutDataValue != appDataValue) {
1108 qCWarning(KABOUTDATA) << appDataString << appDataValue << "is out-of-sync with" << aboutDataString << aboutDataValue;
1109 }
1110}
1111
1112}
1113
1114KAboutData KAboutData::applicationData()
1115{
1116 QCoreApplication *app = QCoreApplication::instance();
1117
1118 KAboutData *aboutData = s_registry->m_appData;
1119
1120 // not yet existing
1121 if (!aboutData) {
1122 // init from current Q*Application data
1123 aboutData = new KAboutData(QCoreApplication::applicationName(), QString(), QString());
1124 // Unset the default (KDE) bug address, this is likely a third-party app. https://bugs.kde.org/show_bug.cgi?id=473517
1125 aboutData->setBugAddress(QByteArray());
1126 // For applicationDisplayName & desktopFileName, which are only properties of QGuiApplication,
1127 // we have to try to get them via the property system, as the static getter methods are
1128 // part of QtGui only. Disadvantage: requires an app instance.
1129 // Either get all or none of the properties & warn about it
1130 if (app) {
1131 aboutData->setOrganizationDomain(QCoreApplication::organizationDomain().toUtf8());
1132 aboutData->setVersion(QCoreApplication::applicationVersion().toUtf8());
1133 aboutData->setDisplayName(app->property(name: "applicationDisplayName").toString());
1134 aboutData->setDesktopFileName(app->property(name: "desktopFileName").toString());
1135 } else {
1136 qCWarning(KABOUTDATA) << "Could not initialize the properties of KAboutData::applicationData by the equivalent properties from Q*Application: no "
1137 "app instance (yet) existing.";
1138 }
1139
1140 s_registry->m_appData = aboutData;
1141 } else {
1142 // check if in-sync with Q*Application metadata, as their setters could have been called
1143 // after the last KAboutData::setApplicationData, with different values
1144 warnIfOutOfSync(aboutDataString: "KAboutData::applicationData().componentName",
1145 aboutDataValue: aboutData->componentName(),
1146 appDataString: "QCoreApplication::applicationName",
1147 appDataValue: QCoreApplication::applicationName());
1148 warnIfOutOfSync(aboutDataString: "KAboutData::applicationData().version",
1149 aboutDataValue: aboutData->version(),
1150 appDataString: "QCoreApplication::applicationVersion",
1151 appDataValue: QCoreApplication::applicationVersion());
1152 warnIfOutOfSync(aboutDataString: "KAboutData::applicationData().organizationDomain",
1153 aboutDataValue: aboutData->organizationDomain(),
1154 appDataString: "QCoreApplication::organizationDomain",
1155 appDataValue: QCoreApplication::organizationDomain());
1156 if (app) {
1157 warnIfOutOfSync(aboutDataString: "KAboutData::applicationData().displayName",
1158 aboutDataValue: aboutData->displayName(),
1159 appDataString: "QGuiApplication::applicationDisplayName",
1160 appDataValue: app->property(name: "applicationDisplayName").toString());
1161 warnIfOutOfSync(aboutDataString: "KAboutData::applicationData().desktopFileName",
1162 aboutDataValue: aboutData->desktopFileName(),
1163 appDataString: "QGuiApplication::desktopFileName",
1164 appDataValue: app->property(name: "desktopFileName").toString());
1165 }
1166 }
1167
1168 return *aboutData;
1169}
1170
1171void KAboutData::setApplicationData(const KAboutData &aboutData)
1172{
1173 if (s_registry->m_appData) {
1174 *s_registry->m_appData = aboutData;
1175 } else {
1176 s_registry->m_appData = new KAboutData(aboutData);
1177 }
1178
1179 // For applicationDisplayName & desktopFileName, which are only properties of QGuiApplication,
1180 // we have to try to set them via the property system, as the static getter methods are
1181 // part of QtGui only. Disadvantage: requires an app instance.
1182 // So set either all or none of the properties & warn about it
1183 QCoreApplication *app = QCoreApplication::instance();
1184 if (app) {
1185 app->setApplicationVersion(aboutData.version());
1186 app->setApplicationName(aboutData.componentName());
1187 app->setOrganizationDomain(aboutData.organizationDomain());
1188 app->setProperty(name: "applicationDisplayName", value: aboutData.displayName());
1189 app->setProperty(name: "desktopFileName", value: aboutData.desktopFileName());
1190 } else {
1191 qCWarning(KABOUTDATA) << "Could not initialize the equivalent properties of Q*Application: no instance (yet) existing.";
1192 }
1193
1194 // KF6: Rethink the current relation between KAboutData::applicationData and the Q*Application metadata
1195 // Always overwriting the Q*Application metadata here, but not updating back the KAboutData
1196 // in applicationData() is unbalanced and can result in out-of-sync data if the Q*Application
1197 // setters have been called meanwhile
1198 // Options are to remove the overlapping properties of KAboutData for cleancode, or making the
1199 // overlapping properties official shadow properties of their Q*Application countparts, though
1200 // that increases behavioural complexity a little.
1201
1202 if (KAboutDataListener::s_theListener) {
1203 Q_EMIT KAboutDataListener::s_theListener->applicationDataChanged();
1204 }
1205}
1206
1207// only for KCrash (no memory allocation allowed)
1208const KAboutData *KAboutData::applicationDataPointer()
1209{
1210 if (s_registry.exists()) {
1211 return s_registry->m_appData;
1212 }
1213 return nullptr;
1214}
1215
1216bool KAboutData::setupCommandLine(QCommandLineParser *parser)
1217{
1218 if (!d->_shortDescription.isEmpty()) {
1219 parser->setApplicationDescription(d->_shortDescription);
1220 }
1221
1222 parser->addHelpOption();
1223
1224 QCoreApplication *app = QCoreApplication::instance();
1225 if (app && !app->applicationVersion().isEmpty()) {
1226 parser->addVersionOption();
1227 }
1228
1229 return parser->addOption(commandLineOption: QCommandLineOption(QStringLiteral("author"), QCoreApplication::translate(context: "KAboutData CLI", key: "Show author information.")))
1230 && parser->addOption(commandLineOption: QCommandLineOption(QStringLiteral("license"), QCoreApplication::translate(context: "KAboutData CLI", key: "Show license information.")))
1231 && parser->addOption(commandLineOption: QCommandLineOption(QStringLiteral("desktopfile"),
1232 QCoreApplication::translate(context: "KAboutData CLI", key: "The base file name of the desktop entry for this application."),
1233 QCoreApplication::translate(context: "KAboutData CLI", key: "file name")));
1234}
1235
1236void KAboutData::processCommandLine(QCommandLineParser *parser)
1237{
1238 bool foundArgument = false;
1239 if (parser->isSet(QStringLiteral("author"))) {
1240 foundArgument = true;
1241 if (d->_authorList.isEmpty()) {
1242 printf(format: "%s\n",
1243 qPrintable(QCoreApplication::translate("KAboutData CLI", "This application was written by somebody who wants to remain anonymous.")));
1244 } else {
1245 printf(format: "%s\n", qPrintable(QCoreApplication::translate("KAboutData CLI", "%1 was written by:").arg(qAppName())));
1246 for (const KAboutPerson &person : std::as_const(t&: d->_authorList)) {
1247 QString authorData = QLatin1String(" ") + person.name();
1248 if (!person.emailAddress().isEmpty()) {
1249 authorData.append(s: QLatin1String(" <") + person.emailAddress() + QLatin1Char('>'));
1250 }
1251 printf(format: "%s\n", qPrintable(authorData));
1252 }
1253 }
1254 if (!customAuthorTextEnabled()) {
1255 if (bugAddress() == QLatin1String("submit@bugs.kde.org")) {
1256 printf(format: "%s\n", qPrintable(QCoreApplication::translate("KAboutData CLI", "Please use https://bugs.kde.org to report bugs.")));
1257 } else if (!bugAddress().isEmpty()) {
1258 printf(format: "%s\n", qPrintable(QCoreApplication::translate("KAboutData CLI", "Please report bugs to %1.").arg(bugAddress())));
1259 }
1260 } else {
1261 printf(format: "%s\n", qPrintable(customAuthorPlainText()));
1262 }
1263 } else if (parser->isSet(QStringLiteral("license"))) {
1264 foundArgument = true;
1265 for (const KAboutLicense &license : std::as_const(t&: d->_licenseList)) {
1266 printf(format: "%s\n", qPrintable(license.text()));
1267 }
1268 }
1269
1270 const QString desktopFileName = parser->value(QStringLiteral("desktopfile"));
1271 if (!desktopFileName.isEmpty()) {
1272 d->desktopFileName = desktopFileName;
1273 }
1274
1275 if (foundArgument) {
1276 ::exit(EXIT_SUCCESS);
1277 }
1278}
1279
1280std::unique_ptr<KAboutDataListener> KAboutDataListener::s_theListener = nullptr;
1281
1282KAboutDataListener *KAboutDataListener::instance()
1283{
1284 if (!s_theListener) {
1285 s_theListener = std::make_unique<KAboutDataListener>();
1286 }
1287 return s_theListener.get();
1288}
1289
1290#include "moc_kaboutdata.cpp"
1291

source code of kcoreaddons/src/lib/kaboutdata.cpp