28 #include <sys/types.h>
35 #include <QtCore/QDir>
36 #include <QtCore/QFile>
37 #include <QtGui/QImage>
38 #include <QtCore/QTimer>
39 #include <QtCore/QRegExp>
49 #include <QtCore/QLinkedList>
56 namespace KIO {
struct PreviewItem; }
59 struct KIO::PreviewItem
68 enum { STATE_STATORIG,
77 QHash<QString, QStringList> m_remoteProtocolPlugins;
80 QLinkedList<PreviewItem> items;
82 PreviewItem currentItem;
102 bool ignoreMaximumSize;
121 void getOrCreateThumbnail();
122 bool statResultThumbnail();
123 void createThumbnail(
const QString& );
124 void determineNextFile();
125 void emitPreview(
const QImage &thumb);
128 void slotThumbData(
KIO::Job *,
const QByteArray &);
133 #ifndef KDE_NO_DEPRECATED
135 int iconSize,
int iconAlpha,
bool scale,
bool save,
137 : KIO::
Job(*new PreviewJobPrivate)
143 d->initialItems = items;
146 d->height = height ? height : width;
147 d->cacheWidth = d->width;
148 d->cacheHeight = d->height;
149 d->iconSize = iconSize;
150 d->iconAlpha = iconAlpha;
152 d->bSave = save && scale;
153 d->succeeded =
false;
154 d->thumbRoot = QDir::homePath() + QLatin1String(
"/.thumbnails/");
155 d->ignoreMaximumSize =
false;
156 d->sequenceIndex = 0;
157 d->maximumLocalSize = 0;
158 d->maximumRemoteSize = 0;
161 QTimer::singleShot(0,
this, SLOT(startPreview()));
168 KIO::
Job(*new PreviewJobPrivate)
174 d->initialItems = items;
175 if (enabledPlugins) {
176 d->enabledPlugins = *enabledPlugins;
180 <<
"directorythumbnail"
184 d->width = size.width();
185 d->height = size.height();
186 d->cacheWidth = d->width;
187 d->cacheHeight = d->height;
192 d->succeeded =
false;
193 d->thumbRoot = QDir::homePath() + QLatin1String(
"/.thumbnails/");
194 d->ignoreMaximumSize =
false;
195 d->sequenceIndex = 0;
196 d->maximumLocalSize = 0;
197 d->maximumRemoteSize = 0;
200 QTimer::singleShot(0,
this, SLOT(startPreview()));
208 shmdt((
char*)d->shmaddr);
209 shmctl(d->shmid, IPC_RMID, 0);
229 d->iconAlpha = qBound(0, alpha, 255);
268 void PreviewJobPrivate::startPreview()
274 QHash<QString, QHash<QString, KService::Ptr> > protocolMap;
275 for (KService::List::ConstIterator it = plugins.constBegin(); it != plugins.constEnd(); ++it) {
276 QStringList protocols = (*it)->property(
"X-KDE-Protocols").toStringList();
277 const QString p = (*it)->property(
"X-KDE-Protocol").toString();
281 foreach (
const QString &protocol, protocols) {
285 foreach (
const QString &_mtype, mtypes) {
286 if (!((*it)->hasMimeType(_mtype))) {
287 mtypes.removeAll(_mtype);
291 QStringList &_ms = m_remoteProtocolPlugins[protocol];
292 foreach (
const QString &_m, mtypes) {
293 protocolMap[protocol].insert(_m, *it);
294 if (!_ms.contains(_m)) {
299 if (enabledPlugins.contains((*it)->desktopEntryName())) {
301 for (QStringList::ConstIterator mt = mimeTypes.constBegin(); mt != mimeTypes.constEnd(); ++mt)
302 mimeMap.insert(*mt, *it);
307 bool bNeedCache =
false;
308 KFileItemList::const_iterator kit = initialItems.constBegin();
309 const KFileItemList::const_iterator kend = initialItems.constEnd();
310 for ( ; kit != kend; ++kit )
314 const QString mimeType = item.item.mimetype();
318 QHash<QString, QHash<QString, KService::Ptr> >::const_iterator it = protocolMap.constFind(item.item.url().protocol());
319 if (it != protocolMap.constEnd()) {
320 plugin = it.value().value(mimeType);
325 if (pluginIt == mimeMap.constEnd()) {
326 QString groupMimeType = mimeType;
327 groupMimeType.replace(QRegExp(
"/.*"),
"/*");
328 pluginIt = mimeMap.constFind(groupMimeType);
330 if (pluginIt == mimeMap.constEnd()) {
335 Q_FOREACH(
const QString& parentMimeType, parentMimeTypes) {
336 pluginIt = mimeMap.constFind(parentMimeType);
337 if (pluginIt != mimeMap.constEnd())
344 if (pluginIt != mimeMap.constEnd()) {
350 item.plugin = plugin;
352 if (!bNeedCache && bSave &&
353 ((*kit).url().protocol() !=
"file" ||
355 plugin->property(
"CacheThumbnail").toBool()) {
359 emit q->failed( *kit );
364 maximumLocalSize = cg.readEntry(
"MaximumSize", 5*1024*1024LL );
365 maximumRemoteSize = cg.readEntry(
"MaximumRemoteSize", 0 );
369 if (width <= 128 && height <= 128) cacheWidth = cacheHeight = 128;
370 else cacheWidth = cacheHeight = 256;
371 thumbPath = thumbRoot + (cacheWidth == 128 ?
"normal/" :
"large/");
377 initialItems.clear();
384 for (QLinkedList<PreviewItem>::Iterator it = d->items.begin(); it != d->items.end(); ++it)
385 if ((*it).item.url() == url)
391 if (d->currentItem.item.url() == url)
396 d->determineNextFile();
401 d_func()->sequenceIndex = index;
405 return d_func()->sequenceIndex;
410 d_func()->ignoreMaximumSize = ignoreSize;
413 void PreviewJobPrivate::determineNextFile()
416 if (!currentItem.item.isNull())
419 emit q->failed( currentItem.item );
422 if ( items.isEmpty() )
430 state = PreviewJobPrivate::STATE_STATORIG;
431 currentItem = items.first();
448 case PreviewJobPrivate::STATE_STATORIG:
453 d->determineNextFile();
459 bool skipCurrentItem =
false;
461 const KUrl itemUrl = d->currentItem.item.mostLocalUrl();
465 skipCurrentItem = !d->ignoreMaximumSize && size > d->maximumLocalSize
466 && !d->currentItem.plugin->property(
"IgnoreMaximumSize").toBool();
471 skipCurrentItem = !d->ignoreMaximumSize && size > d->maximumRemoteSize;
474 if (!skipCurrentItem) {
477 if (mime && mime->
is(
"inode/directory")) {
478 skipCurrentItem =
true;
484 d->determineNextFile();
488 bool pluginHandlesSequences = d->currentItem.plugin->property(
"HandleSequences", QVariant::Bool).toBool();
489 if ( !d->currentItem.plugin->property(
"CacheThumbnail" ).toBool() || (d->sequenceIndex && pluginHandlesSequences) )
493 d->getOrCreateThumbnail();
497 if ( d->statResultThumbnail() )
500 d->getOrCreateThumbnail();
503 case PreviewJobPrivate::STATE_GETORIG:
507 d->determineNextFile();
511 d->createThumbnail( static_cast<KIO::FileCopyJob*>(job)->destUrl().toLocalFile() );
514 case PreviewJobPrivate::STATE_CREATETHUMB:
516 if (!d->tempName.isEmpty())
518 QFile::remove(d->tempName);
521 d->determineNextFile();
527 bool PreviewJobPrivate::statResultThumbnail()
529 if ( thumbPath.isEmpty() )
532 KUrl url = currentItem.item.mostLocalUrl();
535 origName = url.
url();
537 KMD5 md5( QFile::encodeName( origName ) );
538 thumbName = QFile::encodeName( md5.hexDigest() ) +
".png";
541 if ( !thumb.load( thumbPath + thumbName ) )
return false;
543 if ( thumb.text(
"Thumb::URI", 0 ) != origName ||
544 thumb.text(
"Thumb::MTime", 0 ).toInt() != tOrig )
return false;
546 QString thumbnailerVersion = currentItem.plugin->property(
"ThumbnailerVersion", QVariant::String).toString();
548 if (!thumbnailerVersion.isEmpty() && thumb.text(
"Software", 0).startsWith(
"KDE Thumbnail Generator")) {
551 QString softwareString = thumb.text(
"Software", 0).remove(
"KDE Thumbnail Generator").trimmed();
552 if (softwareString.isEmpty()) {
556 int versionIndex = softwareString.lastIndexOf(
"(v");
557 if (versionIndex < 0) {
561 QString cachedVersion = softwareString.remove(0, versionIndex+2);
562 cachedVersion.chop(1);
563 uint thumbnailerMajor = thumbnailerVersion.toInt();
564 uint cachedMajor = cachedVersion.toInt();
565 if (thumbnailerMajor > cachedMajor) {
571 emitPreview( thumb );
578 void PreviewJobPrivate::getOrCreateThumbnail()
582 const KFileItem& item = currentItem.item;
584 if (!localPath.isEmpty()) {
585 createThumbnail( localPath );
587 const KUrl fileUrl = item.
url();
589 bool supportsProtocol =
false;
590 if (m_remoteProtocolPlugins.value(fileUrl.scheme()).contains(item.
mimetype())) {
592 supportsProtocol =
true;
593 }
else if (m_remoteProtocolPlugins.value(
"KIO").contains(item.
mimetype())) {
596 supportsProtocol =
true;
599 if (supportsProtocol) {
600 createThumbnail(fileUrl.
url());
605 state = PreviewJobPrivate::STATE_GETORIG;
607 localFile.setAutoRemove(
false);
610 localURL.
setPath( tempName = localFile.fileName() );
618 void PreviewJobPrivate::createThumbnail(
const QString &pixPath )
621 state = PreviewJobPrivate::STATE_CREATETHUMB;
627 q->connect(job, SIGNAL(data(
KIO::Job*,QByteArray)), SLOT(slotThumbData(
KIO::Job*,QByteArray)));
628 bool save = bSave && currentItem.plugin->property(
"CacheThumbnail").toBool() && !sequenceIndex;
629 job->
addMetaData(
"mimeType", currentItem.item.mimetype());
634 job->
addMetaData(
"plugin", currentItem.plugin->library());
642 shmdt((
char*)shmaddr);
643 shmctl(shmid, IPC_RMID, 0);
645 shmid = shmget(IPC_PRIVATE, cacheWidth * cacheHeight * 4, IPC_CREAT|0600);
648 shmaddr = (uchar *)(shmat(shmid, 0, SHM_RDONLY));
649 if (shmaddr == (uchar *)-1)
651 shmctl(shmid, IPC_RMID, 0);
664 void PreviewJobPrivate::slotThumbData(
KIO::Job *,
const QByteArray &data)
667 currentItem.plugin->property(
"CacheThumbnail").toBool() &&
668 (currentItem.item.url().protocol() !=
"file" ||
675 QDataStream str(data);
678 str >> width >> height >> iFormat;
679 QImage::Format format =
static_cast<QImage::Format
>( iFormat );
680 thumb = QImage(shmaddr, width, height, format ).copy();
684 thumb.loadFromData(data);
686 if (thumb.isNull()) {
692 bool savedCorrectly =
false;
695 thumb.setText(
"Thumb::URI", origName);
697 thumb.setText(
"Thumb::Size",
number(currentItem.item.size()));
698 thumb.setText(
"Thumb::Mimetype", currentItem.item.mimetype());
699 QString thumbnailerVersion = currentItem.plugin->property(
"ThumbnailerVersion", QVariant::String).toString();
700 QString signature =
QString(
"KDE Thumbnail Generator "+currentItem.plugin->name());
701 if (!thumbnailerVersion.isEmpty()) {
702 signature.append(
" (v"+thumbnailerVersion+
')');
704 thumb.setText(
"Software", signature);
708 temp.setAutoRemove(
false);
711 tempFileName = temp.fileName();
712 savedCorrectly = thumb.save(tempFileName,
"PNG");
717 Q_ASSERT(!tempFileName.isEmpty());
720 emitPreview( thumb );
724 void PreviewJobPrivate::emitPreview(
const QImage &thumb)
728 if (thumb.width() > width || thumb.height() > height)
729 pix = QPixmap::fromImage( thumb.scaled(
QSize(width, height), Qt::KeepAspectRatio, Qt::SmoothTransformation) );
731 pix = QPixmap::fromImage( thumb );
732 emit q->gotPreview(currentItem.item, pix);
739 for (KService::List::ConstIterator it = plugins.begin(); it != plugins.end(); ++it)
740 if (!result.contains((*it)->desktopEntryName()))
741 result.append((*it)->desktopEntryName());
749 for (KService::List::ConstIterator it = plugins.begin(); it != plugins.end(); ++it)
750 result += (*it)->serviceTypes();
754 #ifndef KDE_NO_DEPRECATED
756 int iconSize,
int iconAlpha,
bool scale,
bool save,
759 return new PreviewJob(items, width, height, iconSize, iconAlpha,
760 scale, save, enabledPlugins);
764 int iconSize,
int iconAlpha,
bool scale,
bool save,
768 for (KUrl::List::ConstIterator it = items.begin(); it != items.end(); ++it) {
769 Q_ASSERT( (*it).isValid() );
772 return new PreviewJob(fileItems, width, height, iconSize, iconAlpha,
773 scale, save, enabledPlugins);
779 return new PreviewJob(items, size, enabledPlugins);
782 #ifndef KDE_NO_DEPRECATED
786 return cg.
readEntry(
"MaximumSize", 5*1024*1024LL );
790 #include "previewjob.moc"