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 | |
32 | Q_DECLARE_LOGGING_CATEGORY(KABOUTDATA) |
33 | // logging category for this framework, default: log stuff >= warning |
34 | Q_LOGGING_CATEGORY(KABOUTDATA, "kf.coreaddons.kaboutdata" , QtWarningMsg) |
35 | |
36 | class KAboutPersonPrivate : public QSharedData |
37 | { |
38 | public: |
39 | QString _name; |
40 | QString _task; |
41 | QString _emailAddress; |
42 | QString _webAddress; |
43 | QUrl _avatarUrl; |
44 | }; |
45 | |
46 | KAboutPerson::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 | |
56 | KAboutPerson::KAboutPerson(const QString &_name, const QString &_email, bool) |
57 | : d(new KAboutPersonPrivate) |
58 | { |
59 | d->_name = _name; |
60 | d->_emailAddress = _email; |
61 | } |
62 | |
63 | KAboutPerson::KAboutPerson(const KAboutPerson &other) = default; |
64 | |
65 | KAboutPerson::~KAboutPerson() = default; |
66 | |
67 | QString KAboutPerson::name() const |
68 | { |
69 | return d->_name; |
70 | } |
71 | |
72 | QString KAboutPerson::task() const |
73 | { |
74 | return d->_task; |
75 | } |
76 | |
77 | QString KAboutPerson::emailAddress() const |
78 | { |
79 | return d->_emailAddress; |
80 | } |
81 | |
82 | QString KAboutPerson::webAddress() const |
83 | { |
84 | return d->_webAddress; |
85 | } |
86 | |
87 | QUrl KAboutPerson::avatarUrl() const |
88 | { |
89 | return d->_avatarUrl; |
90 | } |
91 | |
92 | KAboutPerson &KAboutPerson::operator=(const KAboutPerson &other) = default; |
93 | |
94 | KAboutPerson KAboutPerson::fromJSON(const QJsonObject &obj) |
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 | |
104 | class KAboutLicensePrivate : public QSharedData |
105 | { |
106 | public: |
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 | |
120 | KAboutLicensePrivate::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 | |
130 | KAboutLicensePrivate::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 | |
140 | QString KAboutLicensePrivate::spdxID() const |
141 | { |
142 | switch (_licenseKey) { |
143 | case KAboutLicense::GPL_V2: |
144 | return QStringLiteral("GPL-2.0" ); |
145 | case KAboutLicense::LGPL_V2: |
146 | return QStringLiteral("LGPL-2.0" ); |
147 | case KAboutLicense::BSDL: |
148 | return QStringLiteral("BSD-2-Clause" ); |
149 | case KAboutLicense::Artistic: |
150 | return QStringLiteral("Artistic-1.0" ); |
151 | case KAboutLicense::GPL_V3: |
152 | return QStringLiteral("GPL-3.0" ); |
153 | case KAboutLicense::LGPL_V3: |
154 | return QStringLiteral("LGPL-3.0" ); |
155 | case KAboutLicense::LGPL_V2_1: |
156 | return QStringLiteral("LGPL-2.1" ); |
157 | case KAboutLicense::MIT: |
158 | return QStringLiteral("MIT" ); |
159 | case KAboutLicense::Custom: |
160 | case KAboutLicense::File: |
161 | case KAboutLicense::Unknown: |
162 | return QString(); |
163 | } |
164 | return QString(); |
165 | } |
166 | |
167 | KAboutLicense::KAboutLicense() |
168 | : d(new KAboutLicensePrivate(Unknown, {}, nullptr)) |
169 | { |
170 | } |
171 | |
172 | KAboutLicense::KAboutLicense(LicenseKey licenseType, VersionRestriction versionRestriction, const KAboutData *aboutData) |
173 | : d(new KAboutLicensePrivate(licenseType, versionRestriction, aboutData)) |
174 | { |
175 | } |
176 | |
177 | KAboutLicense::KAboutLicense(LicenseKey licenseType, const KAboutData *aboutData) |
178 | : d(new KAboutLicensePrivate(licenseType, OnlyThisVersion, aboutData)) |
179 | { |
180 | } |
181 | |
182 | KAboutLicense::KAboutLicense(const KAboutData *aboutData) |
183 | : d(new KAboutLicensePrivate(Unknown, OnlyThisVersion, aboutData)) |
184 | { |
185 | } |
186 | |
187 | KAboutLicense::KAboutLicense(const KAboutLicense &other) |
188 | : d(other.d) |
189 | { |
190 | } |
191 | |
192 | KAboutLicense::~KAboutLicense() |
193 | { |
194 | } |
195 | |
196 | void KAboutLicense::setLicenseFromPath(const QString &pathToFile) |
197 | { |
198 | d->_licenseKey = KAboutLicense::File; |
199 | d->_pathToLicenseTextFile = pathToFile; |
200 | } |
201 | |
202 | void KAboutLicense::setLicenseFromText(const QString &licenseText) |
203 | { |
204 | d->_licenseKey = KAboutLicense::Custom; |
205 | d->_licenseText = licenseText; |
206 | } |
207 | |
208 | QString 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) { |
221 | case KAboutLicense::File: |
222 | pathToFile = d->_pathToLicenseTextFile; |
223 | break; |
224 | case KAboutLicense::GPL_V2: |
225 | knownLicense = true; |
226 | pathToFile = QStringLiteral("GPL_V2" ); |
227 | break; |
228 | case KAboutLicense::LGPL_V2: |
229 | knownLicense = true; |
230 | pathToFile = QStringLiteral("LGPL_V2" ); |
231 | break; |
232 | case KAboutLicense::BSDL: |
233 | knownLicense = true; |
234 | pathToFile = QStringLiteral("BSD" ); |
235 | break; |
236 | case KAboutLicense::Artistic: |
237 | knownLicense = true; |
238 | pathToFile = QStringLiteral("ARTISTIC" ); |
239 | break; |
240 | case KAboutLicense::GPL_V3: |
241 | knownLicense = true; |
242 | pathToFile = QStringLiteral("GPL_V3" ); |
243 | break; |
244 | case KAboutLicense::LGPL_V3: |
245 | knownLicense = true; |
246 | pathToFile = QStringLiteral("LGPL_V3" ); |
247 | break; |
248 | case KAboutLicense::LGPL_V2_1: |
249 | knownLicense = true; |
250 | pathToFile = QStringLiteral("LGPL_V21" ); |
251 | break; |
252 | case KAboutLicense::MIT: |
253 | knownLicense = true; |
254 | pathToFile = QStringLiteral("MIT" ); |
255 | break; |
256 | case KAboutLicense::Custom: |
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 = QStandardPaths::locate(QStandardPaths::GenericDataLocation, QStringLiteral("kf6/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 | |
289 | QString 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 | |
309 | QString KAboutLicense::name(KAboutLicense::NameFormat formatName) const |
310 | { |
311 | QString licenseShort; |
312 | QString licenseFull; |
313 | |
314 | switch (d->_licenseKey) { |
315 | case KAboutLicense::GPL_V2: |
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; |
319 | case KAboutLicense::LGPL_V2: |
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; |
323 | case KAboutLicense::BSDL: |
324 | licenseShort = QCoreApplication::translate("KAboutLicense" , "BSD License" , "@item license (short name)" ); |
325 | licenseFull = QCoreApplication::translate("KAboutLicense" , "BSD License" , "@item license" ); |
326 | break; |
327 | case KAboutLicense::Artistic: |
328 | licenseShort = QCoreApplication::translate("KAboutLicense" , "Artistic License" , "@item license (short name)" ); |
329 | licenseFull = QCoreApplication::translate("KAboutLicense" , "Artistic License" , "@item license" ); |
330 | break; |
331 | case KAboutLicense::GPL_V3: |
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; |
335 | case KAboutLicense::LGPL_V3: |
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; |
339 | case KAboutLicense::LGPL_V2_1: |
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; |
343 | case KAboutLicense::MIT: |
344 | licenseShort = QCoreApplication::translate("KAboutLicense" , "MIT License" , "@item license (short name)" ); |
345 | licenseFull = QCoreApplication::translate("KAboutLicense" , "MIT License" , "@item license" ); |
346 | break; |
347 | case KAboutLicense::Custom: |
348 | case KAboutLicense::File: |
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 | |
360 | KAboutLicense &KAboutLicense::operator=(const KAboutLicense &other) |
361 | { |
362 | d = other.d; |
363 | return *this; |
364 | } |
365 | |
366 | KAboutLicense::LicenseKey KAboutLicense::key() const |
367 | { |
368 | return d->_licenseKey; |
369 | } |
370 | |
371 | KAboutLicense KAboutLicense::byKeyword(const QString &rawKeyword) |
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.remove(QLatin1Char(' ')); |
396 | keyword.remove(QLatin1Char('.')); |
397 | keyword.remove(QLatin1Char('-')); |
398 | |
399 | LicenseKey license = licenseDict.value(keyword.toLatin1(), KAboutLicense::Custom); |
400 | auto restriction = keyword.endsWith(QLatin1Char('+')) ? OrLaterVersions : OnlyThisVersion; |
401 | return KAboutLicense(license, restriction, nullptr); |
402 | } |
403 | |
404 | class KAboutComponentPrivate : public QSharedData |
405 | { |
406 | public: |
407 | QString _name; |
408 | QString _description; |
409 | QString _version; |
410 | QString _webAddress; |
411 | KAboutLicense _license; |
412 | }; |
413 | |
414 | KAboutComponent::KAboutComponent(const QString &_name, |
415 | const QString &_description, |
416 | const QString &_version, |
417 | const QString &_webAddress, |
418 | enum KAboutLicense::LicenseKey licenseType) |
419 | : d(new KAboutComponentPrivate) |
420 | { |
421 | d->_name = _name; |
422 | d->_description = _description; |
423 | d->_version = _version; |
424 | d->_webAddress = _webAddress; |
425 | d->_license = KAboutLicense(licenseType, nullptr); |
426 | } |
427 | |
428 | KAboutComponent::KAboutComponent(const QString &_name, |
429 | const QString &_description, |
430 | const QString &_version, |
431 | const QString &_webAddress, |
432 | const QString &pathToLicenseFile) |
433 | : d(new KAboutComponentPrivate) |
434 | { |
435 | d->_name = _name; |
436 | d->_description = _description; |
437 | d->_version = _version; |
438 | d->_webAddress = _webAddress; |
439 | d->_license = KAboutLicense(); |
440 | d->_license.setLicenseFromPath(pathToLicenseFile); |
441 | } |
442 | |
443 | KAboutComponent::KAboutComponent(const KAboutComponent &other) = default; |
444 | |
445 | KAboutComponent::~KAboutComponent() = default; |
446 | |
447 | QString KAboutComponent::name() const |
448 | { |
449 | return d->_name; |
450 | } |
451 | |
452 | QString KAboutComponent::description() const |
453 | { |
454 | return d->_description; |
455 | } |
456 | |
457 | QString KAboutComponent::version() const |
458 | { |
459 | return d->_version; |
460 | } |
461 | |
462 | QString KAboutComponent::webAddress() const |
463 | { |
464 | return d->_webAddress; |
465 | } |
466 | |
467 | KAboutLicense KAboutComponent::license() const |
468 | { |
469 | return d->_license; |
470 | } |
471 | |
472 | KAboutComponent &KAboutComponent::operator=(const KAboutComponent &other) = default; |
473 | |
474 | class KAboutDataPrivate |
475 | { |
476 | public: |
477 | KAboutDataPrivate() |
478 | : customAuthorTextEnabled(false) |
479 | { |
480 | } |
481 | QString _componentName; |
482 | QString _displayName; |
483 | QString _shortDescription; |
484 | QString _copyrightStatement; |
485 | QString _otherText; |
486 | QString _homepageAddress; |
487 | QList<KAboutPerson> _authorList; |
488 | QList<KAboutPerson> _creditList; |
489 | QList<KAboutPerson> _translatorList; |
490 | QList<KAboutComponent> _componentList; |
491 | QList<KAboutLicense> _licenseList; |
492 | QVariant programLogo; |
493 | QString customAuthorPlainText, customAuthorRichText; |
494 | bool customAuthorTextEnabled; |
495 | |
496 | QString organizationDomain; |
497 | QString desktopFileName; |
498 | |
499 | // Everything dr.konqi needs, we store as utf-8, so we |
500 | // can just give it a pointer, w/o any allocations. |
501 | QByteArray _internalProgramName; |
502 | QByteArray _version; |
503 | QByteArray _bugAddress; |
504 | QByteArray productName; |
505 | |
506 | static QList<KAboutPerson> parseTranslators(const QString &translatorName, const QString &translatorEmail); |
507 | }; |
508 | |
509 | KAboutData::KAboutData(const QString &_componentName, |
510 | const QString &_displayName, |
511 | const QString &_version, |
512 | const QString &_shortDescription, |
513 | enum KAboutLicense::LicenseKey licenseType, |
514 | const QString &_copyrightStatement, |
515 | const QString &text, |
516 | const QString &homePageAddress, |
517 | const QString &bugAddress) |
518 | : d(new KAboutDataPrivate) |
519 | { |
520 | d->_componentName = _componentName; |
521 | int p = d->_componentName.indexOf(QLatin1Char('/')); |
522 | if (p >= 0) { |
523 | d->_componentName = d->_componentName.mid(p + 1); |
524 | } |
525 | |
526 | d->_displayName = _displayName; |
527 | if (!d->_displayName.isEmpty()) { // KComponentData("klauncher") gives empty program name |
528 | d->_internalProgramName = _displayName.toUtf8(); |
529 | } |
530 | d->_version = _version.toUtf8(); |
531 | d->_shortDescription = _shortDescription; |
532 | d->_licenseList.append(KAboutLicense(licenseType, this)); |
533 | d->_copyrightStatement = _copyrightStatement; |
534 | d->_otherText = text; |
535 | d->_homepageAddress = homePageAddress; |
536 | d->_bugAddress = bugAddress.toUtf8(); |
537 | |
538 | QUrl homePageUrl(homePageAddress); |
539 | if (!homePageUrl.isValid() || homePageUrl.scheme().isEmpty()) { |
540 | // Default domain if nothing else is better |
541 | homePageUrl.setUrl(QStringLiteral("https://kde.org/" )); |
542 | } |
543 | |
544 | const QChar dotChar(QLatin1Char('.')); |
545 | QStringList hostComponents = homePageUrl.host().split(dotChar); |
546 | |
547 | // Remove leading component unless 2 (or less) components are present |
548 | if (hostComponents.size() > 2) { |
549 | hostComponents.removeFirst(); |
550 | } |
551 | |
552 | d->organizationDomain = hostComponents.join(dotChar); |
553 | |
554 | // KF6: do not set a default desktopFileName value here, but remove this code and leave it empty |
555 | // see KAboutData::desktopFileName() for details |
556 | |
557 | // desktop file name is reverse domain name |
558 | std::reverse(hostComponents.begin(), hostComponents.end()); |
559 | hostComponents.append(_componentName); |
560 | |
561 | d->desktopFileName = hostComponents.join(dotChar); |
562 | } |
563 | |
564 | KAboutData::KAboutData(const QString &_componentName, const QString &_displayName, const QString &_version) |
565 | : d(new KAboutDataPrivate) |
566 | { |
567 | d->_componentName = _componentName; |
568 | int p = d->_componentName.indexOf(QLatin1Char('/')); |
569 | if (p >= 0) { |
570 | d->_componentName = d->_componentName.mid(p + 1); |
571 | } |
572 | |
573 | d->_displayName = _displayName; |
574 | if (!d->_displayName.isEmpty()) { // KComponentData("klauncher") gives empty program name |
575 | d->_internalProgramName = _displayName.toUtf8(); |
576 | } |
577 | d->_version = _version.toUtf8(); |
578 | |
579 | // match behaviour of other constructors |
580 | d->_licenseList.append(KAboutLicense(KAboutLicense::Unknown, this)); |
581 | d->_bugAddress = "submit@bugs.kde.org" ; |
582 | d->organizationDomain = QStringLiteral("kde.org" ); |
583 | // KF6: do not set a default desktopFileName value here, but remove this code and leave it empty |
584 | // see KAboutData::desktopFileName() for details |
585 | d->desktopFileName = QLatin1String("org.kde." ) + d->_componentName; |
586 | } |
587 | |
588 | KAboutData::~KAboutData() = default; |
589 | |
590 | KAboutData::KAboutData(const KAboutData &other) |
591 | : d(new KAboutDataPrivate) |
592 | { |
593 | *d = *other.d; |
594 | for (KAboutLicense &al : d->_licenseList) { |
595 | al.d.detach(); |
596 | al.d->_aboutData = this; |
597 | } |
598 | } |
599 | |
600 | KAboutData &KAboutData::operator=(const KAboutData &other) |
601 | { |
602 | if (this != &other) { |
603 | *d = *other.d; |
604 | for (KAboutLicense &al : d->_licenseList) { |
605 | al.d.detach(); |
606 | al.d->_aboutData = this; |
607 | } |
608 | } |
609 | return *this; |
610 | } |
611 | |
612 | KAboutData &KAboutData::addAuthor(const QString &name, const QString &task, const QString &emailAddress, const QString &webAddress, const QUrl &avatarUrl) |
613 | { |
614 | d->_authorList.append(KAboutPerson(name, task, emailAddress, webAddress, avatarUrl)); |
615 | return *this; |
616 | } |
617 | |
618 | KAboutData &KAboutData::addCredit(const QString &name, const QString &task, const QString &emailAddress, const QString &webAddress, const QUrl &avatarUrl) |
619 | { |
620 | d->_creditList.append(KAboutPerson(name, task, emailAddress, webAddress, avatarUrl)); |
621 | return *this; |
622 | } |
623 | |
624 | KAboutData &KAboutData::setTranslator(const QString &name, const QString &emailAddress) |
625 | { |
626 | d->_translatorList = KAboutDataPrivate::parseTranslators(name, emailAddress); |
627 | return *this; |
628 | } |
629 | |
630 | KAboutData &KAboutData::addComponent(const QString &name, |
631 | const QString &description, |
632 | const QString &version, |
633 | const QString &webAddress, |
634 | KAboutLicense::LicenseKey licenseKey) |
635 | { |
636 | d->_componentList.append(KAboutComponent(name, description, version, webAddress, licenseKey)); |
637 | return *this; |
638 | } |
639 | |
640 | KAboutData & |
641 | KAboutData::addComponent(const QString &name, const QString &description, const QString &version, const QString &webAddress, const QString &pathToLicenseFile) |
642 | { |
643 | d->_componentList.append(KAboutComponent(name, description, version, webAddress, pathToLicenseFile)); |
644 | return *this; |
645 | } |
646 | |
647 | KAboutData &KAboutData::setLicenseText(const QString &licenseText) |
648 | { |
649 | d->_licenseList[0] = KAboutLicense(this); |
650 | d->_licenseList[0].setLicenseFromText(licenseText); |
651 | return *this; |
652 | } |
653 | |
654 | KAboutData &KAboutData::addLicenseText(const QString &licenseText) |
655 | { |
656 | // if the default license is unknown, overwrite instead of append |
657 | KAboutLicense &firstLicense = d->_licenseList[0]; |
658 | KAboutLicense newLicense(this); |
659 | newLicense.setLicenseFromText(licenseText); |
660 | if (d->_licenseList.count() == 1 && firstLicense.d->_licenseKey == KAboutLicense::Unknown) { |
661 | firstLicense = newLicense; |
662 | } else { |
663 | d->_licenseList.append(newLicense); |
664 | } |
665 | |
666 | return *this; |
667 | } |
668 | |
669 | KAboutData &KAboutData::setLicenseTextFile(const QString &pathToFile) |
670 | { |
671 | d->_licenseList[0] = KAboutLicense(this); |
672 | d->_licenseList[0].setLicenseFromPath(pathToFile); |
673 | return *this; |
674 | } |
675 | |
676 | KAboutData &KAboutData::addLicenseTextFile(const QString &pathToFile) |
677 | { |
678 | // if the default license is unknown, overwrite instead of append |
679 | KAboutLicense &firstLicense = d->_licenseList[0]; |
680 | KAboutLicense newLicense(this); |
681 | newLicense.setLicenseFromPath(pathToFile); |
682 | if (d->_licenseList.count() == 1 && firstLicense.d->_licenseKey == KAboutLicense::Unknown) { |
683 | firstLicense = newLicense; |
684 | } else { |
685 | d->_licenseList.append(newLicense); |
686 | } |
687 | return *this; |
688 | } |
689 | |
690 | KAboutData &KAboutData::setComponentName(const QString &componentName) |
691 | { |
692 | d->_componentName = componentName; |
693 | return *this; |
694 | } |
695 | |
696 | KAboutData &KAboutData::setDisplayName(const QString &_displayName) |
697 | { |
698 | d->_displayName = _displayName; |
699 | d->_internalProgramName = _displayName.toUtf8(); |
700 | return *this; |
701 | } |
702 | |
703 | KAboutData &KAboutData::setVersion(const QByteArray &_version) |
704 | { |
705 | d->_version = _version; |
706 | return *this; |
707 | } |
708 | |
709 | KAboutData &KAboutData::setShortDescription(const QString &_shortDescription) |
710 | { |
711 | d->_shortDescription = _shortDescription; |
712 | return *this; |
713 | } |
714 | |
715 | KAboutData &KAboutData::setLicense(KAboutLicense::LicenseKey licenseKey) |
716 | { |
717 | return setLicense(licenseKey, KAboutLicense::OnlyThisVersion); |
718 | } |
719 | |
720 | KAboutData &KAboutData::setLicense(KAboutLicense::LicenseKey licenseKey, KAboutLicense::VersionRestriction versionRestriction) |
721 | { |
722 | d->_licenseList[0] = KAboutLicense(licenseKey, versionRestriction, this); |
723 | return *this; |
724 | } |
725 | |
726 | KAboutData &KAboutData::addLicense(KAboutLicense::LicenseKey licenseKey) |
727 | { |
728 | return addLicense(licenseKey, KAboutLicense::OnlyThisVersion); |
729 | } |
730 | |
731 | KAboutData &KAboutData::addLicense(KAboutLicense::LicenseKey licenseKey, KAboutLicense::VersionRestriction versionRestriction) |
732 | { |
733 | // if the default license is unknown, overwrite instead of append |
734 | KAboutLicense &firstLicense = d->_licenseList[0]; |
735 | if (d->_licenseList.count() == 1 && firstLicense.d->_licenseKey == KAboutLicense::Unknown) { |
736 | firstLicense = KAboutLicense(licenseKey, versionRestriction, this); |
737 | } else { |
738 | d->_licenseList.append(KAboutLicense(licenseKey, versionRestriction, this)); |
739 | } |
740 | return *this; |
741 | } |
742 | |
743 | KAboutData &KAboutData::setCopyrightStatement(const QString &_copyrightStatement) |
744 | { |
745 | d->_copyrightStatement = _copyrightStatement; |
746 | return *this; |
747 | } |
748 | |
749 | KAboutData &KAboutData::setOtherText(const QString &_otherText) |
750 | { |
751 | d->_otherText = _otherText; |
752 | return *this; |
753 | } |
754 | |
755 | KAboutData &KAboutData::setHomepage(const QString &homepage) |
756 | { |
757 | d->_homepageAddress = homepage; |
758 | return *this; |
759 | } |
760 | |
761 | KAboutData &KAboutData::setBugAddress(const QByteArray &_bugAddress) |
762 | { |
763 | d->_bugAddress = _bugAddress; |
764 | return *this; |
765 | } |
766 | |
767 | KAboutData &KAboutData::setOrganizationDomain(const QByteArray &domain) |
768 | { |
769 | d->organizationDomain = QString::fromLatin1(domain.data()); |
770 | return *this; |
771 | } |
772 | |
773 | KAboutData &KAboutData::setProductName(const QByteArray &_productName) |
774 | { |
775 | d->productName = _productName; |
776 | return *this; |
777 | } |
778 | |
779 | QString KAboutData::componentName() const |
780 | { |
781 | return d->_componentName; |
782 | } |
783 | |
784 | QString KAboutData::productName() const |
785 | { |
786 | if (!d->productName.isEmpty()) { |
787 | return QString::fromUtf8(d->productName); |
788 | } |
789 | return componentName(); |
790 | } |
791 | |
792 | const char *KAboutData::internalProductName() const |
793 | { |
794 | return d->productName.isEmpty() ? nullptr : d->productName.constData(); |
795 | } |
796 | |
797 | QString KAboutData::displayName() const |
798 | { |
799 | return d->_displayName; |
800 | } |
801 | |
802 | /// @internal |
803 | /// Return the program name. It is always pre-allocated. |
804 | /// Needed for KCrash in particular. |
805 | const char *KAboutData::internalProgramName() const |
806 | { |
807 | return d->_internalProgramName.constData(); |
808 | } |
809 | |
810 | QVariant KAboutData::programLogo() const |
811 | { |
812 | return d->programLogo; |
813 | } |
814 | |
815 | KAboutData &KAboutData::setProgramLogo(const QVariant &image) |
816 | { |
817 | d->programLogo = image; |
818 | return *this; |
819 | } |
820 | |
821 | QString KAboutData::version() const |
822 | { |
823 | return QString::fromUtf8(d->_version.data()); |
824 | } |
825 | |
826 | /// @internal |
827 | /// Return the untranslated and uninterpreted (to UTF8) string |
828 | /// for the version information. Used in particular for KCrash. |
829 | const char *KAboutData::internalVersion() const |
830 | { |
831 | return d->_version.constData(); |
832 | } |
833 | |
834 | QString KAboutData::shortDescription() const |
835 | { |
836 | return d->_shortDescription; |
837 | } |
838 | |
839 | QString KAboutData::homepage() const |
840 | { |
841 | return d->_homepageAddress; |
842 | } |
843 | |
844 | QString KAboutData::bugAddress() const |
845 | { |
846 | return QString::fromUtf8(d->_bugAddress.constData()); |
847 | } |
848 | |
849 | QString KAboutData::organizationDomain() const |
850 | { |
851 | return d->organizationDomain; |
852 | } |
853 | |
854 | /// @internal |
855 | /// Return the untranslated and uninterpreted (to UTF8) string |
856 | /// for the bug mail address. Used in particular for KCrash. |
857 | const char *KAboutData::internalBugAddress() const |
858 | { |
859 | if (d->_bugAddress.isEmpty()) { |
860 | return nullptr; |
861 | } |
862 | return d->_bugAddress.constData(); |
863 | } |
864 | |
865 | QList<KAboutPerson> KAboutData::authors() const |
866 | { |
867 | return d->_authorList; |
868 | } |
869 | |
870 | QList<KAboutPerson> KAboutData::credits() const |
871 | { |
872 | return d->_creditList; |
873 | } |
874 | |
875 | QList<KAboutPerson> KAboutDataPrivate::parseTranslators(const QString &translatorName, const QString &translatorEmail) |
876 | { |
877 | if (translatorName.isEmpty() || translatorName == QLatin1String("Your names" )) { |
878 | return {}; |
879 | } |
880 | |
881 | // use list of string views to delay creating new QString instances after the white-space trimming |
882 | const QList<QStringView> nameList = QStringView(translatorName).split(QLatin1Char(',')); |
883 | |
884 | QList<QStringView> emailList; |
885 | if (!translatorEmail.isEmpty() && translatorEmail != QLatin1String("Your emails" )) { |
886 | emailList = QStringView(translatorEmail).split(QLatin1Char(','), Qt::KeepEmptyParts); |
887 | } |
888 | |
889 | QList<KAboutPerson> personList; |
890 | personList.reserve(nameList.size()); |
891 | |
892 | auto eit = emailList.constBegin(); |
893 | |
894 | for (const QStringView &name : nameList) { |
895 | QStringView email; |
896 | if (eit != emailList.constEnd()) { |
897 | email = *eit; |
898 | ++eit; |
899 | } |
900 | |
901 | personList.append(KAboutPerson(name.trimmed().toString(), email.trimmed().toString(), true)); |
902 | } |
903 | |
904 | return personList; |
905 | } |
906 | |
907 | QList<KAboutPerson> KAboutData::translators() const |
908 | { |
909 | return d->_translatorList; |
910 | } |
911 | |
912 | QString KAboutData::aboutTranslationTeam() |
913 | { |
914 | return QCoreApplication::translate("KAboutData" , |
915 | "<p>KDE is translated into many languages thanks to the work " |
916 | "of the translation teams all over the world.</p>" |
917 | "<p>For more information on KDE internationalization " |
918 | "visit <a href=\"https://l10n.kde.org\">https://l10n.kde.org</a></p>" , |
919 | "replace this with information about your translation team" ); |
920 | } |
921 | |
922 | QString KAboutData::otherText() const |
923 | { |
924 | return d->_otherText; |
925 | } |
926 | |
927 | QList<KAboutComponent> KAboutData::components() const |
928 | { |
929 | return d->_componentList; |
930 | } |
931 | |
932 | QList<KAboutLicense> KAboutData::licenses() const |
933 | { |
934 | return d->_licenseList; |
935 | } |
936 | |
937 | QString KAboutData::copyrightStatement() const |
938 | { |
939 | return d->_copyrightStatement; |
940 | } |
941 | |
942 | QString KAboutData::customAuthorPlainText() const |
943 | { |
944 | return d->customAuthorPlainText; |
945 | } |
946 | |
947 | QString KAboutData::customAuthorRichText() const |
948 | { |
949 | return d->customAuthorRichText; |
950 | } |
951 | |
952 | bool KAboutData::customAuthorTextEnabled() const |
953 | { |
954 | return d->customAuthorTextEnabled; |
955 | } |
956 | |
957 | KAboutData &KAboutData::setCustomAuthorText(const QString &plainText, const QString &richText) |
958 | { |
959 | d->customAuthorPlainText = plainText; |
960 | d->customAuthorRichText = richText; |
961 | |
962 | d->customAuthorTextEnabled = true; |
963 | |
964 | return *this; |
965 | } |
966 | |
967 | KAboutData &KAboutData::unsetCustomAuthorText() |
968 | { |
969 | d->customAuthorPlainText = QString(); |
970 | d->customAuthorRichText = QString(); |
971 | |
972 | d->customAuthorTextEnabled = false; |
973 | |
974 | return *this; |
975 | } |
976 | |
977 | KAboutData &KAboutData::setDesktopFileName(const QString &desktopFileName) |
978 | { |
979 | d->desktopFileName = desktopFileName; |
980 | |
981 | return *this; |
982 | } |
983 | |
984 | QString KAboutData::desktopFileName() const |
985 | { |
986 | return d->desktopFileName; |
987 | // KF6: switch to this code and adapt API dox |
988 | #if 0 |
989 | // if desktopFileName has been explicitly set, use that value |
990 | if (!d->desktopFileName.isEmpty()) { |
991 | return d->desktopFileName; |
992 | } |
993 | |
994 | // return a string calculated on-the-fly from the current org domain & component name |
995 | const QChar dotChar(QLatin1Char('.')); |
996 | QStringList hostComponents = d->organizationDomain.split(dotChar); |
997 | |
998 | // desktop file name is reverse domain name |
999 | std::reverse(hostComponents.begin(), hostComponents.end()); |
1000 | hostComponents.append(componentName()); |
1001 | |
1002 | return hostComponents.join(dotChar); |
1003 | #endif |
1004 | } |
1005 | |
1006 | class KAboutDataRegistry |
1007 | { |
1008 | public: |
1009 | KAboutDataRegistry() |
1010 | : m_appData(nullptr) |
1011 | { |
1012 | } |
1013 | ~KAboutDataRegistry() |
1014 | { |
1015 | delete m_appData; |
1016 | } |
1017 | KAboutDataRegistry(const KAboutDataRegistry &) = delete; |
1018 | KAboutDataRegistry &operator=(const KAboutDataRegistry &) = delete; |
1019 | |
1020 | KAboutData *m_appData; |
1021 | }; |
1022 | |
1023 | Q_GLOBAL_STATIC(KAboutDataRegistry, s_registry) |
1024 | |
1025 | namespace |
1026 | { |
1027 | void warnIfOutOfSync(const char *aboutDataString, const QString &aboutDataValue, const char *appDataString, const QString &appDataValue) |
1028 | { |
1029 | if (aboutDataValue != appDataValue) { |
1030 | qCWarning(KABOUTDATA) << appDataString << appDataValue << "is out-of-sync with" << aboutDataString << aboutDataValue; |
1031 | } |
1032 | } |
1033 | |
1034 | } |
1035 | |
1036 | KAboutData KAboutData::applicationData() |
1037 | { |
1038 | QCoreApplication *app = QCoreApplication::instance(); |
1039 | |
1040 | KAboutData *aboutData = s_registry->m_appData; |
1041 | |
1042 | // not yet existing |
1043 | if (!aboutData) { |
1044 | // init from current Q*Application data |
1045 | aboutData = new KAboutData(QCoreApplication::applicationName(), QString(), QString()); |
1046 | // Unset the default (KDE) bug address, this is likely a third-party app. https://bugs.kde.org/show_bug.cgi?id=473517 |
1047 | aboutData->setBugAddress(QByteArray()); |
1048 | // For applicationDisplayName & desktopFileName, which are only properties of QGuiApplication, |
1049 | // we have to try to get them via the property system, as the static getter methods are |
1050 | // part of QtGui only. Disadvantage: requires an app instance. |
1051 | // Either get all or none of the properties & warn about it |
1052 | if (app) { |
1053 | aboutData->setOrganizationDomain(QCoreApplication::organizationDomain().toUtf8()); |
1054 | aboutData->setVersion(QCoreApplication::applicationVersion().toUtf8()); |
1055 | aboutData->setDisplayName(app->property("applicationDisplayName" ).toString()); |
1056 | aboutData->setDesktopFileName(app->property("desktopFileName" ).toString()); |
1057 | } else { |
1058 | qCWarning(KABOUTDATA) << "Could not initialize the properties of KAboutData::applicationData by the equivalent properties from Q*Application: no " |
1059 | "app instance (yet) existing." ; |
1060 | } |
1061 | |
1062 | s_registry->m_appData = aboutData; |
1063 | } else { |
1064 | // check if in-sync with Q*Application metadata, as their setters could have been called |
1065 | // after the last KAboutData::setApplicationData, with different values |
1066 | warnIfOutOfSync("KAboutData::applicationData().componentName" , |
1067 | aboutData->componentName(), |
1068 | "QCoreApplication::applicationName" , |
1069 | QCoreApplication::applicationName()); |
1070 | warnIfOutOfSync("KAboutData::applicationData().version" , |
1071 | aboutData->version(), |
1072 | "QCoreApplication::applicationVersion" , |
1073 | QCoreApplication::applicationVersion()); |
1074 | warnIfOutOfSync("KAboutData::applicationData().organizationDomain" , |
1075 | aboutData->organizationDomain(), |
1076 | "QCoreApplication::organizationDomain" , |
1077 | QCoreApplication::organizationDomain()); |
1078 | if (app) { |
1079 | warnIfOutOfSync("KAboutData::applicationData().displayName" , |
1080 | aboutData->displayName(), |
1081 | "QGuiApplication::applicationDisplayName" , |
1082 | app->property("applicationDisplayName" ).toString()); |
1083 | warnIfOutOfSync("KAboutData::applicationData().desktopFileName" , |
1084 | aboutData->desktopFileName(), |
1085 | "QGuiApplication::desktopFileName" , |
1086 | app->property("desktopFileName" ).toString()); |
1087 | } |
1088 | } |
1089 | |
1090 | return *aboutData; |
1091 | } |
1092 | |
1093 | void KAboutData::setApplicationData(const KAboutData &aboutData) |
1094 | { |
1095 | if (s_registry->m_appData) { |
1096 | *s_registry->m_appData = aboutData; |
1097 | } else { |
1098 | s_registry->m_appData = new KAboutData(aboutData); |
1099 | } |
1100 | |
1101 | // For applicationDisplayName & desktopFileName, which are only properties of QGuiApplication, |
1102 | // we have to try to set them via the property system, as the static getter methods are |
1103 | // part of QtGui only. Disadvantage: requires an app instance. |
1104 | // So set either all or none of the properties & warn about it |
1105 | QCoreApplication *app = QCoreApplication::instance(); |
1106 | if (app) { |
1107 | app->setApplicationVersion(aboutData.version()); |
1108 | app->setApplicationName(aboutData.componentName()); |
1109 | app->setOrganizationDomain(aboutData.organizationDomain()); |
1110 | app->setProperty("applicationDisplayName" , aboutData.displayName()); |
1111 | app->setProperty("desktopFileName" , aboutData.desktopFileName()); |
1112 | } else { |
1113 | qCWarning(KABOUTDATA) << "Could not initialize the equivalent properties of Q*Application: no instance (yet) existing." ; |
1114 | } |
1115 | |
1116 | // KF6: Rethink the current relation between KAboutData::applicationData and the Q*Application metadata |
1117 | // Always overwriting the Q*Application metadata here, but not updating back the KAboutData |
1118 | // in applicationData() is unbalanced and can result in out-of-sync data if the Q*Application |
1119 | // setters have been called meanwhile |
1120 | // Options are to remove the overlapping properties of KAboutData for cleancode, or making the |
1121 | // overlapping properties official shadow properties of their Q*Application countparts, though |
1122 | // that increases behavioural complexity a little. |
1123 | } |
1124 | |
1125 | // only for KCrash (no memory allocation allowed) |
1126 | const KAboutData *KAboutData::applicationDataPointer() |
1127 | { |
1128 | if (s_registry.exists()) { |
1129 | return s_registry->m_appData; |
1130 | } |
1131 | return nullptr; |
1132 | } |
1133 | |
1134 | bool KAboutData::setupCommandLine(QCommandLineParser *parser) |
1135 | { |
1136 | if (!d->_shortDescription.isEmpty()) { |
1137 | parser->setApplicationDescription(d->_shortDescription); |
1138 | } |
1139 | |
1140 | parser->addHelpOption(); |
1141 | |
1142 | QCoreApplication *app = QCoreApplication::instance(); |
1143 | if (app && !app->applicationVersion().isEmpty()) { |
1144 | parser->addVersionOption(); |
1145 | } |
1146 | |
1147 | return parser->addOption(QCommandLineOption(QStringLiteral("author" ), QCoreApplication::translate("KAboutData CLI" , "Show author information." ))) |
1148 | && parser->addOption(QCommandLineOption(QStringLiteral("license" ), QCoreApplication::translate("KAboutData CLI" , "Show license information." ))) |
1149 | && parser->addOption(QCommandLineOption(QStringLiteral("desktopfile" ), |
1150 | QCoreApplication::translate("KAboutData CLI" , "The base file name of the desktop entry for this application." ), |
1151 | QCoreApplication::translate("KAboutData CLI" , "file name" ))); |
1152 | } |
1153 | |
1154 | void KAboutData::processCommandLine(QCommandLineParser *parser) |
1155 | { |
1156 | bool foundArgument = false; |
1157 | if (parser->isSet(QStringLiteral("author" ))) { |
1158 | foundArgument = true; |
1159 | if (d->_authorList.isEmpty()) { |
1160 | printf("%s\n" , |
1161 | qPrintable(QCoreApplication::translate("KAboutData CLI" , "This application was written by somebody who wants to remain anonymous." ))); |
1162 | } else { |
1163 | printf("%s\n" , qPrintable(QCoreApplication::translate("KAboutData CLI" , "%1 was written by:" ).arg(qAppName()))); |
1164 | for (const KAboutPerson &person : std::as_const(d->_authorList)) { |
1165 | QString authorData = QLatin1String(" " ) + person.name(); |
1166 | if (!person.emailAddress().isEmpty()) { |
1167 | authorData.append(QLatin1String(" <" ) + person.emailAddress() + QLatin1Char('>')); |
1168 | } |
1169 | printf("%s\n" , qPrintable(authorData)); |
1170 | } |
1171 | } |
1172 | if (!customAuthorTextEnabled()) { |
1173 | if (bugAddress() == QLatin1String("submit@bugs.kde.org" )) { |
1174 | printf("%s\n" , qPrintable(QCoreApplication::translate("KAboutData CLI" , "Please use https://bugs.kde.org to report bugs." ))); |
1175 | } else if (!bugAddress().isEmpty()) { |
1176 | printf("%s\n" , qPrintable(QCoreApplication::translate("KAboutData CLI" , "Please report bugs to %1." ).arg(bugAddress()))); |
1177 | } |
1178 | } else { |
1179 | printf("%s\n" , qPrintable(customAuthorPlainText())); |
1180 | } |
1181 | } else if (parser->isSet(QStringLiteral("license" ))) { |
1182 | foundArgument = true; |
1183 | for (const KAboutLicense &license : std::as_const(d->_licenseList)) { |
1184 | printf("%s\n" , qPrintable(license.text())); |
1185 | } |
1186 | } |
1187 | |
1188 | const QString desktopFileName = parser->value(QStringLiteral("desktopfile" )); |
1189 | if (!desktopFileName.isEmpty()) { |
1190 | d->desktopFileName = desktopFileName; |
1191 | } |
1192 | |
1193 | if (foundArgument) { |
1194 | ::exit(EXIT_SUCCESS); |
1195 | } |
1196 | } |
1197 | |
1198 | #include "moc_kaboutdata.cpp" |
1199 | |