diff options
Diffstat (limited to 'src/digikam/scanlib.cpp')
-rw-r--r-- | src/digikam/scanlib.cpp | 541 |
1 files changed, 541 insertions, 0 deletions
diff --git a/src/digikam/scanlib.cpp b/src/digikam/scanlib.cpp new file mode 100644 index 00000000..71ea425c --- /dev/null +++ b/src/digikam/scanlib.cpp @@ -0,0 +1,541 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-01-01 + * Description : scan pictures interface. + * + * Copyright (C) 2005-2006 by Tom Albers <[email protected]> + * Copyright (C) 2007-2008 by Gilles Caulier <caulier dot gilles at gmail dot com> + * + * 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 <ctime> +#include <cstdlib> + +// TQt includes. + +#include <tqapplication.h> +#include <tqstring.h> +#include <tqstringlist.h> +#include <tqmap.h> +#include <tqdir.h> +#include <tqfileinfo.h> +#include <tqwhatsthis.h> +#include <tqpixmap.h> + +// KDE includes. + +#include <kprogress.h> +#include <tdemessagebox.h> +#include <tdeapplication.h> +#include <tdelocale.h> +#include <kiconloader.h> + +// Local includes. + +#include "ddebug.h" +#include "dprogressdlg.h" +#include "dmetadata.h" +#include "albumdb.h" +#include "albummanager.h" +#include "splashscreen.h" +#include "scanlib.h" + +/** @file scanlib.cpp*/ + +namespace Digikam +{ + +ScanLib::ScanLib(SplashScreen *splash) +{ + m_splash = splash; + m_progressBar = new DProgressDlg(0); + m_progressBar->setInitialSize(TQSize(500, 100), true); + m_progressBar->setActionListVSBarVisible(false); + TQWhatsThis::add( m_progressBar, i18n("This shows the progress of the " + "scan. During the scan, all files on disk are registered in a " + "database. This is required for sorting by exif-date, and also speeds up " + "the overall performance of digiKam.") ); + + // these two lines prevent the dialog to be shown in + // findFoldersWhichDoNotExist() method. + m_progressBar->progressBar()->setTotalSteps(1); + m_progressBar->progressBar()->setProgress(1); +} + +ScanLib::~ScanLib() +{ + delete m_progressBar; +} + +void ScanLib::startScan() +{ + struct timeval tv1, tv2; + TQPixmap pix = TDEApplication::kApplication()->iconLoader()->loadIcon( + "system-run", TDEIcon::NoGroup, 32); + + TQString message = i18n("Finding non-existent Albums"); + if (m_splash) m_splash->message(message); + else m_progressBar->addedAction(pix, message); + gettimeofday(&tv1, 0); + findFoldersWhichDoNotExist(); + gettimeofday(&tv2, 0); + timing(message, tv1, tv2); + + message = i18n("Finding items not in database"); + if (m_splash) m_splash->message(message); + else m_progressBar->addedAction(pix, message); + gettimeofday(&tv1, 0); + findMissingItems(); + gettimeofday(&tv2, 0); + timing(message, tv1, tv2); + + message = i18n("Updating items without a date"); + if (m_splash) m_splash->message(message); + else m_progressBar->addedAction(pix, message); + gettimeofday(&tv1, 0); + updateItemsWithoutDate(); + gettimeofday(&tv2, 0); + timing(message, tv1, tv2); + + deleteStaleEntries(); + + AlbumDB* db = AlbumManager::instance()->albumDB(); + db->setSetting("Scanned", TQDateTime::currentDateTime().toString(TQt::ISODate)); +} + +void ScanLib::findFoldersWhichDoNotExist() +{ + TQMap<TQString, int> toBeDeleted; + TQString basePath(AlbumManager::instance()->getLibraryPath()); + + AlbumDB* db = AlbumManager::instance()->albumDB(); + AlbumInfo::List aList = db->scanAlbums(); + + for (AlbumInfo::List::iterator it = aList.begin(); it != aList.end(); ++it) + { + AlbumInfo info = *it; + info.url = TQDir::cleanDirPath(info.url); + TQFileInfo fi(basePath + info.url); + if (!fi.exists() || !fi.isDir()) + { + toBeDeleted[info.url] = info.id; + } + } + + kapp->processEvents(); + + if (!toBeDeleted.isEmpty()) + { + int rc = KMessageBox::warningYesNoList(0, + i18n("<p>There is an album in the database which does not appear to " + "be on disk. This album should be removed from the database, " + "however you may lose information because all images " + "associated with this album will be removed from the database " + "as well.<p>" + "digiKam cannot continue without removing the items " + "from the database because all views depend on the information " + "in the database. Do you want them to be removed from the " + "database?", + "<p>There are %n albums in the database which do not appear to " + "be on disk. These albums should be removed from the database, " + "however you may lose information because all images " + "associated with these albums will be removed from the database " + "as well.<p>" + "digiKam cannot continue without removing the items " + "from the database because all views depend on the information " + "in the database. Do you want them to be removed from the " + "database?", + toBeDeleted.count()), + toBeDeleted.keys(), + i18n("Albums are Missing")); + + if (rc != KMessageBox::Yes) + exit(0); + + TQMapIterator<TQString,int> it; + for (it = toBeDeleted.begin() ; it != toBeDeleted.end(); ++it) + { + DDebug() << "Removing Album: " << it.key() << endl; + db->deleteAlbum( it.data() ); + } + } +} + +void ScanLib::findMissingItems(const TQString &path) +{ + allFiles(path); +} + +void ScanLib::findMissingItems() +{ + TQString albumPath = AlbumManager::instance()->getLibraryPath(); + albumPath = TQDir::cleanDirPath(albumPath); + + m_progressBar->setAllowCancel( false ); + m_progressBar->showCancelButton (false ); + m_progressBar->progressBar()->setProgress( 0 ); + m_progressBar->setLabel(i18n("Scanning items, please wait...")); + m_progressBar->progressBar()->setTotalSteps( countItemsInFolder( albumPath ) ); + if (!m_splash) m_progressBar->show(); + kapp->processEvents(); + + TQDir dir(albumPath); + TQStringList fileList(dir.entryList(TQDir::Dirs)); + TQPixmap pix = TDEApplication::kApplication()->iconLoader()->loadIcon( + "folder_image", TDEIcon::NoGroup, 32); + + AlbumDB* db = AlbumManager::instance()->albumDB(); + db->beginTransaction(); + + for (TQStringList::iterator it = fileList.begin(); it != fileList.end(); ++it) + { + if ((*it) == "." || (*it) == "..") + continue; + + TQString path = albumPath + '/' + (*it); + allFiles(path); + m_progressBar->addedAction(pix, path); + } + db->commitTransaction(); + + m_progressBar->hide(); + kapp->processEvents(); +} + +void ScanLib::updateItemsWithoutDate() +{ + AlbumDB* db = AlbumManager::instance()->albumDB(); + TQStringList urls = db->getAllItemURLsWithoutDate(); + + if (urls.isEmpty()) + { + m_progressBar->progressBar()->setTotalSteps(1); + m_progressBar->progressBar()->setProgress(1); + m_progressBar->hide(); + return; + } + + m_progressBar->setAllowCancel( false ); + m_progressBar->showCancelButton (false ); + m_progressBar->progressBar()->setProgress(0); + m_progressBar->progressBar()->setTotalSteps(urls.count()); + m_progressBar->setLabel(i18n("Updating items, please wait...")); + m_progressBar->show(); + kapp->processEvents(); + + TQString basePath = AlbumManager::instance()->getLibraryPath(); + basePath = TQDir::cleanDirPath(basePath); + + db->beginTransaction(); + + int counter=0; + for (TQStringList::iterator it = urls.begin(); it != urls.end(); ++it) + { + m_progressBar->progressBar()->advance(1); + ++counter; + if ( counter % 30 == 0 ) + { + kapp->processEvents(); + } + + TQFileInfo fi(*it); + TQString albumURL = fi.dirPath(); + albumURL = TQDir::cleanDirPath(albumURL.remove(basePath)); + + int albumID = db->getOrCreateAlbumId(albumURL); + + if (albumID <= 0) + { + DWarning() << "Album ID == -1: " << albumURL << endl; + } + + if (fi.exists()) + { + updateItemDate(albumURL, fi.fileName(), albumID); + } + else + { + TQPair<TQString, int> fileID = qMakePair(fi.fileName(),albumID); + + if (m_filesToBeDeleted.findIndex(fileID) == -1) + { + m_filesToBeDeleted.append(fileID); + } + } + } + + db->commitTransaction(); + + m_progressBar->hide(); + kapp->processEvents(); +} + +int ScanLib::countItemsInFolder(const TQString& directory) +{ + int items = 0; + + TQDir dir( directory ); + if ( !dir.exists() or !dir.isReadable() ) + return 0; + + const TQFileInfoList *list = dir.entryInfoList(); + TQFileInfoListIterator it( *list ); + TQFileInfo *fi; + + items += list->count(); + + while ( (fi = it.current()) != 0 ) + { + if ( fi->isDir() && + fi->fileName() != "." && + fi->fileName() != "..") + { + items += countItemsInFolder( fi->filePath() ); + } + + ++it; + } + + return items; +} + +void ScanLib::allFiles(const TQString& directory) +{ + TQDir dir( directory ); + if ( !dir.exists() or !dir.isReadable() ) + { + DWarning() << "Folder does not exist or is not readable: " + << directory << endl; + return; + } + + TQString basePath = AlbumManager::instance()->getLibraryPath(); + basePath = TQDir::cleanDirPath(basePath); + + TQString albumURL = directory; + albumURL = TQDir::cleanDirPath(albumURL.remove(basePath)); + + AlbumDB* db = AlbumManager::instance()->albumDB(); + + int albumID = db->getOrCreateAlbumId(albumURL); + + if (albumID <= 0) + { + DWarning() << "Album ID == -1: " << albumURL << endl; + } + + TQStringList filesInAlbum = db->getItemNamesInAlbum( albumID ); + TQMap<TQString, bool> filesFoundInDB; + + for (TQStringList::iterator it = filesInAlbum.begin(); + it != filesInAlbum.end(); ++it) + { + filesFoundInDB.insert(*it, true); + } + + const TQFileInfoList *list = dir.entryInfoList(); + if (!list) + return; + + TQFileInfoListIterator it( *list ); + TQFileInfo *fi; + + m_progressBar->progressBar()->advance(list->count()); + kapp->processEvents(); + + while ( (fi = it.current()) != 0 ) + { + if ( fi->isFile()) + { + if (filesFoundInDB.contains(fi->fileName()) ) + { + filesFoundInDB.erase(fi->fileName()); + } + else + { + storeItemInDatabase(albumURL, fi->fileName(), albumID); + } + } + else if ( fi->isDir() && fi->fileName() != "." && fi->fileName() != "..") + { + allFiles( fi->filePath() ); + } + + ++it; + } + + // Removing items from the db which we did not see on disk. + if (!filesFoundInDB.isEmpty()) + { + TQMapIterator<TQString,bool> it; + for (it = filesFoundInDB.begin(); it != filesFoundInDB.end(); ++it) + { + if (m_filesToBeDeleted.findIndex(qMakePair(it.key(),albumID)) == -1) + { + m_filesToBeDeleted.append(qMakePair(it.key(),albumID)); + } + } + } +} + +void ScanLib::storeItemInDatabase(const TQString& albumURL, + const TQString& filename, + int albumID) +{ + // Do not store items found in the root of the albumdb + if (albumURL.isEmpty()) + return; + + TQString comment; + TQStringList keywords; + TQDateTime datetime; + int rating; + + TQString filePath( AlbumManager::instance()->getLibraryPath()); + filePath += albumURL + '/' + filename; + + DMetadata metadata(filePath); + + // Try to get comments from image : + // In first, from standard JPEG comments, or + // In second, from EXIF comments tag, or + // In third, from IPTC comments tag. + + comment = metadata.getImageComment(); + + // Try to get date and time from image : + // In first, from EXIF date & time tags, or + // In second, from IPTC date & time tags. + + datetime = metadata.getImageDateTime(); + + // Try to get image rating from IPTC Urgency tag + // else use file system time stamp. + rating = metadata.getImageRating(); + + if ( !datetime.isValid() ) + { + TQFileInfo info( filePath ); + datetime = info.lastModified(); + } + + // Try to get image tags from IPTC keywords tags. + + keywords = metadata.getImageKeywords(); + + AlbumDB* dbstore = AlbumManager::instance()->albumDB(); + dbstore->addItem(albumID, filename, datetime, comment, rating, keywords); +} + +void ScanLib::updateItemDate(const TQString& albumURL, + const TQString& filename, + int albumID) +{ + TQDateTime datetime; + + TQString filePath( AlbumManager::instance()->getLibraryPath()); + filePath += albumURL + '/' + filename; + + DMetadata metadata(filePath); + + // Trying to get date and time from image : + // In first, from EXIF date & time tags, or + // In second, from IPTC date & time tags. + + datetime = metadata.getImageDateTime(); + + if ( !datetime.isValid() ) + { + TQFileInfo info( filePath ); + datetime = info.lastModified(); + } + + AlbumDB* dbstore = AlbumManager::instance()->albumDB(); + dbstore->setItemDate(albumID, filename, datetime); +} + +void ScanLib::deleteStaleEntries() +{ + TQStringList listToBeDeleted; + TQValueList< TQPair<TQString,int> >::iterator it; + + for (it = m_filesToBeDeleted.begin() ; it != m_filesToBeDeleted.end(); ++it) + { + AlbumDB* dbstore = AlbumManager::instance()->albumDB(); + TQString location = " (" + dbstore->getAlbumURL((*it).second) + ')'; + + listToBeDeleted.append((*it).first + location); + } + + if ( !m_filesToBeDeleted.isEmpty() ) + { + int rc = KMessageBox::warningYesNoList(0, + i18n("<p>There is an item in the database which does not " + "appear to be on disk or is located in the root album of " + "the path. This file should be removed from the " + "database, however you may lose information.<p>" + "digiKam cannot continue without removing the item from " + "the database because all views depend on the information " + "in the database. Do you want it to be removed from the " + "database?", + "<p>There are %n items in the database which do not " + "appear to be on disk or are located in the root album of " + "the path. These files should be removed from the " + "database, however you may lose information.<p>" + "digiKam cannot continue without removing these items from " + "the database because all views depend on the information " + "in the database. Do you want them to be removed from the " + "database?", + listToBeDeleted.count()), + listToBeDeleted, + i18n("Files are Missing")); + + if (rc != KMessageBox::Yes) + exit(0); + + AlbumDB* db = AlbumManager::instance()->albumDB(); + db->beginTransaction(); + for (it = m_filesToBeDeleted.begin() ; it != m_filesToBeDeleted.end(); + ++it) + { + DDebug() << "Removing: " << (*it).first << " in " + << (*it).second << endl; + db->deleteItem( (*it).second, (*it).first ); + } + db->commitTransaction(); + } +} + +void ScanLib::timing(const TQString& text, struct timeval tv1, struct timeval tv2) +{ + DDebug() << "ScanLib: " + << text + ": " + << (((tv2.tv_sec-tv1.tv_sec)*1000000 + + (tv2.tv_usec-tv1.tv_usec))/1000) + << " ms" << endl; +} + +} // namespace Digikam |