KIO

deletejob.cpp
1/*
2 This file is part of the KDE libraries
3 SPDX-FileCopyrightText: 2000 Stephan Kulow <coolo@kde.org>
4 SPDX-FileCopyrightText: 2000-2009 David Faure <faure@kde.org>
5 SPDX-FileCopyrightText: 2000 Waldo Bastian <bastian@kde.org>
6
7 SPDX-License-Identifier: LGPL-2.0-or-later
8*/
9
10#include "deletejob.h"
11
12#include "../utils_p.h"
13#include "job.h" // buildErrorString
14#include "kcoredirlister.h"
15#include "kprotocolmanager.h"
16#include "listjob.h"
17#include "statjob.h"
18#include <KDirWatch>
19#include <kdirnotify.h>
20
21#include <KLocalizedString>
22#include <kio/jobuidelegatefactory.h>
23
24#include <QDir>
25#include <QFile>
26#include <QFileInfo>
27#include <QMetaObject>
28#include <QPointer>
29#include <QThread>
30#include <QTimer>
31
32#include "job_p.h"
33
34extern bool kio_resolve_local_urls; // from copyjob.cpp, abused here to save a symbol.
35
36static bool isHttpProtocol(const QString &protocol)
37{
38 return (protocol.startsWith(QLatin1String("webdav"), Qt::CaseInsensitive) || protocol.startsWith(QLatin1String("http"), Qt::CaseInsensitive));
39}
40
41namespace KIO
42{
43enum DeleteJobState {
44 DELETEJOB_STATE_STATING,
45 DELETEJOB_STATE_DELETING_FILES,
46 DELETEJOB_STATE_DELETING_DIRS,
47};
48
49class DeleteJobIOWorker : public QObject
50{
52
54 void rmfileResult(bool succeeded, bool isLink);
55 void rmddirResult(bool succeeded);
56
57public Q_SLOTS:
58
59 /**
60 * Deletes the file @p url points to
61 * The file must be a LocalFile
62 */
63 void rmfile(const QUrl &url, bool isLink)
64 {
65 Q_EMIT rmfileResult(QFile::remove(url.toLocalFile()), isLink);
66 }
67
68 /**
69 * Deletes the directory @p url points to
70 * The directory must be a LocalFile
71 */
72 void rmdir(const QUrl &url)
73 {
74 Q_EMIT rmddirResult(QDir().rmdir(url.toLocalFile()));
75 }
76};
77
78class DeleteJobPrivate : public KIO::JobPrivate
79{
80public:
81 explicit DeleteJobPrivate(const QList<QUrl> &src)
82 : state(DELETEJOB_STATE_STATING)
83 , m_processedFiles(0)
84 , m_processedDirs(0)
85 , m_totalFilesDirs(0)
86 , m_srcList(src)
87 , m_currentStat(m_srcList.begin())
88 , m_reportTimer(nullptr)
89 {
90 }
91 DeleteJobState state;
92 int m_processedFiles;
93 int m_processedDirs;
94 int m_totalFilesDirs;
95 QUrl m_currentURL;
96 QList<QUrl> files;
97 QList<QUrl> symlinks;
98 QList<QUrl> dirs;
99 QList<QUrl> m_srcList;
100 QList<QUrl>::iterator m_currentStat;
101 QSet<QString> m_parentDirs;
102 QTimer *m_reportTimer;
103 DeleteJobIOWorker *m_ioworker = nullptr;
104 QThread *m_thread = nullptr;
105
106 void statNextSrc();
107 DeleteJobIOWorker *worker();
108 void currentSourceStated(bool isDir, bool isLink);
109 void finishedStatPhase();
110 void deleteNextFile();
111 void deleteNextDir();
112 void restoreDirWatch() const;
113 void slotReport();
114 void slotStart();
115 void slotEntries(KIO::Job *, const KIO::UDSEntryList &list);
116
117 /// Callback of worker rmfile
118 void rmFileResult(bool result, bool isLink);
119 /// Callback of worker rmdir
120 void rmdirResult(bool result);
121 void deleteFileUsingJob(const QUrl &url, bool isLink);
122 void deleteDirUsingJob(const QUrl &url);
123
124 ~DeleteJobPrivate() override;
125
126 Q_DECLARE_PUBLIC(DeleteJob)
127
128 static inline DeleteJob *newJob(const QList<QUrl> &src, JobFlags flags)
129 {
130 DeleteJob *job = new DeleteJob(*new DeleteJobPrivate(src));
132 if (!(flags & HideProgressInfo)) {
134 }
135 if (!(flags & NoPrivilegeExecution)) {
136 job->d_func()->m_privilegeExecutionEnabled = true;
137 job->d_func()->m_operationType = Delete;
138 }
139 return job;
140 }
141};
142
143} // namespace KIO
144
145using namespace KIO;
146
147DeleteJob::DeleteJob(DeleteJobPrivate &dd)
148 : Job(dd)
149{
150 Q_D(DeleteJob);
151
152 d->m_reportTimer = new QTimer(this);
153 connect(d->m_reportTimer, &QTimer::timeout, this, [d]() {
154 d->slotReport();
155 });
156 // this will update the report dialog with 5 Hz, I think this is fast enough, aleXXX
157 d->m_reportTimer->start(200);
158
159 QTimer::singleShot(0, this, [d]() {
160 d->slotStart();
161 });
162}
163
164DeleteJob::~DeleteJob()
165{
166}
167
168DeleteJobPrivate::~DeleteJobPrivate()
169{
170 if (m_thread) {
171 m_thread->quit();
172 m_thread->wait();
173 delete m_thread;
174 }
175}
176
178{
179 return d_func()->m_srcList;
180}
181
182void DeleteJobPrivate::slotStart()
183{
184 statNextSrc();
185}
186
187DeleteJobIOWorker *DeleteJobPrivate::worker()
188{
189 Q_Q(DeleteJob);
190
191 if (!m_ioworker) {
192 m_thread = new QThread();
193
194 m_ioworker = new DeleteJobIOWorker;
195 m_ioworker->moveToThread(m_thread);
197 QObject::connect(m_ioworker, &DeleteJobIOWorker::rmfileResult, q, [=, this](bool result, bool isLink) {
198 this->rmFileResult(result, isLink);
199 });
200 QObject::connect(m_ioworker, &DeleteJobIOWorker::rmddirResult, q, [=, this](bool result) {
201 this->rmdirResult(result);
202 });
203 m_thread->start();
204 }
205
206 return m_ioworker;
207}
208
209void DeleteJobPrivate::slotReport()
210{
211 Q_Q(DeleteJob);
212 Q_EMIT q->deleting(q, m_currentURL);
213
214 // TODO: maybe we could skip everything else when (flags & HideProgressInfo) ?
215 JobPrivate::emitDeleting(q, m_currentURL);
216
217 switch (state) {
218 case DELETEJOB_STATE_STATING:
219 q->setTotalAmount(KJob::Files, files.count());
220 q->setTotalAmount(KJob::Directories, dirs.count());
221 break;
222 case DELETEJOB_STATE_DELETING_DIRS:
223 q->setProcessedAmount(KJob::Directories, m_processedDirs);
224 q->emitPercent(m_processedFiles + m_processedDirs, m_totalFilesDirs);
225 break;
226 case DELETEJOB_STATE_DELETING_FILES:
227 q->setProcessedAmount(KJob::Files, m_processedFiles);
228 q->emitPercent(m_processedFiles, m_totalFilesDirs);
229 break;
230 }
231}
232
233void DeleteJobPrivate::slotEntries(KIO::Job *job, const UDSEntryList &list)
234{
237 for (; it != end; ++it) {
238 const UDSEntry &entry = *it;
239 const QString displayName = entry.stringValue(KIO::UDSEntry::UDS_NAME);
240
241 Q_ASSERT(!displayName.isEmpty());
242 if (displayName != QLatin1String("..") && displayName != QLatin1String(".")) {
243 QUrl url;
244 const QString urlStr = entry.stringValue(KIO::UDSEntry::UDS_URL);
245 if (!urlStr.isEmpty()) {
246 url = QUrl(urlStr);
247 } else {
248 url = static_cast<SimpleJob *>(job)->url(); // assumed to be a dir
249 url.setPath(Utils::concatPaths(url.path(), displayName));
250 }
251
252 // qDebug() << displayName << "(" << url << ")";
253 if (entry.isLink()) {
254 symlinks.append(url);
255 } else if (entry.isDir()) {
256 dirs.append(url);
257 } else {
258 files.append(url);
259 }
260 }
261 }
262}
263
264void DeleteJobPrivate::statNextSrc()
265{
266 Q_Q(DeleteJob);
267 // qDebug();
268 if (m_currentStat != m_srcList.end()) {
269 m_currentURL = (*m_currentStat);
270
271 // if the file system doesn't support deleting, we do not even stat
272 if (!KProtocolManager::supportsDeleting(m_currentURL)) {
273 QPointer<DeleteJob> that = q;
274 ++m_currentStat;
275 Q_EMIT q->warning(q, buildErrorString(ERR_CANNOT_DELETE, m_currentURL.toDisplayString()));
276 if (that) {
277 statNextSrc();
278 }
279 return;
280 }
281 // Stat it
282 state = DELETEJOB_STATE_STATING;
283
284 // Fast path for KFileItems in directory views
285 while (m_currentStat != m_srcList.end()) {
286 m_currentURL = (*m_currentStat);
287 const KFileItem cachedItem = KCoreDirLister::cachedItemForUrl(m_currentURL);
288 if (cachedItem.isNull()) {
289 break;
290 }
291 // qDebug() << "Found cached info about" << m_currentURL << "isDir=" << cachedItem.isDir() << "isLink=" << cachedItem.isLink();
292 currentSourceStated(cachedItem.isDir(), cachedItem.isLink());
293 ++m_currentStat;
294 }
295
296 // Hook for unit test to disable the fast path.
297 if (!kio_resolve_local_urls) {
298 // Fast path for local files
299 // (using a loop, instead of a huge recursion)
300 while (m_currentStat != m_srcList.end() && (*m_currentStat).isLocalFile()) {
301 m_currentURL = (*m_currentStat);
302 QFileInfo fileInfo(m_currentURL.toLocalFile());
303 currentSourceStated(fileInfo.isDir(), fileInfo.isSymLink());
304 ++m_currentStat;
305 }
306 }
307 if (m_currentStat == m_srcList.end()) {
308 // Done, jump to the last else of this method
309 statNextSrc();
310 } else {
311 KIO::SimpleJob *job = KIO::stat(m_currentURL, StatJob::SourceSide, KIO::StatBasic, KIO::HideProgressInfo);
312 // qDebug() << "stat'ing" << m_currentURL;
313 q->addSubjob(job);
314 }
315 } else {
316 if (!q->hasSubjobs()) { // don't go there yet if we're still listing some subdirs
317 finishedStatPhase();
318 }
319 }
320}
321
322void DeleteJobPrivate::finishedStatPhase()
323{
324 m_totalFilesDirs = files.count() + symlinks.count() + dirs.count();
325 slotReport();
326 // Now we know which dirs hold the files we're going to delete.
327 // To speed things up and prevent double-notification, we disable KDirWatch
328 // on those dirs temporarily (using KDirWatch::self, that's the instance
329 // used by e.g. kdirlister).
330 for (const QString &dir : std::as_const(m_parentDirs)) {
332 }
333 state = DELETEJOB_STATE_DELETING_FILES;
334 deleteNextFile();
335}
336
337void DeleteJobPrivate::rmFileResult(bool result, bool isLink)
338{
339 if (result) {
340 m_processedFiles++;
341
342 if (isLink) {
343 symlinks.removeFirst();
344 } else {
345 files.removeFirst();
346 }
347
348 deleteNextFile();
349 } else {
350 // fallback if QFile::remove() failed (we'll use the job's error handling in that case)
351 deleteFileUsingJob(m_currentURL, isLink);
352 }
353}
354
355void DeleteJobPrivate::deleteFileUsingJob(const QUrl &url, bool isLink)
356{
357 Q_Q(DeleteJob);
358
359 SimpleJob *job;
360 if (isHttpProtocol(url.scheme())) {
362 } else {
364 job->setParentJob(q);
365 }
366
367 if (isLink) {
368 symlinks.removeFirst();
369 } else {
370 files.removeFirst();
371 }
372
373 q->addSubjob(job);
374}
375
376void DeleteJobPrivate::deleteNextFile()
377{
378 // qDebug();
379
380 // if there is something else to delete
381 // the loop is run using callbacks slotResult and rmFileResult
382 if (!files.isEmpty() || !symlinks.isEmpty()) {
383 // Take first file to delete out of list
384 QList<QUrl>::iterator it = files.begin();
385 const bool isLink = (it == files.end()); // No more files
386 if (isLink) {
387 it = symlinks.begin(); // Pick up a symlink to delete
388 }
389 m_currentURL = (*it);
390
391 // If local file, try do it directly
392 if (m_currentURL.isLocalFile()) {
393 // separate thread will do the work
394 DeleteJobIOWorker *w = worker();
395 auto rmfileFunc = [this, w, isLink]() {
396 w->rmfile(m_currentURL, isLink);
397 };
399 } else {
400 // if remote, use a job
401 deleteFileUsingJob(m_currentURL, isLink);
402 }
403 return;
404 }
405
406 state = DELETEJOB_STATE_DELETING_DIRS;
407 deleteNextDir();
408}
409
410void DeleteJobPrivate::rmdirResult(bool result)
411{
412 if (result) {
413 m_processedDirs++;
414 dirs.removeLast();
415 deleteNextDir();
416 } else {
417 // fallback
418 deleteDirUsingJob(m_currentURL);
419 }
420}
421
422void DeleteJobPrivate::deleteDirUsingJob(const QUrl &url)
423{
424 Q_Q(DeleteJob);
425
426 // Call rmdir - works for KIO workers with canDeleteRecursive too,
427 // CMD_DEL will trigger the recursive deletion in the worker.
428 SimpleJob *job = KIO::rmdir(url);
429 job->setParentJob(q);
430 job->addMetaData(QStringLiteral("recurse"), QStringLiteral("true"));
431 dirs.removeLast();
432 q->addSubjob(job);
433}
434
435void DeleteJobPrivate::deleteNextDir()
436{
437 Q_Q(DeleteJob);
438
439 if (!dirs.isEmpty()) { // some dirs to delete ?
440
441 // the loop is run using callbacks slotResult and rmdirResult
442 // Take first dir to delete out of list - last ones first !
443 QList<QUrl>::iterator it = --dirs.end();
444 m_currentURL = (*it);
445 // If local dir, try to rmdir it directly
446 if (m_currentURL.isLocalFile()) {
447 // delete it on separate worker thread
448 DeleteJobIOWorker *w = worker();
449 auto rmdirFunc = [this, w]() {
450 w->rmdir(m_currentURL);
451 };
453 } else {
454 deleteDirUsingJob(m_currentURL);
455 }
456 return;
457 }
458
459 // Re-enable watching on the dirs that held the deleted files
460 restoreDirWatch();
461
462 // Finished - tell the world
463 if (!m_srcList.isEmpty()) {
464 // qDebug() << "KDirNotify'ing FilesRemoved" << m_srcList;
465#ifdef WITH_QTDBUS
466 org::kde::KDirNotify::emitFilesRemoved(m_srcList);
467#endif
468 }
469 if (m_reportTimer != nullptr) {
470 m_reportTimer->stop();
471 }
472 // display final numbers
473 q->setProcessedAmount(KJob::Directories, m_processedDirs);
474 q->setProcessedAmount(KJob::Files, m_processedFiles);
475 q->emitPercent(m_processedFiles + m_processedDirs, m_totalFilesDirs);
476
477 q->emitResult();
478}
479
480void DeleteJobPrivate::restoreDirWatch() const
481{
482 const auto itEnd = m_parentDirs.constEnd();
483 for (auto it = m_parentDirs.constBegin(); it != itEnd; ++it) {
485 }
486}
487
488void DeleteJobPrivate::currentSourceStated(bool isDir, bool isLink)
489{
490 Q_Q(DeleteJob);
491 const QUrl url = (*m_currentStat);
492 if (isDir && !isLink) {
493 // Add toplevel dir in list of dirs
494 dirs.append(url);
495 if (url.isLocalFile()) {
496 // We are about to delete this dir, no need to watch it
497 // Maybe we should ask kdirwatch to remove all watches recursively?
498 // But then there would be no feedback (things disappearing progressively) during huge deletions
500 }
502 // qDebug() << url << "is a directory, let's list it";
503 ListJob *newjob = KIO::listRecursive(url, KIO::HideProgressInfo);
504 newjob->addMetaData(QStringLiteral("details"), QString::number(KIO::StatBasic));
505 newjob->setUnrestricted(true); // No KIOSK restrictions
506 QObject::connect(newjob, &KIO::ListJob::entries, q, [this](KIO::Job *job, const KIO::UDSEntryList &list) {
507 slotEntries(job, list);
508 });
509 q->addSubjob(newjob);
510 // Note that this listing job will happen in parallel with other stat jobs.
511 }
512 } else {
513 if (isLink) {
514 // qDebug() << "Target is a symlink";
515 symlinks.append(url);
516 } else {
517 // qDebug() << "Target is a file";
518 files.append(url);
519 }
520 }
521 if (url.isLocalFile()) {
522 const QString parentDir = url.adjusted(QUrl::RemoveFilename | QUrl::StripTrailingSlash).path();
523 m_parentDirs.insert(parentDir);
524 }
525}
526
527void DeleteJob::slotResult(KJob *job)
528{
529 Q_D(DeleteJob);
530 switch (d->state) {
531 case DELETEJOB_STATE_STATING:
532 removeSubjob(job);
533
534 // Was this a stat job or a list job? We do both in parallel.
535 if (StatJob *statJob = qobject_cast<StatJob *>(job)) {
536 // Was there an error while stating ?
537 if (job->error()) {
538 // Probably : doesn't exist
539 Job::slotResult(job); // will set the error and emit result(this)
540 d->restoreDirWatch();
541 return;
542 }
543
544 const UDSEntry &entry = statJob->statResult();
545 // Is it a file or a dir ?
546 const bool isLink = entry.isLink();
547 const bool isDir = entry.isDir();
548 d->currentSourceStated(isDir, isLink);
549
550 ++d->m_currentStat;
551 d->statNextSrc();
552 } else {
553 if (job->error()) {
554 // Try deleting nonetheless, it may be empty (and non-listable)
555 }
556 if (!hasSubjobs()) {
557 d->finishedStatPhase();
558 }
559 }
560 break;
561 case DELETEJOB_STATE_DELETING_FILES:
562 // Propagate the subjob's metadata (a SimpleJob) to the real DeleteJob
563 // FIXME: setMetaData() in the KIO API only allows access to outgoing metadata,
564 // but we need to alter the incoming one
565 d->m_incomingMetaData = dynamic_cast<KIO::Job *>(job)->metaData();
566
567 if (job->error()) {
568 Job::slotResult(job); // will set the error and emit result(this)
569 d->restoreDirWatch();
570 return;
571 }
572 removeSubjob(job);
573 Q_ASSERT(!hasSubjobs());
574 d->m_processedFiles++;
575
576 d->deleteNextFile();
577 break;
578 case DELETEJOB_STATE_DELETING_DIRS:
579 if (job->error()) {
580 Job::slotResult(job); // will set the error and emit result(this)
581 d->restoreDirWatch();
582 return;
583 }
584 removeSubjob(job);
585 Q_ASSERT(!hasSubjobs());
586 d->m_processedDirs++;
587 // emit processedAmount( this, KJob::Directories, d->m_processedDirs );
588 // emitPercent( d->m_processedFiles + d->m_processedDirs, d->m_totalFilesDirs );
589
590 d->deleteNextDir();
591 break;
592 default:
593 Q_ASSERT(0);
594 }
595}
596
597DeleteJob *KIO::del(const QUrl &src, JobFlags flags)
598{
599 QList<QUrl> srcList;
600 srcList.append(src);
601 DeleteJob *job = DeleteJobPrivate::newJob(srcList, flags);
602 if (job->uiDelegateExtension()) {
603 job->uiDelegateExtension()->createClipboardUpdater(job, JobUiDelegateExtension::RemoveContent);
604 }
605 return job;
606}
607
609{
610 DeleteJob *job = DeleteJobPrivate::newJob(src, flags);
611 if (job->uiDelegateExtension()) {
612 job->uiDelegateExtension()->createClipboardUpdater(job, JobUiDelegateExtension::RemoveContent);
613 }
614 return job;
615}
616
617#include "deletejob.moc"
618#include "moc_deletejob.cpp"
bool hasSubjobs() const
virtual void slotResult(KJob *job)
static KFileItem cachedItemForUrl(const QUrl &url)
Return the KFileItem for the given URL, if it was listed recently and it's still in the cache,...
bool stopDirScan(const QString &path)
static KDirWatch * self()
bool restartDirScan(const QString &path)
bool isNull() const
Return true if default-constructed.
A more complex Job to delete files and directories.
Definition deletejob.h:34
QList< QUrl > urls() const
Returns the list of URLs.
virtual ClipboardUpdater * createClipboardUpdater(Job *job, ClipboardUpdaterMode mode)
Creates a clipboard updater as a child of the given job.
The base class for all jobs.
Definition job_base.h:45
void setParentJob(Job *parentJob)
Set the parent Job.
Definition job.cpp:192
JobUiDelegateExtension * uiDelegateExtension() const
Retrieves the UI delegate extension used by this job.
Definition job.cpp:44
bool removeSubjob(KJob *job) override
Mark a sub job as being done.
Definition job.cpp:80
bool addSubjob(KJob *job) override
Add a job that has to be finished before a result is emitted.
Definition job.cpp:56
MetaData metaData() const
Get meta data received from the worker.
Definition job.cpp:205
void addMetaData(const QString &key, const QString &value)
Add key/value pair to the meta data that is sent to the worker.
Definition job.cpp:221
void entries(KIO::Job *job, const KIO::UDSEntryList &list)
This signal emits the entry found by the job while listing.
void setUnrestricted(bool unrestricted)
Do not apply any KIOSK restrictions to this job.
Definition listjob.cpp:249
QString stringValue(uint field) const
Definition udsentry.cpp:365
bool isLink() const
Definition udsentry.cpp:380
@ UDS_URL
An alternative URL (If different from the caption).
Definition udsentry.h:251
@ UDS_NAME
Filename - as displayed in directory listings etc.
Definition udsentry.h:224
bool isDir() const
Definition udsentry.cpp:375
virtual void registerJob(KJob *job)
int error() const
void setUiDelegate(KJobUiDelegate *delegate)
static bool supportsDeleting(const QUrl &url)
Returns whether the protocol can delete files/objects.
static bool canDeleteRecursive(const QUrl &url)
Returns whether the protocol can recursively delete directories by itself.
AKONADI_CALENDAR_EXPORT QString displayName(Akonadi::ETMCalendar *calendar, const Akonadi::Collection &collection)
A namespace for KIO globals.
KIOCORE_EXPORT DeleteJob * del(const QUrl &src, JobFlags flags=DefaultFlags)
Delete a file or directory.
KIOCORE_EXPORT SimpleJob * rmdir(const QUrl &url)
Removes a single directory.
KIOCORE_EXPORT TransferJob * http_delete(const QUrl &url, JobFlags flags=DefaultFlags)
HTTP DELETE.
KIOCORE_EXPORT StatJob * stat(const QUrl &url, JobFlags flags=DefaultFlags)
Find all details for one file or directory.
Definition statjob.cpp:203
KIOCORE_EXPORT QString buildErrorString(int errorCode, const QString &errorText)
Returns a translated error message for errorCode using the additional error information provided by e...
Definition job_error.cpp:31
KIOCORE_EXPORT SimpleJob * file_delete(const QUrl &src, JobFlags flags=DefaultFlags)
Delete a single file.
Definition job.cpp:373
KIOCORE_EXPORT KJobUiDelegate * createDefaultJobUiDelegate()
Convenience method: use default factory, if there's one, to create a delegate and return it.
KIOCORE_EXPORT ListJob * listRecursive(const QUrl &url, JobFlags flags=DefaultFlags, ListJob::ListFlags listFlags=ListJob::ListFlag::IncludeHidden)
The same as the previous method, but recurses subdirectories.
Definition listjob.cpp:244
QList< UDSEntry > UDSEntryList
A directory listing is a list of UDSEntry instances.
Definition udsentry.h:379
QFlags< JobFlag > JobFlags
Stores a combination of JobFlag values.
Definition job_base.h:281
@ HideProgressInfo
Hide progress information dialog, i.e. don't show a GUI.
Definition job_base.h:251
@ NoPrivilegeExecution
When set, notifies the worker that application/job does not want privilege execution.
Definition job_base.h:276
@ StatBasic
Filename, access, type, size, linkdest.
Definition global.h:255
KIOCORE_EXPORT KJobTrackerInterface * getJobTracker()
Returns the job tracker to be used by all KIO jobs (in which HideProgressInfo is not set)
KIOCORE_EXPORT QStringList list(const QString &fileClass)
Returns a list of directories associated with this file-class.
const QList< QKeySequence > & begin()
const QList< QKeySequence > & end()
bool remove()
void append(QList< T > &&value)
iterator begin()
iterator end()
bool invokeMethod(QObject *context, Functor &&function, FunctorReturnType *ret)
Q_EMITQ_EMIT
Q_OBJECTQ_OBJECT
Q_SIGNALSQ_SIGNALS
Q_SLOTSQ_SLOTS
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
void deleteLater()
void moveToThread(QThread *targetThread)
T qobject_cast(QObject *object)
QString & insert(qsizetype position, QChar ch)
bool isEmpty() const const
QString number(double n, char format, int precision)
bool startsWith(QChar c, Qt::CaseSensitivity cs) const const
CaseInsensitive
QueuedConnection
QFuture< ArgsType< Signal > > connect(Sender *sender, Signal signal)
void finished()
void timeout()
StripTrailingSlash
QUrl adjusted(FormattingOptions options) const const
bool isLocalFile() const const
QString path(ComponentFormattingOptions options) const const
QString scheme() const const
void setPath(const QString &path, ParsingMode mode)
QString toLocalFile() const const
Q_D(Todo)
This file is part of the KDE documentation.
Documentation copyright © 1996-2025 The KDE developers.
Generated on Fri Jan 24 2025 11:49:36 by doxygen 1.13.2 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.