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 <tqdir.h>
00038 #include <tqfile.h>
00039 #include <tqimage.h>
00040 #include <tqtimer.h>
00041 #include <tqregexp.h>
00042
00043 #include <kdatastream.h>
00044 #include <tdefileitem.h>
00045 #include <tdeapplication.h>
00046 #include <tdetempfile.h>
00047 #include <ktrader.h>
00048 #include <kmdcodec.h>
00049 #include <tdeglobal.h>
00050 #include <kstandarddirs.h>
00051
00052 #include <tdeio/kservice.h>
00053
00054 #include "previewjob.moc"
00055
00056 namespace TDEIO { struct PreviewItem; }
00057 using namespace TDEIO;
00058
00059 struct TDEIO::PreviewItem
00060 {
00061 KFileItem *item;
00062 KService::Ptr plugin;
00063 };
00064
00065 struct TDEIO::PreviewJobPrivate
00066 {
00067 enum { STATE_STATORIG,
00068 STATE_GETORIG,
00069 STATE_CREATETHUMB
00070 } state;
00071 KFileItemList initialItems;
00072 const TQStringList *enabledPlugins;
00073
00074 TQValueList<PreviewItem> items;
00075
00076 PreviewItem currentItem;
00077
00078 time_t tOrig;
00079
00080 TQString thumbPath;
00081
00082
00083 TQString origName;
00084
00085 TQString 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 TQString 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 TQString thumbRoot;
00114 bool ignoreMaximumSize;
00115 TQTimer startPreviewTimer;
00116 };
00117
00118 PreviewJob::PreviewJob( const KFileItemList &items, int width, int height,
00119 int iconSize, int iconAlpha, bool scale, bool save,
00120 const TQStringList *enabledPlugins, bool deleteItems )
00121 : TDEIO::Job( false )
00122 {
00123 d = new PreviewJobPrivate;
00124 d->tOrig = 0;
00125 d->shmid = -1;
00126 d->shmaddr = 0;
00127 d->initialItems = items;
00128 d->enabledPlugins = enabledPlugins;
00129 d->width = width;
00130 d->height = height ? height : width;
00131 d->cacheWidth = d->width;
00132 d->cacheHeight = d->height;
00133 d->iconSize = iconSize;
00134 d->iconAlpha = iconAlpha;
00135 d->deleteItems = deleteItems;
00136 d->bScale = scale;
00137 d->bSave = save && scale;
00138 d->succeeded = false;
00139 d->currentItem.item = 0;
00140 d->thumbRoot = TQDir::homeDirPath() + "/.thumbnails/";
00141 d->ignoreMaximumSize = false;
00142
00143
00144 connect(&d->startPreviewTimer, TQT_SIGNAL(timeout()), TQT_SLOT(startPreview()) );
00145 d->startPreviewTimer.start(0, true);
00146 }
00147
00148 PreviewJob::~PreviewJob()
00149 {
00150 #ifdef Q_OS_UNIX
00151 if (d->shmaddr) {
00152 shmdt((char*)d->shmaddr);
00153 shmctl(d->shmid, IPC_RMID, 0);
00154 }
00155 #endif
00156 delete d;
00157 }
00158
00159 void PreviewJob::startPreview()
00160 {
00161
00162 TDETrader::OfferList plugins = TDETrader::self()->query("ThumbCreator");
00163 TQMap<TQString, KService::Ptr> mimeMap;
00164
00165 for (TDETrader::OfferList::ConstIterator it = plugins.begin(); it != plugins.end(); ++it)
00166 if (!d->enabledPlugins || d->enabledPlugins->contains((*it)->desktopEntryName()))
00167 {
00168 TQStringList mimeTypes = (*it)->property("MimeTypes").toStringList();
00169 for (TQStringList::ConstIterator mt = mimeTypes.begin(); mt != mimeTypes.end(); ++mt)
00170 mimeMap.insert(*mt, *it);
00171 }
00172
00173
00174 bool bNeedCache = false;
00175 for (KFileItemListIterator it(d->initialItems); it.current(); ++it )
00176 {
00177 PreviewItem item;
00178 item.item = it.current();
00179 TQMap<TQString, KService::Ptr>::ConstIterator plugin = mimeMap.find(it.current()->mimetype());
00180 if (plugin == mimeMap.end()
00181 && (it.current()->mimetype() != "application/x-desktop")
00182 && (it.current()->mimetype() != "media/builtin-mydocuments")
00183 && (it.current()->mimetype() != "media/builtin-mycomputer")
00184 && (it.current()->mimetype() != "media/builtin-mynetworkplaces")
00185 && (it.current()->mimetype() != "media/builtin-printers")
00186 && (it.current()->mimetype() != "media/builtin-trash")
00187 && (it.current()->mimetype() != "media/builtin-webbrowser"))
00188 {
00189 TQString mimeType = it.current()->mimetype();
00190 plugin = mimeMap.find(mimeType.replace(TQRegExp("/.*"), "/*"));
00191
00192 if (plugin == mimeMap.end())
00193 {
00194
00195 KMimeType::Ptr mimeInfo = KMimeType::mimeType(it.current()->mimetype());
00196 TQString parentMimeType = mimeInfo->parentMimeType();
00197 while (!parentMimeType.isEmpty())
00198 {
00199 plugin = mimeMap.find(parentMimeType);
00200 if (plugin != mimeMap.end()) break;
00201
00202 KMimeType::Ptr parentMimeInfo = KMimeType::mimeType(parentMimeType);
00203 if (!parentMimeInfo) break;
00204
00205 parentMimeType = parentMimeInfo->parentMimeType();
00206 }
00207 }
00208
00209 if (plugin == mimeMap.end())
00210 {
00211
00212 KMimeType::Ptr mimeInfo = KMimeType::mimeType(it.current()->mimetype());
00213 TQVariant textProperty = mimeInfo->property("X-TDE-text");
00214 if (textProperty.isValid() && textProperty.type() == TQVariant::Bool)
00215 {
00216 if (textProperty.toBool())
00217 {
00218 plugin = mimeMap.find("text/plain");
00219 if (plugin == mimeMap.end())
00220 {
00221 plugin = mimeMap.find( "text/*" );
00222 }
00223 }
00224 }
00225 }
00226 }
00227
00228 if (plugin != mimeMap.end())
00229 {
00230 item.plugin = *plugin;
00231 d->items.append(item);
00232 if (!bNeedCache && d->bSave &&
00233 (it.current()->url().protocol() != "file" ||
00234 !it.current()->url().directory( false ).startsWith(d->thumbRoot)) &&
00235 (*plugin)->property("CacheThumbnail").toBool())
00236 bNeedCache = true;
00237 }
00238 else
00239 {
00240 emitFailed(it.current());
00241 if (d->deleteItems)
00242 delete it.current();
00243 }
00244 }
00245
00246
00247 TDEConfig * config = TDEGlobal::config();
00248 TDEConfigGroupSaver cgs( config, "PreviewSettings" );
00249 d->maximumSize = config->readNumEntry( "MaximumSize", 1024*1024 );
00250
00251 if (bNeedCache)
00252 {
00253 if (d->width <= 128 && d->height <= 128) d->cacheWidth = d->cacheHeight = 128;
00254 else d->cacheWidth = d->cacheHeight = 256;
00255 d->thumbPath = d->thumbRoot + (d->cacheWidth == 128 ? "normal/" : "large/");
00256 TDEStandardDirs::makeDir(d->thumbPath, 0700);
00257 }
00258 else
00259 d->bSave = false;
00260 determineNextFile();
00261 }
00262
00263 void PreviewJob::removeItem( const KFileItem *item )
00264 {
00265 for (TQValueList<PreviewItem>::Iterator it = d->items.begin(); it != d->items.end(); ++it)
00266 if ((*it).item == item)
00267 {
00268 d->items.remove(it);
00269 break;
00270 }
00271
00272 if (d->currentItem.item == item)
00273 {
00274 subjobs.first()->kill();
00275 subjobs.removeFirst();
00276 determineNextFile();
00277 }
00278 }
00279
00280 void PreviewJob::setIgnoreMaximumSize(bool ignoreSize)
00281 {
00282 d->ignoreMaximumSize = ignoreSize;
00283 }
00284
00285 void PreviewJob::determineNextFile()
00286 {
00287 if (d->currentItem.item)
00288 {
00289 if (!d->succeeded)
00290 emitFailed();
00291 if (d->deleteItems) {
00292 delete d->currentItem.item;
00293 d->currentItem.item = 0L;
00294 }
00295 }
00296
00297 if ( d->items.isEmpty() )
00298 {
00299 emitResult();
00300 return;
00301 }
00302 else
00303 {
00304
00305 d->state = PreviewJobPrivate::STATE_STATORIG;
00306 d->currentItem = d->items.first();
00307 d->succeeded = false;
00308 d->items.remove(d->items.begin());
00309 TDEIO::Job *job = TDEIO::stat( d->currentItem.item->url(), false );
00310 job->addMetaData( "no-auth-prompt", "true" );
00311 addSubjob(job);
00312 }
00313 }
00314
00315 void PreviewJob::slotResult( TDEIO::Job *job )
00316 {
00317 subjobs.remove( job );
00318 Q_ASSERT ( subjobs.isEmpty() );
00319 switch ( d->state )
00320 {
00321 case PreviewJobPrivate::STATE_STATORIG:
00322 {
00323 if (job->error())
00324 {
00325
00326 determineNextFile();
00327 return;
00328 }
00329 TDEIO::UDSEntry entry = ((TDEIO::StatJob*)job)->statResult();
00330 TDEIO::UDSEntry::ConstIterator it = entry.begin();
00331 d->tOrig = 0;
00332 int found = 0;
00333 for( ; it != entry.end() && found < 2; it++ )
00334 {
00335 if ( (*it).m_uds == TDEIO::UDS_MODIFICATION_TIME )
00336 {
00337 d->tOrig = (time_t)((*it).m_long);
00338 found++;
00339 }
00340 else if ( (*it).m_uds == TDEIO::UDS_SIZE )
00341 {
00342 if ( filesize_t((*it).m_long) > d->maximumSize &&
00343 !d->ignoreMaximumSize &&
00344 !d->currentItem.plugin->property("IgnoreMaximumSize").toBool() )
00345 {
00346 determineNextFile();
00347 return;
00348 }
00349 found++;
00350 }
00351 }
00352
00353 if ( !d->currentItem.plugin->property( "CacheThumbnail" ).toBool() )
00354 {
00355
00356
00357 getOrCreateThumbnail();
00358 return;
00359 }
00360
00361 if ( statResultThumbnail() )
00362 return;
00363
00364 getOrCreateThumbnail();
00365 return;
00366 }
00367 case PreviewJobPrivate::STATE_GETORIG:
00368 {
00369 if (job->error())
00370 {
00371 determineNextFile();
00372 return;
00373 }
00374
00375 createThumbnail( static_cast<TDEIO::FileCopyJob*>(job)->destURL().path() );
00376 return;
00377 }
00378 case PreviewJobPrivate::STATE_CREATETHUMB:
00379 {
00380 if (!d->tempName.isEmpty())
00381 {
00382 TQFile::remove(d->tempName);
00383 d->tempName = TQString::null;
00384 }
00385 determineNextFile();
00386 return;
00387 }
00388 }
00389 }
00390
00391 bool PreviewJob::statResultThumbnail()
00392 {
00393 if ( d->thumbPath.isEmpty() )
00394 return false;
00395
00396 KURL url = d->currentItem.item->url();
00397
00398 url.setPass(TQString::null);
00399
00400
00401 #ifdef KURL_TRIPLE_SLASH_FILE_PROT
00402 d->origName = url.url();
00403 #else
00404 if (url.protocol() == "file") d->origName = "file://" + url.path();
00405 else d->origName = url.url();
00406 #endif
00407
00408 KMD5 md5( TQFile::encodeName( d->origName ).data() );
00409 d->thumbName = TQFile::encodeName( md5.hexDigest() ) + ".png";
00410
00411 TQImage thumb;
00412 if ( !thumb.load( d->thumbPath + d->thumbName ) ) return false;
00413
00414 if ( thumb.text( "Thumb::URI", 0 ) != d->origName ||
00415 thumb.text( "Thumb::MTime", 0 ).toInt() != d->tOrig ) return false;
00416
00417
00418 emitPreview( thumb );
00419 d->succeeded = true;
00420 determineNextFile();
00421 return true;
00422 }
00423
00424
00425 void PreviewJob::getOrCreateThumbnail()
00426 {
00427
00428 const KFileItem* item = d->currentItem.item;
00429 const TQString localPath = item->localPath();
00430 if ( !localPath.isEmpty() )
00431 createThumbnail( localPath );
00432 else
00433 {
00434 d->state = PreviewJobPrivate::STATE_GETORIG;
00435 KTempFile localFile;
00436 KURL localURL;
00437 localURL.setPath( d->tempName = localFile.name() );
00438 const KURL currentURL = item->url();
00439 TDEIO::Job * job = TDEIO::file_copy( currentURL, localURL, -1, true,
00440 false, false );
00441 job->addMetaData("thumbnail","1");
00442 addSubjob(job);
00443 }
00444 }
00445
00446
00447 void PreviewJob::createThumbnail( TQString pixPath )
00448 {
00449 d->state = PreviewJobPrivate::STATE_CREATETHUMB;
00450 KURL thumbURL;
00451 thumbURL.setProtocol("thumbnail");
00452 thumbURL.setPath(pixPath);
00453 TDEIO::TransferJob *job = TDEIO::get(thumbURL, false, false);
00454 addSubjob(job);
00455 connect(job, TQT_SIGNAL(data(TDEIO::Job *, const TQByteArray &)), TQT_SLOT(slotThumbData(TDEIO::Job *, const TQByteArray &)));
00456 bool save = d->bSave && d->currentItem.plugin->property("CacheThumbnail").toBool();
00457 job->addMetaData("mimeType", d->currentItem.item->mimetype());
00458 job->addMetaData("width", TQString().setNum(save ? d->cacheWidth : d->width));
00459 job->addMetaData("height", TQString().setNum(save ? d->cacheHeight : d->height));
00460 job->addMetaData("iconSize", TQString().setNum(save ? 64 : d->iconSize));
00461 job->addMetaData("iconAlpha", TQString().setNum(d->iconAlpha));
00462 job->addMetaData("plugin", d->currentItem.plugin->library());
00463 #ifdef Q_OS_UNIX
00464 if (d->shmid == -1)
00465 {
00466 if (d->shmaddr) {
00467 shmdt((char*)d->shmaddr);
00468 shmctl(d->shmid, IPC_RMID, 0);
00469 }
00470 d->shmid = shmget(IPC_PRIVATE, d->cacheWidth * d->cacheHeight * 4, IPC_CREAT|0600);
00471 if (d->shmid != -1)
00472 {
00473 d->shmaddr = (uchar *)(shmat(d->shmid, 0, SHM_RDONLY));
00474 if (d->shmaddr == (uchar *)-1)
00475 {
00476 shmctl(d->shmid, IPC_RMID, 0);
00477 d->shmaddr = 0;
00478 d->shmid = -1;
00479 }
00480 }
00481 else
00482 d->shmaddr = 0;
00483 }
00484 if (d->shmid != -1)
00485 job->addMetaData("shmid", TQString().setNum(d->shmid));
00486 #endif
00487 }
00488
00489 void PreviewJob::slotThumbData(TDEIO::Job *, const TQByteArray &data)
00490 {
00491 bool save = d->bSave &&
00492 d->currentItem.plugin->property("CacheThumbnail").toBool() &&
00493 (d->currentItem.item->url().protocol() != "file" ||
00494 !d->currentItem.item->url().directory( false ).startsWith(d->thumbRoot));
00495 TQImage thumb;
00496 #ifdef Q_OS_UNIX
00497 if (d->shmaddr)
00498 {
00499 TQDataStream str(data, IO_ReadOnly);
00500 int width, height, depth;
00501 bool alpha;
00502 str >> width >> height >> depth >> alpha;
00503 thumb = TQImage(d->shmaddr, width, height, depth, 0, 0, TQImage::IgnoreEndian);
00504 thumb.setAlphaBuffer(alpha);
00505 }
00506 else
00507 #endif
00508 thumb.loadFromData(data);
00509
00510 if (save)
00511 {
00512 thumb.setText("Thumb::URI", 0, d->origName);
00513 thumb.setText("Thumb::MTime", 0, TQString::number(d->tOrig));
00514 thumb.setText("Thumb::Size", 0, number(d->currentItem.item->size()));
00515 thumb.setText("Thumb::Mimetype", 0, d->currentItem.item->mimetype());
00516 thumb.setText("Software", 0, "KDE Thumbnail Generator");
00517 KTempFile temp(d->thumbPath + "kde-tmp-", ".png");
00518 if (temp.status() == 0)
00519 {
00520 thumb.save(temp.name(), "PNG");
00521 rename(TQFile::encodeName(temp.name()), TQFile::encodeName(d->thumbPath + d->thumbName));
00522 }
00523 }
00524 emitPreview( thumb );
00525 d->succeeded = true;
00526 }
00527
00528 void PreviewJob::emitPreview(const TQImage &thumb)
00529 {
00530 TQPixmap pix;
00531 if (thumb.width() > d->width || thumb.height() > d->height)
00532 {
00533 double imgRatio = (double)thumb.height() / (double)thumb.width();
00534 if (imgRatio > (double)d->height / (double)d->width)
00535 pix.convertFromImage(
00536 thumb.smoothScale((int)TQMAX((double)d->height / imgRatio, 1), d->height));
00537 else pix.convertFromImage(
00538 thumb.smoothScale(d->width, (int)TQMAX((double)d->width * imgRatio, 1)));
00539 }
00540 else pix.convertFromImage(thumb);
00541 emit gotPreview(d->currentItem.item, pix);
00542 }
00543
00544 void PreviewJob::emitFailed(const KFileItem *item)
00545 {
00546 if (!item)
00547 item = d->currentItem.item;
00548 emit failed(item);
00549 }
00550
00551 TQStringList PreviewJob::availablePlugins()
00552 {
00553 TQStringList result;
00554 TDETrader::OfferList plugins = TDETrader::self()->query("ThumbCreator");
00555 for (TDETrader::OfferList::ConstIterator it = plugins.begin(); it != plugins.end(); ++it)
00556 if (!result.contains((*it)->desktopEntryName()))
00557 result.append((*it)->desktopEntryName());
00558 return result;
00559 }
00560
00561 TQStringList PreviewJob::supportedMimeTypes()
00562 {
00563 TQStringList result;
00564 TDETrader::OfferList plugins = TDETrader::self()->query("ThumbCreator");
00565 for (TDETrader::OfferList::ConstIterator it = plugins.begin(); it != plugins.end(); ++it)
00566 result += (*it)->property("MimeTypes").toStringList();
00567 return result;
00568 }
00569
00570 void PreviewJob::kill( bool quietly )
00571 {
00572 d->startPreviewTimer.stop();
00573 Job::kill( quietly );
00574 }
00575
00576 PreviewJob *TDEIO::filePreview( const KFileItemList &items, int width, int height,
00577 int iconSize, int iconAlpha, bool scale, bool save,
00578 const TQStringList *enabledPlugins )
00579 {
00580 return new PreviewJob(items, width, height, iconSize, iconAlpha,
00581 scale, save, enabledPlugins);
00582 }
00583
00584 PreviewJob *TDEIO::filePreview( const KURL::List &items, int width, int height,
00585 int iconSize, int iconAlpha, bool scale, bool save,
00586 const TQStringList *enabledPlugins )
00587 {
00588 KFileItemList fileItems;
00589 for (KURL::List::ConstIterator it = items.begin(); it != items.end(); ++it)
00590 fileItems.append(new KFileItem(KFileItem::Unknown, KFileItem::Unknown, *it, true));
00591 return new PreviewJob(fileItems, width, height, iconSize, iconAlpha,
00592 scale, save, enabledPlugins, true);
00593 }
00594
00595 void PreviewJob::virtual_hook( int id, void* data )
00596 { TDEIO::Job::virtual_hook( id, data ); }
00597