KIO

previewjob.cpp
1 // -*- c++ -*-
2 /*
3  This file is part of the KDE libraries
4  SPDX-FileCopyrightText: 2000 David Faure <[email protected]>
5  SPDX-FileCopyrightText: 2000 Carsten Pfeiffer <[email protected]>
6  SPDX-FileCopyrightText: 2001 Malte Starostik <[email protected]>
7 
8  SPDX-License-Identifier: LGPL-2.0-or-later
9 */
10 
11 #include "previewjob.h"
12 #include "kio_widgets_debug.h"
13 
14 #if defined(Q_OS_UNIX) && !defined(Q_OS_ANDROID)
15 #define WITH_SHM 1
16 #else
17 #define WITH_SHM 0
18 #endif
19 
20 #if WITH_SHM
21 #include <sys/ipc.h>
22 #include <sys/shm.h>
23 #endif
24 
25 #include <limits>
26 #include <set>
27 
28 #include <QDir>
29 #include <QFile>
30 #include <QImage>
31 #include <QPixmap>
32 #include <QRegularExpression>
33 #include <QSaveFile>
34 #include <QTemporaryFile>
35 #include <QTimer>
36 
37 #include <QCryptographicHash>
38 
39 #include <KConfigGroup>
40 #include <KMountPoint>
41 #include <KPluginInfo>
42 #include <KPluginMetaData>
43 #include <KService>
44 #include <KServiceTypeTrader>
45 #include <KSharedConfig>
46 #include <QMimeDatabase>
47 #include <QStandardPaths>
48 #include <Solid/Device>
49 #include <Solid/StorageAccess>
50 #include <kprotocolinfo.h>
51 
52 #include <algorithm>
53 #include <cmath>
54 
55 #include "job_p.h"
56 
57 namespace
58 {
59 static int s_defaultDevicePixelRatio = 1;
60 }
61 
62 namespace KIO
63 {
64 struct PreviewItem;
65 }
66 using namespace KIO;
67 
68 struct KIO::PreviewItem {
69  KFileItem item;
70  KPluginMetaData plugin;
71 };
72 
73 class KIO::PreviewJobPrivate : public KIO::JobPrivate
74 {
75 public:
76  PreviewJobPrivate(const KFileItemList &items, const QSize &size)
77  : initialItems(items)
78  , width(size.width())
79  , height(size.height())
80  , cacheSize(0)
81  , bScale(true)
82  , bSave(true)
83  , ignoreMaximumSize(false)
84  , sequenceIndex(0)
85  , succeeded(false)
86  , maximumLocalSize(0)
87  , maximumRemoteSize(0)
88 #if KIOWIDGETS_BUILD_DEPRECATED_SINCE(5, 102)
89  , iconSize(0)
90  , iconAlpha(70)
91 #endif
92  , shmid(-1)
93  , shmaddr(nullptr)
94  {
95  // https://specifications.freedesktop.org/thumbnail-spec/thumbnail-spec-latest.html#DIRECTORY
97  }
98 
99  enum {
100  STATE_STATORIG, // if the thumbnail exists
101  STATE_GETORIG, // if we create it
102  STATE_CREATETHUMB, // thumbnail:/ worker
103  STATE_DEVICE_INFO, // additional state check to get needed device ids
104  } state;
105 
106  KFileItemList initialItems;
107  QStringList enabledPlugins;
108  // Some plugins support remote URLs, <protocol, mimetypes>
109  QHash<QString, QStringList> m_remoteProtocolPlugins;
110  // Our todo list :)
111  // We remove the first item at every step, so use std::list
112  std::list<PreviewItem> items;
113  // The current item
114  PreviewItem currentItem;
115  // The modification time of that URL
116  QDateTime tOrig;
117  // Path to thumbnail cache for the current size
118  QString thumbPath;
119  // Original URL of current item in RFC2396 format
120  // (file:///path/to/a%20file instead of file:/path/to/a file)
121  QByteArray origName;
122  // Thumbnail file name for current item
123  QString thumbName;
124  // Size of thumbnail
125  int width;
126  int height;
127  // Unscaled size of thumbnail (128, 256 or 512 if cache is enabled)
128  short cacheSize;
129  // Whether the thumbnail should be scaled
130  bool bScale;
131  // Whether we should save the thumbnail
132  bool bSave;
133  bool ignoreMaximumSize;
134  int sequenceIndex;
135  bool succeeded;
136  // If the file to create a thumb for was a temp file, this is its name
137  QString tempName;
138  KIO::filesize_t maximumLocalSize;
139  KIO::filesize_t maximumRemoteSize;
140 #if KIOWIDGETS_BUILD_DEPRECATED_SINCE(5, 102)
141  // the size for the icon overlay
142  int iconSize;
143  // the transparency of the blended MIME type icon
144  int iconAlpha;
145 #endif
146  // Shared memory segment Id. The segment is allocated to a size
147  // of extent x extent x 4 (32 bit image) on first need.
148  int shmid;
149  // And the data area
150  uchar *shmaddr;
151  // Size of the shm segment
152  size_t shmsize;
153  // Root of thumbnail cache
154  QString thumbRoot;
155  // Metadata returned from the KIO thumbnail worker
156  QMap<QString, QString> thumbnailWorkerMetaData;
157  int devicePixelRatio = s_defaultDevicePixelRatio;
158  static const int idUnknown = -1;
159  // Id of a device storing currently processed file
160  int currentDeviceId = 0;
161  // Device ID for each file. Stored while in STATE_DEVICE_INFO state, used later on.
162  QMap<QString, int> deviceIdMap;
163  enum CachePolicy { Prevent, Allow, Unknown } currentDeviceCachePolicy = Unknown;
164 
165  void getOrCreateThumbnail();
166  bool statResultThumbnail();
167  void createThumbnail(const QString &);
168  void cleanupTempFile();
169  void determineNextFile();
170  void emitPreview(const QImage &thumb);
171 
172  void startPreview();
173  void slotThumbData(KIO::Job *, const QByteArray &);
174  // Checks if thumbnail is on encrypted partition different than thumbRoot
175  CachePolicy canBeCached(const QString &path);
176  int getDeviceId(const QString &path);
177 
178  Q_DECLARE_PUBLIC(PreviewJob)
179 
180  static QVector<KPluginMetaData> loadAvailablePlugins()
181  {
182  static QVector<KPluginMetaData> jsonMetaDataPlugins;
183  if (!jsonMetaDataPlugins.isEmpty()) {
184  return jsonMetaDataPlugins;
185  }
186  jsonMetaDataPlugins = KPluginMetaData::findPlugins(QStringLiteral("kf" QT_STRINGIFY(QT_VERSION_MAJOR) "/thumbcreator"));
187  std::set<QString> pluginIds;
188  for (const KPluginMetaData &data : std::as_const(jsonMetaDataPlugins)) {
189  pluginIds.insert(data.pluginId());
190  }
191 #if KSERVICE_ENABLE_DEPRECATED_SINCE(5, 88)
192  QT_WARNING_PUSH
193  QT_WARNING_DISABLE_CLANG("-Wdeprecated-declarations")
194  QT_WARNING_DISABLE_GCC("-Wdeprecated-declarations")
195  const KService::List plugins = KServiceTypeTrader::self()->query(QStringLiteral("ThumbCreator"));
196  for (const auto &plugin : plugins) {
197  if (KPluginInfo info(plugin); info.isValid()) {
198  if (auto [it, inserted] = pluginIds.insert(plugin->desktopEntryName()); inserted) {
199  jsonMetaDataPlugins << info.toMetaData();
200  }
201  } else {
202  // Hack for directory thumbnailer: It has a hardcoded plugin id in the KIO worker and not any C++ plugin
203  // Consequently we just use the base name as the plugin file for our KPluginMetaData object
204  const QString path = QStandardPaths::locate(QStandardPaths::GenericDataLocation, QLatin1String("kservices5/") + plugin->entryPath());
206  jsonMetaDataPlugins << KPluginMetaData(tmpData.rawData(), QFileInfo(path).baseName(), path);
207  }
208  }
209  QT_WARNING_POP
210 #else
211 #pragma message("TODO: directory thumbnailer needs a non-desktop file solution ")
212 #endif
213  return jsonMetaDataPlugins;
214  }
215 };
216 
217 #if KIOWIDGETS_BUILD_DEPRECATED_SINCE(5, 86)
218 void PreviewJob::setDefaultDevicePixelRatio(int defaultDevicePixelRatio)
219 {
220  s_defaultDevicePixelRatio = defaultDevicePixelRatio;
221 }
222 #endif
223 
224 void PreviewJob::setDefaultDevicePixelRatio(qreal defaultDevicePixelRatio)
225 {
226  s_defaultDevicePixelRatio = std::ceil(defaultDevicePixelRatio);
227 }
228 
229 #if KIOWIDGETS_BUILD_DEPRECATED_SINCE(4, 7)
230 PreviewJob::PreviewJob(const KFileItemList &items, int width, int height, int iconSize, int iconAlpha, bool scale, bool save, const QStringList *enabledPlugins)
231  : KIO::Job(*new PreviewJobPrivate(items, QSize(width, height ? height : width)))
232 {
233  Q_D(PreviewJob);
234  d->enabledPlugins = enabledPlugins ? *enabledPlugins : availablePlugins();
235  d->iconSize = iconSize;
236  d->iconAlpha = iconAlpha;
237  d->bScale = scale;
238  d->bSave = save && scale;
239 
240  // Return to event loop first, determineNextFile() might delete this;
241  QTimer::singleShot(0, this, [d]() {
242  d->startPreview();
243  });
244 }
245 #endif
246 
247 PreviewJob::PreviewJob(const KFileItemList &items, const QSize &size, const QStringList *enabledPlugins)
248  : KIO::Job(*new PreviewJobPrivate(items, size))
249 {
250  Q_D(PreviewJob);
251 
252  if (enabledPlugins) {
253  d->enabledPlugins = *enabledPlugins;
254  } else {
255  const KConfigGroup globalConfig(KSharedConfig::openConfig(), "PreviewSettings");
256  d->enabledPlugins =
257  globalConfig.readEntry("Plugins",
258  QStringList{QStringLiteral("directorythumbnail"), QStringLiteral("imagethumbnail"), QStringLiteral("jpegthumbnail")});
259  }
260 
261  // Return to event loop first, determineNextFile() might delete this;
262  QTimer::singleShot(0, this, [d]() {
263  d->startPreview();
264  });
265 }
266 
267 PreviewJob::~PreviewJob()
268 {
269 #if WITH_SHM
270  Q_D(PreviewJob);
271  if (d->shmaddr) {
272  shmdt((char *)d->shmaddr);
273  shmctl(d->shmid, IPC_RMID, nullptr);
274  }
275 #endif
276 }
277 
278 #if KIOWIDGETS_BUILD_DEPRECATED_SINCE(5, 102)
280 {
281  Q_D(PreviewJob);
282  d->iconSize = size;
283 }
284 #endif
285 
286 #if KIOWIDGETS_BUILD_DEPRECATED_SINCE(5, 102)
288 {
289  Q_D(const PreviewJob);
290  return d->iconSize;
291 }
292 #endif
293 
294 #if KIOWIDGETS_BUILD_DEPRECATED_SINCE(5, 102)
296 {
297  Q_D(PreviewJob);
298  d->iconAlpha = qBound(0, alpha, 255);
299 }
300 #endif
301 
302 #if KIOWIDGETS_BUILD_DEPRECATED_SINCE(5, 102)
304 {
305  Q_D(const PreviewJob);
306  return d->iconAlpha;
307 }
308 #endif
309 
311 {
312  Q_D(PreviewJob);
313  switch (type) {
314  case Unscaled:
315  d->bScale = false;
316  d->bSave = false;
317  break;
318  case Scaled:
319  d->bScale = true;
320  d->bSave = false;
321  break;
322  case ScaledAndCached:
323  d->bScale = true;
324  d->bSave = true;
325  break;
326  default:
327  break;
328  }
329 }
330 
332 {
333  Q_D(const PreviewJob);
334  if (d->bScale) {
335  return d->bSave ? ScaledAndCached : Scaled;
336  }
337  return Unscaled;
338 }
339 
340 void PreviewJobPrivate::startPreview()
341 {
342  Q_Q(PreviewJob);
343  // Load the list of plugins to determine which MIME types are supported
344  const QVector<KPluginMetaData> plugins = KIO::PreviewJobPrivate::loadAvailablePlugins();
347 
348  for (const KPluginMetaData &plugin : plugins) {
349  QStringList protocols = plugin.value(QStringLiteral("X-KDE-Protocols"), QStringList());
350  const QString p = plugin.value(QStringLiteral("X-KDE-Protocol"));
351  if (!p.isEmpty()) {
352  protocols.append(p);
353  }
354  for (const QString &protocol : std::as_const(protocols)) {
355  // Add supported MIME type for this protocol
356  QStringList &_ms = m_remoteProtocolPlugins[protocol];
357  const auto mimeTypes = plugin.mimeTypes();
358  for (const QString &_m : mimeTypes) {
359  protocolMap[protocol].insert(_m, plugin);
360  if (!_ms.contains(_m)) {
361  _ms.append(_m);
362  }
363  }
364  }
365  if (enabledPlugins.contains(plugin.pluginId())) {
366  const auto mimeTypes = plugin.mimeTypes();
367  for (const QString &mimeType : mimeTypes) {
368  mimeMap.insert(mimeType, plugin);
369  }
370  }
371  }
372 
373  // Look for images and store the items in our todo list :)
374  bool bNeedCache = false;
375  for (const auto &fileItem : std::as_const(initialItems)) {
376  PreviewItem item;
377  item.item = fileItem;
378 
379  const QString mimeType = item.item.mimetype();
380  KPluginMetaData plugin;
381 
382  // look for protocol-specific thumbnail plugins first
383  auto it = protocolMap.constFind(item.item.url().scheme());
384  if (it != protocolMap.constEnd()) {
385  plugin = it.value().value(mimeType);
386  }
387 
388  if (!plugin.isValid()) {
389  auto pluginIt = mimeMap.constFind(mimeType);
390  if (pluginIt == mimeMap.constEnd()) {
391  // check MIME type inheritance, resolve aliases
392  QMimeDatabase db;
393  const QMimeType mimeInfo = db.mimeTypeForName(mimeType);
394  if (mimeInfo.isValid()) {
395  const QStringList parentMimeTypes = mimeInfo.allAncestors();
396  for (const QString &parentMimeType : parentMimeTypes) {
397  pluginIt = mimeMap.constFind(parentMimeType);
398  if (pluginIt != mimeMap.constEnd()) {
399  break;
400  }
401  }
402  }
403 
404  if (pluginIt == mimeMap.constEnd()) {
405  // Check the wildcards last, see BUG 453480
406  QString groupMimeType = mimeType;
407  const int slashIdx = groupMimeType.indexOf(QLatin1Char('/'));
408  if (slashIdx != -1) {
409  // Replace everything after '/' with '*'
410  groupMimeType.truncate(slashIdx + 1);
411  groupMimeType += QLatin1Char('*');
412  }
413  pluginIt = mimeMap.constFind(groupMimeType);
414  }
415  }
416 
417  if (pluginIt != mimeMap.constEnd()) {
418  plugin = *pluginIt;
419  }
420  }
421 
422  if (plugin.isValid()) {
423  item.plugin = plugin;
424  items.push_back(item);
425  if (!bNeedCache && bSave && plugin.value(QStringLiteral("CacheThumbnail"), true)) {
426  const QUrl url = fileItem.url();
427  if (!url.isLocalFile() || !url.adjusted(QUrl::RemoveFilename).toLocalFile().startsWith(thumbRoot)) {
428  bNeedCache = true;
429  }
430  }
431  } else {
432  Q_EMIT q->failed(fileItem);
433  }
434  }
435 
436  KConfigGroup cg(KSharedConfig::openConfig(), "PreviewSettings");
437  maximumLocalSize = cg.readEntry("MaximumSize", std::numeric_limits<KIO::filesize_t>::max());
438  maximumRemoteSize = cg.readEntry<KIO::filesize_t>("MaximumRemoteSize", 0);
439 
440  if (bNeedCache) {
441  const int longer = std::max(width, height);
442  if (longer <= 128) {
443  cacheSize = 128;
444  } else if (longer <= 256) {
445  cacheSize = 256;
446  } else if (longer <= 512) {
447  cacheSize = 512;
448  } else {
449  cacheSize = 1024;
450  }
451 
452  struct CachePool {
453  QString path;
454  int minSize;
455  };
456 
457  const static auto pools = {
458  CachePool{QStringLiteral("/normal/"), 128},
459  CachePool{QStringLiteral("/large/"), 256},
460  CachePool{QStringLiteral("/x-large/"), 512},
461  CachePool{QStringLiteral("/xx-large/"), 1024},
462  };
463 
464  QString thumbDir;
465  int wants = devicePixelRatio * cacheSize;
466  for (const auto &p : pools) {
467  if (p.minSize < wants) {
468  continue;
469  } else {
470  thumbDir = p.path;
471  break;
472  }
473  }
474  thumbPath = thumbRoot + thumbDir;
475 
476  if (!QDir(thumbPath).exists()) {
477  if (QDir().mkpath(thumbPath)) { // Qt5 TODO: mkpath(dirPath, permissions)
478  QFile f(thumbPath);
479  f.setPermissions(QFile::ReadUser | QFile::WriteUser | QFile::ExeUser); // 0700
480  }
481  }
482  } else {
483  bSave = false;
484  }
485 
486  initialItems.clear();
487  determineNextFile();
488 }
489 
490 void PreviewJob::removeItem(const QUrl &url)
491 {
492  Q_D(PreviewJob);
493 
494  auto it = std::find_if(d->items.cbegin(), d->items.cend(), [&url](const PreviewItem &pItem) {
495  return url == pItem.item.url();
496  });
497  if (it != d->items.cend()) {
498  d->items.erase(it);
499  }
500 
501  if (d->currentItem.item.url() == url) {
502  KJob *job = subjobs().first();
503  job->kill();
504  removeSubjob(job);
505  d->determineNextFile();
506  }
507 }
508 
510 {
511  d_func()->sequenceIndex = index;
512 }
513 
515 {
516  return d_func()->sequenceIndex;
517 }
518 
520 {
521  return d_func()->thumbnailWorkerMetaData.value(QStringLiteral("sequenceIndexWraparoundPoint"), QStringLiteral("-1.0")).toFloat();
522 }
523 
525 {
526  return d_func()->thumbnailWorkerMetaData.value(QStringLiteral("handlesSequences")) == QStringLiteral("1");
527 }
528 
529 #if KIOWIDGETS_BUILD_DEPRECATED_SINCE(5, 86)
531 {
532  d_func()->devicePixelRatio = dpr;
533 }
534 #endif
535 
537 {
538  d_func()->devicePixelRatio = std::ceil(dpr);
539 }
540 
541 void PreviewJob::setIgnoreMaximumSize(bool ignoreSize)
542 {
543  d_func()->ignoreMaximumSize = ignoreSize;
544 }
545 
546 void PreviewJobPrivate::cleanupTempFile()
547 {
548  if (!tempName.isEmpty()) {
549  Q_ASSERT((!QFileInfo(tempName).isDir() && QFileInfo(tempName).isFile()) || QFileInfo(tempName).isSymLink());
550  QFile::remove(tempName);
551  tempName.clear();
552  }
553 }
554 
555 void PreviewJobPrivate::determineNextFile()
556 {
557  Q_Q(PreviewJob);
558  if (!currentItem.item.isNull()) {
559  if (!succeeded) {
560  Q_EMIT q->failed(currentItem.item);
561  }
562  }
563  // No more items ?
564  if (items.empty()) {
565  q->emitResult();
566  return;
567  } else {
568  // First, stat the orig file
569  state = PreviewJobPrivate::STATE_STATORIG;
570  currentItem = items.front();
571  items.pop_front();
572  succeeded = false;
573  KIO::Job *job = KIO::statDetails(currentItem.item.url(), StatJob::SourceSide, KIO::StatDefaultDetails | KIO::StatInode, KIO::HideProgressInfo);
574  job->addMetaData(QStringLiteral("thumbnail"), QStringLiteral("1"));
575  job->addMetaData(QStringLiteral("no-auth-prompt"), QStringLiteral("true"));
576  q->addSubjob(job);
577  }
578 }
579 
580 void PreviewJob::slotResult(KJob *job)
581 {
582  Q_D(PreviewJob);
583 
584  removeSubjob(job);
585  Q_ASSERT(!hasSubjobs()); // We should have only one job at a time ...
586  switch (d->state) {
587  case PreviewJobPrivate::STATE_STATORIG: {
588  if (job->error()) { // that's no good news...
589  // Drop this one and move on to the next one
590  d->determineNextFile();
591  return;
592  }
593  const KIO::UDSEntry statResult = static_cast<KIO::StatJob *>(job)->statResult();
594  d->currentDeviceId = statResult.numberValue(KIO::UDSEntry::UDS_DEVICE_ID, 0);
596 
597  bool skipCurrentItem = false;
599  const QUrl itemUrl = d->currentItem.item.mostLocalUrl();
600 
601  if (itemUrl.isLocalFile() || KProtocolInfo::protocolClass(itemUrl.scheme()) == QLatin1String(":local")) {
602  skipCurrentItem = !d->ignoreMaximumSize && size > d->maximumLocalSize && !d->currentItem.plugin.value(QStringLiteral("IgnoreMaximumSize"), false);
603  } else {
604  // For remote items the "IgnoreMaximumSize" plugin property is not respected
605  skipCurrentItem = !d->ignoreMaximumSize && size > d->maximumRemoteSize;
606 
607  // Remote directories are not supported, don't try to do a file_copy on them
608  if (!skipCurrentItem) {
609  // TODO update item.mimeType from the UDS entry, in case it wasn't set initially
610  // But we don't use the MIME type anymore, we just use isDir().
611  if (d->currentItem.item.isDir()) {
612  skipCurrentItem = true;
613  }
614  }
615  }
616  if (skipCurrentItem) {
617  d->determineNextFile();
618  return;
619  }
620 
621  bool pluginHandlesSequences = d->currentItem.plugin.value(QStringLiteral("HandleSequences"), false);
622  if (!d->currentItem.plugin.value(QStringLiteral("CacheThumbnail"), true) || (d->sequenceIndex && pluginHandlesSequences)) {
623  // This preview will not be cached, no need to look for a saved thumbnail
624  // Just create it, and be done
625  d->getOrCreateThumbnail();
626  return;
627  }
628 
629  if (d->statResultThumbnail()) {
630  return;
631  }
632 
633  d->getOrCreateThumbnail();
634  return;
635  }
636  case PreviewJobPrivate::STATE_DEVICE_INFO: {
637  KIO::StatJob *statJob = static_cast<KIO::StatJob *>(job);
638  int id;
639  QString path = statJob->url().toLocalFile();
640  if (job->error()) {
641  // We set id to 0 to know we tried getting it
642  qCWarning(KIO_WIDGETS) << "Cannot read information about filesystem under path" << path;
643  id = 0;
644  } else {
646  }
647  d->deviceIdMap[path] = id;
648  d->createThumbnail(d->currentItem.item.localPath());
649  return;
650  }
651  case PreviewJobPrivate::STATE_GETORIG: {
652  if (job->error()) {
653  d->cleanupTempFile();
654  d->determineNextFile();
655  return;
656  }
657 
658  d->createThumbnail(static_cast<KIO::FileCopyJob *>(job)->destUrl().toLocalFile());
659  return;
660  }
661  case PreviewJobPrivate::STATE_CREATETHUMB: {
662  d->cleanupTempFile();
663  d->determineNextFile();
664  return;
665  }
666  }
667 }
668 
669 bool PreviewJobPrivate::statResultThumbnail()
670 {
671  if (thumbPath.isEmpty()) {
672  return false;
673  }
674 
675  bool isLocal;
676  const QUrl url = currentItem.item.mostLocalUrl(&isLocal);
677  if (isLocal) {
678  const QFileInfo localFile(url.toLocalFile());
679  const QString canonicalPath = localFile.canonicalFilePath();
681  if (origName.isEmpty()) {
682  qCWarning(KIO_WIDGETS) << "Failed to convert" << url << "to canonical path";
683  return false;
684  }
685  } else {
686  // Don't include the password if any
687  origName = url.toEncoded(QUrl::RemovePassword);
688  }
689 
691  md5.addData(origName);
692  thumbName = QString::fromLatin1(md5.result().toHex()) + QLatin1String(".png");
693 
694  QImage thumb;
695  QFile thumbFile(thumbPath + thumbName);
696  if (!thumbFile.open(QIODevice::ReadOnly) || !thumb.load(&thumbFile, "png")) {
697  return false;
698  }
699 
700  if (thumb.text(QStringLiteral("Thumb::URI")) != QString::fromUtf8(origName)
701  || thumb.text(QStringLiteral("Thumb::MTime")).toLongLong() != tOrig.toSecsSinceEpoch()) {
702  return false;
703  }
704 
705  const QString origSize = thumb.text(QStringLiteral("Thumb::Size"));
706  if (!origSize.isEmpty() && origSize.toULongLong() != currentItem.item.size()) {
707  // Thumb::Size is not required, but if it is set it should match
708  return false;
709  }
710 
711  // The DPR of the loaded thumbnail is unspecified (and typically irrelevant).
712  // When a thumbnail is DPR-invariant, use the DPR passed in the request.
713  thumb.setDevicePixelRatio(devicePixelRatio);
714 
715  QString thumbnailerVersion = currentItem.plugin.value(QStringLiteral("ThumbnailerVersion"));
716 
717  if (!thumbnailerVersion.isEmpty() && thumb.text(QStringLiteral("Software")).startsWith(QLatin1String("KDE Thumbnail Generator"))) {
718  // Check if the version matches
719  // The software string should read "KDE Thumbnail Generator pluginName (vX)"
720  QString softwareString = thumb.text(QStringLiteral("Software")).remove(QStringLiteral("KDE Thumbnail Generator")).trimmed();
721  if (softwareString.isEmpty()) {
722  // The thumbnail has been created with an older version, recreating
723  return false;
724  }
725  int versionIndex = softwareString.lastIndexOf(QLatin1String("(v"));
726  if (versionIndex < 0) {
727  return false;
728  }
729 
730  QString cachedVersion = softwareString.remove(0, versionIndex + 2);
731  cachedVersion.chop(1);
732  uint thumbnailerMajor = thumbnailerVersion.toInt();
733  uint cachedMajor = cachedVersion.toInt();
734  if (thumbnailerMajor > cachedMajor) {
735  return false;
736  }
737  }
738 
739  // Found it, use it
740  emitPreview(thumb);
741  succeeded = true;
742  determineNextFile();
743  return true;
744 }
745 
746 void PreviewJobPrivate::getOrCreateThumbnail()
747 {
748  Q_Q(PreviewJob);
749  // We still need to load the orig file ! (This is getting tedious) :)
750  const KFileItem &item = currentItem.item;
751  const QString localPath = item.localPath();
752  if (!localPath.isEmpty()) {
753  createThumbnail(localPath);
754  } else {
755  const QUrl fileUrl = item.url();
756  // heuristics for remote URL support
757  bool supportsProtocol = false;
758  if (m_remoteProtocolPlugins.value(fileUrl.scheme()).contains(item.mimetype())) {
759  // There's a plugin supporting this protocol and MIME type
760  supportsProtocol = true;
761  } else if (m_remoteProtocolPlugins.value(QStringLiteral("KIO")).contains(item.mimetype())) {
762  // Assume KIO understands any URL, ThumbCreator workers who have
763  // X-KDE-Protocols=KIO will get fed the remote URL directly.
764  supportsProtocol = true;
765  }
766 
767  if (supportsProtocol) {
768  createThumbnail(fileUrl.toString());
769  return;
770  }
771  if (item.isDir()) {
772  // Skip remote dirs (bug 208625)
773  cleanupTempFile();
774  determineNextFile();
775  return;
776  }
777  // No plugin support access to this remote content, copy the file
778  // to the local machine, then create the thumbnail
779  state = PreviewJobPrivate::STATE_GETORIG;
780  QTemporaryFile localFile;
781  localFile.setAutoRemove(false);
782  localFile.open();
783  tempName = localFile.fileName();
784  const QUrl currentURL = item.mostLocalUrl();
785  KIO::Job *job = KIO::file_copy(currentURL, QUrl::fromLocalFile(tempName), -1, KIO::Overwrite | KIO::HideProgressInfo /* No GUI */);
786  job->addMetaData(QStringLiteral("thumbnail"), QStringLiteral("1"));
787  q->addSubjob(job);
788  }
789 }
790 
791 PreviewJobPrivate::CachePolicy PreviewJobPrivate::canBeCached(const QString &path)
792 {
793  // If checked file is directory on a different filesystem than its parent, we need to check it separately
794  int separatorIndex = path.lastIndexOf(QLatin1Char('/'));
795  // special case for root folders
796  const QString parentDirPath = separatorIndex == 0 ? path : path.left(separatorIndex);
797 
798  int parentId = getDeviceId(parentDirPath);
799  if (parentId == idUnknown) {
800  return CachePolicy::Unknown;
801  }
802 
803  bool isDifferentSystem = !parentId || parentId != currentDeviceId;
804  if (!isDifferentSystem && currentDeviceCachePolicy != CachePolicy::Unknown) {
805  return currentDeviceCachePolicy;
806  }
807  int checkedId;
808  QString checkedPath;
809  if (isDifferentSystem) {
810  checkedId = currentDeviceId;
811  checkedPath = path;
812  } else {
813  checkedId = getDeviceId(parentDirPath);
814  checkedPath = parentDirPath;
815  if (checkedId == idUnknown) {
816  return CachePolicy::Unknown;
817  }
818  }
819  // If we're checking different filesystem or haven't checked yet see if filesystem matches thumbRoot
820  int thumbRootId = getDeviceId(thumbRoot);
821  if (thumbRootId == idUnknown) {
822  return CachePolicy::Unknown;
823  }
824  bool shouldAllow = checkedId && checkedId == thumbRootId;
825  if (!shouldAllow) {
827  if (device.isValid()) {
828  // If the checked device is encrypted, allow thumbnailing if the thumbnails are stored in an encrypted location.
829  // Or, if the checked device is unencrypted, allow thumbnailing.
830  if (device.as<Solid::StorageAccess>()->isEncrypted()) {
831  const Solid::Device thumbRootDevice = Solid::Device::storageAccessFromPath(thumbRoot);
832  shouldAllow = thumbRootDevice.isValid() && thumbRootDevice.as<Solid::StorageAccess>()->isEncrypted();
833  } else {
834  shouldAllow = true;
835  }
836  }
837  }
838  if (!isDifferentSystem) {
839  currentDeviceCachePolicy = shouldAllow ? CachePolicy::Allow : CachePolicy::Prevent;
840  }
841  return shouldAllow ? CachePolicy::Allow : CachePolicy::Prevent;
842 }
843 
844 int PreviewJobPrivate::getDeviceId(const QString &path)
845 {
846  Q_Q(PreviewJob);
847  auto iter = deviceIdMap.find(path);
848  if (iter != deviceIdMap.end()) {
849  return iter.value();
850  }
851  QUrl url = QUrl::fromLocalFile(path);
852  if (!url.isValid()) {
853  qCWarning(KIO_WIDGETS) << "Could not get device id for file preview, Invalid url" << path;
854  return 0;
855  }
856  state = PreviewJobPrivate::STATE_DEVICE_INFO;
858  job->addMetaData(QStringLiteral("no-auth-prompt"), QStringLiteral("true"));
859  q->addSubjob(job);
860 
861  return idUnknown;
862 }
863 
864 void PreviewJobPrivate::createThumbnail(const QString &pixPath)
865 {
866  Q_Q(PreviewJob);
867  state = PreviewJobPrivate::STATE_CREATETHUMB;
868  QUrl thumbURL;
869  thumbURL.setScheme(QStringLiteral("thumbnail"));
870  thumbURL.setPath(pixPath);
871 
872  bool save = bSave && currentItem.plugin.value(QStringLiteral("CacheThumbnail"), true) && !sequenceIndex;
873 
874  bool isRemoteProtocol = currentItem.item.localPath().isEmpty();
875  CachePolicy cachePolicy = isRemoteProtocol ? CachePolicy::Prevent : canBeCached(pixPath);
876 
877  if (cachePolicy == CachePolicy::Unknown) {
878  // If Unknown is returned, creating thumbnail should be called again by slotResult
879  return;
880  }
881 
882  KIO::TransferJob *job = KIO::get(thumbURL, NoReload, HideProgressInfo);
883  q->addSubjob(job);
884  q->connect(job, &KIO::TransferJob::data, q, [this](KIO::Job *job, const QByteArray &data) {
885  slotThumbData(job, data);
886  });
887 
888  int thumb_width = width;
889  int thumb_height = height;
890 #if KIOWIDGETS_BUILD_DEPRECATED_SINCE(5, 102)
891  int thumb_iconSize = iconSize;
892 #endif
893  if (save) {
894  thumb_width = thumb_height = cacheSize;
895 #if KIOWIDGETS_BUILD_DEPRECATED_SINCE(5, 102)
896  thumb_iconSize = 64;
897 #endif
898  }
899 
900  job->addMetaData(QStringLiteral("mimeType"), currentItem.item.mimetype());
901  job->addMetaData(QStringLiteral("width"), QString::number(thumb_width));
902  job->addMetaData(QStringLiteral("height"), QString::number(thumb_height));
903 #if KIOWIDGETS_BUILD_DEPRECATED_SINCE(5, 102)
904  job->addMetaData(QStringLiteral("iconSize"), QString::number(thumb_iconSize));
905  job->addMetaData(QStringLiteral("iconAlpha"), QString::number(iconAlpha));
906 #endif
907  job->addMetaData(QStringLiteral("plugin"), currentItem.plugin.fileName());
908  job->addMetaData(QStringLiteral("enabledPlugins"), enabledPlugins.join(QLatin1Char(',')));
909  job->addMetaData(QStringLiteral("devicePixelRatio"), QString::number(devicePixelRatio));
910  job->addMetaData(QStringLiteral("cache"), QString::number(cachePolicy == CachePolicy::Allow));
911  if (sequenceIndex) {
912  job->addMetaData(QStringLiteral("sequence-index"), QString::number(sequenceIndex));
913  }
914 
915 #if WITH_SHM
916  size_t requiredSize = thumb_width * devicePixelRatio * thumb_height * devicePixelRatio * 4;
917  if (shmid == -1 || shmsize < requiredSize) {
918  if (shmaddr) {
919  // clean previous shared memory segment
920  shmdt((char *)shmaddr);
921  shmaddr = nullptr;
922  shmctl(shmid, IPC_RMID, nullptr);
923  shmid = -1;
924  }
925  if (requiredSize > 0) {
926  shmid = shmget(IPC_PRIVATE, requiredSize, IPC_CREAT | 0600);
927  if (shmid != -1) {
928  shmsize = requiredSize;
929  shmaddr = (uchar *)(shmat(shmid, nullptr, SHM_RDONLY));
930  if (shmaddr == (uchar *)-1) {
931  shmctl(shmid, IPC_RMID, nullptr);
932  shmaddr = nullptr;
933  shmid = -1;
934  }
935  }
936  }
937  }
938  if (shmid != -1) {
939  job->addMetaData(QStringLiteral("shmid"), QString::number(shmid));
940  }
941 #endif
942 }
943 
944 void PreviewJobPrivate::slotThumbData(KIO::Job *job, const QByteArray &data)
945 {
946  thumbnailWorkerMetaData = job->metaData();
947  /* clang-format off */
948  const bool save = bSave
949  && !sequenceIndex
950  && currentDeviceCachePolicy == CachePolicy::Allow
951  && currentItem.plugin.value(QStringLiteral("CacheThumbnail"), true)
952  && (!currentItem.item.url().isLocalFile()
953  || !currentItem.item.url().adjusted(QUrl::RemoveFilename).toLocalFile().startsWith(thumbRoot));
954  /* clang-format on */
955 
956  QImage thumb;
957 #if WITH_SHM
958  if (shmaddr) {
959  // Keep this in sync with kio-extras|thumbnail/thumbnail.cpp
960  QDataStream str(data);
961  int width;
962  int height;
963  quint8 iFormat;
964  int imgDevicePixelRatio = 1;
965  // TODO KF6: add a version number as first parameter
966  str >> width >> height >> iFormat;
967  if (iFormat & 0x80) {
968  // HACK to deduce if imgDevicePixelRatio is present
969  iFormat &= 0x7f;
970  str >> imgDevicePixelRatio;
971  }
972  QImage::Format format = static_cast<QImage::Format>(iFormat);
973  thumb = QImage(shmaddr, width, height, format).copy();
974  thumb.setDevicePixelRatio(imgDevicePixelRatio);
975  } else {
976  thumb.loadFromData(data);
977  }
978 #else
979  thumb.loadFromData(data);
980 #endif
981 
982  if (thumb.isNull()) {
983  QDataStream s(data);
984  s >> thumb;
985  }
986 
987  if (save) {
988  thumb.setText(QStringLiteral("Thumb::URI"), QString::fromUtf8(origName));
989  thumb.setText(QStringLiteral("Thumb::MTime"), QString::number(tOrig.toSecsSinceEpoch()));
990  thumb.setText(QStringLiteral("Thumb::Size"), number(currentItem.item.size()));
991  thumb.setText(QStringLiteral("Thumb::Mimetype"), currentItem.item.mimetype());
992  QString thumbnailerVersion = currentItem.plugin.value(QStringLiteral("ThumbnailerVersion"));
993  QString signature = QLatin1String("KDE Thumbnail Generator ") + currentItem.plugin.name();
994  if (!thumbnailerVersion.isEmpty()) {
995  signature.append(QLatin1String(" (v") + thumbnailerVersion + QLatin1Char(')'));
996  }
997  thumb.setText(QStringLiteral("Software"), signature);
998  QSaveFile saveFile(thumbPath + thumbName);
999  if (saveFile.open(QIODevice::WriteOnly)) {
1000  if (thumb.save(&saveFile, "PNG")) {
1001  saveFile.commit();
1002  }
1003  }
1004  }
1005  emitPreview(thumb);
1006  succeeded = true;
1007 }
1008 
1009 void PreviewJobPrivate::emitPreview(const QImage &thumb)
1010 {
1011  Q_Q(PreviewJob);
1012  QPixmap pix;
1013  const qreal ratio = thumb.devicePixelRatio();
1014  if (thumb.width() > width * ratio || thumb.height() > height * ratio) {
1015  pix = QPixmap::fromImage(thumb.scaled(QSize(width * ratio, height * ratio), Qt::KeepAspectRatio, Qt::SmoothTransformation));
1016  } else {
1017  pix = QPixmap::fromImage(thumb);
1018  }
1019  pix.setDevicePixelRatio(ratio);
1020  Q_EMIT q->gotPreview(currentItem.item, pix);
1021 }
1022 
1024 {
1025  return PreviewJobPrivate::loadAvailablePlugins();
1026 }
1027 
1029 {
1031  const auto plugins = KIO::PreviewJobPrivate::loadAvailablePlugins();
1032  for (const KPluginMetaData &plugin : plugins) {
1033  result << plugin.pluginId();
1034  }
1035  return result;
1036 }
1037 
1039 {
1040  const QStringList blacklist = QStringList() << QStringLiteral("textthumbnail");
1041 
1043  for (const QString &plugin : blacklist) {
1044  defaultPlugins.removeAll(plugin);
1045  }
1046 
1047  return defaultPlugins;
1048 }
1049 
1051 {
1053  const auto plugins = KIO::PreviewJobPrivate::loadAvailablePlugins();
1054  for (const KPluginMetaData &plugin : plugins) {
1055  result += plugin.mimeTypes();
1056  }
1057  return result;
1058 }
1059 
1060 #if KIOWIDGETS_BUILD_DEPRECATED_SINCE(4, 7)
1061 PreviewJob *
1062 KIO::filePreview(const KFileItemList &items, int width, int height, int iconSize, int iconAlpha, bool scale, bool save, const QStringList *enabledPlugins)
1063 {
1064  return new PreviewJob(items, width, height, iconSize, iconAlpha, scale, save, enabledPlugins);
1065 }
1066 #endif
1067 
1068 #if KIOWIDGETS_BUILD_DEPRECATED_SINCE(4, 7)
1069 PreviewJob *
1070 KIO::filePreview(const QList<QUrl> &items, int width, int height, int iconSize, int iconAlpha, bool scale, bool save, const QStringList *enabledPlugins)
1071 {
1072  KFileItemList fileItems;
1073  fileItems.reserve(items.size());
1074  for (const QUrl &url : items) {
1075  Q_ASSERT(url.isValid()); // please call us with valid urls only
1076  fileItems.append(KFileItem(url));
1077  }
1078  return new PreviewJob(fileItems, width, height, iconSize, iconAlpha, scale, save, enabledPlugins);
1079 }
1080 #endif
1081 
1082 PreviewJob *KIO::filePreview(const KFileItemList &items, const QSize &size, const QStringList *enabledPlugins)
1083 {
1084  return new PreviewJob(items, size, enabledPlugins);
1085 }
1086 
1087 #if KIOWIDGETS_BUILD_DEPRECATED_SINCE(4, 5)
1089 {
1090  KConfigGroup cg(KSharedConfig::openConfig(), "PreviewSettings");
1091  return cg.readEntry("MaximumSize", 5 * 1024 * 1024LL /* 5MB */);
1092 }
1093 #endif
1094 
1095 #include "moc_previewjob.cpp"
@ Overwrite
When set, automatically overwrite the destination if it exists already.
Definition: job_base.h:291
void append(const T &value)
T & first()
QString readEntry(const char *key, const char *aDefault=nullptr) const
static QStringList supportedMimeTypes()
Returns a list of all supported MIME types.
QJsonObject rawData() const
const T value(const Key &key) const const
void truncate(int position)
void setSequenceIndex(int index)
Sets the sequence index given to the thumb creators.
Definition: previewjob.cpp:509
bool isEmpty() const const
QString text(const QString &key) const const
QPixmap fromImage(const QImage &image, Qt::ImageConversionFlags flags)
QString number(int n, int base)
int height() const const
QString fromUtf8(const char *str, int size)
QByteArray toEncoded(QUrl::FormattingOptions options) const const
bool remove()
static QVector< KPluginMetaData > availableThumbnailerPlugins()
Returns all plugins that are considered when a preview is generated The result is internally cached,...
void result(KJob *job)
int removeAll(const T &value)
QString scheme() const const
void setOverlayIconSize(int size)
Sets the size of the MIME-type icon which overlays the preview.
Definition: previewjob.cpp:279
qulonglong filesize_t
64-bit file size
Definition: global.h:39
bool contains(const QString &str, Qt::CaseSensitivity cs) const const
QString trimmed() const const
void clear()
QString url(QUrl::FormattingOptions options) const const
void setAutoRemove(bool b)
static QStringList availablePlugins()
Returns a list of all available preview plugins.
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:228
@ Scaled
The preview will be scaled to the size specified when constructing the PreviewJob.
Definition: previewjob.h:50
void chop(int n)
bool isValid() const
QImage scaled(int width, int height, Qt::AspectRatioMode aspectRatioMode, Qt::TransformationMode transformMode) const const
static Device storageAccessFromPath(const QString &path)
QString writableLocation(QStandardPaths::StandardLocation type)
@ StatInode
dev, inode
Definition: global.h:379
KCALUTILS_EXPORT QString mimeType()
KIOCORE_EXPORT TransferJob * get(const QUrl &url, LoadType reload=NoReload, JobFlags flags=DefaultFlags)
Get (means: read).
const QUrl & url() const
Returns the SimpleJob's URL.
Definition: simplejob.cpp:70
int sequenceIndex() const
Returns the currently set sequence index.
Definition: previewjob.cpp:514
bool kill(KillVerbosity verbosity=Quietly)
int lastIndexOf(QChar ch, int from, Qt::CaseSensitivity cs) const const
QString locate(QStandardPaths::StandardLocation type, const QString &fileName, QStandardPaths::LocateOptions options)
QMap::const_iterator constFind(const Key &key) const const
bool loadFromData(const uchar *data, int len, const char *format)
bool load(QIODevice *device, const char *format)
static KSharedConfig::Ptr openConfig(const QString &fileName=QString(), OpenFlags mode=FullConfig, QStandardPaths::StandardLocation type=QStandardPaths::GenericConfigLocation)
KIOCORE_EXPORT FileCopyJob * file_copy(const QUrl &src, const QUrl &dest, int permissions=-1, JobFlags flags=DefaultFlags)
Copy a single file.
QHash::iterator insert(const Key &key, const T &value)
void setScaleType(ScaleType type)
Sets the scale type for the generated preview.
Definition: previewjob.cpp:310
void removeItem(const QUrl &url)
Removes an item from preview processing.
Definition: previewjob.cpp:490
virtual QString fileName() const const override
void setScheme(const QString &scheme)
void setDevicePixelRatio(qreal scaleFactor)
void reserve(int alloc)
bool isValid() const const
QMap::iterator insert(const Key &key, const T &value)
bool isValid() const
const QList< KJob * > & subjobs() const
int size() const const
QMap::iterator end()
ScaleType scaleType() const
Definition: previewjob.cpp:331
qulonglong toULongLong(bool *ok, int base) const const
QString toString(QUrl::FormattingOptions options) const const
KeepAspectRatio
QMap::iterator find(const Key &key)
MetaData metaData() const
Get meta data received from the worker.
Definition: job.cpp:212
QStringList mimeTypes() const
RemoveFilename
QMimeType mimeTypeForName(const QString &nameOrAlias) const const
QHash::const_iterator constEnd() const const
FullyEncoded
QMap::const_iterator constEnd() const const
static KIO::filesize_t maximumFileSize()
Returns the default "maximum file size", in bytes, used by PreviewJob.
bool isEmpty() const const
PreviewJob(const KFileItemList &items, int width, int height, int iconSize, int iconAlpha, bool scale, bool save, const QStringList *enabledPlugins)
Creates a new PreviewJob.
Definition: previewjob.cpp:230
QUrl fromLocalFile(const QString &localFile)
DevIface * as()
float sequenceIndexWraparoundPoint() const
Returns the index at which the thumbs of a ThumbSequenceCreator start wrapping around ("looping").
Definition: previewjob.cpp:519
bool isNull() const const
bool isEncrypted() const
void setDevicePixelRatio(int dpr)
Request preview to use the device pixel ratio dpr.
Definition: previewjob.cpp:530
@ UDS_SIZE
Size of the file.
Definition: udsentry.h:230
void setText(const QString &key, const QString &text)
int toInt(bool *ok, int base) const const
QString toLocalFile() const const
void setIgnoreMaximumSize(bool ignoreSize=true)
If ignoreSize is true, then the preview is always generated regardless of the settings.
Definition: previewjob.cpp:541
@ Unscaled
The original size of the preview will be returned.
Definition: previewjob.h:45
bool isValid() const const
QString join(const QString &separator) const const
int indexOf(QChar ch, int from, Qt::CaseSensitivity cs) const const
KIOCORE_EXPORT MkpathJob * mkpath(const QUrl &url, const QUrl &baseUrl=QUrl(), JobFlags flags=DefaultFlags)
Creates a directory, creating parent directories as needed.
Definition: mkpathjob.cpp:148
QHash::const_iterator constFind(const Key &key) const const
bool isValid() const
const UDSEntry & statResult() const
Result of the stat operation.
Definition: statjob.cpp:115
bool handlesSequences() const
Determines whether the ThumbCreator in use is a ThumbSequenceCreator.
Definition: previewjob.cpp:524
bool value(const QString &key, bool defaultValue) const
QString & remove(int position, int n)
static QVector< KPluginMetaData > findPlugins(const QString &directory, std::function< bool(const KPluginMetaData &)> filter, KPluginMetaDataOption option)
bool startsWith(const QString &s, Qt::CaseSensitivity cs) const const
qint64 toSecsSinceEpoch() const const
bool isEmpty() const const
long long numberValue(uint field, long long defaultValue=0) const
Definition: udsentry.cpp:381
@ UDS_MODIFICATION_TIME
The last time the file was modified. Required time format: seconds since UNIX epoch.
Definition: udsentry.h:259
void setOverlayIconAlpha(int alpha)
Sets the alpha-value for the MIME-type icon which overlays the preview.
Definition: previewjob.cpp:295
QString path(const QString &relativePath)
KIO Job to get a thumbnail picture.
Definition: previewjob.h:30
QString left(int n) const const
QString fromLatin1(const char *str, int size)
void setPath(const QString &path, QUrl::ParsingMode mode)
const QList< QKeySequence > & save()
bool isLocalFile() const const
QUrl adjusted(QUrl::FormattingOptions options) const const
QDateTime fromSecsSinceEpoch(qint64 secs, Qt::TimeSpec spec, int offsetSeconds)
KIOCORE_EXPORT StatJob * statDetails(const QUrl &url, KIO::StatJob::StatSide side, KIO::StatDetails details=KIO::StatDefaultDetails, JobFlags flags=DefaultFlags)
Find all details for one file or directory.
Definition: statjob.cpp:263
A namespace for KIO globals.
void clear()
QStringList mimeTypes(Mode mode=Writing)
ScaleType
Specifies the type of scaling that is applied to the generated preview.
Definition: previewjob.h:40
@ HideProgressInfo
Hide progress information dialog, i.e. don't show a GUI.
Definition: job_base.h:275
KIOCORE_EXPORT QString number(KIO::filesize_t size)
Converts a size to a string representation Not unlike QString::number(...)
Definition: global.cpp:55
QUrl mostLocalUrl(bool *local=nullptr) const
Tries to return a local URL for this file item if possible.
Definition: kfileitem.cpp:1482
static KPluginMetaData fromDesktopFile(const QString &file, const QStringList &serviceTypes=QStringList())
@ UDS_DEVICE_ID
Device number for this file, used to detect hardlinks.
Definition: udsentry.h:327
int overlayIconSize() const
Definition: previewjob.cpp:287
int error() const
static QStringList defaultPlugins()
Returns a list of plugins that should be enabled by default, which is all plugins Minus the plugins s...
bool hasSubjobs() const
SmoothTransformation
void data(KIO::Job *job, const QByteArray &data)
Data from the worker has arrived.
QString pluginId() const
bool removeSubjob(KJob *job) override
Mark a sub job as being done.
Definition: job.cpp:87
static void setDefaultDevicePixelRatio(int devicePixelRatio)
Sets a default device Pixel Ratio used for Previews.
Definition: previewjob.cpp:218
int overlayIconAlpha() const
Definition: previewjob.cpp:303
bool save(const QString &fileName, const char *format, int quality) const const
KIOWIDGETS_EXPORT PreviewJob * filePreview(const KFileItemList &items, int width, int height=0, int iconSize=0, int iconAlpha=70, bool scale=true, bool save=true, const QStringList *enabledPlugins=nullptr)
Creates a PreviewJob to generate or retrieve a preview image for the given URL.
@ ScaledAndCached
The preview will be scaled to the size specified when constructing the PreviewJob.
Definition: previewjob.h:56
T value(int i) const const
QString & append(QChar ch)
QImage copy(const QRect &rectangle) const const
Q_D(Todo)
@ StatDefaultDetails
Default StatDetail flag when creating a StatJob.
Definition: global.h:389
void setDevicePixelRatio(qreal scaleFactor)
qreal devicePixelRatio() const const
static QString protocolClass(const QString &protocol)
Returns the protocol class for the specified protocol.
int width() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2023 The KDE developers.
Generated on Mon Sep 25 2023 03:51:43 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.