/* ============================================================ * * 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 * Copyright (C) 2007-2009 by Gilles Caulier * Copyright (C) 2007 by Arnd Baecker * * 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 } // C++ includes. #include #include // TQt includes. #include #include #include #include #include #include #include #include #include // KDE includes. #include #include #include #include // 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 itemMap; TQMap invalidatedItems; TQMap dayFilter; TQValueList 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 -> tdeio_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& days) { d->dayFilter.clear(); for (TQValueList::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& 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 tagIDs = info->tagIDs(); TQValueList::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 newFilteredItemsList; TQPtrList 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 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, TQt::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