You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
ksquirrel/ksquirrel/sq_thumbnailloadjob.cpp

481 lines
12 KiB

/*
copyright : (C) 2004 by Baryshev Dmitry
KSquirrel - image viewer for KDE
*/
/* This file is part of the KDE project
Copyright (C) 2000 David Faure <faure@kde.org>
2000 Carsten Pfeiffer <pfeiffer@kde.org>
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.
This program 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <tqdir.h>
#include <tqimage.h>
#include <tqpainter.h>
#include <tqlabel.h>
#include <tqdatetime.h>
#include <kfileitem.h>
#include <kiconloader.h>
#include <kstandarddirs.h>
#include <ktempfile.h>
#include <kdebug.h>
#include "sq_config.h"
#include "sq_widgetstack.h"
#include "sq_pixmapcache.h"
#include "sq_dirthumbs.h"
#include "sq_thumbnailloadjob.h"
#include "sq_libraryhandler.h"
#include "sq_thumbnailsize.h"
#include "sq_thumbnailsunused.h"
#include "sq_utils.h"
#include "sq_filethumbview.h"
#include "sq_filethumbviewitem.h"
#include "sq_imageloader.h"
#include <ksquirrel-libs/fmt_defs.h>
#include <string>
#define SQ_PREDOWNLOAD_SIZE 20
SQ_ThumbnailLoadJob::SQ_ThumbnailLoadJob(const KFileItemList &items, SQ_FileThumbView *parnt)
: TDEIO::Job(false), parent(parnt)
{
mBrokenThumbnail.thumbnail = TDEGlobal::iconLoader()->loadIcon("file_broken", KIcon::Desktop, SQ_ThumbnailSize::smallest());
mItems = items;
donothing = false;
dir = new SQ_DirThumbs;
}
SQ_ThumbnailLoadJob::~SQ_ThumbnailLoadJob()
{
delete dir;
}
void SQ_ThumbnailLoadJob::start()
{
if(mItems.isEmpty())
{
emit done();
delete this;
return;
}
determineNextIcon();
}
void SQ_ThumbnailLoadJob::appendItem(const KFileItem* item)
{
mItems.append(item);
}
void SQ_ThumbnailLoadJob::itemRemoved(const KFileItem* item)
{
mItems.removeRef(item);
nextFile(item == mCurrentItem);
}
void SQ_ThumbnailLoadJob::itemsRemoved(const KFileItemList &items)
{
KFileItem *item;
bool next = false;
KFileItemList *m_items = const_cast<KFileItemList *>(&items);
if(!mItems.isEmpty() && !items.isEmpty())
{
for(item = m_items->first();item;item = m_items->next())
{
mItems.removeRef(item);
if(item == mCurrentItem)
next = true;
}
}
nextFile(next);
}
void SQ_ThumbnailLoadJob::pop(const KFileItemList &items)
{
KFileItem *item;
bool next = false;
KFileItemList *m_items = const_cast<KFileItemList *>(&items);
if(!mItems.isEmpty() && !items.isEmpty())
{
for(item = m_items->first();item;item = m_items->next())
{
mItems.removeRef(item);
if(item == mCurrentItem)
next = true;
}
for(item = m_items->last();item;item = m_items->prev())
mItems.prepend(item);
}
nextFile(next);
}
void SQ_ThumbnailLoadJob::nextFile(bool b)
{
if(b)
{
TDEIO::Job *j = subjobs.first();
if(j)
{
j->kill();
subjobs.removeFirst();
}
determineNextIcon();
}
}
void SQ_ThumbnailLoadJob::determineNextIcon()
{
if(donothing)
return;
KFileItem *item = 0;
SQ_FileThumbViewItem *tfi;
while(true)
{
item = mItems.first();
if(!item)
{
emit done();
delete this;
return;
}
SQ_WidgetStack::instance()->thumbnailProcess();
tfi = reinterpret_cast<SQ_FileThumbViewItem *>(item->extraData(parent));
// 1) local urls that are 100% supported, or
// 2) remote urls that are 100% supported or _maybe_ supported (application/octet-stream)
if(item->isReadable() && SQ_LibraryHandler::instance()->maybeSupported(item->url(), item->mimetype()) != SQ_LibraryHandler::No)
break;
else
{
mItems.removeFirst();
tfi->setListed(true);
}
}
if(mItems.isEmpty() || !item)
{
emit done();
delete this;
return;
}
else
{
mState = STATE_STATORIG;
mCurrentItem = mItems.first();
mCurrentURL = mCurrentItem->url();
mItems.removeFirst();
TDEIO::StatJob *job = TDEIO::stat(mCurrentURL, false);
job->setSide(true);
job->setDetails(0);
addSubjob(job);
}
}
void SQ_ThumbnailLoadJob::slotResult(TDEIO::Job *job)
{
subjobs.remove(job);
// Q_ASSERT(subjobs.isEmpty());
switch(mState)
{
case STATE_STATORIG:
{
TDEIO::UDSEntry entry = mCurrentItem->entry();
TDEIO::UDSEntry::ConstIterator it = entry.begin();
mOriginalTime = 0;
size = totalSize = 0;
TQString suff;
for(; it != entry.end(); ++it)
{
if((*it).m_uds == TDEIO::UDS_MODIFICATION_TIME)
{
mOriginalTime = (time_t)((*it).m_long);
}
else if((*it).m_uds == TDEIO::UDS_SIZE)
{
totalSize = (TDEIO::filesize_t)((*it).m_long);
}
else if((*it).m_uds == TDEIO::UDS_NAME)
{
suff = (*it).m_str;
}
}
mThumbURL.setPath(dir->absPath(mCurrentURL));
if(mCurrentURL.isLocalFile())
{
if(!statResultThumbnail())
createThumbnail(mCurrentURL);
determineNextIcon();
}
else if(statResultThumbnail())
determineNextIcon();
else if(SQ_ThumbnailsUnused::instance()->needUpdate(mCurrentURL, mOriginalTime))
{
mState = STATE_PREDOWNLOAD;
continueDownload = false;
tmp = new KTempFile(TQString(), TQString::fromLatin1(".%1").arg(suff), 0600);
tmp->setAutoDelete(true);
tmp->close();
mTempURL.setPath(tmp->name());
TDEIO::Job *cpjob = TDEIO::get(mCurrentURL, false, false);
connect(cpjob, TQT_SIGNAL(data(TDEIO::Job *, const TQByteArray &)),
this, TQT_SLOT(slotData(TDEIO::Job *, const TQByteArray &)));
addSubjob(cpjob);
}
else
determineNextIcon();
}
break;
case STATE_PREDOWNLOAD:
{
// error
if(job->error() && job->error() != TDEIO::ERR_USER_CANCELED)
emitThumbnailLoadingFailed();
// no errors, or canceled
delete tmp;
determineNextIcon();
}
break;
case STATE_DOWNLOAD:
{
if(job->error())
emitThumbnailLoadingFailed();
else if(SQ_LibraryHandler::instance()->libraryForFile(mTempURL.path()))
createThumbnail(mTempURL);
else
SQ_ThumbnailsUnused::instance()->insert(mCurrentURL, mOriginalTime);
delete tmp;
determineNextIcon();
}
break;
}
}
void SQ_ThumbnailLoadJob::slotData(TDEIO::Job *job, const TQByteArray &data)
{
if(!data.size())
return;
size += data.size();
TQFile f(mTempURL.path());
if(f.open(IO_WriteOnly | IO_Append))
{
f.writeBlock(data);
f.close();
}
// 20 bytes are enough to determine file type
if(size >= SQ_PREDOWNLOAD_SIZE && !continueDownload)
{
// cancel download (file type is not supported)
if(totalSize != size
&& !SQ_LibraryHandler::instance()->libraryForFile(mTempURL.path()))
{
SQ_ThumbnailsUnused::instance()->insert(mCurrentURL, mOriginalTime);
job->kill(false); // kill job & emit result
}
else if(!statResultThumbnail())
{
// nice, we can open this image -
// go to 'STATE_DOWNLOAD' stage and continue download
continueDownload = true;
mState = STATE_DOWNLOAD;
}
else if(size != totalSize)
{
job->kill(true); // kill job
subjobs.remove(job);
determineNextIcon();
}
}
}
bool SQ_ThumbnailLoadJob::statResultThumbnail()
{
SQ_Thumbnail th;
if(SQ_PixmapCache::instance()->contains2(mCurrentURL, th))
{
emitThumbnailLoaded(th);
return true;
}
if(dir->needUpdate(mThumbURL.path(), mOriginalTime))
{
// kdDebug() << "STAT **** thumbnailTime < mOriginalTime ****" << endl;
return false;
}
if(!th.thumbnail.load(mThumbURL.path(), sqdirThumbFormat))
return false;
SQ_LIBRARY *lib = SQ_LibraryHandler::instance()->libraryForFile(mCurrentURL);
th.w = th.thumbnail.text("Thumb::Image::Width").toInt();
th.h = th.thumbnail.text("Thumb::Image::Height").toInt();
if((!th.w || !th.h) && mCurrentURL.isLocalFile())
SQ_ImageLoader::instance()->tasteImage(mCurrentURL.path(), &th.w, &th.h, lib);
th.mime = lib->mime;
th.originalTime = mOriginalTime;
th.mime.load(dir->absPath(mCurrentURL), sqdirMimeFormat);
insertOrSync(mCurrentURL, th);
emitThumbnailLoaded(th);
return true;
}
void SQ_ThumbnailLoadJob::createThumbnail(const KURL &pixPath)
{
SQ_Thumbnail th;
bool loaded = false;
if(SQ_PixmapCache::instance()->contains2(pixPath, th))
{
emitThumbnailLoaded(th);
// kdDebug() << "CREATE found in cache \"" << pixPath << "\"" << endl;
return;
}
loaded = SQ_Utils::loadThumbnail(pixPath, th);
if(loaded)
{
th.originalTime = mOriginalTime;
th.thumbnail = th.thumbnail.swapRGB();
insertOrSync(mCurrentURL, th);
emitThumbnailLoaded(th);
}
else
emitThumbnailLoadingFailed();
}
void SQ_ThumbnailLoadJob::insertOrSync(const KURL &url, SQ_Thumbnail &th)
{
if(!SQ_PixmapCache::instance()->full())
{
SQ_PixmapCache::instance()->insert(url, th);
// kdDebug() << "IOSYNC inserting \"" << url.url() << "\"" << endl;
}
else
{
// kdDebug() << "IOSYNC SQ_PixmapCache is full! Cache is ignored!" << endl;
SQ_PixmapCache::instance()->syncEntry(url, th);
}
}
void SQ_ThumbnailLoadJob::emitThumbnailLoaded(SQ_Thumbnail &t)
{
int biggestDimension = TQMAX(t.thumbnail.width(), t.thumbnail.height());
int thumbPixelSize = SQ_ThumbnailSize::instance()->pixelSize();
if(biggestDimension <= thumbPixelSize)
{
emit thumbnailLoaded(mCurrentItem, t);
return;
}
t.thumbnail = SQ_Utils::scaleImage(
t.thumbnail.bits(),
t.thumbnail.width(),
t.thumbnail.height(),
thumbPixelSize);
emit thumbnailLoaded(mCurrentItem, t);
}
void SQ_ThumbnailLoadJob::emitThumbnailLoadingFailed()
{
emit thumbnailLoaded(mCurrentItem, mBrokenThumbnail);
}
void SQ_ThumbnailLoadJob::appendItems(const KFileItemList &items)
{
KFileItem *item;
KFileItemList *m_items = const_cast<KFileItemList *>(&items);
if(!mItems.isEmpty() && !items.isEmpty())
{
for(item = m_items->first();item;item = m_items->next())
mItems.append(item);
}
}
void SQ_ThumbnailLoadJob::prependItems(const KFileItemList &items)
{
KFileItem *item;
KFileItemList *m_items = const_cast<KFileItemList *>(&items);
if(!mItems.isEmpty() && !items.isEmpty())
{
for(item = m_items->last();item;item = m_items->prev())
mItems.prepend(item);
}
}
void SQ_ThumbnailLoadJob::kill(bool q)
{
donothing = true;
TDEIO::Job::kill(q);
}
#include "sq_thumbnailloadjob.moc"