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/albumlister.cpp

641 lines
16 KiB

/* ============================================================
*
* This file is a part of digiKam project
* http://www.digikam.org
*
* Date : 2004-06-26
* Description : Albums lister.
*
* Copyright (C) 2004-2005 by Renchi Raju <renchi@pooh.tam.uiuc.edu>
* Copyright (C) 2007-2009 by Gilles Caulier <caulier dot gilles at gmail dot com>
* Copyright (C) 2007 by Arnd Baecker <arnd dot baecker at web dot 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 Ansi includes.
extern "C"
{
#include <sys/time.h>
}
// C++ includes.
#include <cstdio>
#include <ctime>
// TQt includes.
#include <tqstring.h>
#include <tqcstring.h>
#include <tqdatastream.h>
#include <tqfileinfo.h>
#include <tqdir.h>
#include <tqmap.h>
#include <tqpair.h>
#include <tqvaluelist.h>
#include <tqtimer.h>
// KDE includes.
#include <kapplication.h>
#include <kcursor.h>
#include <tdeio/job.h>
#include <kurl.h>
// Local includes.
#include "ddebug.h"
#include "mimefilter.h"
#include "album.h"
#include "albummanager.h"
#include "albumsettings.h"
#include "albumlister.h"
#include "albumlister.moc"
namespace Digikam
{
class AlbumListerPriv
{
public:
AlbumListerPriv()
{
untaggedFilter = false;
ratingFilter = 0;
filterTimer = 0;
job = 0;
currAlbum = 0;
namesFilter = "*";
mimeTypeFilter = MimeFilter::AllFiles;
ratingCond = AlbumLister::GreaterEqualCondition;
matchingCond = AlbumLister::OrCondition;
recurseAlbums = false;
recurseTags = false;
}
bool untaggedFilter;
int ratingFilter;
int recurseAlbums;
int recurseTags;
TQString namesFilter;
TQString textFilter;
TQMap<TQ_LLONG, ImageInfo*> itemMap;
TQMap<int, int> invalidatedItems;
TQMap<TQDateTime, bool> dayFilter;
TQValueList<int> tagFilter;
TQTimer *filterTimer;
TDEIO::TransferJob *job;
ImageInfoList itemList;
Album *currAlbum;
MimeFilter::TypeMimeFilter mimeTypeFilter;
AlbumLister::MatchingCondition matchingCond;
AlbumLister::RatingCondition ratingCond;
};
AlbumLister* AlbumLister::m_instance = 0;
AlbumLister* AlbumLister::instance()
{
if (!m_instance)
new AlbumLister();
return m_instance;
}
AlbumLister::AlbumLister()
{
m_instance = this;
d = new AlbumListerPriv;
d->itemList.setAutoDelete(true);
d->filterTimer = new TQTimer(this);
connect(d->filterTimer, TQT_SIGNAL(timeout()),
this, TQT_SLOT(slotFilterItems()));
}
AlbumLister::~AlbumLister()
{
delete d->filterTimer;
delete d;
m_instance = 0;
}
void AlbumLister::openAlbum(Album *album)
{
d->currAlbum = album;
d->filterTimer->stop();
emit signalClear();
d->itemList.clear();
d->itemMap.clear();
if (d->job)
{
d->job->kill();
d->job = 0;
}
if (!album)
return;
TQByteArray ba;
TQDataStream ds(ba, IO_WriteOnly);
ds << AlbumManager::instance()->getLibraryPath();
ds << album->kurl();
ds << d->namesFilter;
ds << AlbumSettings::instance()->getIconShowResolution();
ds << d->recurseAlbums;
ds << d->recurseTags;
// Protocol = digikamalbums -> kio_digikamalbums
d->job = new TDEIO::TransferJob(album->kurl(), TDEIO::CMD_SPECIAL,
ba, TQByteArray(), false);
connect(d->job, TQT_SIGNAL(result(TDEIO::Job*)),
this, TQT_SLOT(slotResult(TDEIO::Job*)));
connect(d->job, TQT_SIGNAL(data(TDEIO::Job*, const TQByteArray&)),
this, TQT_SLOT(slotData(TDEIO::Job*, const TQByteArray&)));
}
void AlbumLister::refresh()
{
if (!d->currAlbum)
return;
d->filterTimer->stop();
if (d->job)
{
d->job->kill();
d->job = 0;
}
d->itemMap.clear();
ImageInfo* item;
for (ImageInfoListIterator it(d->itemList); (item = it.current()); ++it)
{
d->itemMap.insert(item->id(), item);
}
TQByteArray ba;
TQDataStream ds(ba, IO_WriteOnly);
ds << AlbumManager::instance()->getLibraryPath();
ds << d->currAlbum->kurl();
ds << d->namesFilter;
ds << AlbumSettings::instance()->getIconShowResolution();
ds << d->recurseAlbums;
ds << d->recurseTags;
d->job = new TDEIO::TransferJob(d->currAlbum->kurl(), TDEIO::CMD_SPECIAL,
ba, TQByteArray(), false);
connect(d->job, TQT_SIGNAL(result(TDEIO::Job*)),
this, TQT_SLOT(slotResult(TDEIO::Job*)));
connect(d->job, TQT_SIGNAL(data(TDEIO::Job*, const TQByteArray&)),
this, TQT_SLOT(slotData(TDEIO::Job*, const TQByteArray&)));
}
void AlbumLister::setDayFilter(const TQValueList<TQDateTime>& days)
{
d->dayFilter.clear();
for (TQValueList<TQDateTime>::const_iterator it = days.begin(); it != days.end(); ++it)
d->dayFilter.insert(*it, true);
d->filterTimer->start(100, true);
}
bool AlbumLister::tagFiltersIsActive()
{
if (!d->tagFilter.isEmpty() || d->untaggedFilter)
return true;
return false;
}
void AlbumLister::setTagFilter(const TQValueList<int>& tags, const MatchingCondition& matchingCond,
bool showUnTagged)
{
d->tagFilter = tags;
d->matchingCond = matchingCond;
d->untaggedFilter = showUnTagged;
d->filterTimer->start(100, true);
}
void AlbumLister::setRatingFilter(int rating, const RatingCondition& ratingCond)
{
d->ratingFilter = rating;
d->ratingCond = ratingCond;
d->filterTimer->start(100, true);
}
void AlbumLister::setMimeTypeFilter(int mimeTypeFilter)
{
d->mimeTypeFilter = (MimeFilter::TypeMimeFilter)mimeTypeFilter;
d->filterTimer->start(100, true);
}
void AlbumLister::setTextFilter(const TQString& text)
{
d->textFilter = text;
d->filterTimer->start(100, true);
}
void AlbumLister::setRecurseAlbums(bool recursive)
{
d->recurseAlbums = recursive;
refresh();
}
void AlbumLister::setRecurseTags(bool recursive)
{
d->recurseTags = recursive;
refresh();
}
bool AlbumLister::matchesFilter(const ImageInfo* info, bool &foundText)
{
if (d->dayFilter.isEmpty() && d->tagFilter.isEmpty() && d->textFilter.isEmpty() &&
!d->untaggedFilter && d->ratingFilter==-1)
return true;
bool match = false;
if (!d->tagFilter.isEmpty())
{
TQValueList<int> tagIDs = info->tagIDs();
TQValueList<int>::iterator it;
if (d->matchingCond == OrCondition)
{
for (it = d->tagFilter.begin(); it != d->tagFilter.end(); ++it)
{
if (tagIDs.contains(*it))
{
match = true;
break;
}
}
}
else
{
// AND matching condition...
for (it = d->tagFilter.begin(); it != d->tagFilter.end(); ++it)
{
if (!tagIDs.contains(*it))
break;
}
if (it == d->tagFilter.end())
match = true;
}
match |= (d->untaggedFilter && tagIDs.isEmpty());
}
else if (d->untaggedFilter)
{
match = info->tagIDs().isEmpty();
}
else
{
match = true;
}
if (!d->dayFilter.isEmpty())
{
match &= d->dayFilter.contains(TQDateTime(info->dateTime().date(), TQTime()));
}
//-- Filter by rating ---------------------------------------------------------
if (d->ratingFilter >= 0)
{
if (d->ratingCond == GreaterEqualCondition)
{
// If the rating is not >=, i.e it is <, then it does not match.
if (info->rating() < d->ratingFilter)
{
match = false;
}
}
else if (d->ratingCond == EqualCondition)
{
// If the rating is not =, i.e it is !=, then it does not match.
if (info->rating() != d->ratingFilter)
{
match = false;
}
}
else
{
// If the rating is not <=, i.e it is >, then it does not match.
if (info->rating() > d->ratingFilter)
{
match = false;
}
}
}
// -- Filter by mime type -----------------------------------------------------
TQFileInfo fi(info->filePath());
TQString mimeType = fi.extension(false).upper();
switch(d->mimeTypeFilter)
{
case MimeFilter::ImageFiles:
{
TQString imageFilesExt(AlbumSettings::instance()->getImageFileFilter());
imageFilesExt.append(AlbumSettings::instance()->getRawFileFilter());
if (!imageFilesExt.upper().contains(mimeType))
match = false;
break;
}
case MimeFilter::JPGFiles:
{
if (mimeType != TQString("JPG") && mimeType != TQString("JPE") &&
mimeType != TQString("JPEG"))
match = false;
break;
}
case MimeFilter::PNGFiles:
{
if (mimeType != TQString("PNG"))
match = false;
break;
}
case MimeFilter::TIFFiles:
{
if (mimeType != TQString("TIF") && mimeType != TQString("TIFF"))
match = false;
break;
}
case MimeFilter::NoRAWFiles:
{
TQString rawFilesExt(AlbumSettings::instance()->getRawFileFilter());
if (rawFilesExt.upper().contains(mimeType))
match = false;
break;
}
case MimeFilter::RAWFiles:
{
TQString rawFilesExt(AlbumSettings::instance()->getRawFileFilter());
if (!rawFilesExt.upper().contains(mimeType))
match = false;
break;
}
case MimeFilter::MoviesFiles:
{
TQString moviesFilesExt(AlbumSettings::instance()->getMovieFileFilter());
if (!moviesFilesExt.upper().contains(mimeType))
match = false;
break;
}
case MimeFilter::AudioFiles:
{
TQString audioFilesExt(AlbumSettings::instance()->getAudioFileFilter());
if (!audioFilesExt.upper().contains(mimeType))
match = false;
break;
}
default: // All Files: do nothing...
break;
}
//-- Filter by text -----------------------------------------------------------
if (!d->textFilter.isEmpty())
{
foundText = false;
if (info->name().lower().contains(d->textFilter.lower()))
{
foundText = true;
}
if (info->caption().lower().contains(d->textFilter.lower()))
foundText = true;
TQStringList tags = info->tagNames();
for (TQStringList::const_iterator it = tags.constBegin() ; it != tags.constEnd() ; ++it)
{
if ((*it).lower().contains(d->textFilter.lower()))
foundText = true;
}
// check for folder names
PAlbum* palbum = AlbumManager::instance()->findPAlbum(info->albumID());
if ((palbum && palbum->title().lower().contains(d->textFilter.lower())))
{
foundText = true;
}
match &= foundText;
}
return match;
}
void AlbumLister::stop()
{
d->currAlbum = 0;
d->filterTimer->stop();
emit signalClear();
d->itemList.clear();
d->itemMap.clear();
if (d->job)
{
d->job->kill();
d->job = 0;
}
}
void AlbumLister::setNamesFilter(const TQString& namesFilter)
{
d->namesFilter = namesFilter;
}
void AlbumLister::invalidateItem(const ImageInfo *item)
{
d->invalidatedItems.insert(item->id(), item->id());
}
void AlbumLister::slotFilterItems()
{
if (d->job)
{
d->filterTimer->start(100, true);
return;
}
TQPtrList<ImageInfo> newFilteredItemsList;
TQPtrList<ImageInfo> deleteFilteredItemsList;
ImageInfo *item = 0;
bool matchForText = false;
bool match = false;
for (ImageInfoListIterator it(d->itemList);
(item = it.current()); ++it)
{
bool foundText = false;
if (matchesFilter(item, foundText))
{
match = true;
if (!item->getViewItem())
newFilteredItemsList.append(item);
}
else
{
if (item->getViewItem())
deleteFilteredItemsList.append(item);
}
if (foundText)
matchForText = true;
}
// This takes linear time - and deleting seems to take longer. Set wait cursor for large numbers.
bool setCursor = (3*deleteFilteredItemsList.count() + newFilteredItemsList.count()) > 1500;
if (setCursor)
kapp->setOverrideCursor(KCursor::waitCursor());
emit signalItemsTextFilterMatch(matchForText);
emit signalItemsFilterMatch(match);
if (!deleteFilteredItemsList.isEmpty())
{
for (ImageInfo *info=deleteFilteredItemsList.first(); info; info = deleteFilteredItemsList.next())
emit signalDeleteFilteredItem(info);
}
if (!newFilteredItemsList.isEmpty())
{
emit signalNewFilteredItems(newFilteredItemsList);
}
if (setCursor)
kapp->restoreOverrideCursor();
}
void AlbumLister::slotResult(TDEIO::Job* job)
{
d->job = 0;
if (job->error())
{
DWarning() << "Failed to list url: " << job->errorString() << endl;
d->itemMap.clear();
d->invalidatedItems.clear();
return;
}
typedef TQMap<TQ_LLONG, ImageInfo*> ImMap;
for (ImMap::iterator it = d->itemMap.begin();
it != d->itemMap.end(); ++it)
{
emit signalDeleteItem(it.data());
emit signalDeleteFilteredItem(it.data());
d->itemList.remove(it.data());
}
d->itemMap.clear();
d->invalidatedItems.clear();
emit signalCompleted();
}
void AlbumLister::slotData(TDEIO::Job*, const TQByteArray& data)
{
if (data.isEmpty())
return;
TQ_LLONG imageID;
int albumID;
TQString name;
TQString date;
size_t size;
TQSize dims;
ImageInfoList newItemsList;
ImageInfoList newFilteredItemsList;
TQDataStream ds(data, IO_ReadOnly);
while (!ds.atEnd())
{
bool foundText = false;
ds >> imageID;
ds >> albumID;
ds >> name;
ds >> date;
ds >> size;
ds >> dims;
if (d->itemMap.contains(imageID))
{
ImageInfo* info = d->itemMap[imageID];
d->itemMap.remove(imageID);
if (d->invalidatedItems.contains(imageID))
{
emit signalDeleteItem(info);
emit signalDeleteFilteredItem(info);
d->itemList.remove(info);
}
else
{
if (!matchesFilter(info, foundText))
{
emit signalDeleteFilteredItem(info);
}
continue;
}
}
ImageInfo* info = new ImageInfo(imageID, albumID, name,
TQDateTime::fromString(date, Qt::ISODate),
size, dims);
if (matchesFilter(info, foundText))
newFilteredItemsList.append(info);
newItemsList.append(info);
d->itemList.append(info);
}
if (!newFilteredItemsList.isEmpty())
emit signalNewFilteredItems(newFilteredItemsList);
if (!newItemsList.isEmpty())
emit signalNewItems(newItemsList);
slotFilterItems();
}
} // namespace Digikam