|
|
|
/* ============================================================
|
|
|
|
*
|
|
|
|
* This file is a part of digiKam project
|
|
|
|
* http://www.digikam.org
|
|
|
|
*
|
|
|
|
* Date : 2006-01-11
|
|
|
|
* Description : shared image loading and caching
|
|
|
|
*
|
|
|
|
* Copyright (C) 2005-2007 by Marcel Wiesweg <marcel.wiesweg@gmx.de>
|
|
|
|
*
|
|
|
|
* 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, 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.
|
|
|
|
*
|
|
|
|
* ============================================================ */
|
|
|
|
|
|
|
|
// TQt includes.
|
|
|
|
|
|
|
|
#include <tqapplication.h>
|
|
|
|
#include <tqvariant.h>
|
|
|
|
|
|
|
|
// KDE includes.
|
|
|
|
|
|
|
|
#include <kdirwatch.h>
|
|
|
|
|
|
|
|
// Local includes.
|
|
|
|
|
|
|
|
#include "ddebug.h"
|
|
|
|
#include "loadingcache.h"
|
|
|
|
#include "loadingcache.moc"
|
|
|
|
|
|
|
|
namespace Digikam
|
|
|
|
{
|
|
|
|
|
|
|
|
class LoadingCachePriv
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
|
|
|
|
TQCache<DImg> imageCache;
|
|
|
|
TQDict<LoadingProcess> loadingDict;
|
|
|
|
TQMutex mutex;
|
|
|
|
TQWaitCondition condVar;
|
|
|
|
KDirWatch *watch;
|
|
|
|
TQStringList watchedFiles;
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
LoadingCache *LoadingCache::m_instance = 0;
|
|
|
|
|
|
|
|
LoadingCache *LoadingCache::cache()
|
|
|
|
{
|
|
|
|
if (!m_instance)
|
|
|
|
m_instance = new LoadingCache;
|
|
|
|
return m_instance;
|
|
|
|
}
|
|
|
|
|
|
|
|
void LoadingCache::cleanUp()
|
|
|
|
{
|
|
|
|
if (m_instance)
|
|
|
|
delete m_instance;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
LoadingCache::LoadingCache()
|
|
|
|
{
|
|
|
|
d = new LoadingCachePriv;
|
|
|
|
|
|
|
|
d->imageCache.setAutoDelete(true);
|
|
|
|
// default value: 60 MB of cache
|
|
|
|
setCacheSize(60);
|
|
|
|
|
|
|
|
d->watch = new KDirWatch;
|
|
|
|
|
|
|
|
connect(d->watch, TQT_SIGNAL(dirty(const TQString &)),
|
|
|
|
this, TQT_SLOT(slotFileDirty(const TQString &)));
|
|
|
|
}
|
|
|
|
|
|
|
|
LoadingCache::~LoadingCache()
|
|
|
|
{
|
|
|
|
delete d->watch;
|
|
|
|
delete d;
|
|
|
|
m_instance = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
DImg *LoadingCache::retrieveImage(const TQString &cacheKey)
|
|
|
|
{
|
|
|
|
return d->imageCache.find(cacheKey);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool LoadingCache::putImage(const TQString &cacheKey, DImg *img, const TQString &filePath)
|
|
|
|
{
|
|
|
|
bool successfulyInserted;
|
|
|
|
|
|
|
|
// use size of image as cache cost, take care for wrapped preview TQImages
|
|
|
|
int cost = img->numBytes();
|
|
|
|
TQVariant attribute(img->attribute("previewTQImage"));
|
|
|
|
if (attribute.isValid())
|
|
|
|
{
|
|
|
|
cost = attribute.toImage().numBytes();
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( d->imageCache.insert(cacheKey, img, cost) )
|
|
|
|
{
|
|
|
|
if (!filePath.isEmpty())
|
|
|
|
{
|
|
|
|
// store file path as attribute for our own use
|
|
|
|
img->setAttribute("loadingCacheFilePath", TQVariant(filePath));
|
|
|
|
}
|
|
|
|
successfulyInserted = true;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// need to delete object if it was not successfuly inserted (too large)
|
|
|
|
delete img;
|
|
|
|
successfulyInserted = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!filePath.isEmpty())
|
|
|
|
{
|
|
|
|
// schedule update of file watch
|
|
|
|
// KDirWatch can only be accessed from main thread!
|
|
|
|
addToWatch();
|
|
|
|
//TQApplication::postEvent(this, new TQCustomEvent(TQEvent::User));
|
|
|
|
}
|
|
|
|
return successfulyInserted;
|
|
|
|
}
|
|
|
|
|
|
|
|
void LoadingCache::removeImage(const TQString &cacheKey)
|
|
|
|
{
|
|
|
|
d->imageCache.remove(cacheKey);
|
|
|
|
}
|
|
|
|
|
|
|
|
void LoadingCache::removeImages()
|
|
|
|
{
|
|
|
|
d->imageCache.clear();
|
|
|
|
}
|
|
|
|
|
|
|
|
void LoadingCache::slotFileDirty(const TQString &path)
|
|
|
|
{
|
|
|
|
// Signal comes from main thread, we need to lock ourselves.
|
|
|
|
CacheLock lock(this);
|
|
|
|
//DDebug() << "LoadingCache slotFileDirty " << path << endl;
|
|
|
|
for (TQCacheIterator<DImg> it(d->imageCache); it.current(); ++it)
|
|
|
|
{
|
|
|
|
if (it.current()->attribute("loadingCacheFilePath").toString() == path)
|
|
|
|
{
|
|
|
|
//DDebug() << " removing watch and cache entry for " << path << endl;
|
|
|
|
d->imageCache.remove(it.currentKey());
|
|
|
|
d->watch->removeFile(path);
|
|
|
|
d->watchedFiles.remove(path);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void LoadingCache::addToWatch( )
|
|
|
|
//void LoadingCache::customEvent(TQCustomEvent *)
|
|
|
|
{
|
|
|
|
// Event comes from main thread, we need to lock ourselves.
|
|
|
|
//CacheLock lock(this);
|
|
|
|
|
|
|
|
// get a list of files in cache that need watch
|
|
|
|
TQStringList toBeAdded;
|
|
|
|
TQStringList toBeRemoved = d->watchedFiles;
|
|
|
|
for (TQCacheIterator<DImg> it(d->imageCache); it.current(); ++it)
|
|
|
|
{
|
|
|
|
TQString watchPath = it.current()->attribute("loadingCacheFilePath").toString();
|
|
|
|
if (!watchPath.isEmpty())
|
|
|
|
{
|
|
|
|
if (!d->watchedFiles.contains(watchPath))
|
|
|
|
toBeAdded.append(watchPath);
|
|
|
|
toBeRemoved.remove(watchPath);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for (TQStringList::iterator it = toBeRemoved.begin(); it != toBeRemoved.end(); ++it)
|
|
|
|
{
|
|
|
|
//DDebug() << "removing watch for " << *it << endl;
|
|
|
|
d->watch->removeFile(*it);
|
|
|
|
d->watchedFiles.remove(*it);
|
|
|
|
}
|
|
|
|
|
|
|
|
for (TQStringList::iterator it = toBeAdded.begin(); it != toBeAdded.end(); ++it)
|
|
|
|
{
|
|
|
|
//DDebug() << "adding watch for " << *it << endl;
|
|
|
|
d->watch->addFile(*it);
|
|
|
|
d->watchedFiles.append(*it);
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
bool LoadingCache::isCacheable(const DImg *img)
|
|
|
|
{
|
|
|
|
// return whether image fits in cache
|
|
|
|
return (uint)d->imageCache.maxCost() >= img->numBytes();
|
|
|
|
}
|
|
|
|
|
|
|
|
void LoadingCache::addLoadingProcess(LoadingProcess *process)
|
|
|
|
{
|
|
|
|
d->loadingDict.insert(process->cacheKey(), process);
|
|
|
|
}
|
|
|
|
|
|
|
|
LoadingProcess *LoadingCache::retrieveLoadingProcess(const TQString &cacheKey)
|
|
|
|
{
|
|
|
|
return d->loadingDict.find(cacheKey);
|
|
|
|
}
|
|
|
|
|
|
|
|
void LoadingCache::removeLoadingProcess(LoadingProcess *process)
|
|
|
|
{
|
|
|
|
d->loadingDict.remove(process->cacheKey());
|
|
|
|
}
|
|
|
|
|
|
|
|
void LoadingCache::notifyNewLoadingProcess(LoadingProcess *process, LoadingDescription description)
|
|
|
|
{
|
|
|
|
for (TQDictIterator<LoadingProcess> it(d->loadingDict); it.current(); ++it)
|
|
|
|
{
|
|
|
|
it.current()->notifyNewLoadingProcess(process, description);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void LoadingCache::setCacheSize(int megabytes)
|
|
|
|
{
|
|
|
|
d->imageCache.setMaxCost(megabytes * 1024 * 1024);
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------------------------------------------------
|
|
|
|
|
|
|
|
LoadingCache::CacheLock::CacheLock(LoadingCache *cache)
|
|
|
|
: m_cache(cache)
|
|
|
|
{
|
|
|
|
m_cache->d->mutex.lock();
|
|
|
|
}
|
|
|
|
|
|
|
|
LoadingCache::CacheLock::~CacheLock()
|
|
|
|
{
|
|
|
|
m_cache->d->mutex.unlock();
|
|
|
|
}
|
|
|
|
|
|
|
|
void LoadingCache::CacheLock::wakeAll()
|
|
|
|
{
|
|
|
|
// obviously the mutex is locked when this function is called
|
|
|
|
m_cache->d->condVar.wakeAll();
|
|
|
|
}
|
|
|
|
|
|
|
|
void LoadingCache::CacheLock::timedWait()
|
|
|
|
{
|
|
|
|
// same as above, the mutex is certainly locked
|
|
|
|
m_cache->d->condVar.wait(&m_cache->d->mutex, 1000);
|
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace Digikam
|
|
|
|
|