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