/*************************************************************************** thumbnailprovider.cpp - description ------------------- begin : Sun Jan 21 2007 copyright : (C) 2007 by Pedro Ferreira (C) 2007 by Diederik van der Boor email : pedro.ferreira@fe.up.pt "vdboor" --at-- "codingdomain.com" ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #include "thumbnailprovider.h" #include "kmessdebug.h" #include <QPixmap> #include <QTextDocument> #include <QTimer> #include <KIconLoader> #include <KIO/PreviewJob> #include <KMimeType> /** * @brief The constructor * * Starts a KIO::PreviewJob to get the preview. * When the job completed or failed, gotResult() is fired. * * @param fileName Name of the file to request. * @param size Size of the thumbnails. Windows Live Messenger also uses 96x96 thumbnails for all files. * Depending on it's "file preview size" setting, it scales the received images down afterwards. */ 00044 ThumbnailProvider::ThumbnailProvider( const QString &fileName, int size ) : QObject( 0 ) , fileName_( fileName ) , resultError_( false ) , size_( size ) { #ifdef KMESSDEBUG_THUMBNAILPROVIDER kDebug() << "requesting thumbnail for" << fileName; #endif // The quick method (also displays a transfer dialog): // KIO::NetAccess::download( "thumbnail://" + fileName, tempFile, 0 ) // thumbnailImage_ = QImage( tempFile ); // KIO::NetAccess::removeTempFile( tempFile ); // The complex (but async) method: // Wrap file in a file list, use KIO::PreviewJob KUrl url( fileName ); fileList_ = KUrl::List( url ); // Remove preview for text files // TODO: preview should work, but doesn't enabledPlugins_ = KIO::PreviewJob::availablePlugins(); enabledPlugins_.removeAll( "textthumbnail" ); #ifdef KMESSDEBUG_THUMBNAILPROVIDER kDebug() << "enabled preview plugins: " << enabledPlugins_; #endif // Get file type KMimeType::Ptr type = KMimeType::findByUrl( url ); #ifdef KMESSDEBUG_THUMBNAILPROVIDER kDebug() << "file mime type '" << type->name() << "'"; #endif // For some file types, don't generate a preview // TODO: improve, files like .h and .cpp have a different mime type. if( type->name() == "text/plain" ) { #ifdef KMESSDEBUG_THUMBNAILPROVIDER kDebug() << "not creating previews for this file type."; #endif // Make slot call delayed, so clients can still connect to signals first. QTimer::singleShot( 0, this, SLOT(slotFailed()) ); return; } // Create the preview job KIO::PreviewJob *previewJob = KIO::filePreview( fileList_, size, size, 100, // 100% alpha true, // scale (default value) true, // save (default value) &enabledPlugins_ ); // Connect signals. connect( previewJob, SIGNAL( gotPreview(const KFileItem&, const QPixmap&) ), this, SLOT( slotGotPreview(const KFileItem&, const QPixmap&) )); connect( previewJob, SIGNAL( failed(const KFileItem&) ), this, SLOT( slotFailed(const KFileItem&) )); // The KIO::PreviewJob auto deletes itself. } /** * @brief Indicate whether a thumbnail was created. */ 00114 bool ThumbnailProvider::isSuccessful() const { return ! resultError_; } /** * @brief Return the generated image as byte array. */ 00124 const QByteArray &ThumbnailProvider::getData() const { return thumbnailData_; } /** * @brief Generate the fallback image. * * This can be called when isSuccessful() returns false. * It overwrites the internal image, so it can be requested with getData() and getImage(). */ 00137 void ThumbnailProvider::generateFallbackImage() { QString iconTitle( KMimeType::iconNameForUrl( KUrl( fileName_ ) ) ); KIconLoader *loader = KIconLoader::global(); QString iconPath ( loader->iconPath( iconTitle, -size_, false ) ); storeImage( QImage( iconPath ) ); } /** * @brief Return the generated image. */ 00150 const QImage &ThumbnailProvider::getImage() const { return thumbnailImage_; } /** * Return the HTML image tag. */ 00160 QString ThumbnailProvider::getImageTag( const QString &altText ) const { if( thumbnailData_.isNull() || thumbnailImage_.isNull() ) { return QString::null; } QString previewData( thumbnailData_.toBase64() ); return "<img src=\"data:image/png;base64," + previewData + "\"" " width=\"" + QString::number( thumbnailImage_.width() ) + "\"" " height=\"" + QString::number( thumbnailImage_.height() ) + "\"" " alt=\"" + Qt::escape( altText ) + "\" />"; } /** * @brief Called when a preview is available. */ 00179 void ThumbnailProvider::slotGotPreview( const KFileItem& /*fileItem*/, const QPixmap &preview ) { #ifdef KMESSDEBUG_THUMBNAILPROVIDER kDebug() << "thumbnail generated."; #endif // The KFileItem already gets deleted by KIO // Downloads QPixmap from the X server again to a QImage in the client memory. // So far this can't be avoided because KIO::PreviewJob already generates a QPixmap. // Resize canvas to square size for Windows Live Messenger. // This way the image is not chopped by the standard small preview size. if( preview.width() != preview.height() ) { int size = qMax( preview.width(), preview.height() ); // The smooth transformation is slower but better than Qt::FastTransformation QPixmap resized = preview.scaled( size, size, Qt::KeepAspectRatio, Qt::SmoothTransformation ); storeImage( resized.toImage() ); // Create square canvas with transparent background // QImage fixedImage( size, size, QImage::Format_ARGB32 ); // fixedImage.fill( qRgba( 0xff, 0xff, 0xff, 0xff ) ); // white transparent. // Qt bug: the fill() works in stand-alone apps, but not here. // Found at openSUSE, Qt 3.3.8, KDE 3.5.6. The following the scanlines with the same color. // memset( fixedImage.bits(), 0, size * size * 4 ); /* uint **imageBits = (uint**) fixedImage.jumpTable(); for( int y = 0; y < fixedImage.height(); y++ ) { uint *scanLine = (uint*) imageBits[y]; for( int x = 0; x < fixedImage.width(); x++ ) { scanLine[x] = 0; } } */ // Copy image data centered in new image. // bitBlt( &fixedImage, ( size - image.width() ) / 2, ( size - image.height() ) / 2, // dest, dx, dy // &image, 0, 0, -1, -1, 0 ); // src, x, y, width, height, conversion_flags // storeImage( fixedImage ); } else { storeImage( preview.toImage() ); } // Store and emit result emit gotResult(); } /** * @brief Called when a preview won't be generated. */ 00239 void ThumbnailProvider::slotFailed() { #ifdef KMESSDEBUG_THUMBNAILPROVIDER kDebug() << "thumbnail won't not be generated."; #endif resultError_ = true; emit gotResult(); } /** * @brief Called when a preview could not be generated. */ 00254 void ThumbnailProvider::slotFailed( const KFileItem &/*fileItem*/ ) { #ifdef KMESSDEBUG_THUMBNAILPROVIDER kDebug() << "thumbnail could not be generated."; #endif // The KFileItem already gets deleted by KIO. // No file name, get generic file icon instead. resultError_ = true; emit gotResult(); } /** * @brief Store the image in the instance fields. */ 00272 void ThumbnailProvider::storeImage( const QImage &image ) { // Set state resultError_ = false; thumbnailImage_ = image; // Scale down if needed. Don't scale large images up // The client may scale the image down though depending on the settings. if( thumbnailImage_.width() > size_ || thumbnailImage_.height() > size_ ) { // The smooth transformation is slower but better than Qt::FastTransformation thumbnailImage_ = thumbnailImage_.scaled( size_, size_, Qt::KeepAspectRatio, Qt::SmoothTransformation ); } // Also save as PNG data for the file transfers. QBuffer buffer( &thumbnailData_ ); buffer.open( QIODevice::WriteOnly ); thumbnailImage_.save( &buffer, "PNG" ); } #include "thumbnailprovider.moc"