////////////////////////////////////////////////////////////////////////////// // // FINDDUPPLICATEIMAGES.CPP // // Copyright (C) 2001 Richard Groult (from ShowImg project) // Copyright (C) 2004 Gilles Caulier // Copyright (C) 2004 Richard Groult // // 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 of the License, 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. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Cambridge, MA 02110-1301, USA. // ////////////////////////////////////////////////////////////////////////////// // Include files for C ansi extern "C" { #include #include } // Include files for TQt #include #include #include #include #include #include // Include files for KDE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // Local include files #include "finddupplicateimages.h" #include "finddupplicatedialog.h" #include "displaycompare.h" #include "actions.h" #include #include "imagesimilaritydata.h" #include "fuzzycompare.h" #include "fastcompare.h" namespace KIPIFindDupplicateImagesPlugin { //////////////////////////////////// CONSTRUCTOR //////////////////////////////////////////// FindDuplicateImages::FindDuplicateImages( KIPI::Interface* interface, TQObject *parent) : TQObject(parent), TQThread(), m_interface( interface ), m_cacheDir(TDEGlobal::dirs()->saveLocation("cache", "kipi-findduplicate/")), m_compareOp( 0 ) { KImageIO::registerFormats(); parent_ = parent; } //////////////////////////////////// DESTRUCTOR ///////////////////////////////////////////// FindDuplicateImages::~FindDuplicateImages() { delete m_findDuplicateDialog; wait(); } ///////////////////////////////////////////////////////////////////////////////////////////////////// void FindDuplicateImages::writeSettings(void) { config = new TDEConfig("kipirc"); config->setGroup("FindDuplicateImages Settings"); // Method dialogbox setup tab config->writeEntry("FindMethod", m_findDuplicateDialog->getFindMethod()); config->writeEntry("ApproximateThreeshold", m_findDuplicateDialog->getApproximateThreeshold()); config->sync(); delete config; } ///////////////////////////////////////////////////////////////////////////////////////////// void FindDuplicateImages::readSettings(void) { config = new TDEConfig("kipirc"); config->setGroup("FindDuplicateImages Settings"); // Method dialogbox setup tab m_findDuplicateDialog->setFindMethod( config->readNumEntry("FindMethod", FindDuplicateDialog::MethodAlmost ) ); m_findDuplicateDialog->setApproximateThreeshold( config->readNumEntry("ApproximateThreeshold", 88) ); delete config; // Get the image files filters from the hosts app. m_imagesFileFilter = m_interface->fileExtensions(); } ///////////////////////////////////////////////////////////////////////////////////////////// bool FindDuplicateImages::execDialog() { tqApp->setOverrideCursor( TQCursor(TQt::WaitCursor) ); m_findDuplicateDialog = new FindDuplicateDialog( m_interface, kapp->activeWindow() ); tqApp->restoreOverrideCursor(); readSettings(); connect( m_findDuplicateDialog, TQ_SIGNAL(updateCache(TQStringList)), this, TQ_SLOT(slotUpdateCache(TQStringList)) ); connect( m_findDuplicateDialog, TQ_SIGNAL(clearCache(TQStringList)), this, TQ_SLOT(slotClearCache(TQStringList)) ); connect( m_findDuplicateDialog, TQ_SIGNAL(clearAllCache()), this, TQ_SLOT(slotClearAllCache()) ); if ( m_findDuplicateDialog->exec() == TQDialog::Accepted ) { // This is the value for approximate comparison level between 2 images. m_approximateLevel = (float) m_findDuplicateDialog->getApproximateThreeshold() / (float)100; writeSettings(); return true; } return false; } ///////////////////////////////////////////////////////////////////////////////////////////// void FindDuplicateImages::showResult() { if( !m_res.isEmpty() ) DisplayCompare((TQWidget *)(kapp->activeWindow()), m_interface, m_res).exec(); else KMessageBox::information(kapp->activeWindow(), i18n("No identical files found")); } ///////////////////////////////////////////////////////////////////////////////////////////// void FindDuplicateImages::compareAlbums(void) { tqApp->setOverrideCursor( TQCursor(TQt::WaitCursor) ); writeSettings(); // Prepare the data for the threaded operations. TQValueList ListAlbums(m_findDuplicateDialog->getSelectedAlbums()); filesList.clear(); for( TQValueList::Iterator it = ListAlbums.begin(); it != ListAlbums.end(); ++it ) { KURL::List Files = (*it).images(); for( KURL::List::Iterator it2 = Files.begin(); it2 != Files.end(); ++it2 ) { if ( !filesList.contains( (*it2).path() ) ) { filesList.append( (*it2).path() ); // PENDING(blackie) handle remote URLS } } kapp->processEvents(); } if ( m_findDuplicateDialog->getFindMethod() == FindDuplicateDialog::MethodAlmost ) { FuzzyCompare *op = new FuzzyCompare( parent_, m_cacheDir ); op->setApproximateThreeshold( m_approximateLevel ); m_compareOp = op; } else m_compareOp = new FastCompare( parent_ ); start(); // Starting the thread. tqApp->restoreOverrideCursor(); } ///////////////////////////////////////////////////////////////////////////////////////////// // List of threaded operations. void FindDuplicateImages::run() { m_res = m_compareOp->compare(filesList ); sendMessage( parent_, KIPIFindDupplicateImagesPlugin::Progress, TQString(), 0, false, true ); } ///////////////////////////////////////////////////////////////////////////////////////////// // Nota: original source code from ShowImg ! void FindDuplicateImages::slotClearCache(TQStringList fromDirs) { bool delOk = true; for ( TQStringList::Iterator it = fromDirs.begin(); it != fromDirs.end(); ++it ) { TQString deleteImage = m_cacheDir + *it ; if ( DeleteDir(deleteImage) == false ) delOk = false; } if ( delOk == true ) KMessageBox::information(m_findDuplicateDialog, i18n("Selected Albums cache purged successfully!")); else KMessageBox::error(m_findDuplicateDialog, i18n("Cannot purge selected Albums cache!")); } ///////////////////////////////////////////////////////////////////////////////////////////// void FindDuplicateImages::slotClearAllCache(void) { bool delOk = DeleteDir(m_cacheDir); if ( delOk == true ) KMessageBox::information(m_findDuplicateDialog, i18n("All cache purged successfully!")); else KMessageBox::error(m_findDuplicateDialog, i18n("Cannot purge all cache!")); } ///////////////////////////////////////////////////////////////////////////////////////////// // Nota: original source code from ShowImg ! void FindDuplicateImages::slotUpdateCache(TQStringList fromDirs) { pdCache = new TQProgressDialog (m_findDuplicateDialog, "tmppb", true); pdCache->setLabelText(i18n("Updating in progress...")); pdCache->setTotalSteps(2); pdCache->show(); pdCache->setProgress(2); for ( TQStringList::Iterator it = fromDirs.begin(); it != fromDirs.end(); ++it ) updateCache(*it); pdCache->close(); delete(pdCache); KMessageBox::information(m_findDuplicateDialog, i18n("Selected Albums cache updated successfully!")); } ///////////////////////////////////////////////////////////////////////////////////////////// // Nota: original source code from ShowImg ! void FindDuplicateImages::updateCache(TQString fromDir) { // PENDING(blackie) this method doesn't seem to work. kdDebug( 51000 ) << fromDir.ascii() << endl; pdCache->setLabelText(i18n("Updating in progress for:\n") + fromDir); TQDir d(m_cacheDir + fromDir); int len = m_cacheDir.length()-1; // Remove trailing / bool delDir = false; kdDebug( 51000 ) << m_cacheDir + fromDir.latin1() << endl; if ( !TQFileInfo(fromDir).exists() ) delDir = true; // If the source folder have been removed, remove also the cache... d.setFilter( TQDir::All | TQDir::Hidden | TQDir::NoSymLinks ); const TQFileInfoList *list = d.entryInfoList(); if ( !list ) return; TQFileInfoListIterator it( *list ); TQFileInfo *fi; while ( (fi = it.current()) != 0 ) { kapp->processEvents(); TQString fCache=fi->absFilePath(); TQString orgFile=fCache.right(fCache.length()-len); if ( fi->isDir() && !fromDir.startsWith(orgFile) ) { updateCache(orgFile); } else { if ( !TQFileInfo(orgFile).exists() && TQFileInfo(orgFile).extension(false) != "dat" ) { TQDir().remove(fCache); TQDir().remove(fCache + ".dat"); } } ++it; } if (delDir) TQDir().rmdir(m_cacheDir + fromDir); } ///////////////////////////////////////////////////////////////////////////////////////////// // Nota: original source code from ShowImg ! float FindDuplicateImages::image_sim_compare(ImageSimilarityData *a, ImageSimilarityData *b) { float sim; int i; if ( !a || !b || !a->filled || !b->filled ) return 0.0; sim = 0.0; for( i = 0; i < PAS*PAS; i++ ) { sim += (float)abs(a->avg_r[i] - b->avg_r[i]) / 255.0; sim += (float)abs(a->avg_g[i] - b->avg_g[i]) / 255.0; sim += (float)abs(a->avg_b[i] - b->avg_b[i]) / 255.0; } sim /= (1024.0 * 3.0); return 1.0 - sim; } ///////////////////////////////////////////////////////////////////////////////////////////////////// bool FindDuplicateImages::DeleteDir(TQString dirname) { if ( !dirname.isEmpty() ) { TQDir dir; if (dir.exists ( dirname ) == true) { if (deldir(dirname) == false) return false; if (dir.rmdir( dirname ) == false ) return false; } else return false; } else return false; return true; } ///////////////////////////////////////////////////////////////////////////////////////////////////// bool FindDuplicateImages::deldir(TQString dirname) { TQDir *dir = new TQDir(dirname); dir->setFilter ( TQDir::Dirs | TQDir::Files | TQDir::NoSymLinks ); const TQFileInfoList* fileinfolist = dir->entryInfoList(); TQFileInfoListIterator it(*fileinfolist); TQFileInfo* fi; while( (fi = it.current() ) ) { if(fi->fileName() == "." || fi->fileName() == ".." ) { ++it; continue; } if( fi->isDir() ) { if (deldir( fi->absFilePath() ) == false) return false; if (dir->rmdir( fi->absFilePath() ) == false) return false; } else if( fi->isFile() ) if (dir->remove(fi->absFilePath() ) == false) return false; kapp->processEvents(); ++it; } return true; } } // NameSpace KIPIFindDupplicateImagesPlugin void KIPIFindDupplicateImagesPlugin::FindDuplicateImages::stopPlease() { if ( m_compareOp ) m_compareOp->stopPlease(); } #include "finddupplicateimages.moc"