delay a few things on startup, such as setting the visibility mode, which ensures...
[personal-kdebase.git] / runtime / kioslave / thumbnail / thumbnail.cpp
blob3d95ad6bc000a5e6e53c8021ba134cf5cd656e58
1 /* This file is part of the KDE libraries
2 Copyright (C) 2000 Malte Starostik <malte@kde.org>
3 2000 Carsten Pfeiffer <pfeiffer@kde.org>
5 This library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Library General Public
7 License as published by the Free Software Foundation; either
8 version 2 of the License, or (at your option) any later version.
10 This library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Library General Public License for more details.
15 You should have received a copy of the GNU Library General Public License
16 along with this library; see the file COPYING.LIB. If not, write to
17 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18 Boston, MA 02110-1301, USA.
21 #include "thumbnail.h"
23 #include <stdlib.h>
24 #include <unistd.h>
25 #ifdef __FreeBSD__
26 #include <machine/param.h>
27 #endif
28 #include <sys/types.h>
29 #ifndef Q_WS_WIN
30 #include <sys/ipc.h>
31 #include <sys/shm.h>
32 #endif
34 #include <QtCore/QBuffer>
35 #include <QtCore/QFile>
36 #include <QtGui/QBitmap>
37 #include <QtGui/QImage>
38 #include <QtGui/QPainter>
39 #include <QtGui/QPixmap>
41 #include <kurl.h>
42 #include <kapplication.h>
43 #include <kcmdlineargs.h>
44 #include <kaboutdata.h>
45 #include <kglobal.h>
46 #include <kiconloader.h>
47 #include <kmimetype.h>
48 #include <klibloader.h>
49 #include <kdebug.h>
50 #include <kservice.h>
51 #include <kservicetype.h>
52 #include <kservicetypetrader.h>
53 #include <kmimetypetrader.h>
54 #include <kfilemetainfo.h>
55 #include <klocale.h>
57 #include <config-runtime.h> // For HAVE_NICE
58 #include <kio/thumbcreator.h>
59 #include <kconfiggroup.h>
61 // Use correctly KComponentData instead of KApplication (but then no QPixmap)
62 #undef USE_KINSTANCE
63 // Fix thumbnail: protocol
64 #define THUMBNAIL_HACK (1)
66 #ifdef THUMBNAIL_HACK
67 # include <QFileInfo>
68 #endif
70 // Recognized metadata entries:
71 // mimeType - the mime type of the file, used for the overlay icon if any
72 // width - maximum width for the thumbnail
73 // height - maximum height for the thumbnail
74 // iconSize - the size of the overlay icon to use if any
75 // iconAlpha - the transparency value used for icon overlays
76 // plugin - the name of the plugin library to be used for thumbnail creation.
77 // Provided by the application to save an addition KTrader
78 // query here.
79 // shmid - the shared memory segment id to write the image's data to.
80 // The segment is assumed to provide enough space for a 32-bit
81 // image sized width x height pixels.
82 // If this is given, the data returned by the slave will be:
83 // int width
84 // int height
85 // int depth
86 // Otherwise, the data returned is the image in PNG format.
88 using namespace KIO;
90 extern "C"
92 KDE_EXPORT int kdemain(int argc, char **argv);
96 int kdemain(int argc, char **argv)
98 #ifdef HAVE_NICE
99 nice( 5 );
100 #endif
102 #ifdef USE_KINSTANCE
103 KComponentData componentData("kio_thumbnail");
104 #else
105 // creating KApplication in a slave in not a very good idea,
106 // as dispatchLoop() doesn't allow it to process its messages,
107 // so it for example wouldn't reply to ksmserver - on the other
108 // hand, this slave uses QPixmaps for some reason, and they
109 // need QApplication
110 // and HTML previews need even KApplication :(
111 putenv(strdup("SESSION_MANAGER="));
112 //KApplication::disableAutoDcopRegistration();
113 KAboutData about("kio_thumbnail", 0, ki18n("kio_thumbmail"), "KDE 4.x.x");
114 KCmdLineArgs::init(&about);
116 KApplication app( true);
117 #endif
119 if (argc != 4)
121 kError(7115) << "Usage: kio_thumbnail protocol domain-socket1 domain-socket2" << endl;
122 exit(-1);
125 ThumbnailProtocol slave(argv[2], argv[3]);
126 slave.dispatchLoop();
128 return 0;
131 ThumbnailProtocol::ThumbnailProtocol(const QByteArray &pool, const QByteArray &app)
132 : SlaveBase("thumbnail", pool, app)
134 m_iconSize = 0;
137 ThumbnailProtocol::~ThumbnailProtocol()
139 qDeleteAll( m_creators );
140 m_creators.clear();
143 void ThumbnailProtocol::get(const KUrl &url)
145 m_mimeType = metaData("mimeType");
146 kDebug(7115) << "Wanting MIME Type:" << m_mimeType;
147 #ifdef THUMBNAIL_HACK
148 // ### HACK
149 bool direct=false;
150 if (m_mimeType.isEmpty())
152 kDebug(7115) << "PATH: " << url.path();
153 QFileInfo info(url.path());
154 if (info.isDir())
156 // We cannot process a directory
157 error(KIO::ERR_IS_DIRECTORY,url.path());
158 return;
160 else if (!info.exists())
162 // The file does not exist
163 error(KIO::ERR_DOES_NOT_EXIST,url.path());
164 return;
166 else if (!info.isReadable())
168 // The file is not readable!
169 error(KIO::ERR_COULD_NOT_READ,url.path());
170 return;
172 m_mimeType = KMimeType::findByUrl(url)->name();
173 kDebug(7115) << "Guessing MIME Type:" << m_mimeType;
174 direct=true; // thumbnail: was probably called from Konqueror
176 #endif
178 if (m_mimeType.isEmpty())
180 error(KIO::ERR_INTERNAL, i18n("No MIME Type specified."));
181 return;
184 m_width = metaData("width").toInt();
185 m_height = metaData("height").toInt();
186 int iconSize = metaData("iconSize").toInt();
188 if (m_width < 0 || m_height < 0)
190 error(KIO::ERR_INTERNAL, i18n("No or invalid size specified."));
191 return;
193 #ifdef THUMBNAIL_HACK
194 else if (!m_width || !m_height)
196 kDebug(7115) << "Guessing height, width, icon size!";
197 m_width=128;
198 m_height=128;
199 iconSize=128;
201 #endif
203 if (!iconSize)
204 iconSize = KIconLoader::global()->currentSize(KIconLoader::Desktop);
205 if (iconSize != m_iconSize) {
206 m_iconDict.clear();
208 m_iconSize = iconSize;
210 m_iconAlpha = metaData("iconAlpha").toInt();
212 QImage img;
214 KConfigGroup group( KGlobal::config(), "PreviewSettings" );
217 // ### KFMI
218 bool kfmiThumb = false;
219 if (group.readEntry( "UseFileThumbnails", true)) {
220 KService::Ptr service =
221 KMimeTypeTrader::self()->preferredService( m_mimeType, "KFilePlugin");
223 if (service && service->isValid() &&
224 service->property("SupportsThumbnail").toBool())
226 // was: KFileMetaInfo info(url.path(), m_mimeType, KFileMetaInfo::Thumbnail);
227 // but m_mimeType and WhatFlags are now unused in KFileMetaInfo, and not present in the
228 // call that takes a KUrl
229 KFileMetaInfo info(url);
230 if (info.isValid())
232 KFileMetaInfoItem item = info.item("thumbnail");
233 if (item.isValid() && item.value().type() == QVariant::Image)
235 img = item.value().value<QImage>();
236 kDebug(7115) << "using KFMI for the thumbnail\n";
237 kfmiThumb = true;
242 ThumbCreator::Flags flags = ThumbCreator::None;
244 if (!kfmiThumb)
246 kDebug(7115) << "using thumb creator for the thumbnail\n";
247 QString plugin = metaData("plugin");
248 #ifdef THUMBNAIL_HACK
249 if (plugin.isEmpty())
251 KService::List offers = KMimeTypeTrader::self()->query( m_mimeType, QLatin1String( "ThumbCreator" ) );
253 if(!offers.isEmpty())
255 KService::Ptr serv;
256 serv = offers.first();
257 plugin = serv->library();
260 kDebug(7115) << "Guess plugin: " << plugin;
261 #endif
262 if (plugin.isEmpty())
264 error(KIO::ERR_INTERNAL, i18n("No plugin specified."));
265 return;
268 ThumbCreator *creator = m_creators[plugin];
269 if (!creator)
271 // Don't use KLibFactory here, this is not a QObject and
272 // neither is ThumbCreator
273 KLibrary *library = KLibLoader::self()->library(plugin);
274 if (library)
276 newCreator create = (newCreator)library->resolveFunction("new_creator");
277 if (create)
278 creator = create();
280 if (!creator)
282 error(KIO::ERR_INTERNAL, i18n("Cannot load ThumbCreator %1", plugin));
283 return;
285 m_creators.insert(plugin, creator);
288 if (!creator->create(url.path(), m_width, m_height, img))
290 error(KIO::ERR_INTERNAL, i18n("Cannot create thumbnail for %1", url.path()));
291 return;
293 flags = creator->flags();
296 if (img.width() > m_width || img.height() > m_height)
298 double imgRatio = (double)img.height() / (double)img.width();
299 if (imgRatio > (double)m_height / (double)m_width)
300 img = img.scaled( int(qMax((double)m_height / imgRatio, 1.0)), m_height, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
301 else
302 img = img.scaled(m_width, int(qMax((double)m_width * imgRatio, 1.0)), Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
305 // ### FIXME
306 #ifndef USE_KINSTANCE
307 if (flags & ThumbCreator::DrawFrame)
309 QPixmap pix = QPixmap::fromImage( img );
310 int x2 = pix.width() - 1;
311 int y2 = pix.height() - 1;
312 // paint a black rectangle around the "page"
313 QPainter p;
314 p.begin( &pix );
315 p.setPen( QColor( 48, 48, 48 ));
316 p.drawLine( x2, 0, x2, y2 );
317 p.drawLine( 0, y2, x2, y2 );
318 p.setPen( QColor( 215, 215, 215 ));
319 p.drawLine( 0, 0, x2, 0 );
320 p.drawLine( 0, 0, 0, y2 );
321 p.end();
323 const QBitmap& mask = pix.mask();
324 if ( !mask.isNull() ) // need to update it so we can see the frame
326 QBitmap bitmap( mask );
327 QPainter painter;
328 painter.begin( &bitmap );
329 painter.drawLine( x2, 0, x2, y2 );
330 painter.drawLine( 0, y2, x2, y2 );
331 painter.drawLine( 0, 0, x2, 0 );
332 painter.drawLine( 0, 0, 0, y2 );
333 painter.end();
335 pix.setMask( bitmap );
338 img = pix.toImage();
340 #endif
342 if ((flags & ThumbCreator::BlendIcon) && KIconLoader::global()->alphaBlending(KIconLoader::Desktop))
344 // blending the mimetype icon in
345 QImage icon = getIcon();
347 int x = img.width() - icon.width() - 4;
348 x = qMax( x, 0 );
349 int y = img.height() - icon.height() - 6;
350 y = qMax( y, 0 );
351 QPainter p(&img);
352 p.setOpacity(m_iconAlpha/255.0);
353 p.drawImage(x, y, icon);
356 if (img.isNull())
358 error(KIO::ERR_INTERNAL, i18n("Failed to create a thumbnail."));
359 return;
362 const QString shmid = metaData("shmid");
363 if (shmid.isEmpty())
365 #ifdef THUMBNAIL_HACK
366 if (direct)
368 // If thumbnail was called directly from Konqueror, then the image needs to be raw
369 //kDebug(7115) << "RAW IMAGE TO STREAM";
370 QBuffer buf;
371 if (!buf.open(QIODevice::WriteOnly))
373 error(KIO::ERR_INTERNAL, i18n("Could not write image."));
374 return;
376 img.save(&buf,"PNG");
377 buf.close();
378 data(buf.buffer());
380 else
381 #endif
383 QByteArray imgData;
384 QDataStream stream( &imgData, QIODevice::WriteOnly );
385 //kDebug(7115) << "IMAGE TO STREAM";
386 stream << img;
387 data(imgData);
390 else
392 #ifndef Q_WS_WIN
393 QByteArray imgData;
394 QDataStream stream( &imgData, QIODevice::WriteOnly );
395 //kDebug(7115) << "IMAGE TO SHMID";
396 void *shmaddr = shmat(shmid.toInt(), 0, 0);
397 if (shmaddr == (void *)-1)
399 error(KIO::ERR_INTERNAL, i18n("Failed to attach to shared memory segment %1", shmid));
400 return;
402 if (img.width() * img.height() > m_width * m_height)
404 error(KIO::ERR_INTERNAL, i18n("Image is too big for the shared memory segment"));
405 shmdt((char*)shmaddr);
406 return;
408 if( img.depth() != 32 ) // KIO::PreviewJob and this code below completely ignores colortable :-/,
409 img = img.convertToFormat(QImage::Format_ARGB32); // so make sure there is none
410 // Keep in sync with kdelibs/kio/kio/previewjob.cpp
411 stream << img.width() << img.height() << quint8(img.format());
412 memcpy(shmaddr, img.bits(), img.numBytes());
413 shmdt((char*)shmaddr);
414 data(imgData);
415 #endif
417 finished();
420 const QImage ThumbnailProtocol::getIcon()
422 if ( !m_iconDict.contains(m_mimeType) ) { // generate it
423 QImage icon( KIconLoader::global()->loadMimeTypeIcon( KMimeType::mimeType(m_mimeType)->iconName(), KIconLoader::Desktop, m_iconSize ).toImage() );
424 icon = icon.convertToFormat( QImage::Format_ARGB32 );
425 m_iconDict.insert( m_mimeType, icon );
427 return icon;
430 return m_iconDict.value( m_mimeType );