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->cache = Cache2::getCache(configFileBasename);
198 qCDebug(KNEWSTUFFCORE) << "Cache is" << d->cache << "for" << configFileBasename;
199 d->cache->readRegistry();
200 // This is a facade for cache2, no need to call anything on it.
201 d->legacyCache = Cache::getCache(configFileBasename);
202
203 // Cache cleanup option, to help work around people deleting files from underneath KNewStuff (this
204 // happens a lot with e.g. wallpapers and icons)
205 if (d->installation->uncompressionSetting() == Installation::UseKPackageUncompression) {
206 d->shouldRemoveDeletedEntries = true;
207 }
208
209 d->shouldRemoveDeletedEntries = group.readEntry("RemoveDeadEntries", d->shouldRemoveDeletedEntries);
210 if (d->shouldRemoveDeletedEntries) {
211 d->cache->removeDeletedEntries();
212 }
213
214 loadProviders();
215
216 return true;
217}
218
219void EngineBase::loadProviders()
220{
221 if (d->providerFileUrl.isEmpty()) {
222 // it would be nicer to move the attica stuff into its own class
223 qCDebug(KNEWSTUFFCORE) << "Using OCS default providers";
224 delete d->atticaProviderManager;
225 d->atticaProviderManager = new Attica::ProviderManager;
226 connect(d->atticaProviderManager, &Attica::ProviderManager::providerAdded, this, &EngineBase::atticaProviderLoaded);
227 connect(d->atticaProviderManager, &Attica::ProviderManager::failedToLoad, this, &EngineBase::slotProvidersFailed);
228 d->atticaProviderManager->loadDefaultProviders();
229 } else {
230 qCDebug(KNEWSTUFFCORE) << "loading providers from " << d->providerFileUrl;
231 Q_EMIT loadingProvider();
232
233 XmlLoader *loader = s_engineProviderLoaders()->localData().value(d->providerFileUrl);
234 if (!loader) {
235 qCDebug(KNEWSTUFFCORE) << "No xml loader for this url yet, so create one and temporarily store that" << d->providerFileUrl;
236 loader = new XmlLoader(this);
237 s_engineProviderLoaders()->localData().insert(d->providerFileUrl, loader);
238 connect(loader, &XmlLoader::signalLoaded, this, [this]() {
239 s_engineProviderLoaders()->localData().remove(d->providerFileUrl);
240 });
241 connect(loader, &XmlLoader::signalFailed, this, [this]() {
242 s_engineProviderLoaders()->localData().remove(d->providerFileUrl);
243 });
244 connect(loader, &XmlLoader::signalHttpError, this, [this](int status, QList<QNetworkReply::RawHeaderPair> rawHeaders) {
245 if (status == 503) { // Temporarily Unavailable
246 QDateTime retryAfter;
247 static const QByteArray retryAfterKey{"Retry-After"};
248 for (const QNetworkReply::RawHeaderPair &headerPair : rawHeaders) {
249 if (headerPair.first == retryAfterKey) {
250 // Retry-After is not a known header, so we need to do a bit of running around to make that work
251 // Also, the fromHttpDate function is in the private qnetworkrequest header, so we can't use that
252 // So, simple workaround, just pass it through a dummy request and get a formatted date out (the
253 // cost is sufficiently low here, given we've just done a bunch of i/o heavy things, so...)
254 QNetworkRequest dummyRequest;
255 dummyRequest.setRawHeader(QByteArray{"Last-Modified"}, headerPair.second);
256 retryAfter = dummyRequest.header(QNetworkRequest::LastModifiedHeader).toDateTime();
257 break;
258 }
259 }
260 QTimer::singleShot(retryAfter.toMSecsSinceEpoch() - QDateTime::currentMSecsSinceEpoch(), this, &EngineBase::loadProviders);
261 // if it's a matter of a human moment's worth of seconds, just reload
262 if (retryAfter.toSecsSinceEpoch() - QDateTime::currentSecsSinceEpoch() > 2) {
263 // more than that, spit out TryAgainLaterError to let the user know what we're doing with their time
264 static const KFormat formatter;
265 Q_EMIT signalErrorCode(KNSCore::ErrorCode::TryAgainLaterError,
266 i18n("The service is currently undergoing maintenance and is expected to be back in %1.",
268 {retryAfter});
269 }
270 }
271 });
272 loader->load(d->providerFileUrl);
273 }
274 connect(loader, &XmlLoader::signalLoaded, this, &EngineBase::slotProviderFileLoaded);
275 connect(loader, &XmlLoader::signalFailed, this, &EngineBase::slotProvidersFailed);
276 }
277}
278
280{
281 return d->name;
282}
283
285{
286 return d->categories;
287}
288
289#if KNEWSTUFFCORE_BUILD_DEPRECATED_SINCE(6, 9)
291{
293 for (const auto &data : d->categoriesMetadata) {
294 list.append(Provider::CategoryMetadata{.id = data.id(), .name = data.name(), .displayName = data.displayName()});
295 }
296 return list;
297}
298#endif
299
301{
302 return d->categoriesMetadata;
303}
304
305#if KNEWSTUFFCORE_BUILD_DEPRECATED_SINCE(6, 9)
307{
309 for (const auto &preset : d->searchPresets) {
310 // This is slightly mad backwards compat. We back-convert a SearchPreset which requires a convert of
311 // SearchRequest and all the involved enums.
312 // Since this is the only place we need it this has been implemented thusly.
313 // Should someone find it offensive feel free to tear it apart into functions, but understand they are only
314 // used here.
315 list.append(KNSCompat::searchPresetToLegacy(preset));
316 }
317 return list;
318}
319#endif
320
322{
323 return d->searchPresets;
324}
325
327{
328 return d->useLabel;
329}
330
332{
333 return d->uploadEnabled;
334}
335
336#if KNEWSTUFFCORE_BUILD_DEPRECATED_SINCE(6, 9)
338{
339 // Connections are established in the modern variant of this function. No need to do anything.
340}
341#endif
342
343void EngineBase::providerInitialized([[maybe_unused]] Provider *p)
344{
345 // Unused. Replaced by lambda. Here for ABI stability.
346}
347
348void EngineBase::slotProvidersFailed()
349{
350 Q_EMIT signalErrorCode(KNSCore::ErrorCode::ProviderError,
351 i18n("Loading of providers from file: %1 failed", d->providerFileUrl.toString()),
352 d->providerFileUrl);
353}
354
355void EngineBase::slotProviderFileLoaded(const QDomDocument &doc)
356{
357 qCDebug(KNEWSTUFFCORE) << "slotProvidersLoaded";
358
359 bool isAtticaProviderFile = false;
360
361 // get each provider element, and create a provider object from it
363
364 if (providers.tagName() == QLatin1String("providers")) {
365 isAtticaProviderFile = true;
366 } else if (providers.tagName() != QLatin1String("ghnsproviders") && providers.tagName() != QLatin1String("knewstuffproviders")) {
367 qWarning() << "No document in providers.xml.";
368 Q_EMIT signalErrorCode(KNSCore::ErrorCode::ProviderError,
369 i18n("Could not load get hot new stuff providers from file: %1", d->providerFileUrl.toString()),
370 d->providerFileUrl);
371 return;
372 }
373
374 QDomElement n = providers.firstChildElement(QStringLiteral("provider"));
375 while (!n.isNull()) {
376 qCDebug(KNEWSTUFFCORE) << "Provider attributes: " << n.attribute(QStringLiteral("type"));
377
379 if (isAtticaProviderFile || n.attribute(QStringLiteral("type")).toLower() == QLatin1String("rest")) {
380 provider.reset(new ProviderCore(new AtticaProvider(d->categories, {})));
381 connect(provider->d->base, &ProviderBase::categoriesMetadataLoaded, this, [this](const QList<CategoryMetadata> &categories) {
382 d->categoriesMetadata = categories;
383 Q_EMIT signalCategoriesMetadataLoaded(categories);
384 });
385#ifdef SYNDICATION_FOUND
386 } else if (n.attribute(QStringLiteral("type")).toLower() == QLatin1String("opds")) {
387 provider.reset(new ProviderCore(new OPDSProvider));
388 connect(provider->d->base, &ProviderBase::searchPresetsLoaded, this, [this](const QList<SearchPreset> &presets) {
389 d->searchPresets = presets;
390 Q_EMIT signalSearchPresetsLoaded(presets);
391 });
392#endif
393 } else {
394 provider.reset(new ProviderCore(new StaticXmlProvider));
395 }
396
397 if (provider->d->base->setProviderXML(n)) {
398 d->addProvider(provider);
399 } else {
400 Q_EMIT signalErrorCode(KNSCore::ErrorCode::ProviderError, i18n("Error initializing provider."), d->providerFileUrl);
401 }
402 n = n.nextSiblingElement();
403 }
404 Q_EMIT loadingProvider();
405}
406
407void EngineBase::atticaProviderLoaded(const Attica::Provider &atticaProvider)
408{
409 qCDebug(KNEWSTUFFCORE) << "atticaProviderLoaded called";
410 if (!atticaProvider.hasContentService()) {
411 qCDebug(KNEWSTUFFCORE) << "Found provider: " << atticaProvider.baseUrl() << " but it does not support content";
412 return;
413 }
414 auto provider = QSharedPointer<KNSCore::ProviderCore>(new KNSCore::ProviderCore(new AtticaProvider(atticaProvider, d->categories, {})));
415 d->addProvider(provider);
416}
417
418#if KNEWSTUFFCORE_BUILD_DEPRECATED_SINCE(6, 9)
420{
421 return d->legacyCache;
422}
423#endif
424
426{
427 d->tagFilter = filter;
428 for (const auto &core : std::as_const(d->providerCores)) {
429 core->d->base->setTagFilter(d->tagFilter);
430 }
431}
432
434{
435 return d->tagFilter;
436}
437
439{
440 d->tagFilter << filter;
441 for (const auto &core : std::as_const(d->providerCores)) {
442 core->d->base->setTagFilter(d->tagFilter);
443 }
444}
445
447{
448 d->downloadTagFilter = filter;
449 for (const auto &core : std::as_const(d->providerCores)) {
450 core->d->base->setDownloadTagFilter(d->downloadTagFilter);
451 }
452}
453
455{
456 return d->downloadTagFilter;
457}
458
460{
461 d->downloadTagFilter << filter;
462 for (const auto &core : std::as_const(d->providerCores)) {
463 core->d->base->setDownloadTagFilter(d->downloadTagFilter);
464 }
465}
466
468{
469 // This function is absolutely horrific. Unfortunately used in discover.
471 ret.reserve(d->providerCores.size());
472 for (const auto &core : d->providerCores) {
473 if (const auto &provider = qobject_cast<AtticaProvider *>(core->d->base)) {
474 ret.append(provider->provider());
475 }
476 }
477 return ret;
478}
479
481{
482 const auto &core = d->providerCores.value(entry.providerId());
483 return core->d->base->userCanVote();
484}
485
486void EngineBase::vote(const Entry &entry, uint rating)
487{
488 const auto &core = d->providerCores.value(entry.providerId());
489 core->d->base->vote(entry, rating);
490}
491
493{
494 const auto &core = d->providerCores.value(entry.providerId());
495 return core->d->base->userCanBecomeFan();
496}
497
498void EngineBase::becomeFan(const Entry &entry)
499{
500 const auto &core = d->providerCores.value(entry.providerId());
501 core->d->base->becomeFan(entry);
502}
503
504#if KNEWSTUFFCORE_BUILD_DEPRECATED_SINCE(6, 9)
506{
507 return d->legacyProviders.value(providerId);
508}
509#endif
510
511#if KNEWSTUFFCORE_BUILD_DEPRECATED_SINCE(6, 9)
513{
514 if (!d->legacyProviders.isEmpty()) {
515 return d->legacyProviders.constBegin().value();
516 }
517 return nullptr;
518}
519#endif
520
522{
523 return d->legacyProviders.keys();
524}
525
527{
528 return !d->adoptionCommand.isEmpty();
529}
530
531void EngineBase::updateStatus()
532{
533}
534
535Installation *EngineBase::installation() const
536{
537 return d->installation;
538}
539
540#if KNEWSTUFFCORE_BUILD_DEPRECATED_SINCE(6, 9)
542{
543 return new ResultsStream(searchRequestFromLegacy(request), this);
544}
545#endif
546
548{
549 return d->contentWarningType;
550}
551
552#if KNEWSTUFFCORE_BUILD_DEPRECATED_SINCE(6, 9)
554{
555 return d->legacyProviders.values();
556}
557#endif
558
560{
561 return new ResultsStream(request, this);
562}
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 Sat Dec 21 2024 17:02:29 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.