summaryrefslogtreecommitdiffstats
path: root/kioslave/thumbnail/thumbnail.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'kioslave/thumbnail/thumbnail.cpp')
-rw-r--r--kioslave/thumbnail/thumbnail.cpp440
1 files changed, 440 insertions, 0 deletions
diff --git a/kioslave/thumbnail/thumbnail.cpp b/kioslave/thumbnail/thumbnail.cpp
new file mode 100644
index 000000000..99cb3a89d
--- /dev/null
+++ b/kioslave/thumbnail/thumbnail.cpp
@@ -0,0 +1,440 @@
+/* This file is part of the KDE libraries
+ Copyright (C) 2000 Malte Starostik <[email protected]>
+ 2000 Carsten Pfeiffer <[email protected]>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#include <stdlib.h>
+#include <unistd.h>
+#ifdef __FreeBSD__
+ #include <machine/param.h>
+#endif
+#include <sys/types.h>
+#include <sys/ipc.h>
+#include <sys/shm.h>
+
+#include <qfile.h>
+#include <qbitmap.h>
+#include <qpixmap.h>
+#include <qpainter.h>
+#include <qimage.h>
+#include <qbuffer.h>
+
+#include <kdatastream.h> // Do not remove, needed for correct bool serialization
+#include <kurl.h>
+#include <kapplication.h>
+#include <kglobal.h>
+#include <kiconloader.h>
+#include <kimageeffect.h>
+#include <kmimetype.h>
+#include <klibloader.h>
+#include <kdebug.h>
+#include <kservice.h>
+#include <kservicetype.h>
+#include <kuserprofile.h>
+#include <kfilemetainfo.h>
+#include <klocale.h>
+
+#include <config.h> // For HAVE_NICE
+#include "thumbnail.h"
+#include <kio/thumbcreator.h>
+
+// Use correctly KInstance instead of KApplication (but then no QPixmap)
+#undef USE_KINSTANCE
+// Fix thumbnail: protocol
+#define THUMBNAIL_HACK (1)
+
+#ifdef THUMBNAIL_HACK
+# include <qfileinfo.h>
+# include <ktrader.h>
+#endif
+
+// Recognized metadata entries:
+// mimeType - the mime type of the file, used for the overlay icon if any
+// width - maximum width for the thumbnail
+// height - maximum height for the thumbnail
+// iconSize - the size of the overlay icon to use if any
+// iconAlpha - the transparency value used for icon overlays
+// plugin - the name of the plugin library to be used for thumbnail creation.
+// Provided by the application to save an addition KTrader
+// query here.
+// shmid - the shared memory segment id to write the image's data to.
+// The segment is assumed to provide enough space for a 32-bit
+// image sized width x height pixels.
+// If this is given, the data returned by the slave will be:
+// int width
+// int height
+// int depth
+// Otherwise, the data returned is the image in PNG format.
+
+using namespace KIO;
+
+extern "C"
+{
+ KDE_EXPORT int kdemain(int argc, char **argv);
+}
+
+
+int kdemain(int argc, char **argv)
+{
+#ifdef HAVE_NICE
+ nice( 5 );
+#endif
+
+#ifdef USE_KINSTANCE
+ KInstance instance("kio_thumbnail");
+#else
+ // creating KApplication in a slave in not a very good idea,
+ // as dispatchLoop() doesn't allow it to process its messages,
+ // so it for example wouldn't reply to ksmserver - on the other
+ // hand, this slave uses QPixmaps for some reason, and they
+ // need QApplication
+ // and HTML previews need even KApplication :(
+ putenv(strdup("SESSION_MANAGER="));
+ KApplication::disableAutoDcopRegistration();
+
+ KApplication app(argc, argv, "kio_thumbnail", false, true);
+#endif
+
+ if (argc != 4)
+ {
+ kdError(7115) << "Usage: kio_thumbnail protocol domain-socket1 domain-socket2" << endl;
+ exit(-1);
+ }
+
+ ThumbnailProtocol slave(argv[2], argv[3]);
+ slave.dispatchLoop();
+
+ return 0;
+}
+
+ThumbnailProtocol::ThumbnailProtocol(const QCString &pool, const QCString &app)
+ : SlaveBase("thumbnail", pool, app)
+{
+ m_creators.setAutoDelete(true);
+ m_iconDict.setAutoDelete(true);
+ m_iconSize = 0;
+}
+
+ThumbnailProtocol::~ThumbnailProtocol()
+{
+}
+
+void ThumbnailProtocol::get(const KURL &url)
+{
+ m_mimeType = metaData("mimeType");
+ kdDebug(7115) << "Wanting MIME Type:" << m_mimeType << endl;
+#ifdef THUMBNAIL_HACK
+ // ### HACK
+ bool direct=false;
+ if (m_mimeType.isEmpty())
+ {
+ kdDebug(7115) << "PATH: " << url.path() << endl;
+ QFileInfo info(url.path());
+ if (info.isDir())
+ {
+ // We cannot process a directory
+ error(KIO::ERR_IS_DIRECTORY,url.path());
+ return;
+ }
+ else if (!info.exists())
+ {
+ // The file does not exist
+ error(KIO::ERR_DOES_NOT_EXIST,url.path());
+ return;
+ }
+ else if (!info.isReadable())
+ {
+ // The file is not readable!
+ error(KIO::ERR_COULD_NOT_READ,url.path());
+ return;
+ }
+ m_mimeType = KMimeType::findByURL(url)->name();
+ kdDebug(7115) << "Guessing MIME Type:" << m_mimeType << endl;
+ direct=true; // thumbnail: was probably called from Konqueror
+ }
+#endif
+
+ if (m_mimeType.isEmpty())
+ {
+ error(KIO::ERR_INTERNAL, i18n("No MIME Type specified."));
+ return;
+ }
+
+ m_width = metaData("width").toInt();
+ m_height = metaData("height").toInt();
+ int iconSize = metaData("iconSize").toInt();
+
+ if (m_width < 0 || m_height < 0)
+ {
+ error(KIO::ERR_INTERNAL, i18n("No or invalid size specified."));
+ return;
+ }
+#ifdef THUMBNAIL_HACK
+ else if (!m_width || !m_height)
+ {
+ kdDebug(7115) << "Guessing height, width, icon sizre!" << endl;
+ m_width=128;
+ m_height=128;
+ iconSize=128;
+ }
+#endif
+
+ if (!iconSize)
+ iconSize = KGlobal::iconLoader()->currentSize(KIcon::Desktop);
+ if (iconSize != m_iconSize)
+ m_iconDict.clear();
+ m_iconSize = iconSize;
+
+ m_iconAlpha = metaData("iconAlpha").toInt();
+ if (m_iconAlpha)
+ m_iconAlpha = (m_iconAlpha << 24) | 0xffffff;
+
+ QImage img;
+
+ KConfigGroup group( KGlobal::config(), "PreviewSettings" );
+
+
+ // ### KFMI
+ bool kfmiThumb = false;
+ if (group.readBoolEntry( "UseFileThumbnails", true )) {
+ KService::Ptr service =
+ KServiceTypeProfile::preferredService( m_mimeType, "KFilePlugin");
+
+ if ( service && service->isValid() && /*url.isLocalFile() && */
+ service->property("SupportsThumbnail").toBool())
+ {
+ KFileMetaInfo info(url.path(), m_mimeType, KFileMetaInfo::Thumbnail);
+ if (info.isValid())
+ {
+ KFileMetaInfoItem item = info.item(KFileMimeTypeInfo::Thumbnail);
+ if (item.isValid() && item.value().type() == QVariant::Image)
+ {
+ img = item.value().toImage();
+ kdDebug(7115) << "using KFMI for the thumbnail\n";
+ kfmiThumb = true;
+ }
+ }
+ }
+ }
+ ThumbCreator::Flags flags = ThumbCreator::None;
+
+ if (!kfmiThumb)
+ {
+ kdDebug(7115) << "using thumb creator for the thumbnail\n";
+ QString plugin = metaData("plugin");
+#ifdef THUMBNAIL_HACK
+ if (plugin.isEmpty())
+ {
+ KTrader::OfferList plugins = KTrader::self()->query("ThumbCreator");
+ QMap<QString, KService::Ptr> mimeMap;
+
+ for (KTrader::OfferList::ConstIterator it = plugins.begin(); it != plugins.end(); ++it)
+ {
+ QStringList mimeTypes = (*it)->property("MimeTypes").toStringList();
+ for (QStringList::ConstIterator mt = mimeTypes.begin(); mt != mimeTypes.end(); ++mt)
+ {
+ if ((*mt)==m_mimeType)
+ {
+ plugin=(*it)->library();
+ break;
+ }
+ }
+ if (!plugin.isEmpty())
+ break;
+ }
+ }
+ kdDebug(7115) << "Guess plugin: " << plugin << endl;
+#endif
+ if (plugin.isEmpty())
+ {
+ error(KIO::ERR_INTERNAL, i18n("No plugin specified."));
+ return;
+ }
+
+ ThumbCreator *creator = m_creators[plugin];
+ if (!creator)
+ {
+ // Don't use KLibFactory here, this is not a QObject and
+ // neither is ThumbCreator
+ KLibrary *library = KLibLoader::self()->library(QFile::encodeName(plugin));
+ if (library)
+ {
+ newCreator create = (newCreator)library->symbol("new_creator");
+ if (create)
+ creator = create();
+ }
+ if (!creator)
+ {
+ error(KIO::ERR_INTERNAL, i18n("Cannot load ThumbCreator %1").arg(plugin));
+ return;
+ }
+ m_creators.insert(plugin, creator);
+ }
+
+ if (!creator->create(url.path(), m_width, m_height, img))
+ {
+ error(KIO::ERR_INTERNAL, i18n("Cannot create thumbnail for %1").arg(url.path()));
+ return;
+ }
+ flags = creator->flags();
+ }
+
+ if (img.width() > m_width || img.height() > m_height)
+ {
+ double imgRatio = (double)img.height() / (double)img.width();
+ if (imgRatio > (double)m_height / (double)m_width)
+ img = img.smoothScale( int(QMAX((double)m_height / imgRatio, 1)), m_height);
+ else
+ img = img.smoothScale(m_width, int(QMAX((double)m_width * imgRatio, 1)));
+ }
+
+// ### FIXME
+#ifndef USE_KINSTANCE
+ if (flags & ThumbCreator::DrawFrame)
+ {
+ QPixmap pix;
+ pix.convertFromImage(img);
+ int x2 = pix.width() - 1;
+ int y2 = pix.height() - 1;
+ // paint a black rectangle around the "page"
+ QPainter p;
+ p.begin( &pix );
+ p.setPen( QColor( 48, 48, 48 ));
+ p.drawLine( x2, 0, x2, y2 );
+ p.drawLine( 0, y2, x2, y2 );
+ p.setPen( QColor( 215, 215, 215 ));
+ p.drawLine( 0, 0, x2, 0 );
+ p.drawLine( 0, 0, 0, y2 );
+ p.end();
+
+ const QBitmap *mask = pix.mask();
+ if ( mask ) // need to update it so we can see the frame
+ {
+ QBitmap bitmap( *mask );
+ QPainter painter;
+ painter.begin( &bitmap );
+ painter.drawLine( x2, 0, x2, y2 );
+ painter.drawLine( 0, y2, x2, y2 );
+ painter.drawLine( 0, 0, x2, 0 );
+ painter.drawLine( 0, 0, 0, y2 );
+ painter.end();
+
+ pix.setMask( bitmap );
+ }
+
+ img = pix.convertToImage();
+ }
+#endif
+
+ if ((flags & ThumbCreator::BlendIcon) && KGlobal::iconLoader()->alphaBlending(KIcon::Desktop))
+ {
+ // blending the mimetype icon in
+ QImage icon = getIcon();
+
+ int x = img.width() - icon.width() - 4;
+ x = QMAX( x, 0 );
+ int y = img.height() - icon.height() - 6;
+ y = QMAX( y, 0 );
+ KImageEffect::blendOnLower( x, y, icon, img );
+ }
+
+ if (img.isNull())
+ {
+ error(KIO::ERR_INTERNAL, i18n("Failed to create a thumbnail."));
+ return;
+ }
+
+ const QString shmid = metaData("shmid");
+ if (shmid.isEmpty())
+ {
+#ifdef THUMBNAIL_HACK
+ if (direct)
+ {
+ // If thumbnail was called directly from Konqueror, then the image needs to be raw
+ //kdDebug(7115) << "RAW IMAGE TO STREAM" << endl;
+ QBuffer buf;
+ if (!buf.open(IO_WriteOnly))
+ {
+ error(KIO::ERR_INTERNAL, i18n("Could not write image."));
+ return;
+ }
+ img.save(&buf,"PNG");
+ buf.close();
+ data(buf.buffer());
+ }
+ else
+#endif
+ {
+ QByteArray imgData;
+ QDataStream stream( imgData, IO_WriteOnly );
+ //kdDebug(7115) << "IMAGE TO STREAM" << endl;
+ stream << img;
+ data(imgData);
+ }
+ }
+ else
+ {
+ QByteArray imgData;
+ QDataStream stream( imgData, IO_WriteOnly );
+ //kdDebug(7115) << "IMAGE TO SHMID" << endl;
+ void *shmaddr = shmat(shmid.toInt(), 0, 0);
+ if (shmaddr == (void *)-1)
+ {
+ error(KIO::ERR_INTERNAL, i18n("Failed to attach to shared memory segment %1").arg(shmid));
+ return;
+ }
+ if (img.width() * img.height() > m_width * m_height)
+ {
+ error(KIO::ERR_INTERNAL, i18n("Image is too big for the shared memory segment"));
+ shmdt((char*)shmaddr);
+ return;
+ }
+ if( img.depth() != 32 ) // KIO::PreviewJob and this code below completely
+ img = img.convertDepth( 32 ); // ignores colortable :-/, so make sure there is none
+ stream << img.width() << img.height() << img.depth()
+ << img.hasAlphaBuffer();
+ memcpy(shmaddr, img.bits(), img.numBytes());
+ shmdt((char*)shmaddr);
+ data(imgData);
+ }
+ finished();
+}
+
+const QImage& ThumbnailProtocol::getIcon()
+{
+ QImage* icon = m_iconDict.find(m_mimeType);
+ if ( !icon ) // generate it!
+ {
+ icon = new QImage( KMimeType::mimeType(m_mimeType)->pixmap( KIcon::Desktop, m_iconSize ).convertToImage() );
+ icon->setAlphaBuffer( true );
+
+ int w = icon->width();
+ int h = icon->height();
+ for ( int y = 0; y < h; y++ )
+ {
+ QRgb *line = (QRgb *) icon->scanLine( y );
+ for ( int x = 0; x < w; x++ )
+ line[x] &= m_iconAlpha; // transparency
+ }
+
+ m_iconDict.insert( m_mimeType, icon );
+ }
+
+ return *icon;
+}
+