Baloo Widgets

filemetadataprovider.cpp
1/*
2 SPDX-FileCopyrightText: 2010 Peter Penz <peter.penz@gmx.at>
3 SPDX-FileCopyrightText: 2012 Vishesh Handa <me@vhanda.in>
4 SPDX-FileCopyrightText: 2021 Kai Uwe Broulik <kde@broulik.de>
5
6 SPDX-License-Identifier: LGPL-2.0-or-later
7*/
8
9#include "filemetadataprovider.h"
10#include "filemetadatautil_p.h"
11#include "filefetchjob.h"
12
13#include <Baloo/IndexerConfig>
14#include <KFileMetaData/PropertyInfo>
15#include <KFormat>
16#include <KLocalizedString>
17#include <KProtocolInfo>
18#include <KShell>
19
20#include <QPair>
21#include <QSize>
22
23// Required includes for subDirectoriesCount():
24#ifdef Q_OS_WIN
25#include <QDir>
26#else
27#include <QFile>
28#include <dirent.h>
29#endif
30
31using namespace Baloo;
32
33namespace
34{
35/**
36 * The standard QMap::unite will contain the key multiple times if both \p v1 and \p v2
37 * contain the same key.
38 *
39 * This will only take the key from \p v2 into account
40 */
41QVariantMap unite(const QVariantMap &v1, const QVariantMap &v2)
42{
43 QVariantMap v(v1);
45 while (it.hasNext()) {
46 it.next();
47
48 v[it.key()] = it.value();
49 }
50
51 return v;
52}
53
54/**
55* @return The number of files and hidden files for the directory path.
56*/
57QPair<int, int> subDirectoriesCount(const QString &path)
58{
59#ifdef Q_OS_WIN
60 QDir dir(path);
61 int count = dir.entryList(QDir::AllEntries | QDir::NoDotAndDotDot | QDir::System).count();
62 int hiddenCount = dir.entryList(QDir::AllEntries | QDir::NoDotAndDotDot | QDir::System | QDir::Hidden).count();
63 return QPair<int, int>(count, hiddenCount);
64#else
65 // Taken from kdelibs/kio/kio/kdirmodel.cpp
66 // SPDX-FileCopyrightText: 2006 David Faure <faure@kde.org>
67
68 int count = -1;
69 int hiddenCount = -1;
70 DIR *dir = ::opendir(QFile::encodeName(path).constData());
71 if (dir) {
72 count = 0;
73 hiddenCount = 0;
74 struct dirent *dirEntry = nullptr;
75 while ((dirEntry = ::readdir(dir))) { // krazy:exclude=syscalls
76 if (dirEntry->d_name[0] == '.') {
77 if (dirEntry->d_name[1] == '\0') {
78 // Skip "."
79 continue;
80 }
81 if (dirEntry->d_name[1] == '.' && dirEntry->d_name[2] == '\0') {
82 // Skip ".."
83 continue;
84 }
85 // hidden files
86 hiddenCount++;
87 } else {
88 ++count;
89 }
90 }
91 ::closedir(dir);
92 }
93 return QPair<int, int>(count, hiddenCount);
94#endif
95}
96
97/**
98 * Fill \p data with properties can be derived from others
99 */
100void extractDerivedProperties(QVariantMap &data)
101{
102 const auto width = data.value(QStringLiteral("width"));
103 const auto height = data.value(QStringLiteral("height"));
104 if (!width.isNull() && !height.isNull()) {
105 data.insert(QStringLiteral("dimensions"), QSize(width.toInt(), height.toInt()));
106 }
107
108 bool okLatitude;
109 const auto gpsLatitude = data.value(QStringLiteral("photoGpsLatitude")).toFloat(&okLatitude);
110 bool okLongitude;
111 const auto gpsLongitude = data.value(QStringLiteral("photoGpsLongitude")).toFloat(&okLongitude);
112
113 if (okLatitude && okLongitude) {
114 data.insert(QStringLiteral("gpsLocation"), QVariant::fromValue(QPair<float, float>(gpsLatitude, gpsLongitude)));
115 }
116}
117} // anonymous namespace
118
119class Q_DECL_HIDDEN Baloo::FileMetaDataProviderPrivate : public QObject
120{
121public:
122 FileMetaDataProviderPrivate(FileMetaDataProvider *parent)
123 : QObject(parent)
124 , m_parent(parent)
125 , m_readOnly(false)
126 , m_fetchJob(nullptr)
127 {
128 }
129
130 ~FileMetaDataProviderPrivate()
131 {
132 m_parent->cancel();
133 }
134
135 void insertEditableData();
136
137 void processFileItems();
138
139 void setFileItem();
140 void setFileItems();
141
142 /**
143 * Insert basic data of a single file
144 */
145 void insertSingleFileBasicData();
146
147 /**
148 * Insert basic data of a list of files
149 */
150 void insertFilesListBasicData();
151
152 FileMetaDataProvider *m_parent;
153
154 bool m_readOnly;
155
156 QList<KFileItem> m_fileItems;
157
158 QVariantMap m_data;
159 Baloo::IndexerConfig m_config;
160
161 FileFetchJob *m_fetchJob;
162
163public Q_SLOTS:
164 void slotFileFetchFinished(KJob *job);
165};
166
167void FileMetaDataProviderPrivate::slotFileFetchFinished(KJob *job)
168{
169 auto fetchJob = static_cast<FileFetchJob *>(job);
170 QList<QVariantMap> files = fetchJob->data();
171
172 Q_ASSERT(!files.isEmpty());
173
174 if (files.size() > 1) {
175 Baloo::Private::mergeCommonData(m_data, files);
176 } else {
177 m_data = unite(m_data, files.first());
178 }
179 extractDerivedProperties(m_data);
180 m_readOnly = !fetchJob->canEditAll();
181
182 insertEditableData();
183
184 // Not cancellable anymore
185 m_fetchJob = nullptr;
186
187 Q_EMIT m_parent->loadingFinished();
188}
189
190void FileMetaDataProviderPrivate::insertSingleFileBasicData()
191{
192 // TODO: Handle case if remote URLs are used properly. isDir() does
193 // not work, the modification date needs also to be adjusted...
194 Q_ASSERT(m_fileItems.count() == 1);
195 {
196 const KFileItem &item = m_fileItems.first();
197
198 KFormat format;
199 if (item.isDir()) {
200 if (item.isLocalFile() && !item.isSlow()) {
201 const QPair<int, int> counts = subDirectoriesCount(item.url().path());
202 const int count = counts.first;
203 if (count != -1) {
204 QString itemCountString = i18ncp("@item:intable", "%1 item", "%1 items", count);
205 m_data.insert(QStringLiteral("kfileitem#size"), itemCountString);
206
207 const int hiddenCount = counts.second;
208 if (hiddenCount > 0) {
209 // add hidden items count
210 QString hiddenCountString = i18ncp("@item:intable", "%1 item", "%1 items", hiddenCount);
211 m_data.insert(QStringLiteral("kfileitem#hiddenItems"), hiddenCountString);
212 }
213 }
214 } else if (item.entry().contains(KIO::UDSEntry::UDS_SIZE)) {
215 m_data.insert(QStringLiteral("kfileitem#size"), format.formatByteSize(item.size()));
216 }
218 m_data.insert(QStringLiteral("kfileitem#totalSize"), format.formatByteSize(item.recursiveSize()));
219 }
220 } else {
222 m_data.insert(QStringLiteral("kfileitem#size"), format.formatByteSize(item.size()));
223 }
224 }
225
226 m_data.insert(QStringLiteral("kfileitem#type"), item.mimeComment());
227 if (item.isLink()) {
228 m_data.insert(QStringLiteral("kfileitem#linkDest"), item.linkDest());
229 }
231 m_data.insert(QStringLiteral("kfileitem#targetUrl"), KShell::tildeCollapse(item.targetUrl().toDisplayString(QUrl::PreferLocalFile)));
232 }
233 QDateTime modificationTime = item.time(KFileItem::ModificationTime);
234 if (modificationTime.isValid()) {
235 m_data.insert(QStringLiteral("kfileitem#modified"), modificationTime);
236 }
237 QDateTime creationTime = item.time(KFileItem::CreationTime);
238 if (creationTime.isValid()) {
239 m_data.insert(QStringLiteral("kfileitem#created"), creationTime);
240 }
241 QDateTime accessTime = item.time(KFileItem::AccessTime);
242 if (accessTime.isValid()) {
243 m_data.insert(QStringLiteral("kfileitem#accessed"), accessTime);
244 }
245
246 m_data.insert(QStringLiteral("kfileitem#owner"), item.user());
247 m_data.insert(QStringLiteral("kfileitem#group"), item.group());
248 m_data.insert(QStringLiteral("kfileitem#permissions"), item.permissionsString());
249
250 const auto extraFields = KProtocolInfo::extraFields(item.url());
251 for (int i = 0; i < extraFields.count(); ++i) {
252 const auto &field = extraFields.at(i);
253 if (field.type == KProtocolInfo::ExtraField::Invalid) {
254 continue;
255 }
256
257 const QString text = item.entry().stringValue(KIO::UDSEntry::UDS_EXTRA + i);
258 if (text.isEmpty()) {
259 continue;
260 }
261
262 const QString key = QStringLiteral("kfileitem#extra_%1_%2").arg(item.url().scheme(), QString::number(i + 1));
263
264 if (field.type == KProtocolInfo::ExtraField::DateTime) {
265 const QDateTime date = QDateTime::fromString(text, Qt::ISODate);
266 if (!date.isValid()) {
267 continue;
268 }
269
270 m_data.insert(key, date);
271 } else {
272 m_data.insert(key, text);
273 }
274 }
275 }
276}
277
278void FileMetaDataProviderPrivate::insertFilesListBasicData()
279{
280 // If all directories
281 Q_ASSERT(m_fileItems.count() > 1);
282 bool allDirectories = true;
283 for (const KFileItem &item : std::as_const(m_fileItems)) {
284 allDirectories &= item.isDir();
285 if (!allDirectories) {
286 break;
287 }
288 }
289
290 if (allDirectories) {
291 int count = 0;
292 int hiddenCount = 0;
293 for (const KFileItem &item : std::as_const(m_fileItems)) {
294 if (!item.isLocalFile() || item.isSlow()) {
295 return;
296 }
297 const QPair<int, int> counts = subDirectoriesCount(item.url().path());
298 const int subcount = counts.first;
299 if (subcount == -1) {
300 return;
301 }
302 count += subcount;
303 hiddenCount += counts.second;
304 }
305 QString itemCountString = i18ncp("@item:intable", "%1 item", "%1 items", count);
306 if (hiddenCount > 0) {
307 // add hidden items count
308 QString hiddenCountString = i18ncp("@item:intable", "%1 item", "%1 items", hiddenCount);
309 m_data.insert(QStringLiteral("kfileitem#hiddenItems"), hiddenCountString);
310 }
311 m_data.insert(QStringLiteral("kfileitem#totalSize"), itemCountString);
312
313 } else {
314 // Calculate the size of all items
315 quint64 totalSize = 0;
316 for (const KFileItem &item : std::as_const(m_fileItems)) {
317 if (!item.isDir() && !item.isLink()) {
318 totalSize += item.size();
319 }
320 }
321 KFormat format;
322 m_data.insert(QStringLiteral("kfileitem#totalSize"), format.formatByteSize(totalSize));
323 }
324}
325
326void FileMetaDataProviderPrivate::insertEditableData()
327{
328 if (!m_readOnly) {
329 if (!m_data.contains(QStringLiteral("tags"))) {
330 m_data.insert(QStringLiteral("tags"), QVariant());
331 }
332 if (!m_data.contains(QStringLiteral("rating"))) {
333 m_data.insert(QStringLiteral("rating"), 0);
334 }
335 if (!m_data.contains(QStringLiteral("userComment"))) {
336 m_data.insert(QStringLiteral("userComment"), QVariant());
337 }
338 }
339}
340
341FileMetaDataProvider::FileMetaDataProvider(QObject *parent)
342 : QObject(parent)
343 , d(new FileMetaDataProviderPrivate(this))
344{
345}
346
347FileMetaDataProvider::~FileMetaDataProvider() = default;
348
349void FileMetaDataProviderPrivate::processFileItems()
350{
351 // There are several code paths -
352 // Remote file
353 // Single local file -
354 // * Not Indexed
355 // * Indexed
356 //
357 // Multiple Files -
358 // * Not Indexed
359 // * Indexed
360
361 bool singleFileMode = m_fileItems.size() <= 1;
362
363 QStringList urls;
364 urls.reserve(m_fileItems.size());
365 // Only extract data from indexed files,
366 // it would be too expensive otherwise.
367 for (const KFileItem &item : std::as_const(m_fileItems)) {
368 const QUrl url = item.targetUrl();
369 if (url.isLocalFile() && !item.isSlow()) {
370 urls << url.toLocalFile();
371 }
372 }
373
374 if (singleFileMode) {
375 insertSingleFileBasicData();
376 } else {
377 insertFilesListBasicData();
378 }
379
380 if (!urls.isEmpty()) {
381 // Editing only if all URLs are local
382 bool canEdit = (urls.size() == m_fileItems.size());
383
384 // Don't use indexing when we have multiple files
385 auto indexingMode = FileFetchJob::UseRealtimeIndexing::Disabled;
386
387 if (singleFileMode) {
388 // Fully indexed by Baloo
389 indexingMode = FileFetchJob::UseRealtimeIndexing::Fallback;
390
391 if (!m_config.fileIndexingEnabled() || !m_config.shouldBeIndexed(urls.first()) || m_config.onlyBasicIndexing()) {
392 // Not indexed or only basic file indexing (no content)
393 indexingMode = FileFetchJob::UseRealtimeIndexing::Only;
394 }
395 }
396
397 auto job = new FileFetchJob(urls, canEdit, indexingMode, this);
398 // Can be cancelled
399 m_fetchJob = job;
400
401 connect(job, &FileFetchJob::finished, this, &FileMetaDataProviderPrivate::slotFileFetchFinished);
402 job->start();
403
404 } else {
405 // FIXME - are extended attributes supported for remote files?
406 m_readOnly = true;
407 Q_EMIT m_parent->loadingFinished();
408 }
409}
410
412{
413 if (d->m_fetchJob) {
414 auto job = d->m_fetchJob;
415 d->m_fetchJob = nullptr;
416 job->kill();
417 }
418}
419
421{
422 d->m_fileItems = items;
423 d->m_data.clear();
424
425 refresh();
426}
427
429{
430 cancel();
431
432 if (d->m_fileItems.isEmpty()) {
434 } else {
435 d->processFileItems();
436 }
437}
438
439QString FileMetaDataProvider::label(const QString &metaDataLabel) const
440{
441 static QHash<QString, QString> hash = {
442 {QStringLiteral("kfileitem#comment"), i18nc("@label", "Comment")},
443 {QStringLiteral("kfileitem#created"), i18nc("@label", "Created")},
444 {QStringLiteral("kfileitem#accessed"), i18nc("@label", "Accessed")},
445 {QStringLiteral("kfileitem#modified"), i18nc("@label", "Modified")},
446 {QStringLiteral("kfileitem#owner"), i18nc("@label", "Owner")},
447 {QStringLiteral("kfileitem#group"), i18nc("@label", "Group")},
448 {QStringLiteral("kfileitem#permissions"), i18nc("@label", "Permissions")},
449 {QStringLiteral("kfileitem#rating"), i18nc("@label", "Rating")},
450 {QStringLiteral("kfileitem#size"), i18nc("@label", "Size")},
451 {QStringLiteral("kfileitem#tags"), i18nc("@label", "Tags")},
452 {QStringLiteral("kfileitem#totalSize"), i18nc("@label", "Total Size")},
453 {QStringLiteral("kfileitem#hiddenItems"), i18nc("@label", "Hidden items")},
454 {QStringLiteral("kfileitem#type"), i18nc("@label", "Type")},
455 {QStringLiteral("kfileitem#linkDest"), i18nc("@label", "Link to")},
456 {QStringLiteral("kfileitem#targetUrl"), i18nc("@label", "Points to")},
457 {QStringLiteral("tags"), i18nc("@label", "Tags")},
458 {QStringLiteral("rating"), i18nc("@label", "Rating")},
459 {QStringLiteral("userComment"), i18nc("@label", "Comment")},
460 {QStringLiteral("originUrl"), i18nc("@label", "Downloaded From")},
461 {QStringLiteral("dimensions"), i18nc("@label", "Dimensions")},
462 {QStringLiteral("gpsLocation"), i18nc("@label", "GPS Location")},
463 };
464
465 QString value = hash.value(metaDataLabel);
466 if (value.isEmpty()) {
467 static const auto extraPrefix = QStringLiteral("kfileitem#extra_");
468 if (metaDataLabel.startsWith(extraPrefix)) {
469#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
470 const auto parts = metaDataLabel.splitRef(QLatin1Char('_'));
471#else
472 const auto parts = metaDataLabel.split(QLatin1Char('_'));
473#endif
474 Q_ASSERT(parts.count() == 3);
475 const auto protocol = parts.at(1);
476 const int extraNumber = parts.at(2).toInt() - 1;
477
478 // Have to construct a dummy URL for KProtocolInfo::extraFields...
479 QUrl url;
480#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
481 url.setScheme(protocol.toString());
482#else
483 url.setScheme(protocol);
484#endif
485 const auto extraFields = KProtocolInfo::extraFields(url);
486 auto field = extraFields.value(extraNumber);
487 if (field.type != KProtocolInfo::ExtraField::Invalid) {
488 value = field.name;
489 }
490 }
491 }
492
493 if (value.isEmpty()) {
494 value = KFileMetaData::PropertyInfo::fromName(metaDataLabel).displayName();
495 }
496
497 return value;
498}
499
501{
502 static QHash<QString, QString> uriGrouper = {
503
504 // KFileItem Data
505 {QStringLiteral("kfileitem#type"), QStringLiteral("0FileItemA")},
506 {QStringLiteral("kfileitem#linkDest"), QStringLiteral("0FileItemB")},
507 {QStringLiteral("kfileitem#size"), QStringLiteral("0FileItemC")},
508 {QStringLiteral("kfileitem#totalSize"), QStringLiteral("0FileItemC")},
509 {QStringLiteral("kfileitem#hiddenItems"), QStringLiteral("0FileItemD")},
510 {QStringLiteral("kfileitem#modified"), QStringLiteral("0FileItemE")},
511 {QStringLiteral("kfileitem#accessed"), QStringLiteral("0FileItemF")},
512 {QStringLiteral("kfileitem#created"), QStringLiteral("0FileItemG")},
513 {QStringLiteral("kfileitem#owner"), QStringLiteral("0FileItemH")},
514 {QStringLiteral("kfileitem#group"), QStringLiteral("0FileItemI")},
515 {QStringLiteral("kfileitem#permissions"), QStringLiteral("0FileItemJ")},
516
517 // Editable Data
518 {QStringLiteral("tags"), QStringLiteral("1EditableDataA")},
519 {QStringLiteral("rating"), QStringLiteral("1EditableDataB")},
520 {QStringLiteral("userComment"), QStringLiteral("1EditableDataC")},
521
522 // Image Data
523 {QStringLiteral("width"), QStringLiteral("2ImageA")},
524 {QStringLiteral("height"), QStringLiteral("2ImageB")},
525 {QStringLiteral("dimensions"), QStringLiteral("2ImageCA")},
526 {QStringLiteral("photoFNumber"), QStringLiteral("2ImageC")},
527 {QStringLiteral("photoExposureTime"), QStringLiteral("2ImageD")},
528 {QStringLiteral("photoExposureBiasValue"), QStringLiteral("2ImageE")},
529 {QStringLiteral("photoISOSpeedRatings"), QStringLiteral("2ImageF")},
530 {QStringLiteral("photoFocalLength"), QStringLiteral("2ImageG")},
531 {QStringLiteral("photoFocalLengthIn35mmFilm"), QStringLiteral("2ImageH")},
532 {QStringLiteral("photoFlash"), QStringLiteral("2ImageI")},
533 {QStringLiteral("imageOrientation"), QStringLiteral("2ImageJ")},
534 {QStringLiteral("photoGpsLocation"), QStringLiteral("2ImageK")},
535 {QStringLiteral("photoGpsLatitude"), QStringLiteral("2ImageL")},
536 {QStringLiteral("photoGpsLongitude"), QStringLiteral("2ImageM")},
537 {QStringLiteral("photoGpsAltitude"), QStringLiteral("2ImageN")},
538 {QStringLiteral("manufacturer"), QStringLiteral("2ImageO")},
539 {QStringLiteral("model"), QStringLiteral("2ImageP")},
540
541 // Media Data
542 {QStringLiteral("title"), QStringLiteral("3MediaA")},
543 {QStringLiteral("artist"), QStringLiteral("3MediaB")},
544 {QStringLiteral("album"), QStringLiteral("3MediaC")},
545 {QStringLiteral("albumArtist"), QStringLiteral("3MediaD")},
546 {QStringLiteral("genre"), QStringLiteral("3MediaE")},
547 {QStringLiteral("trackNumber"), QStringLiteral("3MediaF")},
548 {QStringLiteral("discNumber"), QStringLiteral("3MediaG")},
549 {QStringLiteral("releaseYear"), QStringLiteral("3MediaH")},
550 {QStringLiteral("duration"), QStringLiteral("3MediaI")},
551 {QStringLiteral("sampleRate"), QStringLiteral("3MediaJ")},
552 {QStringLiteral("bitRate"), QStringLiteral("3MediaK")},
553
554 // Miscellaneous Data
555 {QStringLiteral("originUrl"), QStringLiteral("4MiscA")},
556 };
557
558 const QString val = uriGrouper.value(label);
559 if (val.isEmpty()) {
560 return QStringLiteral("lastGroup");
561 }
562 return val;
563}
564
565KFileItemList FileMetaDataProvider::items() const
566{
567 return d->m_fileItems;
568}
569
571{
572 d->m_readOnly = readOnly;
573}
574
575bool FileMetaDataProvider::isReadOnly() const
576{
577 return d->m_readOnly;
578}
579
580QVariantMap FileMetaDataProvider::data() const
581{
582 return d->m_data;
583}
584
585#include "moc_filemetadataprovider.cpp"
Provides the data for the MetaDataWidget.
void refresh()
Refresh data with latest info from storage.
void loadingFinished()
Emitted once per KFileMetaDataProvider::setItems() after data loading is finished.
void cancel()
Cancel data loading if it's in progress.
void setItems(const KFileItemList &items)
Sets the items, where the meta data should be requested.
virtual QString group(const QString &label) const
Meta data items are sorted alphabetically by their translated label per default.
void setReadOnly(bool readOnly)
If set to true, data such as the comment, tag or rating cannot be changed by the user.
virtual QString label(const QString &metaDataLabel) const
bool shouldBeIndexed(const QString &path) const
bool isSlow() const
QUrl targetUrl() const
bool isLocalFile() const
QString user() const
KIO::filesize_t size() const
bool isLink() const
Q_INVOKABLE QDateTime time(KFileItem::FileTimes which) const
KIO::filesize_t recursiveSize() const
QString group() const
QString linkDest() const
bool isDir() const
KIO::UDSEntry entry() const
QString permissionsString() const
QString mimeComment() const
QUrl url() const
QString displayName() const
static PropertyInfo fromName(const QString &name)
QString formatByteSize(double size, int precision=1, KFormat::BinaryUnitDialect dialect=KFormat::DefaultBinaryDialect, KFormat::BinarySizeUnits units=KFormat::DefaultBinaryUnits) const
QString stringValue(uint field) const
bool contains(uint field) const
void finished(KJob *job)
virtual Q_SCRIPTABLE void start()=0
bool kill(KJob::KillVerbosity verbosity=KJob::Quietly)
static ExtraFieldList extraFields(const QUrl &url)
QString i18nc(const char *context, const char *text, const TYPE &arg...)
QString i18ncp(const char *context, const char *singular, const char *plural, const TYPE &arg...)
KIOCORE_EXPORT QString dir(const QString &fileClass)
KCOREADDONS_EXPORT QString tildeCollapse(const QString &path)
QDateTime fromString(QStringView string, QStringView format, QCalendar cal)
bool isValid() const const
QByteArray encodeName(const QString &fileName)
T value(const Key &key) const const
qsizetype count() const const
pointer data()
T & first()
bool isEmpty() const const
void reserve(qsizetype size)
qsizetype size() const const
Q_EMITQ_EMIT
Q_SLOTSQ_SLOTS
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
qsizetype count() const const
QString arg(Args &&... args) const const
bool isEmpty() const const
QString number(double n, char format, int precision)
QStringList split(QChar sep, Qt::SplitBehavior behavior, Qt::CaseSensitivity cs) const const
bool startsWith(QChar c, Qt::CaseSensitivity cs) const const
PreferLocalFile
bool isLocalFile() const const
QString path(ComponentFormattingOptions options) const const
QString scheme() const const
void setScheme(const QString &scheme)
QString toDisplayString(FormattingOptions options) const const
QString toLocalFile() const const
QVariant fromValue(T &&value)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Mon Nov 18 2024 12:18:20 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.