KIO

previewjob.cpp
1 // -*- c++ -*-
2 /* This file is part of the KDE libraries
3  Copyright (C) 2000 David Faure <[email protected]>
4  2000 Carsten Pfeiffer <[email protected]>
5  2001 Malte Starostik <[email protected]>
6 
7  This library is free software; you can redistribute it and/or
8  modify it under the terms of the GNU Library General Public
9  License as published by the Free Software Foundation; either
10  version 2 of the License, or (at your option) any later version.
11 
12  This library is distributed in the hope that it will be useful,
13  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15  Library General Public License for more details.
16 
17  You should have received a copy of the GNU Library General Public License
18  along with this library; see the file COPYING.LIB. If not, write to
19  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20  Boston, MA 02110-1301, USA.
21 */
22 
23 #include "previewjob.h"
24 
25 #if defined(Q_OS_UNIX) && !defined(Q_OS_ANDROID)
26 #define WITH_SHM 1
27 #else
28 #define WITH_SHM 0
29 #endif
30 
31 #if WITH_SHM
32 #include <sys/ipc.h>
33 #include <sys/shm.h>
34 #endif
35 
36 #include <limits>
37 
38 #include <QDir>
39 #include <QFile>
40 #include <QImage>
41 #include <QPixmap>
42 #include <QTimer>
43 #include <QRegularExpression>
44 #include <QTemporaryFile>
45 #include <QSaveFile>
46 
47 #include <QCryptographicHash>
48 
49 #include <kfileitem.h>
50 #include <KServiceTypeTrader>
51 #include <KService>
52 #include <KSharedConfig>
53 #include <KConfigGroup>
54 #include <kprotocolinfo.h>
55 #include <QMimeDatabase>
56 #include <QStandardPaths>
57 #include <KMountPoint>
58 
59 #include <algorithm>
60 
61 #include "job_p.h"
62 
63 namespace KIO
64 {
65 struct PreviewItem;
66 }
67 using namespace KIO;
68 
69 struct KIO::PreviewItem {
70  KFileItem item;
71  KService::Ptr plugin;
72 };
73 
74 class KIO::PreviewJobPrivate: public KIO::JobPrivate
75 {
76 public:
77  PreviewJobPrivate(const KFileItemList &items, const QSize &size)
78  : initialItems(items),
79  width(size.width()),
80  height(size.height()),
81  cacheWidth(width),
82  cacheHeight(height),
83  bScale(true),
84  bSave(true),
85  ignoreMaximumSize(false),
86  sequenceIndex(0),
87  succeeded(false),
88  maximumLocalSize(0),
89  maximumRemoteSize(0),
90  iconSize(0),
91  iconAlpha(70),
92  shmid(-1),
93  shmaddr(nullptr)
94  {
95  // http://specifications.freedesktop.org/thumbnail-spec/thumbnail-spec-latest.html#DIRECTORY
97  }
98 
99  enum { STATE_STATORIG, // if the thumbnail exists
100  STATE_GETORIG, // if we create it
101  STATE_CREATETHUMB // thumbnail:/ slave
102  } state;
103 
104  KFileItemList initialItems;
105  QStringList enabledPlugins;
106  // Some plugins support remote URLs, <protocol, mimetypes>
107  QHash<QString, QStringList> m_remoteProtocolPlugins;
108  // Our todo list :)
109  // We remove the first item at every step, so use std::list
110  std::list<PreviewItem> items;
111  // The current item
112  PreviewItem currentItem;
113  // The modification time of that URL
114  QDateTime tOrig;
115  // Path to thumbnail cache for the current size
116  QString thumbPath;
117  // Original URL of current item in RFC2396 format
118  // (file:///path/to/a%20file instead of file:/path/to/a file)
119  QByteArray origName;
120  // Thumbnail file name for current item
121  QString thumbName;
122  // Size of thumbnail
123  int width;
124  int height;
125  // Unscaled size of thumbnail (128 or 256 if cache is enabled)
126  int cacheWidth;
127  int cacheHeight;
128  // Whether the thumbnail should be scaled
129  bool bScale;
130  // Whether we should save the thumbnail
131  bool bSave;
132  bool ignoreMaximumSize;
133  int sequenceIndex;
134  bool succeeded;
135  // If the file to create a thumb for was a temp file, this is its name
136  QString tempName;
137  KIO::filesize_t maximumLocalSize;
138  KIO::filesize_t maximumRemoteSize;
139  // the size for the icon overlay
140  int iconSize;
141  // the transparency of the blended mimetype icon
142  int iconAlpha;
143  // Shared memory segment Id. The segment is allocated to a size
144  // of extent x extent x 4 (32 bit image) on first need.
145  int shmid;
146  // And the data area
147  uchar *shmaddr;
148  // Root of thumbnail cache
149  QString thumbRoot;
150  // List of encrypted mount points for checking if we should save thumbnail
151  KMountPoint::List encryptedMountsList;
152 
153  void getOrCreateThumbnail();
154  bool statResultThumbnail();
155  void createThumbnail(const QString &);
156  void cleanupTempFile();
157  void determineNextFile();
158  void emitPreview(const QImage &thumb);
159 
160  void startPreview();
161  void slotThumbData(KIO::Job *, const QByteArray &);
162 
163  Q_DECLARE_PUBLIC(PreviewJob)
164 };
165 
166 #if KIOWIDGETS_BUILD_DEPRECATED_SINCE(4, 7)
167 PreviewJob::PreviewJob(const KFileItemList &items, int width, int height,
168  int iconSize, int iconAlpha, bool scale, bool save,
169  const QStringList *enabledPlugins)
170  : KIO::Job(*new PreviewJobPrivate(items, QSize(width, height ? height : width)))
171 {
172  Q_D(PreviewJob);
173  d->enabledPlugins = enabledPlugins ? *enabledPlugins : availablePlugins();
174  d->iconSize = iconSize;
175  d->iconAlpha = iconAlpha;
176  d->bScale = scale;
177  d->bSave = save && scale;
178 
179  // Return to event loop first, determineNextFile() might delete this;
180  QTimer::singleShot(0, this, SLOT(startPreview()));
181 }
182 #endif
183 
185  const QSize &size,
186  const QStringList *enabledPlugins) :
187  KIO::Job(*new PreviewJobPrivate(items, size))
188 {
189  Q_D(PreviewJob);
190 
191  if (enabledPlugins) {
192  d->enabledPlugins = *enabledPlugins;
193  } else {
194  const KConfigGroup globalConfig(KSharedConfig::openConfig(), "PreviewSettings");
195  d->enabledPlugins = globalConfig.readEntry("Plugins", QStringList {
196  QStringLiteral("directorythumbnail"),
197  QStringLiteral("imagethumbnail"),
198  QStringLiteral("jpegthumbnail")});
199  }
200 
201  // Return to event loop first, determineNextFile() might delete this;
202  QTimer::singleShot(0, this, SLOT(startPreview()));
203 }
204 
205 PreviewJob::~PreviewJob()
206 {
207 #if WITH_SHM
208  Q_D(PreviewJob);
209  if (d->shmaddr) {
210  shmdt((char *)d->shmaddr);
211  shmctl(d->shmid, IPC_RMID, nullptr);
212  }
213 #endif
214 }
215 
217 {
218  Q_D(PreviewJob);
219  d->iconSize = size;
220 }
221 
223 {
224  Q_D(const PreviewJob);
225  return d->iconSize;
226 }
227 
229 {
230  Q_D(PreviewJob);
231  d->iconAlpha = qBound(0, alpha, 255);
232 }
233 
235 {
236  Q_D(const PreviewJob);
237  return d->iconAlpha;
238 }
239 
241 {
242  Q_D(PreviewJob);
243  switch (type) {
244  case Unscaled:
245  d->bScale = false;
246  d->bSave = false;
247  break;
248  case Scaled:
249  d->bScale = true;
250  d->bSave = false;
251  break;
252  case ScaledAndCached:
253  d->bScale = true;
254  d->bSave = true;
255  break;
256  default:
257  break;
258  }
259 }
260 
262 {
263  Q_D(const PreviewJob);
264  if (d->bScale) {
265  return d->bSave ? ScaledAndCached : Scaled;
266  }
267  return Unscaled;
268 }
269 
270 void PreviewJobPrivate::startPreview()
271 {
272  Q_Q(PreviewJob);
273  // Load the list of plugins to determine which mimetypes are supported
274  const KService::List plugins = KServiceTypeTrader::self()->query(QStringLiteral("ThumbCreator"));
277 
278  for (KService::List::ConstIterator it = plugins.constBegin(); it != plugins.constEnd(); ++it) {
279  QStringList protocols = (*it)->property(QStringLiteral("X-KDE-Protocols")).toStringList();
280  const QString p = (*it)->property(QStringLiteral("X-KDE-Protocol")).toString();
281  if (!p.isEmpty()) {
282  protocols.append(p);
283  }
284  for (const QString &protocol : qAsConst(protocols)) {
285  // We cannot use mimeTypes() here, it doesn't support groups such as: text/*
286  const QStringList mtypes = (*it)->serviceTypes();
287  // Add supported mimetype for this protocol
288  QStringList &_ms = m_remoteProtocolPlugins[protocol];
289  for (const QString &_m : mtypes) {
290  if (_m != QLatin1String("ThumbCreator")) {
291  protocolMap[protocol].insert(_m, *it);
292  if (!_ms.contains(_m)) {
293  _ms.append(_m);
294  }
295  }
296  }
297  }
298  if (enabledPlugins.contains((*it)->desktopEntryName())) {
299  const QStringList mimeTypes = (*it)->serviceTypes();
300  for (QStringList::ConstIterator mt = mimeTypes.constBegin(); mt != mimeTypes.constEnd(); ++mt) {
301  if (*mt != QLatin1String("ThumbCreator")) {
302  mimeMap.insert(*mt, *it);
303  }
304  }
305  }
306  }
307 
308  //Prepare encryptedMountsList which will be used in ::slotThumbData
309  const auto mountsList = KMountPoint::currentMountPoints();
310  const auto thumbRootMount = mountsList.findByPath(thumbRoot);
311  std::copy_if(mountsList.begin(), mountsList.end(),
312  std::back_inserter(encryptedMountsList),
313  [&thumbRootMount] (KMountPoint::Ptr mount) {
314  return (thumbRootMount != mount) &&
315  (mount->mountType() == QLatin1String("fuse.cryfs") ||
316  mount->mountType() == QLatin1String("fuse.encfs"));
317  });
318 
319  // Look for images and store the items in our todo list :)
320  bool bNeedCache = false;
321  KFileItemList::const_iterator kit = initialItems.constBegin();
322  const KFileItemList::const_iterator kend = initialItems.constEnd();
323  for (; kit != kend; ++kit) {
324  PreviewItem item;
325  item.item = *kit;
326 
327  const QString mimeType = item.item.mimetype();
328  KService::Ptr plugin(nullptr);
329 
330  // look for protocol-specific thumbnail plugins first
331  QHash<QString, QHash<QString, KService::Ptr> >::const_iterator it = protocolMap.constFind(item.item.url().scheme());
332  if (it != protocolMap.constEnd()) {
333  plugin = it.value().value(mimeType);
334  }
335 
336  if (!plugin) {
337  QMap<QString, KService::Ptr>::ConstIterator pluginIt = mimeMap.constFind(mimeType);
338  if (pluginIt == mimeMap.constEnd()) {
339  QString groupMimeType = mimeType;
340  groupMimeType.replace(QRegularExpression(QStringLiteral("/.*")), QStringLiteral("/*"));
341  pluginIt = mimeMap.constFind(groupMimeType);
342 
343  if (pluginIt == mimeMap.constEnd()) {
344  QMimeDatabase db;
345  // check mime type inheritance, resolve aliases
346  const QMimeType mimeInfo = db.mimeTypeForName(mimeType);
347  if (mimeInfo.isValid()) {
348  const QStringList parentMimeTypes = mimeInfo.allAncestors();
349  for (const QString &parentMimeType : parentMimeTypes) {
350  pluginIt = mimeMap.constFind(parentMimeType);
351  if (pluginIt != mimeMap.constEnd()) {
352  break;
353  }
354  }
355  }
356  }
357  }
358 
359  if (pluginIt != mimeMap.constEnd()) {
360  plugin = *pluginIt;
361  }
362  }
363 
364  if (plugin) {
365  item.plugin = plugin;
366  items.push_back(item);
367  if (!bNeedCache && bSave && plugin->property(QStringLiteral("CacheThumbnail")).toBool()) {
368  const QUrl url = (*kit).url();
369  if (!url.isLocalFile() ||
371  bNeedCache = true;
372  }
373  }
374  } else {
375  emit q->failed(*kit);
376  }
377  }
378 
379  KConfigGroup cg(KSharedConfig::openConfig(), "PreviewSettings");
380  maximumLocalSize = cg.readEntry("MaximumSize", std::numeric_limits<KIO::filesize_t>::max());
381  maximumRemoteSize = cg.readEntry("MaximumRemoteSize", 0);
382 
383  if (bNeedCache) {
384  if (width <= 128 && height <= 128) {
385  cacheWidth = cacheHeight = 128;
386  } else {
387  cacheWidth = cacheHeight = 256;
388  }
389  thumbPath = thumbRoot + QLatin1String(cacheWidth == 128 ? "normal/" : "large/");
390  if (!QDir(thumbPath).exists()) {
391  if (QDir().mkpath(thumbPath)) { // Qt5 TODO: mkpath(dirPath, permissions)
392  QFile f(thumbPath);
394  }
395  }
396  } else {
397  bSave = false;
398  }
399 
400  initialItems.clear();
401  determineNextFile();
402 }
403 
404 void PreviewJob::removeItem(const QUrl &url)
405 {
406  Q_D(PreviewJob);
407 
408  auto it = d->items.cbegin();
409  while (it != d->items.cend()) {
410  if ((*it).item.url() == url) {
411  d->items.erase(it);
412  break;
413  }
414  ++it;
415  }
416 
417  if (d->currentItem.item.url() == url) {
418  KJob *job = subjobs().first();
419  job->kill();
420  removeSubjob(job);
421  d->determineNextFile();
422  }
423 }
424 
426 {
427  d_func()->sequenceIndex = index;
428 }
429 
431 {
432  return d_func()->sequenceIndex;
433 }
434 
435 void PreviewJob::setIgnoreMaximumSize(bool ignoreSize)
436 {
437  d_func()->ignoreMaximumSize = ignoreSize;
438 }
439 
440 void PreviewJobPrivate::cleanupTempFile()
441 {
442  if (!tempName.isEmpty()) {
443  Q_ASSERT((!QFileInfo(tempName).isDir() && QFileInfo(tempName).isFile()) || QFileInfo(tempName).isSymLink());
444  QFile::remove(tempName);
445  tempName.clear();
446  }
447 }
448 
449 void PreviewJobPrivate::determineNextFile()
450 {
451  Q_Q(PreviewJob);
452  if (!currentItem.item.isNull()) {
453  if (!succeeded) {
454  emit q->failed(currentItem.item);
455  }
456  }
457  // No more items ?
458  if (items.empty()) {
459  q->emitResult();
460  return;
461  } else {
462  // First, stat the orig file
463  state = PreviewJobPrivate::STATE_STATORIG;
464  currentItem = items.front();
465  items.pop_front();
466  succeeded = false;
467  KIO::Job *job = KIO::stat(currentItem.item.url(), KIO::HideProgressInfo);
468  job->addMetaData(QStringLiteral("thumbnail"), QStringLiteral("1"));
469  job->addMetaData(QStringLiteral("no-auth-prompt"), QStringLiteral("true"));
470  q->addSubjob(job);
471  }
472 }
473 
474 void PreviewJob::slotResult(KJob *job)
475 {
476  Q_D(PreviewJob);
477 
478  removeSubjob(job);
479  Q_ASSERT(!hasSubjobs()); // We should have only one job at a time ...
480  switch (d->state) {
481  case PreviewJobPrivate::STATE_STATORIG: {
482  if (job->error()) { // that's no good news...
483  // Drop this one and move on to the next one
484  d->determineNextFile();
485  return;
486  }
487  const KIO::UDSEntry entry = static_cast<KIO::StatJob *>(job)->statResult();
489 
490  bool skipCurrentItem = false;
492  const QUrl itemUrl = d->currentItem.item.mostLocalUrl();
493 
494  if (itemUrl.isLocalFile() || KProtocolInfo::protocolClass(itemUrl.scheme()) == QLatin1String(":local")) {
495  skipCurrentItem = !d->ignoreMaximumSize && size > d->maximumLocalSize
496  && !d->currentItem.plugin->property(QStringLiteral("IgnoreMaximumSize")).toBool();
497  } else {
498  // For remote items the "IgnoreMaximumSize" plugin property is not respected
499  skipCurrentItem = !d->ignoreMaximumSize && size > d->maximumRemoteSize;
500 
501  // Remote directories are not supported, don't try to do a file_copy on them
502  if (!skipCurrentItem) {
503  // TODO update item.mimeType from the UDS entry, in case it wasn't set initially
504  // But we don't use the mimetype anymore, we just use isDir().
505  if (d->currentItem.item.isDir()) {
506  skipCurrentItem = true;
507  }
508  }
509  }
510  if (skipCurrentItem) {
511  d->determineNextFile();
512  return;
513  }
514 
515  bool pluginHandlesSequences = d->currentItem.plugin->property(QStringLiteral("HandleSequences"), QVariant::Bool).toBool();
516  if (!d->currentItem.plugin->property(QStringLiteral("CacheThumbnail")).toBool() || (d->sequenceIndex && pluginHandlesSequences)) {
517  // This preview will not be cached, no need to look for a saved thumbnail
518  // Just create it, and be done
519  d->getOrCreateThumbnail();
520  return;
521  }
522 
523  if (d->statResultThumbnail()) {
524  return;
525  }
526 
527  d->getOrCreateThumbnail();
528  return;
529  }
530  case PreviewJobPrivate::STATE_GETORIG: {
531  if (job->error()) {
532  d->cleanupTempFile();
533  d->determineNextFile();
534  return;
535  }
536 
537  d->createThumbnail(static_cast<KIO::FileCopyJob *>(job)->destUrl().toLocalFile());
538  return;
539  }
540  case PreviewJobPrivate::STATE_CREATETHUMB: {
541  d->cleanupTempFile();
542  d->determineNextFile();
543  return;
544  }
545  }
546 }
547 
548 bool PreviewJobPrivate::statResultThumbnail()
549 {
550  if (thumbPath.isEmpty()) {
551  return false;
552  }
553 
554  QUrl url = currentItem.item.mostLocalUrl();
555  // Don't include the password if any
556  url.setPassword(QString());
557  origName = url.toEncoded();
558 
562 
563  QImage thumb;
564  if (!thumb.load(thumbPath + thumbName)) {
565  return false;
566  }
567 
568  if (thumb.text(QStringLiteral("Thumb::URI")) != QString::fromUtf8(origName) ||
569  thumb.text(QStringLiteral("Thumb::MTime")).toLongLong() != tOrig.toSecsSinceEpoch()) {
570  return false;
571  }
572 
573  QString thumbnailerVersion = currentItem.plugin->property(QStringLiteral("ThumbnailerVersion"), QVariant::String).toString();
574 
575  if (!thumbnailerVersion.isEmpty() && thumb.text(QStringLiteral("Software")).startsWith(QLatin1String("KDE Thumbnail Generator"))) {
576  //Check if the version matches
577  //The software string should read "KDE Thumbnail Generator pluginName (vX)"
578  QString softwareString = thumb.text(QStringLiteral("Software")).remove(QStringLiteral("KDE Thumbnail Generator")).trimmed();
579  if (softwareString.isEmpty()) {
580  // The thumbnail has been created with an older version, recreating
581  return false;
582  }
583  int versionIndex = softwareString.lastIndexOf(QLatin1String("(v"));
584  if (versionIndex < 0) {
585  return false;
586  }
587 
588  QString cachedVersion = softwareString.remove(0, versionIndex + 2);
589  cachedVersion.chop(1);
590  uint thumbnailerMajor = thumbnailerVersion.toInt();
591  uint cachedMajor = cachedVersion.toInt();
592  if (thumbnailerMajor > cachedMajor) {
593  return false;
594  }
595  }
596 
597  // Found it, use it
598  emitPreview(thumb);
599  succeeded = true;
600  determineNextFile();
601  return true;
602 }
603 
604 void PreviewJobPrivate::getOrCreateThumbnail()
605 {
606  Q_Q(PreviewJob);
607  // We still need to load the orig file ! (This is getting tedious) :)
608  const KFileItem &item = currentItem.item;
609  const QString localPath = item.localPath();
610  if (!localPath.isEmpty()) {
611  createThumbnail(localPath);
612  } else {
613  const QUrl fileUrl = item.url();
614  // heuristics for remote URL support
615  bool supportsProtocol = false;
616  if (m_remoteProtocolPlugins.value(fileUrl.scheme()).contains(item.mimetype())) {
617  // There's a plugin supporting this protocol and mimetype
618  supportsProtocol = true;
619  } else if (m_remoteProtocolPlugins.value(QStringLiteral("KIO")).contains(item.mimetype())) {
620  // Assume KIO understands any URL, ThumbCreator slaves who have
621  // X-KDE-Protocols=KIO will get fed the remote URL directly.
622  supportsProtocol = true;
623  }
624 
625  if (supportsProtocol) {
626  createThumbnail(fileUrl.toString());
627  return;
628  }
629  if (item.isDir()) {
630  // Skip remote dirs (bug 208625)
631  cleanupTempFile();
632  determineNextFile();
633  return;
634  }
635  // No plugin support access to this remote content, copy the file
636  // to the local machine, then create the thumbnail
637  state = PreviewJobPrivate::STATE_GETORIG;
638  QTemporaryFile localFile;
639  localFile.setAutoRemove(false);
640  localFile.open();
641  tempName = localFile.fileName();
642  const QUrl currentURL = item.mostLocalUrl();
643  KIO::Job *job = KIO::file_copy(currentURL, QUrl::fromLocalFile(tempName), -1, KIO::Overwrite | KIO::HideProgressInfo /* No GUI */);
644  job->addMetaData(QStringLiteral("thumbnail"), QStringLiteral("1"));
645  q->addSubjob(job);
646  }
647 }
648 
649 void PreviewJobPrivate::createThumbnail(const QString &pixPath)
650 {
651  Q_Q(PreviewJob);
652  state = PreviewJobPrivate::STATE_CREATETHUMB;
653  QUrl thumbURL;
654  thumbURL.setScheme(QStringLiteral("thumbnail"));
655  thumbURL.setPath(pixPath);
656  KIO::TransferJob *job = KIO::get(thumbURL, NoReload, HideProgressInfo);
657  q->addSubjob(job);
658  q->connect(job, SIGNAL(data(KIO::Job*,QByteArray)), SLOT(slotThumbData(KIO::Job*,QByteArray)));
659  bool save = bSave && currentItem.plugin->property(QStringLiteral("CacheThumbnail")).toBool() && !sequenceIndex;
660  job->addMetaData(QStringLiteral("mimeType"), currentItem.item.mimetype());
661  job->addMetaData(QStringLiteral("width"), QString().setNum(save ? cacheWidth : width));
662  job->addMetaData(QStringLiteral("height"), QString().setNum(save ? cacheHeight : height));
663  job->addMetaData(QStringLiteral("iconSize"), QString().setNum(save ? 64 : iconSize));
664  job->addMetaData(QStringLiteral("iconAlpha"), QString().setNum(iconAlpha));
665  job->addMetaData(QStringLiteral("plugin"), currentItem.plugin->library());
666  job->addMetaData(QStringLiteral("enabledPlugins"), enabledPlugins.join(QLatin1Char(',')));
667  if (sequenceIndex) {
668  job->addMetaData(QStringLiteral("sequence-index"), QString().setNum(sequenceIndex));
669  }
670 
671 #if WITH_SHM
672  if (shmid == -1) {
673  if (shmaddr) {
674  shmdt((char *)shmaddr);
675  shmctl(shmid, IPC_RMID, nullptr);
676  }
677  shmid = shmget(IPC_PRIVATE, cacheWidth * cacheHeight * 4, IPC_CREAT | 0600);
678  if (shmid != -1) {
679  shmaddr = (uchar *)(shmat(shmid, nullptr, SHM_RDONLY));
680  if (shmaddr == (uchar *) - 1) {
681  shmctl(shmid, IPC_RMID, nullptr);
682  shmaddr = nullptr;
683  shmid = -1;
684  }
685  } else {
686  shmaddr = nullptr;
687  }
688  }
689  if (shmid != -1) {
690  job->addMetaData(QStringLiteral("shmid"), QString().setNum(shmid));
691  }
692 #endif
693 }
694 
695 void PreviewJobPrivate::slotThumbData(KIO::Job *, const QByteArray &data)
696 {
697  const bool isEncrypted = encryptedMountsList.findByPath(currentItem.item.url().toLocalFile());
698  bool save = bSave &&
699  !sequenceIndex && !isEncrypted &&
700  currentItem.plugin->property(QStringLiteral("CacheThumbnail")).toBool() &&
701  (!currentItem.item.url().isLocalFile() ||
702  !currentItem.item.url().adjusted(QUrl::RemoveFilename).toLocalFile().startsWith(thumbRoot));
703  QImage thumb;
704 #if WITH_SHM
705  if (shmaddr) {
706  // Keep this in sync with kdebase/kioslave/thumbnail.cpp
707  QDataStream str(data);
708  int width, height;
709  quint8 iFormat;
710  str >> width >> height >> iFormat;
711  QImage::Format format = static_cast<QImage::Format>(iFormat);
712  thumb = QImage(shmaddr, width, height, format).copy();
713  } else
714 #endif
715  thumb.loadFromData(data);
716 
717  if (thumb.isNull()) {
718  QDataStream s(data);
719  s >> thumb;
720  }
721 
722  if (save) {
723  thumb.setText(QStringLiteral("Thumb::URI"), QString::fromUtf8(origName));
724  thumb.setText(QStringLiteral("Thumb::MTime"), QString::number(tOrig.toSecsSinceEpoch()));
725  thumb.setText(QStringLiteral("Thumb::Size"), number(currentItem.item.size()));
726  thumb.setText(QStringLiteral("Thumb::Mimetype"), currentItem.item.mimetype());
727  QString thumbnailerVersion = currentItem.plugin->property(QStringLiteral("ThumbnailerVersion"), QVariant::String).toString();
728  QString signature = QLatin1String("KDE Thumbnail Generator ") + currentItem.plugin->name();
729  if (!thumbnailerVersion.isEmpty()) {
730  signature.append(QLatin1String(" (v") + thumbnailerVersion + QLatin1Char(')'));
731  }
732  thumb.setText(QStringLiteral("Software"), signature);
733  QSaveFile saveFile(thumbPath + thumbName);
734  if (saveFile.open(QIODevice::WriteOnly)) {
735  if (thumb.save(&saveFile, "PNG")) {
736  saveFile.commit();
737  }
738  }
739  }
740  emitPreview(thumb);
741  succeeded = true;
742 }
743 
744 void PreviewJobPrivate::emitPreview(const QImage &thumb)
745 {
746  Q_Q(PreviewJob);
747  QPixmap pix;
748  if (thumb.width() > width || thumb.height() > height) {
750  } else {
751  pix = QPixmap::fromImage(thumb);
752  }
753  emit q->gotPreview(currentItem.item, pix);
754 }
755 
757 {
759  const KService::List plugins = KServiceTypeTrader::self()->query(QStringLiteral("ThumbCreator"));
760  for (const KService::Ptr &plugin : plugins) {
761  const QString desktopEntryName = plugin->desktopEntryName();
762  if (!result.contains(desktopEntryName)) {
763  result.append(desktopEntryName);
764  }
765  }
766  return result;
767 }
768 
770 {
771  const QStringList blacklist = QStringList()
772  << QStringLiteral("textthumbnail");
773 
775  for (const QString &plugin : blacklist) {
776  defaultPlugins.removeAll(plugin);
777  }
778 
779  return defaultPlugins;
780 }
781 
783 {
785  const KService::List plugins = KServiceTypeTrader::self()->query(QStringLiteral("ThumbCreator"));
786  for (const KService::Ptr &plugin : plugins) {
787  result += plugin->mimeTypes();
788  }
789  return result;
790 }
791 
792 #if KIOWIDGETS_BUILD_DEPRECATED_SINCE(4, 7)
793 PreviewJob *KIO::filePreview(const KFileItemList &items, int width, int height,
794  int iconSize, int iconAlpha, bool scale, bool save,
795  const QStringList *enabledPlugins)
796 {
797  return new PreviewJob(items, width, height, iconSize, iconAlpha,
798  scale, save, enabledPlugins);
799 }
800 #endif
801 
802 #if KIOWIDGETS_BUILD_DEPRECATED_SINCE(4, 7)
803 PreviewJob *KIO::filePreview(const QList<QUrl> &items, int width, int height,
804  int iconSize, int iconAlpha, bool scale, bool save,
805  const QStringList *enabledPlugins)
806 {
807  KFileItemList fileItems;
808  fileItems.reserve(items.size());
809  for (const QUrl &url : items) {
810  Q_ASSERT(url.isValid()); // please call us with valid urls only
811  fileItems.append(KFileItem(url));
812  }
813  return new PreviewJob(fileItems, width, height, iconSize, iconAlpha,
814  scale, save, enabledPlugins);
815 }
816 #endif
817 
818 PreviewJob *KIO::filePreview(const KFileItemList &items, const QSize &size, const QStringList *enabledPlugins)
819 {
820  return new PreviewJob(items, size, enabledPlugins);
821 }
822 
823 #if KIOWIDGETS_BUILD_DEPRECATED_SINCE(4, 5)
825 {
826  KConfigGroup cg(KSharedConfig::openConfig(), "PreviewSettings");
827  return cg.readEntry("MaximumSize", 5 * 1024 * 1024LL /* 5MB */);
828 }
829 #endif
830 
831 #include "moc_previewjob.cpp"
QString url(QUrl::FormattingOptions options) const const
RemoveFilename
bool kill(KillVerbosity verbosity=Quietly)
When set, automatically overwrite the destination if it exists already.
Definition: job_base.h:303
static QStringList defaultPlugins()
Returns a list of plugins that should be enabled by default, which is all plugins Minus the plugins s...
Definition: previewjob.cpp:769
static QStringList availablePlugins()
Returns a list of all available preview plugins.
Definition: previewjob.cpp:756
bool loadFromData(const uchar *data, int len, const char *format)
QString & append(QChar ch)
qulonglong filesize_t
64-bit file size
Definition: global.h:51
QHash::iterator insert(const Key &key, const T &value)
bool load(QIODevice *device, const char *format)
void addMetaData(const QString &key, const QString &value)
Add key/value pair to the meta data that is sent to the slave.
Definition: job.cpp:243
bool isDir() const
Returns true if this item represents a directory.
Definition: kfileitem.cpp:1241
QString writableLocation(QStandardPaths::StandardLocation type)
void setOverlayIconSize(int size)
Sets the size of the MIME-type icon which overlays the preview.
Definition: previewjob.cpp:216
QByteArray toHex() const const
Universal Directory Service.
Definition: udsentry.h:88
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:160
QString mimetype() const
Returns the mimetype of the file item.
Definition: kfileitem.cpp:804
bool remove()
A namespace for KIO globals.
Definition: authinfo.h:34
static KServiceTypeTrader * self()
static List currentMountPoints(DetailsNeededFlags infoNeeded=BasicInfoNeeded)
This function gives a list of all currently used mountpoints.
bool save(const QString &fileName, const char *format, int quality) const const
void reserve(int alloc)
Hide progress information dialog, i.e.
Definition: job_base.h:287
void setScaleType(ScaleType type)
Sets the scale type for the generated preview.
Definition: previewjob.cpp:240
QVariant property(const QString &_name, QVariant::Type t) const
bool contains(const QString &str, Qt::CaseSensitivity cs) const const
void setPassword(const QString &password, QUrl::ParsingMode mode)
QPixmap fromImage(const QImage &image, Qt::ImageConversionFlags flags)
QHash::const_iterator constFind(const Key &key) const const
A KIO job that retrieves information about a file or directory.
Definition: statjob.h:38
QMap::const_iterator constFind(const Key &key) const const
KIOCORE_EXPORT FileCopyJob * file_copy(const QUrl &src, const QUrl &dest, int permissions=-1, JobFlags flags=DefaultFlags)
Copy a single file.
virtual bool open(QIODevice::OpenMode mode) override
virtual bool setPermissions(QFileDevice::Permissions permissions) override
QDateTime fromSecsSinceEpoch(qint64 secs, Qt::TimeSpec spec, int offsetSeconds)
bool isNull() const const
QString & remove(int position, int n)
QImage copy(const QRect &rectangle) const const
QString text(const QString &key) const const
void chop(int n)
QString toString(QUrl::FormattingOptions options) const const
bool hasSubjobs() const
The original size of the preview will be returned.
Definition: previewjob.h:54
int size() const const
int overlayIconAlpha() const
Definition: previewjob.cpp:234
int lastIndexOf(QChar ch, int from, Qt::CaseSensitivity cs) const const
KIOCORE_EXPORT SimpleJob * mount(bool ro, const QByteArray &fstype, const QString &dev, const QString &point, JobFlags flags=DefaultFlags)
Mount filesystem.
Definition: simplejob.cpp:392
void setPath(const QString &path, QUrl::ParsingMode mode)
void setText(const QString &key, const QString &text)
int sequenceIndex() const
Returns the currently set sequence index.
Definition: previewjob.cpp:430
KIOCORE_EXPORT QString number(KIO::filesize_t size)
Converts a size to a string representation Not unlike QString::number(...)
Definition: global.cpp:66
void removeItem(const QUrl &url)
Removes an item from preview processing.
Definition: previewjob.cpp:404
QString number(int n, int base)
void append(const T &value)
QString fromUtf8(const char *str, int size)
QHash::const_iterator constEnd() const const
static KIO::filesize_t maximumFileSize()
Returns the default "maximum file size", in bytes, used by PreviewJob.
Definition: previewjob.cpp:824
bool commit()
ScaleType scaleType() const
Definition: previewjob.cpp:261
long long numberValue(uint field, long long defaultValue=0) const
Definition: udsentry.cpp:370
int width() const const
void setAutoRemove(bool b)
int toInt(bool *ok, int base) const const
QString localPath() const
Returns the local path if isLocalFile() == true or the KIO item has a UDS_LOCAL_PATH atom...
Definition: kfileitem.cpp:684
bool isEmpty() const const
int removeAll(const T &value)
QString trimmed() const const
QMap::const_iterator constEnd() const const
bool startsWith(const QString &s, Qt::CaseSensitivity cs) const const
void setScheme(const QString &scheme)
List of KFileItems, which adds a few helper methods to QList<KFileItem>.
Definition: kfileitem.h:599
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.
Definition: previewjob.cpp:793
void addData(const char *data, int length)
T & first()
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:167
static QString protocolClass(const QString &protocol)
Returns the protocol class for the specified protocol.
QString scheme() const const
QMimeType mimeTypeForName(const QString &nameOrAlias) const const
QString toLocalFile() const const
The last time the file was modified.
Definition: udsentry.h:271
KIOCORE_EXPORT TransferJob * get(const QUrl &url, LoadType reload=NoReload, JobFlags flags=DefaultFlags)
Get (means: read).
const T value(const Key &key) const const
KIO Job to get a thumbnail picture.
Definition: previewjob.h:41
bool isValid() const const
static KSharedConfig::Ptr openConfig(const QString &fileName=QString(), OpenFlags mode=FullConfig, QStandardPaths::StandardLocation type=QStandardPaths::GenericConfigLocation)
virtual QString fileName() const const override
QString & replace(int position, int n, QChar after)
KService::List query(const QString &servicetype, const QString &constraint=QString()) const
The preview will be scaled to the size specified when constructing the PreviewJob.
Definition: previewjob.h:65
void setIgnoreMaximumSize(bool ignoreSize=true)
If ignoreSize is true, then the preview is always generated regardless of the settings.
Definition: previewjob.cpp:435
QUrl adjusted(QUrl::FormattingOptions options) const const
KeepAspectRatio
void setOverlayIconAlpha(int alpha)
Sets the alpha-value for the MIME-type icon which overlays the preview.
Definition: previewjob.cpp:228
typedef ConstIterator
The base class for all jobs.
Definition: job_base.h:57
QUrl mostLocalUrl(bool *local=nullptr) const
Tries to give a local URL for this file item if possible.
Definition: kfileitem.cpp:1428
const QList< KJob * > & subjobs() const
bool removeSubjob(KJob *job) override
Mark a sub job as being done.
Definition: job.cpp:98
bool toBool() const const
QByteArray result() const const
QString fromLatin1(const char *str, int size)
List of mount points.
Definition: kmountpoint.h:44
int height() const const
QMap::iterator insert(const Key &key, const T &value)
ScaleType
Specifies the type of scaling that is applied to the generated preview.
Definition: previewjob.h:49
KIOCORE_EXPORT StatJob * stat(const QUrl &url, JobFlags flags=DefaultFlags)
Find all details for one file or directory.
Definition: statjob.cpp:209
QUrl url() const
Returns the url of the file.
Definition: kfileitem.cpp:1494
void result(KJob *job)
QList::const_iterator constEnd() const const
The transfer job pumps data into and/or out of a Slave.
Definition: transferjob.h:38
static QStringList supportedMimeTypes()
Returns a list of all supported MIME types.
Definition: previewjob.cpp:782
QList::const_iterator constBegin() const const
T readEntry(const QString &key, const T &aDefault) const
SmoothTransformation
qlonglong toLongLong(bool *ok, int base) const const
Size of the file.
Definition: udsentry.h:242
The preview will be scaled to the size specified when constructing the PreviewJob.
Definition: previewjob.h:59
QByteArray toEncoded(QUrl::FormattingOptions options) const const
QImage scaled(int width, int height, Qt::AspectRatioMode aspectRatioMode, Qt::TransformationMode transformMode) const const
QByteArray encodeName(const QString &fileName)
A KFileItem is a generic class to handle a file, local or remote.
Definition: kfileitem.h:47
int overlayIconSize() const
Definition: previewjob.cpp:222
QUrl fromLocalFile(const QString &localFile)
int error() const
bool isLocalFile() const const
void setSequenceIndex(int index)
Sets the sequence index given to the thumb creators.
Definition: previewjob.cpp:425
This file is part of the KDE documentation.
Documentation copyright © 1996-2020 The KDE developers.
Generated on Sat Jul 11 2020 23:00:27 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.