/* ============================================================ * * This file is a part of digiKam project * http://www.digikam.org * * Date : 2006-04-14 * Description : Load and cache tag thumbnails * * Copyright (C) 2006-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. * * ============================================================ */ // C includes. #include // TQt includes. #include #include #include // KDE includes. #include #include #include // Local includes. #include "ddebug.h" #include "thumbnailjob.h" #include "thumbnailsize.h" #include "album.h" #include "albummanager.h" #include "albumsettings.h" #include "albumthumbnailloader.h" #include "albumthumbnailloader.moc" namespace Digikam { typedef TQMap > UrlAlbumMap; typedef TQMap TagThumbnailMap; class AlbumThumbnailLoaderPrivate { public: AlbumThumbnailLoaderPrivate() { iconSize = AlbumSettings::instance()->getDefaultTreeIconSize(); minBlendSize = 20; iconAlbumThumbJob = 0; iconTagThumbJob = 0; //cache = new TQCache(101, 211); } int iconSize; int minBlendSize; ThumbnailJob *iconTagThumbJob; ThumbnailJob *iconAlbumThumbJob; UrlAlbumMap urlAlbumMap; TagThumbnailMap tagThumbnailMap; //TQCache *cache; }; class AlbumThumbnailLoaderEvent : public TQCustomEvent { public: AlbumThumbnailLoaderEvent(int albumID, const TQPixmap &thumbnail) : TQCustomEvent(TQEvent::User), albumID(albumID), thumbnail(thumbnail) {}; int albumID; TQPixmap thumbnail; }; AlbumThumbnailLoader *AlbumThumbnailLoader::m_instance = 0; AlbumThumbnailLoader *AlbumThumbnailLoader::instance() { if (!m_instance) m_instance = new AlbumThumbnailLoader; return m_instance; } void AlbumThumbnailLoader::cleanUp() { delete m_instance; } AlbumThumbnailLoader::AlbumThumbnailLoader() { d = new AlbumThumbnailLoaderPrivate; connect(AlbumManager::instance(), TQ_SIGNAL(signalAlbumIconChanged(Album*)), this, TQ_SLOT(slotIconChanged(Album*))); connect(AlbumManager::instance(), TQ_SIGNAL(signalAlbumDeleted(Album*)), this, TQ_SLOT(slotIconChanged(Album*))); } AlbumThumbnailLoader::~AlbumThumbnailLoader() { if (d->iconTagThumbJob) d->iconTagThumbJob->kill(); if (d->iconAlbumThumbJob) d->iconAlbumThumbJob->kill(); //delete d->cache; delete d; m_instance = 0; } TQPixmap AlbumThumbnailLoader::getStandardTagIcon(RelativeSize relativeSize) { return loadIcon("tag", computeIconSize(relativeSize)); } TQPixmap AlbumThumbnailLoader::getStandardTagRootIcon(RelativeSize relativeSize) { return loadIcon("tag-folder", computeIconSize(relativeSize)); } TQPixmap AlbumThumbnailLoader::getStandardTagIcon(TAlbum *album, RelativeSize relativeSize) { if (album->isRoot()) return getStandardTagRootIcon(relativeSize); else return getStandardTagIcon(relativeSize); } TQPixmap AlbumThumbnailLoader::getStandardAlbumIcon(RelativeSize relativeSize) { return loadIcon("folder", computeIconSize(relativeSize)); } TQPixmap AlbumThumbnailLoader::getStandardAlbumRootIcon(RelativeSize relativeSize) { return loadIcon("folder_image", computeIconSize(relativeSize)); } TQPixmap AlbumThumbnailLoader::getStandardAlbumIcon(PAlbum *album, RelativeSize relativeSize) { if (album->isRoot()) return getStandardAlbumRootIcon(relativeSize); else return getStandardAlbumIcon(relativeSize); } int AlbumThumbnailLoader::computeIconSize(RelativeSize relativeSize) { if (relativeSize == SmallerSize) { // when size was 32 smaller was 20. Scale. return lround(20.0 / 32.0 * (double)d->iconSize); } return d->iconSize; } TQRect AlbumThumbnailLoader::computeBlendRect(int iconSize) { // when drawing a 20x20 thumbnail in a 32x32 icon, starting point was (6,9). Scale. double largerSize = iconSize; double x = 6.0 / 32.0 * largerSize; double y = 9.0 / 32.0 * largerSize; double size = 20.0 / 32.0 * largerSize; return TQRect(lround(x), lround(y), lround(size), lround(size)); } TQPixmap AlbumThumbnailLoader::loadIcon(const TQString &name, int size) { TDEIconLoader *iconLoader = TDEApplication::kApplication()->iconLoader(); return iconLoader->loadIcon(name, TDEIcon::NoGroup, size, TDEIcon::DefaultState, 0, true); } bool AlbumThumbnailLoader::getTagThumbnail(TAlbum *album, TQPixmap &icon) { int size = computeIconSize(SmallerSize); /* if (size >= d->minBlendSize) { TQRect rect = computeBlendRect(size); size = rect.width(); } */ if(!album->icon().isEmpty()) { if(album->icon().startsWith("/")) { KURL iconKURL; iconKURL.setPath(album->icon()); addURL(album, iconKURL); icon = TQPixmap(); return true; } else { icon = loadIcon(album->icon(), size); return false; } } else { icon = TQPixmap(); return false; } } bool AlbumThumbnailLoader::getAlbumThumbnail(PAlbum *album) { if(!album->icon().isEmpty() && d->iconSize > d->minBlendSize) { addURL(album, album->iconKURL()); } else { return false; } return true; } void AlbumThumbnailLoader::addURL(Album *album, const KURL &url) { /* TQPixmap* pix = d->cache->find(album->iconKURL().path()); if (pix) return pix; */ // First check cached thumbnails. // At startup, this is not relevant, as the views will add their requests in a row. // This is to speed up context menu and IE imagedescedit TagThumbnailMap::iterator ttit = d->tagThumbnailMap.find(album->globalID()); if (ttit != d->tagThumbnailMap.end()) { // It is not necessary to return cached icon asynchronously - they could be // returned by getTagThumbnail already - but this would make the API // less elegant, it feels much better this way. TQApplication::postEvent(this, new AlbumThumbnailLoaderEvent(album->globalID(), *ttit)); return; } // Check if the URL has already been added (ThumbnailJob will _not_ check this) UrlAlbumMap::iterator it = d->urlAlbumMap.find(url); if (it == d->urlAlbumMap.end()) { // use two IOslaves so that tag and album thumbnails are loaded // in parallel and not first album, then tag thumbnails if (album->type() == Album::TAG) { if(!d->iconTagThumbJob) { d->iconTagThumbJob = new ThumbnailJob(url, d->iconSize, true, AlbumSettings::instance()->getExifRotate()); connect(d->iconTagThumbJob, TQ_SIGNAL(signalThumbnail(const KURL&, const TQPixmap&)), TQ_SLOT(slotGotThumbnailFromIcon(const KURL&, const TQPixmap&))); connect(d->iconTagThumbJob, TQ_SIGNAL(signalFailed(const KURL&)), TQ_SLOT(slotThumbnailLost(const KURL&))); } else { d->iconTagThumbJob->addItem(url); } } else { if(!d->iconAlbumThumbJob) { d->iconAlbumThumbJob = new ThumbnailJob(url, d->iconSize, true, AlbumSettings::instance()->getExifRotate()); connect(d->iconAlbumThumbJob, TQ_SIGNAL(signalThumbnail(const KURL&, const TQPixmap&)), TQ_SLOT(slotGotThumbnailFromIcon(const KURL&, const TQPixmap&))); connect(d->iconAlbumThumbJob, TQ_SIGNAL(signalFailed(const KURL&)), TQ_SLOT(slotThumbnailLost(const KURL&))); } else { d->iconAlbumThumbJob->addItem(url); } } // insert new entry to map, add album globalID TQValueList &list = d->urlAlbumMap[url]; list.remove(album->globalID()); list.push_back(album->globalID()); } else { // only add album global ID to list which is already inserted in map (*it).remove(album->globalID()); (*it).push_back(album->globalID()); } } void AlbumThumbnailLoader::setThumbnailSize(int size) { if (d->iconSize == size) return; d->iconSize = size; // clear task list d->urlAlbumMap.clear(); // clear cached thumbnails d->tagThumbnailMap.clear(); if (d->iconAlbumThumbJob) { d->iconAlbumThumbJob->kill(); d->iconAlbumThumbJob= 0; } if (d->iconTagThumbJob) { d->iconTagThumbJob->kill(); d->iconTagThumbJob= 0; } emit signalReloadThumbnails(); } int AlbumThumbnailLoader::thumbnailSize() const { return d->iconSize; } void AlbumThumbnailLoader::slotGotThumbnailFromIcon(const KURL &url, const TQPixmap &thumbnail) { // We need to find all albums for which the given url has been requested, // and emit a signal for each album. UrlAlbumMap::iterator it = d->urlAlbumMap.find(url); if (it != d->urlAlbumMap.end()) { TQPixmap tagThumbnail; AlbumManager *manager = AlbumManager::instance(); for (TQValueList::iterator vit = (*it).begin(); vit != (*it).end(); ++vit) { // look up with global id Album *album = manager->findAlbum(*vit); if (album) { if (album->type() == Album::TAG) { // create tag thumbnail if needed if (tagThumbnail.isNull()) { tagThumbnail = createTagThumbnail(thumbnail); d->tagThumbnailMap.insert(album->globalID(), tagThumbnail); } emit signalThumbnail(album, tagThumbnail); } else { emit signalThumbnail(album, thumbnail); } } } d->urlAlbumMap.remove(it); } } void AlbumThumbnailLoader::customEvent(TQCustomEvent *e) { // for cached thumbnails AlbumThumbnailLoaderEvent *atle = (AlbumThumbnailLoaderEvent *)e; AlbumManager *manager = AlbumManager::instance(); Album *album = manager->findAlbum(atle->albumID); if (album) { if (atle->thumbnail.isNull()) emit signalFailed(album); else emit signalThumbnail(album, atle->thumbnail); } } void AlbumThumbnailLoader::slotIconChanged(Album* album) { if(!album || album->type() != Album::TAG) return; d->tagThumbnailMap.remove(album->globalID()); } TQPixmap AlbumThumbnailLoader::createTagThumbnail(const TQPixmap &albumThumbnail) { // tag thumbnails are cropped TQPixmap tagThumbnail; int thumbSize = TQMAX(albumThumbnail.width(), albumThumbnail.height()); if(!albumThumbnail.isNull() && thumbSize >= d->minBlendSize) { TQRect rect = computeBlendRect(thumbSize); int w1 = albumThumbnail.width(); int w2 = rect.width(); int h1 = albumThumbnail.height(); int h2 = rect.height(); tagThumbnail.resize(w2,h2); bitBlt(&tagThumbnail, 0, 0, &albumThumbnail, (w1-w2)/2, (h1-h2)/2, w2, h2); } else { tagThumbnail = albumThumbnail; } return tagThumbnail; } void AlbumThumbnailLoader::slotThumbnailLost(const KURL &url) { // Same code as above, only different signal UrlAlbumMap::iterator it = d->urlAlbumMap.find(url); if (it != d->urlAlbumMap.end()) { AlbumManager *manager = AlbumManager::instance(); for (TQValueList::iterator vit = (*it).begin(); vit != (*it).end(); ++vit) { Album *album = manager->findAlbum(*vit); if (album) emit signalFailed(album); } d->urlAlbumMap.remove(it); } } TQPixmap AlbumThumbnailLoader::blendIcons(TQPixmap dstIcon, const TQPixmap &tagIcon) { int dstIconSize = TQMAX(dstIcon.width(), dstIcon.height()); if (dstIconSize >= d->minBlendSize) { if(!tagIcon.isNull()) { TQRect rect = computeBlendRect(dstIconSize); TQPainter p(&dstIcon); p.drawPixmap(rect.x(), rect.y(), tagIcon, 0, 0, rect.width(), rect.height()); p.end(); } return dstIcon; } else { return tagIcon; } } } // namespace Digikam