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.
digikam/digikam/digikam/albumthumbnailloader.cpp

492 lines
13 KiB

/* ============================================================
*
* 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 <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.
*
* ============================================================ */
// C includes.
#include <math.h>
// TQt includes.
#include <tqmap.h>
#include <tqpainter.h>
#include <tqvaluelist.h>
// KDE includes.
#include <tdeapplication.h>
#include <kiconloader.h>
#include <kdebug.h>
// 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<KURL, TQValueList<int> > UrlAlbumMap;
typedef TQMap<int, TQPixmap> TagThumbnailMap;
class AlbumThumbnailLoaderPrivate
{
public:
AlbumThumbnailLoaderPrivate()
{
iconSize = AlbumSettings::instance()->getDefaultTreeIconSize();
minBlendSize = 20;
iconAlbumThumbJob = 0;
iconTagThumbJob = 0;
//cache = new TQCache<TQPixmap>(101, 211);
}
int iconSize;
int minBlendSize;
ThumbnailJob *iconTagThumbJob;
ThumbnailJob *iconAlbumThumbJob;
UrlAlbumMap urlAlbumMap;
TagThumbnailMap tagThumbnailMap;
//TQCache<TQPixmap> *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<int> &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<int>::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<int>::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