KNewStuff

enginebase.cpp
1/*
2 SPDX-FileCopyrightText: 2007 Josef Spillner <spillner@kde.org>
3 SPDX-FileCopyrightText: 2007-2010 Frederik Gladhorn <gladhorn@kde.org>
4 SPDX-FileCopyrightText: 2009 Jeremy Whiting <jpwhiting@kde.org>
5
6 SPDX-License-Identifier: LGPL-2.1-or-later
7*/
8
9#include "enginebase.h"
10#include "enginebase_p.h"
11#include <knewstuffcore_debug.h>
12
13#include <KConfig>
14#include <KConfigGroup>
15#include <KFileUtils>
16#include <KFormat>
17#include <KLocalizedString>
18
19#include <QFileInfo>
20#include <QNetworkRequest>
21#include <QProcess>
22#include <QStandardPaths>
23#include <QThreadStorage>
24#include <QTimer>
25
26#include "attica/atticaprovider_p.h"
27#include "categorymetadata.h"
28#include "compat_p.h"
29#include "opds/opdsprovider_p.h"
30#include "providerbubblewrap_p.h"
31#include "providercore.h"
32#include "providercore_p.h"
33#include "resultsstream.h"
34#include "searchrequest_p.h"
35#include "staticxml/staticxmlprovider_p.h"
36#include "transaction.h"
37#include "xmlloader_p.h"
38
39using namespace KNSCore;
40
42Q_GLOBAL_STATIC(QThreadStorage<EngineProviderLoaderHash>, s_engineProviderLoaders)
43
44namespace
45{
46
47}
48
49EngineBasePrivate::EngineBasePrivate(EngineBase *qptr)
50 : q(qptr)
51{
52}
53
54void EngineBasePrivate::addProvider(const QSharedPointer<KNSCore::ProviderCore> &provider)
55{
56 { // ProviderCore
57 qCDebug(KNEWSTUFFCORE) << "Engine addProvider called with provider with id " << provider->d->base->id();
58 providerCores.insert(provider->d->base->id(), provider);
59 provider->d->base->setTagFilter(tagFilter);
60 provider->d->base->setDownloadTagFilter(downloadTagFilter);
61 QObject::connect(provider->d->base, &ProviderBase::providerInitialized, q, [this, providerBase = provider->d->base] {
62 qCDebug(KNEWSTUFFCORE) << "providerInitialized" << providerBase->name();
63 providerBase->setCachedEntries(cache->registryForProvider(providerBase->id()));
64
65 for (const auto &core : std::as_const(providerCores)) {
66 if (!core->d->base->isInitialized()) {
67 return;
68 }
69 }
70 Q_EMIT q->signalProvidersLoaded();
71 });
72
73 QObject::connect(provider->d->base, &ProviderBase::signalError, q, [this, provider](const QString &msg) {
74 Q_EMIT q->signalErrorCode(ErrorCode::ProviderError, msg, providerFileUrl);
75 });
76 QObject::connect(provider->d->base, &ProviderBase::signalErrorCode, q, &EngineBase::signalErrorCode);
77 QObject::connect(provider->d->base, &ProviderBase::signalInformation, q, &EngineBase::signalMessage);
78 QObject::connect(provider->d->base, &ProviderBase::basicsLoaded, q, &EngineBase::providersChanged);
79 Q_EMIT q->providerAdded(provider.get());
80 }
81
82 { // ProviderBubbleWrap for legacy compatibility
83 QSharedPointer<ProviderBubbleWrap> wrappedProvider(new ProviderBubbleWrap(provider));
84 legacyProviders.insert(wrappedProvider->id(), wrappedProvider);
85 wrappedProvider->setTagFilter(tagFilter);
86 wrappedProvider->setDownloadTagFilter(downloadTagFilter);
87 q->addProvider(wrappedProvider);
88 }
89
90 Q_EMIT q->providersChanged();
91}
92
93EngineBase::EngineBase(QObject *parent)
94 : QObject(parent)
95 , d(new EngineBasePrivate(this))
96{
97 connect(d->installation, &Installation::signalInstallationError, this, [this](const QString &message) {
98 Q_EMIT signalErrorCode(ErrorCode::InstallationError, i18n("An error occurred during the installation process:\n%1", message), QVariant());
99 });
100}
101
103{
104 QStringList configSearchLocations;
106 QStringLiteral("knsrcfiles"),
109 return KFileUtils::findAllUniqueFiles(configSearchLocations, {QStringLiteral("*.knsrc")});
110}
111
112EngineBase::~EngineBase()
113{
114 if (d->cache) {
115 d->cache->writeRegistry();
116 }
117 delete d->atticaProviderManager;
118 delete d->installation;
119}
120
121bool EngineBase::init(const QString &configfile)
122{
123 qCDebug(KNEWSTUFFCORE) << "Initializing KNSCore::EngineBase from" << configfile;
124
125 QString resolvedConfigFilePath;
126 if (QFileInfo(configfile).isAbsolute()) {
127 resolvedConfigFilePath = configfile; // It is an absolute path
128 } else {
129 // Don't do the expensive search unless the config is relative
130 resolvedConfigFilePath = QStandardPaths::locate(QStandardPaths::GenericDataLocation, QStringLiteral("knsrcfiles/%1").arg(configfile));
131 }
132
133 if (!QFileInfo::exists(resolvedConfigFilePath)) {
134 Q_EMIT signalErrorCode(KNSCore::ErrorCode::ConfigFileError, i18n("Configuration file does not exist: \"%1\"", configfile), configfile);
135 qCCritical(KNEWSTUFFCORE) << "The knsrc file" << configfile << "does not exist";
136 return false;
137 }
138
139 const KConfig conf(resolvedConfigFilePath);
140
141 if (conf.accessMode() == KConfig::NoAccess) {
142 Q_EMIT signalErrorCode(KNSCore::ErrorCode::ConfigFileError, i18n("Configuration file exists, but cannot be opened: \"%1\"", configfile), configfile);
143 qCCritical(KNEWSTUFFCORE) << "The knsrc file" << configfile << "was found but could not be opened.";
144 return false;
145 }
146
147 const KConfigGroup group = conf.hasGroup(QStringLiteral("KNewStuff")) ? conf.group(QStringLiteral("KNewStuff")) : conf.group(QStringLiteral("KNewStuff3"));
148 if (!group.exists()) {
149 Q_EMIT signalErrorCode(KNSCore::ErrorCode::ConfigFileError, i18n("Configuration file is invalid: \"%1\"", configfile), configfile);
150 qCCritical(KNEWSTUFFCORE) << configfile << "doesn't contain a KNewStuff or KNewStuff3 section.";
151 return false;
152 }
153
154 d->name = group.readEntry("Name");
155 d->categories = group.readEntry("Categories", QStringList());
156 qCDebug(KNEWSTUFFCORE) << "Categories: " << d->categories;
157 d->adoptionCommand = group.readEntry("AdoptionCommand");
158 d->useLabel = group.readEntry("UseLabel", i18n("Use"));
160 d->uploadEnabled = group.readEntry("UploadEnabled", true);
162
163 d->providerFileUrl = group.readEntry("ProvidersUrl", QUrl(QStringLiteral("https://autoconfig.kde.org/ocs/providers.xml")));
164 if (group.readEntry("UseLocalProvidersFile", false)) {
165 // The local providers file is called "appname.providers", to match "appname.knsrc"
166 d->providerFileUrl = QUrl::fromLocalFile(QLatin1String("%1.providers").arg(configfile.left(configfile.length() - 6)));
167 }
168
169 d->tagFilter = group.readEntry("TagFilter", QStringList(QStringLiteral("ghns_excluded!=1")));
170 d->downloadTagFilter = group.readEntry("DownloadTagFilter", QStringList());
171
172 QByteArray rawContentWarningType = group.readEntry("ContentWarning", QByteArrayLiteral("Static"));
173 bool ok = false;
174 int value = QMetaEnum::fromType<ContentWarningType>().keyToValue(rawContentWarningType.constData(), &ok);
175 if (ok) {
176 d->contentWarningType = static_cast<ContentWarningType>(value);
177 } else {
178 qCWarning(KNEWSTUFFCORE) << "Could not parse ContentWarning, invalid entry" << rawContentWarningType;
179 }
180
182
183 // Make sure that config is valid
184 QString error;
185 if (!d->installation->readConfig(group, error)) {
186 Q_EMIT signalErrorCode(ErrorCode::ConfigFileError,
187 i18n("Could not initialise the installation handler for %1:\n%2\n"
188 "This is a critical error and should be reported to the application author",
189 configfile,
190 error),
191 configfile);
192 return false;
193 }
194
195 const QString configFileBasename = QFileInfo(resolvedConfigFilePath).completeBaseName();
196
197 d->legacyCache = Cache::getCache(configFileBasename);
198 qCDebug(KNEWSTUFFCORE) << "Legacy cache is" << d->legacyCache << "for" << configFileBasename;
199 d->legacyCache->readRegistry();
200
201 d->cache = Cache2::getCache(configFileBasename);
202 qCDebug(KNEWSTUFFCORE) << "Cache is" << d->cache << "for" << configFileBasename;
203 d->cache->readRegistry();
204
205 // Cache cleanup option, to help work around people deleting files from underneath KNewStuff (this
206 // happens a lot with e.g. wallpapers and icons)
207 if (d->installation->uncompressionSetting() == Installation::UseKPackageUncompression) {
208 d->shouldRemoveDeletedEntries = true;
209 }
210
211 d->shouldRemoveDeletedEntries = group.readEntry("RemoveDeadEntries", d->shouldRemoveDeletedEntries);
212 if (d->shouldRemoveDeletedEntries) {
213 d->cache->removeDeletedEntries();
214 }
215
216 loadProviders();
217
218 return true;
219}
220
221void EngineBase::loadProviders()
222{
223 if (d->providerFileUrl.isEmpty()) {
224 // it would be nicer to move the attica stuff into its own class
225 qCDebug(KNEWSTUFFCORE) << "Using OCS default providers";
226 delete d->atticaProviderManager;
227 d->atticaProviderManager = new Attica::ProviderManager;
228 connect(d->atticaProviderManager, &Attica::ProviderManager::providerAdded, this, &EngineBase::atticaProviderLoaded);
229 connect(d->atticaProviderManager, &Attica::ProviderManager::failedToLoad, this, &EngineBase::slotProvidersFailed);
230 d->atticaProviderManager->loadDefaultProviders();
231 } else {
232 qCDebug(KNEWSTUFFCORE) << "loading providers from " << d->providerFileUrl;
233 Q_EMIT loadingProvider();
234
235 XmlLoader *loader = s_engineProviderLoaders()->localData().value(d->providerFileUrl);
236 if (!loader) {
237 qCDebug(KNEWSTUFFCORE) << "No xml loader for this url yet, so create one and temporarily store that" << d->providerFileUrl;
238 loader = new XmlLoader(this);
239 s_engineProviderLoaders()->localData().insert(d->providerFileUrl, loader);
240 connect(loader, &XmlLoader::signalLoaded, this, [this]() {
241 s_engineProviderLoaders()->localData().remove(d->providerFileUrl);
242 });
243 connect(loader, &XmlLoader::signalFailed, this, [this]() {
244 s_engineProviderLoaders()->localData().remove(d->providerFileUrl);
245 });
246 connect(loader, &XmlLoader::signalHttpError, this, [this](int status, QList<QNetworkReply::RawHeaderPair> rawHeaders) {
247 if (status == 503) { // Temporarily Unavailable
248 QDateTime retryAfter;
249 static const QByteArray retryAfterKey{"Retry-After"};
250 for (const QNetworkReply::RawHeaderPair &headerPair : rawHeaders) {
251 if (headerPair.first == retryAfterKey) {
252 // Retry-After is not a known header, so we need to do a bit of running around to make that work
253 // Also, the fromHttpDate function is in the private qnetworkrequest header, so we can't use that
254 // So, simple workaround, just pass it through a dummy request and get a formatted date out (the
255 // cost is sufficiently low here, given we've just done a bunch of i/o heavy things, so...)
256 QNetworkRequest dummyRequest;
257 dummyRequest.setRawHeader(QByteArray{"Last-Modified"}, headerPair.second);
258 retryAfter = dummyRequest.header(QNetworkRequest::LastModifiedHeader).toDateTime();
259 break;
260 }
261 }
262 QTimer::singleShot(retryAfter.toMSecsSinceEpoch() - QDateTime::currentMSecsSinceEpoch(), this, &EngineBase::loadProviders);
263 // if it's a matter of a human moment's worth of seconds, just reload
264 if (retryAfter.toSecsSinceEpoch() - QDateTime::currentSecsSinceEpoch() > 2) {
265 // more than that, spit out TryAgainLaterError to let the user know what we're doing with their time
266 static const KFormat formatter;
267 Q_EMIT signalErrorCode(KNSCore::ErrorCode::TryAgainLaterError,
268 i18n("The service is currently undergoing maintenance and is expected to be back in %1.",
270 {retryAfter});
271 }
272 }
273 });
274 loader->load(d->providerFileUrl);
275 }
276 connect(loader, &XmlLoader::signalLoaded, this, &EngineBase::slotProviderFileLoaded);
277 connect(loader, &XmlLoader::signalFailed, this, &EngineBase::slotProvidersFailed);
278 }
279}
280
282{
283 return d->name;
284}
285
287{
288 return d->categories;
289}
290
291#if KNEWSTUFFCORE_BUILD_DEPRECATED_SINCE(6, 9)
293{
295 for (const auto &data : d->categoriesMetadata) {
296 list.append(Provider::CategoryMetadata{.id = data.id(), .name = data.name(), .displayName = data.displayName()});
297 }
298 return list;
299}
300#endif
301
303{
304 return d->categoriesMetadata;
305}
306
307#if KNEWSTUFFCORE_BUILD_DEPRECATED_SINCE(6, 9)
309{
311 for (const auto &preset : d->searchPresets) {
312 // This is slightly mad backwards compat. We back-convert a SearchPreset which requires a convert of
313 // SearchRequest and all the involved enums.
314 // Since this is the only place we need it this has been implemented thusly.
315 // Should someone find it offensive feel free to tear it apart into functions, but understand they are only
316 // used here.
317 list.append(KNSCompat::searchPresetToLegacy(preset));
318 }
319 return list;
320}
321#endif
322
324{
325 return d->searchPresets;
326}
327
329{
330 return d->useLabel;
331}
332
334{
335 return d->uploadEnabled;
336}
337
338#if KNEWSTUFFCORE_BUILD_DEPRECATED_SINCE(6, 9)
340{
341 // Connections are established in the modern variant of this function. No need to do anything.
342}
343#endif
344
345void EngineBase::providerInitialized([[maybe_unused]] Provider *p)
346{
347 // Unused. Replaced by lambda. Here for ABI stability.
348}
349
350void EngineBase::slotProvidersFailed()
351{
352 Q_EMIT signalErrorCode(KNSCore::ErrorCode::ProviderError,
353 i18n("Loading of providers from file: %1 failed", d->providerFileUrl.toString()),
354 d->providerFileUrl);
355}
356
357void EngineBase::slotProviderFileLoaded(const QDomDocument &doc)
358{
359 qCDebug(KNEWSTUFFCORE) << "slotProvidersLoaded";
360
361 bool isAtticaProviderFile = false;
362
363 // get each provider element, and create a provider object from it
365
366 if (providers.tagName() == QLatin1String("providers")) {
367 isAtticaProviderFile = true;
368 } else if (providers.tagName() != QLatin1String("ghnsproviders") && providers.tagName() != QLatin1String("knewstuffproviders")) {
369 qWarning() << "No document in providers.xml.";
370 Q_EMIT signalErrorCode(KNSCore::ErrorCode::ProviderError,
371 i18n("Could not load get hot new stuff providers from file: %1", d->providerFileUrl.toString()),
372 d->providerFileUrl);
373 return;
374 }
375
376 QDomElement n = providers.firstChildElement(QStringLiteral("provider"));
377 while (!n.isNull()) {
378 qCDebug(KNEWSTUFFCORE) << "Provider attributes: " << n.attribute(QStringLiteral("type"));
379
381 if (isAtticaProviderFile || n.attribute(QStringLiteral("type")).toLower() == QLatin1String("rest")) {
382 provider.reset(new ProviderCore(new AtticaProvider(d->categories, {})));
383 connect(provider->d->base, &ProviderBase::categoriesMetadataLoaded, this, [this](const QList<CategoryMetadata> &categories) {
384 d->categoriesMetadata = categories;
385 Q_EMIT signalCategoriesMetadataLoaded(categories);
386 });
387#ifdef SYNDICATION_FOUND
388 } else if (n.attribute(QStringLiteral("type")).toLower() == QLatin1String("opds")) {
389 provider.reset(new ProviderCore(new OPDSProvider));
390 connect(provider->d->base, &ProviderBase::searchPresetsLoaded, this, [this](const QList<SearchPreset> &presets) {
391 d->searchPresets = presets;
392 Q_EMIT signalSearchPresetsLoaded(presets);
393 });
394#endif
395 } else {
396 provider.reset(new ProviderCore(new StaticXmlProvider));
397 }
398
399 if (provider->d->base->setProviderXML(n)) {
400 d->addProvider(provider);
401 } else {
402 Q_EMIT signalErrorCode(KNSCore::ErrorCode::ProviderError, i18n("Error initializing provider."), d->providerFileUrl);
403 }
404 n = n.nextSiblingElement();
405 }
406 Q_EMIT loadingProvider();
407}
408
409void EngineBase::atticaProviderLoaded(const Attica::Provider &atticaProvider)
410{
411 qCDebug(KNEWSTUFFCORE) << "atticaProviderLoaded called";
412 if (!atticaProvider.hasContentService()) {
413 qCDebug(KNEWSTUFFCORE) << "Found provider: " << atticaProvider.baseUrl() << " but it does not support content";
414 return;
415 }
416 auto provider = QSharedPointer<KNSCore::ProviderCore>(new KNSCore::ProviderCore(new AtticaProvider(atticaProvider, d->categories, {})));
417 d->addProvider(provider);
418}
419
420#if KNEWSTUFFCORE_BUILD_DEPRECATED_SINCE(6, 9)
422{
423 return d->legacyCache;
424}
425#endif
426
428{
429 d->tagFilter = filter;
430 for (const auto &core : std::as_const(d->providerCores)) {
431 core->d->base->setTagFilter(d->tagFilter);
432 }
433}
434
436{
437 return d->tagFilter;
438}
439
441{
442 d->tagFilter << filter;
443 for (const auto &core : std::as_const(d->providerCores)) {
444 core->d->base->setTagFilter(d->tagFilter);
445 }
446}
447
449{
450 d->downloadTagFilter = filter;
451 for (const auto &core : std::as_const(d->providerCores)) {
452 core->d->base->setDownloadTagFilter(d->downloadTagFilter);
453 }
454}
455
457{
458 return d->downloadTagFilter;
459}
460
462{
463 d->downloadTagFilter << filter;
464 for (const auto &core : std::as_const(d->providerCores)) {
465 core->d->base->setDownloadTagFilter(d->downloadTagFilter);
466 }
467}
468
470{
471 // This function is absolutely horrific. Unfortunately used in discover.
473 ret.reserve(d->providerCores.size());
474 for (const auto &core : d->providerCores) {
475 if (const auto &provider = qobject_cast<AtticaProvider *>(core->d->base)) {
476 ret.append(provider->provider());
477 }
478 }
479 return ret;
480}
481
483{
484 const auto &core = d->providerCores.value(entry.providerId());
485 return core->d->base->userCanVote();
486}
487
488void EngineBase::vote(const Entry &entry, uint rating)
489{
490 const auto &core = d->providerCores.value(entry.providerId());
491 core->d->base->vote(entry, rating);
492}
493
495{
496 const auto &core = d->providerCores.value(entry.providerId());
497 return core->d->base->userCanBecomeFan();
498}
499
500void EngineBase::becomeFan(const Entry &entry)
501{
502 const auto &core = d->providerCores.value(entry.providerId());
503 core->d->base->becomeFan(entry);
504}
505
506#if KNEWSTUFFCORE_BUILD_DEPRECATED_SINCE(6, 9)
508{
509 return d->legacyProviders.value(providerId);
510}
511#endif
512
513#if KNEWSTUFFCORE_BUILD_DEPRECATED_SINCE(6, 9)
515{
516 if (d->legacyProviders.count() > 0) {
517 return d->legacyProviders.constBegin().value();
518 }
519 return nullptr;
520}
521#endif
522
524{
525 return d->legacyProviders.keys();
526}
527
529{
530 return !d->adoptionCommand.isEmpty();
531}
532
533void EngineBase::updateStatus()
534{
535}
536
537Installation *EngineBase::installation() const
538{
539 return d->installation;
540}
541
542#if KNEWSTUFFCORE_BUILD_DEPRECATED_SINCE(6, 9)
544{
545 return new ResultsStream(searchRequestFromLegacy(request), this);
546}
547#endif
548
550{
551 return d->contentWarningType;
552}
553
554#if KNEWSTUFFCORE_BUILD_DEPRECATED_SINCE(6, 9)
556{
557 return d->legacyProviders.values();
558}
559#endif
560
562{
563 return new ResultsStream(request, this);
564}
QUrl baseUrl() const
bool hasContentService() const
bool hasGroup(const QString &group) const
KConfigGroup group(const QString &group)
QString readEntry(const char *key, const char *aDefault=nullptr) const
bool exists() const
AccessMode accessMode() const override
QString formatSpelloutDuration(quint64 msecs) const
KNewStuff engine.
Definition enginebase.h:56
bool uploadEnabled
Whether or not the configuration says that the providers are expected to support uploading.
Definition enginebase.h:71
ContentWarningType
The ContentWarningType enum.
Definition enginebase.h:387
QList< Provider::SearchPreset > searchPresets()
void signalErrorCode(KNSCore::ErrorCode::ErrorCode errorCode, const QString &message, const QVariant &metadata)
Fires in the case of any critical or serious errors, such as network or API problems.
void addTagFilter(const QString &filter)
Add a single filter entry to the entry tag filter.
bool userCanBecomeFan(const Entry &entry)
Whether or not the user is allowed to become a fan of a particular entry.
QStringList categories() const
The list of the server-side names of the categories handled by this engine instance.
virtual void addProvider(QSharedPointer< KNSCore::Provider > provider)
Add a provider and connect it to the right slots.
QList< QSharedPointer< Provider > > providers() const
void becomeFan(const Entry &entry)
This will mark the user who is currently authenticated as a fan of the entry passed to the function.
bool hasAdoptionCommand() const
Whether or not an adoption command exists for this engine.
QSharedPointer< Cache > cache() const
Get the entries cache for this engine (note that it may be null if the engine is not yet initialized)...
QList< SearchPreset > searchPresets2()
void setTagFilter(const QStringList &filter)
Set a filter for results, which filters out all entries which do not match the filter,...
QStringList tagFilter() const
Gets the current tag filter list.
QString name() const
The name as defined by the knsrc file.
Q_SIGNAL void contentWarningTypeChanged()
Emitted after the initial config load.
QSharedPointer< Provider > provider(const QString &providerId) const
The Provider instance with the passed ID.
Q_SIGNAL void uploadEnabledChanged()
Fired when the uploadEnabled property changes.
Q_SIGNAL void useLabelChanged()
Signal gets emitted when the useLabel property changes.
bool userCanVote(const Entry &entry)
Whether or not a user is able to vote on the passed entry.
static QStringList availableConfigFiles()
List of all available config files.
QStringList downloadTagFilter() const
Gets the current downloadlink tag filter list.
void providersChanged()
Fired whenever the list of providers changes.
QString useLabel
Text that should be displayed for the adoption button, this defaults to "Use".
Definition enginebase.h:63
virtual bool init(const QString &configfile)
Initializes the engine.
QList< CategoryMetadata > categoriesMetadata2()
The list of metadata for the categories handled by this engine instance.
void vote(const Entry &entry, uint rating)
Cast a vote on the passed entry.
QList< Attica::Provider * > atticaProviders() const
QList< Provider::CategoryMetadata > categoriesMetadata()
ResultsStream * search(const KNSCore::Provider::SearchRequest &request)
Returns a stream object that will fulfill the request.
void addDownloadTagFilter(const QString &filter)
Add a single filter entry to the download tag filter.
QSharedPointer< Provider > defaultProvider() const
Return the first provider in the providers list (usually the default provider)
QStringList providerIDs
Definition enginebase.h:76
ContentWarningType contentWarningType
Definition enginebase.h:81
void setDownloadTagFilter(const QStringList &filter)
Sets a filter to be applied to the downloads for an entry.
void signalMessage(const QString &message)
Indicates a message to be added to the ui's log, or sent to a messagebox.
KNewStuff data entry container.
Definition entry.h:48
KNewStuff Base Provider class.
KNewStuff Base Provider class.
Definition provider.h:47
The ResultsStream is returned by EngineBase::search.
A search request.
Q_SCRIPTABLE CaptureState status()
QString i18n(const char *text, const TYPE &arg...)
KCOREADDONS_EXPORT QStringList findAllUniqueFiles(const QStringList &dirs, const QStringList &nameFilters={})
const char * constData() const const
qint64 currentMSecsSinceEpoch()
qint64 currentSecsSinceEpoch()
qint64 toMSecsSinceEpoch() const const
qint64 toSecsSinceEpoch() const const
QDomElement documentElement() const const
QString attribute(const QString &name, const QString &defValue) const const
QDomElement firstChildElement(const QString &tagName, const QString &namespaceURI) const const
bool isNull() const const
QDomElement nextSiblingElement(const QString &tagName, const QString &namespaceURI) const const
QString completeBaseName() const const
bool exists() const const
void append(QList< T > &&value)
void reserve(qsizetype size)
QMetaEnum fromType()
int keyToValue(const char *key, bool *ok) const const
QVariant header(KnownHeaders header) const const
void setRawHeader(const QByteArray &headerName, const QByteArray &headerValue)
Q_EMITQ_EMIT
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
T qobject_cast(QObject *object)
T * get() const const
QString locate(StandardLocation type, const QString &fileName, LocateOptions options)
QStringList locateAll(StandardLocation type, const QString &fileName, LocateOptions options)
QStringList standardLocations(StandardLocation type)
QString left(qsizetype n) const const
qsizetype length() const const
QFuture< ArgsType< Signal > > connect(Sender *sender, Signal signal)
QUrl fromLocalFile(const QString &localFile)
QDateTime toDateTime() const const
Describes a category: id/name/displayName.
Definition provider.h:112
used to keep track of a search
Definition provider.h:77
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Mon Nov 18 2024 12:20:03 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.