00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
#include "previewjob.h"
00025
00026
#include <sys/stat.h>
00027
#ifdef __FreeBSD__
00028
#include <machine/param.h>
00029
#endif
00030
#include <sys/types.h>
00031
00032
#ifdef Q_OS_UNIX
00033
#include <sys/ipc.h>
00034
#include <sys/shm.h>
00035
#endif
00036
00037
#include <qdir.h>
00038
#include <qfile.h>
00039
#include <qimage.h>
00040
#include <qtimer.h>
00041
#include <qregexp.h>
00042
00043
#include <kdatastream.h>
00044
#include <kfileitem.h>
00045
#include <kapplication.h>
00046
#include <ktempfile.h>
00047
#include <ktrader.h>
00048
#include <kmdcodec.h>
00049
#include <kglobal.h>
00050
#include <kstandarddirs.h>
00051
00052
#include <kio/kservice.h>
00053
00054
#include "previewjob.moc"
00055
00056
namespace KIO {
struct PreviewItem; }
00057
using namespace KIO;
00058
00059
struct KIO::PreviewItem
00060 {
00061
KFileItem *item;
00062 KService::Ptr plugin;
00063 };
00064
00065
struct KIO::PreviewJobPrivate
00066 {
00067
enum { STATE_STATORIG,
00068 STATE_GETORIG,
00069 STATE_CREATETHUMB
00070 } state;
00071
KFileItemList initialItems;
00072
const QStringList *enabledPlugins;
00073
00074
QValueList<PreviewItem> items;
00075
00076 PreviewItem currentItem;
00077
00078 time_t tOrig;
00079
00080
QString thumbPath;
00081
00082
00083
QString origName;
00084
00085
QString thumbName;
00086
00087
int width;
00088
int height;
00089
00090
int cacheWidth;
00091
int cacheHeight;
00092
00093
bool bScale;
00094
00095
bool bSave;
00096
00097
QString tempName;
00098
00099
unsigned long maximumSize;
00100
00101
int iconSize;
00102
00103
int iconAlpha;
00104
00105
00106
int shmid;
00107
00108 uchar *shmaddr;
00109
00110
bool deleteItems;
00111
bool succeeded;
00112
00113
QString thumbRoot;
00114
bool ignoreMaximumSize;
00115 };
00116
00117 PreviewJob::PreviewJob(
const KFileItemList &items,
int width,
int height,
00118
int iconSize,
int iconAlpha,
bool scale,
bool save,
00119
const QStringList *enabledPlugins,
bool deleteItems )
00120 : KIO::
Job( false )
00121 {
00122 d =
new PreviewJobPrivate;
00123 d->tOrig = 0;
00124 d->shmid = -1;
00125 d->shmaddr = 0;
00126 d->initialItems = items;
00127 d->enabledPlugins = enabledPlugins;
00128 d->width = width;
00129 d->height = height ? height : width;
00130 d->cacheWidth = d->width;
00131 d->cacheHeight = d->height;
00132 d->iconSize = iconSize;
00133 d->iconAlpha = iconAlpha;
00134 d->deleteItems = deleteItems;
00135 d->bScale = scale;
00136 d->bSave = save && scale;
00137 d->succeeded =
false;
00138 d->currentItem.item = 0;
00139 d->thumbRoot = QDir::homeDirPath() +
"/.thumbnails/";
00140 d->ignoreMaximumSize =
false;
00141
00142
00143 QTimer::singleShot(0,
this, SLOT(startPreview()));
00144 }
00145
00146 PreviewJob::~PreviewJob()
00147 {
00148
#ifdef Q_OS_UNIX
00149
if (d->shmaddr) {
00150 shmdt((
char*)d->shmaddr);
00151 shmctl(d->shmid, IPC_RMID, 0);
00152 }
00153
#endif
00154
delete d;
00155 }
00156
00157
void PreviewJob::startPreview()
00158 {
00159
00160
KTrader::OfferList plugins = KTrader::self()->query(
"ThumbCreator");
00161
QMap<QString, KService::Ptr> mimeMap;
00162
00163
for (KTrader::OfferList::ConstIterator it = plugins.begin(); it != plugins.end(); ++it)
00164
if (!d->enabledPlugins || d->enabledPlugins->contains((*it)->desktopEntryName()))
00165 {
00166
QStringList mimeTypes = (*it)->property(
"MimeTypes").toStringList();
00167
for (QStringList::ConstIterator mt = mimeTypes.begin(); mt != mimeTypes.end(); ++mt)
00168 mimeMap.insert(*mt, *it);
00169 }
00170
00171
00172
bool bNeedCache =
false;
00173
for (
KFileItemListIterator it(d->initialItems); it.current(); ++it )
00174 {
00175 PreviewItem item;
00176 item.item = it.current();
00177
QMap<QString, KService::Ptr>::ConstIterator plugin = mimeMap.find(it.current()->mimetype());
00178
if (plugin == mimeMap.end())
00179 {
00180
QString mimeType = it.current()->mimetype();
00181 plugin = mimeMap.find(mimeType.replace(
QRegExp(
"/.*"),
"/*"));
00182 }
00183
if (plugin != mimeMap.end())
00184 {
00185 item.plugin = *plugin;
00186 d->items.append(item);
00187
if (!bNeedCache && d->bSave &&
00188 (it.current()->url().protocol() !=
"file" ||
00189 !it.current()->url().directory(
false ).startsWith(d->thumbRoot)) &&
00190 (*plugin)->property(
"CacheThumbnail").toBool())
00191 bNeedCache =
true;
00192 }
00193
else
00194 {
00195 emitFailed(it.current());
00196
if (d->deleteItems)
00197
delete it.current();
00198 }
00199 }
00200
00201
00202
KConfig * config =
KGlobal::config();
00203
KConfigGroupSaver cgs( config,
"PreviewSettings" );
00204 d->maximumSize = config->
readNumEntry(
"MaximumSize", 2*1024*1024 );
00205
00206
if (bNeedCache)
00207 {
00208
if (d->width <= 128 && d->height <= 128) d->cacheWidth = d->cacheHeight = 128;
00209
else d->cacheWidth = d->cacheHeight = 256;
00210 d->thumbPath = d->thumbRoot + (d->cacheWidth == 128 ?
"normal/" :
"large/");
00211
KStandardDirs::makeDir(d->thumbPath, 0700);
00212 }
00213
else
00214 d->bSave =
false;
00215 determineNextFile();
00216 }
00217
00218 void PreviewJob::removeItem(
const KFileItem *item )
00219 {
00220
for (
QValueList<PreviewItem>::Iterator it = d->items.begin(); it != d->items.end(); ++it)
00221
if ((*it).item == item)
00222 {
00223 d->items.remove(it);
00224
break;
00225 }
00226
00227
if (d->currentItem.item == item)
00228 {
00229 subjobs.first()->kill();
00230 subjobs.removeFirst();
00231 determineNextFile();
00232 }
00233 }
00234
00235 void PreviewJob::setIgnoreMaximumSize(
bool ignoreSize)
00236 {
00237 d->ignoreMaximumSize = ignoreSize;
00238 }
00239
00240
void PreviewJob::determineNextFile()
00241 {
00242
if (d->currentItem.item)
00243 {
00244
if (!d->succeeded)
00245 emitFailed();
00246
if (d->deleteItems) {
00247
delete d->currentItem.item;
00248 d->currentItem.item = 0L;
00249 }
00250 }
00251
00252
if ( d->items.isEmpty() )
00253 {
00254
emitResult();
00255
return;
00256 }
00257
else
00258 {
00259
00260 d->state = PreviewJobPrivate::STATE_STATORIG;
00261 d->currentItem = d->items.first();
00262 d->succeeded =
false;
00263 d->items.remove(d->items.begin());
00264
KIO::Job *job =
KIO::stat( d->currentItem.item->url(),
false );
00265 job->
addMetaData(
"no-auth-prompt",
"true" );
00266
addSubjob(job);
00267 }
00268 }
00269
00270 void PreviewJob::slotResult(
KIO::Job *job )
00271 {
00272 subjobs.remove( job );
00273 Q_ASSERT ( subjobs.isEmpty() );
00274
switch ( d->state )
00275 {
00276
case PreviewJobPrivate::STATE_STATORIG:
00277 {
00278
if (job->
error())
00279 {
00280
00281 determineNextFile();
00282
return;
00283 }
00284
KIO::UDSEntry entry = ((
KIO::StatJob*)job)->statResult();
00285 KIO::UDSEntry::ConstIterator it = entry.begin();
00286 d->tOrig = 0;
00287
int found = 0;
00288
for( ; it != entry.end() && found < 2; it++ )
00289 {
00290
if ( (*it).m_uds == KIO::UDS_MODIFICATION_TIME )
00291 {
00292 d->tOrig = (time_t)((*it).m_long);
00293 found++;
00294 }
00295
else if ( (*it).m_uds == KIO::UDS_SIZE )
00296 {
00297
if (
filesize_t((*it).m_long) > d->maximumSize &&
00298 !d->ignoreMaximumSize &&
00299 !d->currentItem.plugin->property(
"IgnoreMaximumSize").toBool() )
00300 {
00301 determineNextFile();
00302
return;
00303 }
00304 found++;
00305 }
00306 }
00307
00308
if ( !d->currentItem.plugin->property(
"CacheThumbnail" ).toBool() )
00309 {
00310
00311
00312 getOrCreateThumbnail();
00313
return;
00314 }
00315
00316
if ( statResultThumbnail() )
00317
return;
00318
00319 getOrCreateThumbnail();
00320
return;
00321 }
00322
case PreviewJobPrivate::STATE_GETORIG:
00323 {
00324
if (job->
error())
00325 {
00326 determineNextFile();
00327
return;
00328 }
00329
00330 createThumbnail( static_cast<KIO::FileCopyJob*>(job)->destURL().path() );
00331
return;
00332 }
00333
case PreviewJobPrivate::STATE_CREATETHUMB:
00334 {
00335
if (!d->tempName.isEmpty())
00336 {
00337 QFile::remove(d->tempName);
00338 d->tempName = QString::null;
00339 }
00340 determineNextFile();
00341
return;
00342 }
00343 }
00344 }
00345
00346
bool PreviewJob::statResultThumbnail()
00347 {
00348
if ( d->thumbPath.isEmpty() )
00349
return false;
00350
00351
KURL url = d->currentItem.item->url();
00352
00353 url.
setPass(QString::null);
00354
00355
00356
#ifdef KURL_TRIPLE_SLASH_FILE_PROT
00357
d->origName = url.
url();
00358
#else
00359
if (url.
protocol() ==
"file") d->origName =
"file://" + url.
path();
00360
else d->origName = url.
url();
00361
#endif
00362
00363
KMD5 md5( QFile::encodeName( d->origName ) );
00364 d->thumbName = QFile::encodeName( md5.
hexDigest() ) +
".png";
00365
00366
QImage thumb;
00367
if ( !thumb.load( d->thumbPath + d->thumbName ) )
return false;
00368
00369
if ( thumb.text(
"Thumb::URI", 0 ) != d->origName ||
00370 thumb.text(
"Thumb::MTime", 0 ).toInt() != d->tOrig )
return false;
00371
00372
00373 emitPreview( thumb );
00374 d->succeeded =
true;
00375 determineNextFile();
00376
return true;
00377 }
00378
00379
00380
void PreviewJob::getOrCreateThumbnail()
00381 {
00382
00383
KURL currentURL = d->currentItem.item->url();
00384
if ( currentURL.
isLocalFile() )
00385 createThumbnail( currentURL.
path() );
00386
else
00387 {
00388 d->state = PreviewJobPrivate::STATE_GETORIG;
00389
KTempFile localFile;
00390
KURL localURL;
00391 localURL.
setPath( d->tempName = localFile.
name() );
00392
KIO::Job * job =
KIO::file_copy( currentURL, localURL, -1,
true,
00393
false,
false );
00394 job->
addMetaData(
"thumbnail",
"1");
00395 addSubjob(job);
00396 }
00397 }
00398
00399
00400
void PreviewJob::createThumbnail(
QString pixPath )
00401 {
00402 d->state = PreviewJobPrivate::STATE_CREATETHUMB;
00403
KURL thumbURL;
00404 thumbURL.
setProtocol(
"thumbnail");
00405 thumbURL.
setPath(pixPath);
00406
KIO::TransferJob *job =
KIO::get(thumbURL,
false,
false);
00407
addSubjob(job);
00408 connect(job, SIGNAL(data(
KIO::Job *,
const QByteArray &)), SLOT(slotThumbData(
KIO::Job *,
const QByteArray &)));
00409
bool save = d->bSave && d->currentItem.plugin->property(
"CacheThumbnail").toBool();
00410 job->
addMetaData(
"mimeType", d->currentItem.item->mimetype());
00411 job->
addMetaData(
"width",
QString().setNum(save ? d->cacheWidth : d->width));
00412 job->
addMetaData(
"height",
QString().setNum(save ? d->cacheHeight : d->height));
00413 job->
addMetaData(
"iconSize",
QString().setNum(save ? 64 : d->iconSize));
00414 job->
addMetaData(
"iconAlpha",
QString().setNum(d->iconAlpha));
00415 job->
addMetaData(
"plugin", d->currentItem.plugin->library());
00416
#ifdef Q_OS_UNIX
00417
if (d->shmid == -1)
00418 {
00419
if (d->shmaddr) {
00420 shmdt((
char*)d->shmaddr);
00421 shmctl(d->shmid, IPC_RMID, 0);
00422 }
00423 d->shmid = shmget(IPC_PRIVATE, d->cacheWidth * d->cacheHeight * 4, IPC_CREAT|0600);
00424
if (d->shmid != -1)
00425 {
00426 d->shmaddr = static_cast<uchar *>(shmat(d->shmid, 0, SHM_RDONLY));
00427
if (d->shmaddr == (uchar *)-1)
00428 {
00429 shmctl(d->shmid, IPC_RMID, 0);
00430 d->shmaddr = 0;
00431 d->shmid = -1;
00432 }
00433 }
00434
else
00435 d->shmaddr = 0;
00436 }
00437
if (d->shmid != -1)
00438 job->
addMetaData(
"shmid",
QString().setNum(d->shmid));
00439
#endif
00440
}
00441
00442
void PreviewJob::slotThumbData(
KIO::Job *,
const QByteArray &data)
00443 {
00444
bool save = d->bSave &&
00445 d->currentItem.plugin->property(
"CacheThumbnail").toBool() &&
00446 (d->currentItem.item->url().protocol() !=
"file" ||
00447 !d->currentItem.item->url().directory(
false ).startsWith(d->thumbRoot));
00448
QImage thumb;
00449
#ifdef Q_OS_UNIX
00450
if (d->shmaddr)
00451 {
00452
QDataStream str(data, IO_ReadOnly);
00453
int width, height, depth;
00454
bool alpha;
00455 str >> width >> height >> depth >> alpha;
00456 thumb =
QImage(d->shmaddr, width, height, depth, 0, 0, QImage::IgnoreEndian);
00457 thumb.setAlphaBuffer(alpha);
00458 }
00459
else
00460
#endif
00461
thumb.loadFromData(data);
00462
00463
if (
save)
00464 {
00465 thumb.setText(
"Thumb::URI", 0, d->origName);
00466 thumb.setText(
"Thumb::MTime", 0, QString::number(d->tOrig));
00467 thumb.setText(
"Thumb::Size", 0,
number(d->currentItem.item->size()));
00468 thumb.setText(
"Thumb::Mimetype", 0, d->currentItem.item->mimetype());
00469 thumb.setText(
"Software", 0,
"KDE Thumbnail Generator");
00470
KTempFile temp(d->thumbPath +
"kde-tmp-",
".png");
00471
if (temp.
status() == 0)
00472 {
00473 thumb.save(temp.
name(),
"PNG");
00474
rename(QFile::encodeName(temp.
name()), QFile::encodeName(d->thumbPath + d->thumbName));
00475 }
00476 }
00477 emitPreview( thumb );
00478 d->succeeded =
true;
00479 }
00480
00481
void PreviewJob::emitPreview(
const QImage &thumb)
00482 {
00483
QPixmap pix;
00484
if (thumb.width() > d->width || thumb.height() > d->height)
00485 {
00486
double imgRatio = (
double)thumb.height() / (
double)thumb.width();
00487
if (imgRatio > (
double)d->height / (
double)d->width)
00488 pix.convertFromImage(
00489 thumb.smoothScale((
int)QMAX((
double)d->height / imgRatio, 1), d->height));
00490
else pix.convertFromImage(
00491 thumb.smoothScale(d->width, (
int)QMAX((
double)d->width * imgRatio, 1)));
00492 }
00493
else pix.convertFromImage(thumb);
00494 emit
gotPreview(d->currentItem.item, pix);
00495 }
00496
00497
void PreviewJob::emitFailed(
const KFileItem *item)
00498 {
00499
if (!item)
00500 item = d->currentItem.item;
00501 emit
failed(item);
00502 }
00503
00504 QStringList PreviewJob::availablePlugins()
00505 {
00506
QStringList result;
00507
KTrader::OfferList plugins = KTrader::self()->query(
"ThumbCreator");
00508
for (KTrader::OfferList::ConstIterator it = plugins.begin(); it != plugins.end(); ++it)
00509
if (!result.contains((*it)->desktopEntryName()))
00510 result.append((*it)->desktopEntryName());
00511
return result;
00512 }
00513
00514 QStringList PreviewJob::supportedMimeTypes()
00515 {
00516
QStringList result;
00517
KTrader::OfferList plugins = KTrader::self()->query(
"ThumbCreator");
00518
for (KTrader::OfferList::ConstIterator it = plugins.begin(); it != plugins.end(); ++it)
00519 result += (*it)->property(
"MimeTypes").toStringList();
00520
return result;
00521 }
00522
00523 PreviewJob *
KIO::filePreview(
const KFileItemList &items,
int width,
int height,
00524
int iconSize,
int iconAlpha,
bool scale,
bool save,
00525
const QStringList *enabledPlugins )
00526 {
00527
return new PreviewJob(items, width, height, iconSize, iconAlpha,
00528 scale, save, enabledPlugins);
00529 }
00530
00531 PreviewJob *
KIO::filePreview(
const KURL::List &items,
int width,
int height,
00532
int iconSize,
int iconAlpha,
bool scale,
bool save,
00533
const QStringList *enabledPlugins )
00534 {
00535
KFileItemList fileItems;
00536
for (KURL::List::ConstIterator it = items.begin(); it != items.end(); ++it)
00537 fileItems.append(
new KFileItem(KFileItem::Unknown, KFileItem::Unknown, *it,
true));
00538
return new PreviewJob(fileItems, width, height, iconSize, iconAlpha,
00539 scale, save, enabledPlugins,
true);
00540 }
00541
00542
void PreviewJob::virtual_hook(
int id,
void* data )
00543 { KIO::Job::virtual_hook(
id, data ); }
00544