/* ============================================================ * * 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 * * 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 #include // KDE includes. #include // Local includes. #include "ddebug.h" #include "loadingcache.h" #include "loadingcache.moc" namespace Digikam { class LoadingCachePriv { public: TQCache imageCache; TQDict 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, TQ_SIGNAL(dirty(const TQString &)), this, TQ_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! 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 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::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 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 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