00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026 #include "albummanager.moc"
00027
00028
00029
00030 extern "C"
00031 {
00032 #include <sys/types.h>
00033 #include <sys/stat.h>
00034 #include <unistd.h>
00035 }
00036
00037
00038
00039 #include <clocale>
00040 #include <cstdlib>
00041 #include <cstdio>
00042 #include <cerrno>
00043
00044
00045
00046 #include <QApplication>
00047 #include <QByteArray>
00048 #include <QComboBox>
00049 #include <QDBusConnection>
00050 #include <QDBusInterface>
00051 #include <QDBusReply>
00052 #include <QDir>
00053 #include <QFile>
00054 #include <QGroupBox>
00055 #include <QHash>
00056 #include <QList>
00057 #include <QLabel>
00058 #include <QMultiHash>
00059 #include <QRadioButton>
00060 #include <QTextCodec>
00061 #include <QTimer>
00062
00063
00064
00065 #include <kconfig.h>
00066 #include <klocale.h>
00067 #include <kdeversion.h>
00068 #include <kmessagebox.h>
00069 #include <kstandarddirs.h>
00070 #include <kio/netaccess.h>
00071 #include <kio/global.h>
00072 #include <kio/job.h>
00073 #include <kdirwatch.h>
00074 #include <kconfiggroup.h>
00075 #include <kdebug.h>
00076
00077
00078
00079 #include "album.h"
00080 #include "albumdb.h"
00081 #include "albumsettings.h"
00082 #include "collectionmanager.h"
00083 #include "collectionlocation.h"
00084 #include "databaseaccess.h"
00085 #include "databaseurl.h"
00086 #include "databaseparameters.h"
00087 #include "databasethumbnailinfoprovider.h"
00088 #include "databasewatch.h"
00089 #include "dio.h"
00090 #include "imagelister.h"
00091 #include "scancontroller.h"
00092 #include "setup.h"
00093 #include "thumbnailloadthread.h"
00094 #include "upgradedb_sqlite2tosqlite3.h"
00095 #include "config-digikam.h"
00096
00097 namespace Digikam
00098 {
00099
00100 class PAlbumPath
00101 {
00102 public:
00103
00104 PAlbumPath()
00105 : albumRootId(-1)
00106 {
00107 }
00108
00109 PAlbumPath(int albumRootId, const QString& albumPath)
00110 : albumRootId(albumRootId), albumPath(albumPath)
00111 {
00112 }
00113
00114 PAlbumPath(PAlbum *album)
00115 {
00116 if (album->isRoot())
00117 {
00118 albumRootId = -1;
00119 }
00120 else
00121 {
00122 albumRootId = album->albumRootId();
00123 albumPath = album->albumPath();
00124 }
00125 }
00126
00127 bool operator==(const PAlbumPath& other) const
00128 {
00129 return other.albumRootId == albumRootId && other.albumPath == albumPath;
00130 }
00131
00132 int albumRootId;
00133 QString albumPath;
00134 };
00135
00136 uint qHash(const PAlbumPath& id)
00137 {
00138 return ::qHash(id.albumRootId) ^ ::qHash(id.albumPath);
00139 }
00140
00141 class AlbumManagerPriv
00142 {
00143
00144 public:
00145
00146 AlbumManagerPriv()
00147 {
00148 changed = false;
00149 hasPriorizedDbPath = false;
00150 dateListJob = 0;
00151 albumListJob = 0;
00152 tagListJob = 0;
00153 dirWatch = 0;
00154 rootPAlbum = 0;
00155 rootTAlbum = 0;
00156 rootDAlbum = 0;
00157 rootSAlbum = 0;
00158 currentAlbum = 0;
00159 changingDB = false;
00160 scanPAlbumsTimer = 0;
00161 scanTAlbumsTimer = 0;
00162 scanSAlbumsTimer = 0;
00163 scanDAlbumsTimer = 0;
00164 updatePAlbumsTimer = 0;
00165 albumItemCountTimer= 0;
00166 tagItemCountTimer = 0;
00167 }
00168
00169 bool changed;
00170 bool hasPriorizedDbPath;
00171
00172 QString dbPath;
00173
00174 QList<QDateTime> dbPathModificationDateList;
00175 QList<QString> dirWatchBlackList;
00176
00177 KIO::TransferJob* albumListJob;
00178 KIO::TransferJob* dateListJob;
00179 KIO::TransferJob* tagListJob;
00180
00181 KDirWatch* dirWatch;
00182
00183 PAlbum* rootPAlbum;
00184 TAlbum* rootTAlbum;
00185 DAlbum* rootDAlbum;
00186 SAlbum* rootSAlbum;
00187
00188 QHash<int,Album *> allAlbumsIdHash;
00189 QHash<PAlbumPath, PAlbum*> albumPathHash;
00190 QHash<int, PAlbum*> albumRootAlbumHash;
00191
00192 QMultiHash<Album*, Album**> guardedPointers;
00193
00194 Album* currentAlbum;
00195
00196 bool changingDB;
00197 QTimer* scanPAlbumsTimer;
00198 QTimer* scanTAlbumsTimer;
00199 QTimer* scanSAlbumsTimer;
00200 QTimer* scanDAlbumsTimer;
00201 QTimer* updatePAlbumsTimer;
00202 QTimer* albumItemCountTimer;
00203 QTimer* tagItemCountTimer;
00204 QSet<int> changedPAlbums;
00205
00206
00207 QList<QDateTime> buildDirectoryModList(const QFileInfo& dbFile)
00208 {
00209
00210 QList<QDateTime> modList;
00211 QFileInfoList fileInfoList = dbFile.dir().entryInfoList(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot);
00212
00213
00214 foreach (const QFileInfo& info, fileInfoList)
00215 {
00216
00217 if (!dirWatchBlackList.contains(info.fileName()))
00218 {
00219 modList << info.lastModified();
00220 }
00221 }
00222 return modList;
00223 }
00224
00225 QString labelForAlbumRootAlbum(const CollectionLocation& location)
00226 {
00227 QString label = location.label();
00228 if (label.isEmpty())
00229 label = location.albumRootPath();
00230 return label;
00231 }
00232 };
00233
00234 class ChangingDB
00235 {
00236 public:
00237
00238 ChangingDB(AlbumManagerPriv *d)
00239 : d(d)
00240 {
00241 d->changingDB = true;
00242 }
00243 ~ChangingDB()
00244 {
00245 d->changingDB = false;
00246 }
00247 AlbumManagerPriv* const d;
00248 };
00249
00250 class AlbumManagerCreator { public: AlbumManager object; };
00251 K_GLOBAL_STATIC(AlbumManagerCreator, creator)
00252
00253
00254 AlbumManager *AlbumManager::internalInstance = 0;
00255
00256 AlbumManager* AlbumManager::instance()
00257 {
00258 return &creator->object;
00259 }
00260
00261 AlbumManager::AlbumManager()
00262 : d(new AlbumManagerPriv)
00263 {
00264 internalInstance = this;
00265
00266
00267 d->scanPAlbumsTimer = new QTimer(this);
00268 d->scanPAlbumsTimer->setInterval(50);
00269 d->scanPAlbumsTimer->setSingleShot(true);
00270 connect(d->scanPAlbumsTimer, SIGNAL(timeout()), this, SLOT(scanPAlbums()));
00271
00272 d->scanTAlbumsTimer = new QTimer(this);
00273 d->scanTAlbumsTimer->setInterval(50);
00274 d->scanTAlbumsTimer->setSingleShot(true);
00275 connect(d->scanTAlbumsTimer, SIGNAL(timeout()), this, SLOT(scanTAlbums()));
00276
00277 d->scanSAlbumsTimer = new QTimer(this);
00278 d->scanSAlbumsTimer->setInterval(50);
00279 d->scanSAlbumsTimer->setSingleShot(true);
00280 connect(d->scanSAlbumsTimer, SIGNAL(timeout()), this, SLOT(scanSAlbums()));
00281
00282 d->updatePAlbumsTimer = new QTimer(this);
00283 d->updatePAlbumsTimer->setInterval(50);
00284 d->updatePAlbumsTimer->setSingleShot(true);
00285 connect(d->updatePAlbumsTimer, SIGNAL(timeout()), this, SLOT(updateChangedPAlbums()));
00286
00287
00288 d->scanDAlbumsTimer = new QTimer(this);
00289 d->scanDAlbumsTimer->setInterval(5000);
00290 d->scanDAlbumsTimer->setSingleShot(true);
00291 connect(d->scanDAlbumsTimer, SIGNAL(timeout()), this, SLOT(scanDAlbums()));
00292
00293
00294 d->albumItemCountTimer = new QTimer(this);
00295 d->albumItemCountTimer->setInterval(1000);
00296 d->albumItemCountTimer->setSingleShot(true);
00297 connect(d->albumItemCountTimer, SIGNAL(timeout()), this, SLOT(getAlbumItemsCount()));
00298
00299
00300 d->tagItemCountTimer = new QTimer(this);
00301 d->tagItemCountTimer->setInterval(2500);
00302 d->tagItemCountTimer->setSingleShot(true);
00303 connect(d->tagItemCountTimer, SIGNAL(timeout()), this, SLOT(getTagItemsCount()));
00304 }
00305
00306 AlbumManager::~AlbumManager()
00307 {
00308 delete d->rootPAlbum;
00309 delete d->rootTAlbum;
00310 delete d->rootDAlbum;
00311 delete d->rootSAlbum;
00312
00313 internalInstance = 0;
00314 delete d;
00315 }
00316
00317 void AlbumManager::cleanUp()
00318 {
00319
00320
00321 if (d->dateListJob)
00322 {
00323 d->dateListJob->kill();
00324 d->dateListJob = 0;
00325 }
00326
00327 if (d->albumListJob)
00328 {
00329 d->albumListJob->kill();
00330 d->albumListJob = 0;
00331 }
00332
00333 if (d->tagListJob)
00334 {
00335 d->tagListJob->kill();
00336 d->tagListJob = 0;
00337 }
00338
00339 delete d->dirWatch;
00340 d->dirWatch = 0;
00341 }
00342
00343 bool AlbumManager::databaseEqual(const QString& dbPath) const
00344 {
00345 return d->dbPath == dbPath;
00346 }
00347
00348 static bool moveToBackup(const QFileInfo& info)
00349 {
00350 if (info.exists())
00351 {
00352 QFileInfo backup(info.dir(), info.fileName() + "-backup-" + QDateTime::currentDateTime().toString(Qt::ISODate));
00353 KIO::Job *job = KIO::file_move(info.filePath(), backup.filePath(), -1, KIO::Overwrite | KIO::HideProgressInfo);
00354 if (!KIO::NetAccess::synchronousRun(job, 0))
00355 {
00356 KMessageBox::error(0, i18n("Failed to backup the existing database file (\"%1\")."
00357 "Refusing to replace file without backup, using the existing file.",
00358 info.filePath()));
00359 return false;
00360 }
00361 }
00362 return true;
00363 }
00364
00365 static bool copyToNewLocation(const QFileInfo& oldFile, const QFileInfo& newFile, const QString otherMessage = QString())
00366 {
00367 QString message = otherMessage;
00368 if (message.isNull())
00369 message = i18n("Failed to copy the old database file (\"%1\") "
00370 "to its new location (\"%2\"). "
00371 "Starting with an empty database.",
00372 oldFile.filePath(), newFile.filePath());
00373
00374 KIO::Job *job = KIO::file_copy(oldFile.filePath(), newFile.filePath(), -1, KIO::Overwrite );
00375 if (!KIO::NetAccess::synchronousRun(job, 0))
00376 {
00377 KMessageBox::error(0, message);
00378 return false;
00379 }
00380 return true;
00381 }
00382
00383 void AlbumManager::checkDatabaseDirsAfterFirstRun(const QString& dbPath, const QString& albumPath)
00384 {
00385
00386 QDir newDir(dbPath);
00387 QDir albumDir(albumPath);
00388 DatabaseParameters newParams = DatabaseParameters::parametersForSQLiteDefaultFile(newDir.path());
00389 QFileInfo digikam4DB(newParams.SQLiteDatabaseFile());
00390
00391 if (!digikam4DB.exists())
00392 {
00393 QFileInfo digikam3DB(newDir, "digikam3.db");
00394 QFileInfo digikamVeryOldDB(newDir, "digikam.db");
00395
00396 if (digikam3DB.exists() || digikamVeryOldDB.exists())
00397 {
00398 KGuiItem startFresh(i18n("Create New Database"), "document-new");
00399 KGuiItem upgrade(i18n("Upgrade Database"), "view-refresh");
00400 int result = KMessageBox::warningYesNo(0,
00401 i18n("<p>You have chosen the folder \"%1\" as the place to store the database. "
00402 "A database file from an older version of digiKam is found in this folder.</p> "
00403 "<p>Would you like to upgrade the old database file - confirming "
00404 "that this database file was indeed created for the pictures located in the folder \"%2\" - "
00405 "or ignore the old file and start with a new database?</p> ",
00406 newDir.path(), albumDir.path()),
00407 i18n("Database Folder"),
00408 upgrade, startFresh);
00409
00410 if (result == KMessageBox::Yes)
00411 {
00412
00413
00414 KSharedConfigPtr config = KGlobal::config();
00415 KConfigGroup group = config->group("Album Settings");
00416 group.writeEntry("Album Path", albumDir.path());
00417 group.sync();
00418 }
00419 else if (result == KMessageBox::No)
00420 {
00421 moveToBackup(digikam3DB);
00422 moveToBackup(digikamVeryOldDB);
00423 }
00424 }
00425 }
00426 }
00427
00428 void AlbumManager::changeDatabase(const QString& dbPath)
00429 {
00430 if (d->dbPath == dbPath)
00431 return;
00432
00433
00434 DatabaseParameters params = DatabaseAccess::parameters();
00435 if (params.isSQLite())
00436 {
00437 QDir oldDir(d->dbPath);
00438 QDir newDir(dbPath);
00439 QFileInfo oldFile(params.SQLiteDatabaseFile());
00440 QFileInfo newFile(newDir, oldFile.fileName());
00441 if (!newFile.exists())
00442 {
00443 QFileInfo digikam3DB(newDir, "digikam3.db");
00444 QFileInfo digikamVeryOldDB(newDir, "digikam.db");
00445
00446 if (digikam3DB.exists() || digikamVeryOldDB.exists())
00447 {
00448 KGuiItem copyCurrent(i18n("Copy Current Database"), "edit-copy");
00449 KGuiItem startFresh(i18n("Create New Database"), "document-new");
00450 KGuiItem upgrade(i18n("Upgrade Database"), "view-refresh");
00451 int result = KMessageBox::warningYesNoCancel(0,
00452 i18n("<p>You have chosen the folder \"%1\" as the new place to store the database. "
00453 "A database file from an older version of digiKam is found in this folder.</p> "
00454 "<p>Would you like to upgrade the old database file, start with a new database, "
00455 "or copy the current database to this location and continue using it?</p> ",
00456 newDir.path()),
00457 i18n("New database folder"),
00458 upgrade, startFresh, copyCurrent);
00459
00460 if (result == KMessageBox::Yes)
00461 {
00462
00463
00464 KSharedConfigPtr config = KGlobal::config();
00465 KConfigGroup group = config->group("Album Settings");
00466 group.writeEntry("Album Path", newDir.path());
00467 group.sync();
00468 }
00469 else if (result == KMessageBox::No)
00470 {
00471 moveToBackup(digikam3DB);
00472 moveToBackup(digikamVeryOldDB);
00473 }
00474 else if (result == KMessageBox::Cancel)
00475 {
00476 copyToNewLocation(oldFile, newFile, i18n("Failed to copy the old database file (\"%1\") "
00477 "to its new location (\"%2\"). "
00478 "Trying to upgrade old databases.",
00479 oldFile.filePath(), newFile.filePath()));
00480 }
00481 }
00482 else
00483 {
00484
00485 KGuiItem copyCurrent(i18n("Copy Current Database"), "edit-copy");
00486 KGuiItem startFresh(i18n("Create New Database"), "document-new");
00487 int result = KMessageBox::warningYesNo(0,
00488 i18n("<p>You have chosen the folder \"%1\" as the new place to store the database.</p>"
00489 "<p>Would you like to copy the current database to this location "
00490 "and continue using it, or start with a new database?</p> ",
00491 newDir.path()),
00492 i18n("New database folder"),
00493 startFresh, copyCurrent);
00494
00495 if (result == KMessageBox::No)
00496 {
00497 copyToNewLocation(oldFile, newFile);
00498 }
00499 }
00500 }
00501 else
00502 {
00503 KGuiItem replaceItem(i18n("Copy Current Database"), "edit-copy");
00504 KGuiItem useExistingItem(i18n("Use Existing File"), "document-open");
00505 int result = KMessageBox::warningYesNo(0,
00506 i18n("<p>You have chosen the folder \"%1\" as the new place to store the database. "
00507 "There is already a database file in this location.</p> "
00508 "<p>Would you like to use this existing file as the new database, or remove it "
00509 "and copy the current database to this place?</p> ",
00510 newDir.path()),
00511 i18n("New database folder"),
00512 replaceItem, useExistingItem);
00513 if (result == KMessageBox::Yes)
00514 {
00515
00516 if (moveToBackup(newFile))
00517 {
00518
00519 copyToNewLocation(oldFile, newFile);
00520 }
00521 }
00522 }
00523 }
00524
00525 if (setDatabase(dbPath, false))
00526 {
00527 QApplication::setOverrideCursor(Qt::WaitCursor);
00528 startScan();
00529 QApplication::restoreOverrideCursor();
00530 ScanController::instance()->completeCollectionScan();
00531 }
00532 }
00533
00534 bool AlbumManager::setDatabase(const QString& dbPath, bool priority, const QString& suggestedAlbumRoot)
00535 {
00536 if (dbPath.isEmpty())
00537 return false;
00538
00539
00540
00541 if (priority)
00542 {
00543 d->hasPriorizedDbPath = true;
00544 }
00545 else if (d->hasPriorizedDbPath && !d->dbPath.isNull())
00546 {
00547
00548
00549 return true;
00550 }
00551
00552 if (d->dbPath == dbPath)
00553 return true;
00554
00555
00556 ScanController::instance()->cancelAllAndSuspendCollectionScan();
00557 QApplication::setOverrideCursor(Qt::WaitCursor);
00558
00559 d->dbPath = dbPath;
00560 d->changed = true;
00561
00562 disconnect(CollectionManager::instance(), 0, this, 0);
00563 if (DatabaseAccess::databaseWatch())
00564 disconnect(DatabaseAccess::databaseWatch(), 0, this, 0);
00565 d->dbPathModificationDateList.clear();
00566
00567 if (d->dateListJob)
00568 {
00569 d->dateListJob->kill();
00570 d->dateListJob = 0;
00571 }
00572
00573 if (d->albumListJob)
00574 {
00575 d->albumListJob->kill();
00576 d->albumListJob = 0;
00577 }
00578
00579 if (d->tagListJob)
00580 {
00581 d->tagListJob->kill();
00582 d->tagListJob = 0;
00583 }
00584
00585 delete d->dirWatch;
00586 d->dirWatch = 0;
00587
00588 QDBusConnection::sessionBus().disconnect(QString(), QString(), "org.kde.KDirNotify", "FileMoved", 0, 0);
00589 QDBusConnection::sessionBus().disconnect(QString(), QString(), "org.kde.KDirNotify", "FilesAdded", 0, 0);
00590 QDBusConnection::sessionBus().disconnect(QString(), QString(), "org.kde.KDirNotify", "FilesRemoved", 0, 0);
00591
00592 d->currentAlbum = 0;
00593 emit signalAlbumCurrentChanged(0);
00594 emit signalAlbumsCleared();
00595
00596 d->albumPathHash.clear();
00597 d->allAlbumsIdHash.clear();
00598 d->albumRootAlbumHash.clear();
00599
00600
00601 delete d->rootPAlbum;
00602 delete d->rootTAlbum;
00603 delete d->rootDAlbum;
00604 delete d->rootSAlbum;
00605
00606 d->rootPAlbum = 0;
00607 d->rootTAlbum = 0;
00608 d->rootDAlbum = 0;
00609 d->rootSAlbum = 0;
00610
00611
00612
00613 DatabaseAccess::setParameters(DatabaseParameters::parametersForSQLiteDefaultFile(d->dbPath),
00614 DatabaseAccess::MainApplication);
00615
00616
00617 ScanController::instance()->resumeCollectionScan();
00618
00619 ScanController::Advice advice = ScanController::instance()->databaseInitialization();
00620
00621 QApplication::restoreOverrideCursor();
00622
00623 switch (advice)
00624 {
00625 case ScanController::Success:
00626 break;
00627 case ScanController::ContinueWithoutDatabase:
00628 {
00629 QString errorMsg = DatabaseAccess().lastError();
00630 if (errorMsg.isEmpty())
00631 {
00632 KMessageBox::error(0, i18n("<p>Failed to open the database. "
00633 "</p><p>You cannot use digiKam without a working database. "
00634 "digiKam will attempt to start now, but it will <b>not</b> be functional. "
00635 "Please check the database settings in the <b>configuration menu</b>.</p>"
00636 ));
00637 }
00638 else
00639 {
00640 KMessageBox::error(0, i18n("<p>Failed to open the database. Error message from database:</p>"
00641 "<p><b>%1</b></p>"
00642 "</p><p>You cannot use digiKam without a working database. "
00643 "digiKam will attempt to start now, but it will <b>not</b> be functional. "
00644 "Please check the database settings in the <b>configuration menu</b>.</p>",
00645 errorMsg));
00646 }
00647 return true;
00648 }
00649 case ScanController::AbortImmediately:
00650 return false;
00651 }
00652
00653
00654
00655 QString currLocale(QTextCodec::codecForLocale()->name());
00656 QString dbLocale = DatabaseAccess().db()->getSetting("Locale");
00657
00658
00659 bool localeChanged = true;
00660
00661 if (dbLocale.isNull())
00662 {
00663 kDebug() << "No locale found in database";
00664
00665
00666
00667 KSharedConfig::Ptr config = KGlobal::config();
00668 KConfigGroup group = config->group("General Settings");
00669 if (group.hasKey("Locale"))
00670 {
00671 kDebug() << "Locale found in configfile";
00672 dbLocale = group.readEntry("Locale", QString());
00673
00674
00675
00676
00677
00678 QString oldConfigLocale = ::setlocale(0, 0);
00679
00680 if (oldConfigLocale == dbLocale)
00681 {
00682 dbLocale = currLocale;
00683 localeChanged = false;
00684 DatabaseAccess().db()->setSetting("Locale", dbLocale);
00685 }
00686 }
00687 else
00688 {
00689 kDebug() << "No locale found in config file";
00690 dbLocale = currLocale;
00691
00692 localeChanged = false;
00693 DatabaseAccess().db()->setSetting("Locale",dbLocale);
00694 }
00695 }
00696 else
00697 {
00698 if (dbLocale == currLocale)
00699 localeChanged = false;
00700 }
00701
00702 if (localeChanged)
00703 {
00704
00705
00706 int result =
00707 KMessageBox::warningYesNo(0,
00708 i18n("Your locale has changed since this "
00709 "album was last opened.\n"
00710 "Old Locale : %1, New Locale : %2\n"
00711 "If you have recently changed your locale, you need not be concerned.\n"
00712 "Please note that if you switched to a locale "
00713 "that does not support some of the filenames in your collection, "
00714 "these files may no longer be found in the collection. "
00715 "If you are sure that you want to "
00716 "continue, click 'Yes'. "
00717 "Otherwise, click 'No' and correct your "
00718 "locale setting before restarting digiKam.",
00719 dbLocale, currLocale));
00720 if (result != KMessageBox::Yes)
00721 exit(0);
00722
00723 DatabaseAccess().db()->setSetting("Locale",currLocale);
00724 }
00725
00726
00727
00728 QList<CollectionLocation> disappearedLocations = CollectionManager::instance()->checkHardWiredLocations();
00729 foreach (const CollectionLocation &loc, disappearedLocations)
00730 {
00731 QString locDescription;
00732 QStringList candidateIds, candidateDescriptions;
00733 CollectionManager::instance()->migrationCandidates(loc, &locDescription, &candidateIds, &candidateDescriptions);
00734 kDebug() << "Migration candidates for" << locDescription << ":" << candidateIds << candidateDescriptions;
00735
00736 KDialog *dialog = new KDialog;
00737
00738 QWidget *widget = new QWidget;
00739 QGridLayout *mainLayout = new QGridLayout;
00740 mainLayout->setColumnStretch(1, 1);
00741
00742 QLabel *deviceIconLabel = new QLabel;
00743 deviceIconLabel->setPixmap(KIconLoader::global()->loadIcon("drive-harddisk", KIconLoader::NoGroup, KIconLoader::SizeHuge));
00744 mainLayout->addWidget(deviceIconLabel, 0, 0);
00745
00746 QLabel *mainLabel = new QLabel(
00747 i18n("<p>The collection </p><p><b>%1</b><br/>(%2)</p><p> is currently not found on your system.<br/> "
00748 "Please choose the most appropriate option to handle this situation:</p>",
00749 loc.label(), locDescription));
00750 mainLabel->setWordWrap(true);
00751 mainLayout->addWidget(mainLabel, 0, 1);
00752
00753 QGroupBox *groupBox = new QGroupBox;
00754 mainLayout->addWidget(groupBox, 1, 0, 1, 2);
00755
00756 QGridLayout *layout = new QGridLayout;
00757 layout->setColumnStretch(1, 1);
00758
00759 QRadioButton *migrateButton = 0;
00760 QComboBox *migrateChoices = 0;
00761 if (!candidateIds.isEmpty())
00762 {
00763 migrateButton = new QRadioButton;
00764 QLabel *migrateLabel = new QLabel(
00765 i18n("<p>The collection is still available, but the identifier changed.<br/>"
00766 "This can be caused by restoring a backup, changing the partition layout "
00767 "or the file system settings.<br/>"
00768 "The collection is now located at this place:</p>"));
00769 migrateLabel->setWordWrap(true);
00770
00771 migrateChoices = new QComboBox;
00772 for (int i=0; i<candidateIds.size(); ++i)
00773 migrateChoices->addItem(candidateDescriptions[i], candidateIds[i]);
00774
00775 layout->addWidget(migrateButton, 0, 0, Qt::AlignTop);
00776 layout->addWidget(migrateLabel, 0, 1);
00777 layout->addWidget(migrateChoices, 1, 1);
00778 }
00779
00780 QRadioButton *isRemovableButton = new QRadioButton;
00781 QLabel *isRemovableLabel = new QLabel(
00782 i18n("The collection is located on a storage device which is not always attached. "
00783 "Mark the collection as a removable collection."));
00784 isRemovableLabel->setWordWrap(true);
00785 layout->addWidget(isRemovableButton, 2, 0, Qt::AlignTop);
00786 layout->addWidget(isRemovableLabel, 2, 1);
00787
00788 QRadioButton *solveManuallyButton = new QRadioButton;
00789 QLabel *solveManuallyLabel = new QLabel(
00790 i18n("Take no action now. I would like to solve the problem "
00791 "later using the setup dialog"));
00792 solveManuallyLabel->setWordWrap(true);
00793 layout->addWidget(solveManuallyButton, 3, 0, Qt::AlignTop);
00794 layout->addWidget(solveManuallyLabel, 3, 1);
00795
00796 groupBox->setLayout(layout);
00797
00798 widget->setLayout(mainLayout);
00799 dialog->setCaption(i18n("Collection not found"));
00800 dialog->setMainWidget(widget);
00801 dialog->setButtons(KDialog::Ok);
00802
00803
00804
00805 if (migrateButton && candidateIds.size() == 1)
00806 migrateButton->setChecked(true);
00807 else
00808 solveManuallyButton->setChecked(true);
00809
00810 if (dialog->exec())
00811 {
00812 if (migrateButton && migrateButton->isChecked())
00813 {
00814 CollectionManager::instance()->migrateToVolume(loc, migrateChoices->itemData(migrateChoices->currentIndex()).toString());
00815 }
00816 else if (isRemovableButton->isChecked())
00817 {
00818 CollectionManager::instance()->changeType(loc, CollectionLocation::TypeVolumeRemovable);
00819 }
00820 }
00821
00822 delete dialog;
00823 }
00824
00825
00826
00827
00828 d->dirWatchBlackList.clear();
00829 DatabaseParameters params = DatabaseAccess::parameters();
00830 if (params.isSQLite())
00831 {
00832 QFileInfo dbFile(params.SQLiteDatabaseFile());
00833 d->dbPathModificationDateList = d->buildDirectoryModList(dbFile);
00834 d->dirWatchBlackList << dbFile.fileName() << dbFile.fileName() + "-journal";
00835 }
00836
00837
00838 if (CollectionManager::instance()->allLocations().isEmpty())
00839 {
00840 if (suggestedAlbumRoot.isEmpty())
00841 Setup::execSinglePage(Setup::CollectionsPage);
00842 else
00843 {
00844 CollectionManager::instance()->addLocation(suggestedAlbumRoot);
00845
00846
00847 }
00848 }
00849
00850
00851
00852 #ifdef USE_THUMBS_DB
00853 QApplication::setOverrideCursor(Qt::WaitCursor);
00854
00855
00856 QFileInfo thumbFile(d->dbPath, "thumbnails-digikam.db");
00857 ThumbnailLoadThread::initializeThumbnailDatabase(thumbFile.filePath(), new DatabaseThumbnailInfoProvider());
00858 d->dirWatchBlackList << "thumbnails-digikam.db" << "thumbnails-digikam.db-journal";
00859
00860 QApplication::restoreOverrideCursor();
00861 #endif
00862
00863
00864
00865 #ifdef HAVE_NEPOMUK
00866 if (checkNepomukService())
00867 {
00868 QDBusInterface serviceInterface("org.kde.nepomuk.services.digikamnepomukservice",
00869 "/digikamnepomukservice", "org.kde.digikam.DigikamNepomukService");
00870 kDebug() << "nepomuk service available" << serviceInterface.isValid();
00871 if (serviceInterface.isValid())
00872 {
00873 DatabaseParameters parameters = DatabaseAccess::parameters();
00874 KUrl url;
00875 parameters.insertInUrl(url);
00876 serviceInterface.call(QDBus::NoBlock, "setDatabase", url.url());
00877 }
00878 }
00879 #endif // HAVE_NEPOMUK
00880
00881 return true;
00882 }
00883
00884 bool AlbumManager::checkNepomukService()
00885 {
00886 bool hasNepomuk = false;
00887
00888 #ifdef HAVE_NEPOMUK
00889 QDBusInterface serviceInterface("org.kde.nepomuk.services.digikamnepomukservice",
00890 "/digikamnepomukservice", "org.kde.digikam.DigikamNepomukService");
00891
00892
00893 if (serviceInterface.isValid())
00894 return true;
00895
00896
00897 QDBusInterface nepomukInterface("org.kde.NepomukServer",
00898 "/servicemanager", "org.kde.nepomuk.ServiceManager");
00899 if (!nepomukInterface.isValid())
00900 {
00901 kDebug() << "Nepomuk server is not reachable. Cannot start Digikam Nepomuk Service";
00902 return false;
00903 }
00904
00905 QDBusReply<QStringList> availableServicesReply = nepomukInterface.call("availableServices");
00906 if (!availableServicesReply.isValid() || !availableServicesReply.value().contains("digikamnepomukservice"))
00907 {
00908 kDebug() << "digikamnepomukservice is not available in NepomukServer";
00909 return false;
00910 }
00911
00912
00913
00914
00915
00916
00917
00918
00919
00920
00921
00922
00923
00924
00925 kDebug() << "Trying to start up digikamnepomukservice";
00926 nepomukInterface.call(QDBus::NoBlock, "startService", "digikamnepomukservice");
00927
00928
00929
00930
00931
00932 hasNepomuk = true;
00933 #endif // HAVE_NEPOMUK
00934
00935 return hasNepomuk;
00936 }
00937
00938 void AlbumManager::startScan()
00939 {
00940 if (!d->changed)
00941 return;
00942 d->changed = false;
00943
00944
00945 d->dirWatch = new KDirWatch(this);
00946 connect(d->dirWatch, SIGNAL(dirty(const QString&)),
00947 this, SLOT(slotDirWatchDirty(const QString&)));
00948
00949 KDirWatch::Method m = d->dirWatch->internalMethod();
00950 QString mName("FAM");
00951 if (m == KDirWatch::DNotify)
00952 mName = QString("DNotify");
00953 else if (m == KDirWatch::Stat)
00954 mName = QString("Stat");
00955 else if (m == KDirWatch::INotify)
00956 mName = QString("INotify");
00957 kDebug() << "KDirWatch method = " << mName;
00958
00959
00960
00961 QDBusConnection::sessionBus().connect(QString(), QString(), "org.kde.KDirNotify", "FileMoved",
00962 this, SLOT(slotKioFileMoved(const QString&, const QString&)));
00963 QDBusConnection::sessionBus().connect(QString(), QString(), "org.kde.KDirNotify", "FilesAdded",
00964 this, SLOT(slotKioFilesAdded(const QString&)));
00965 QDBusConnection::sessionBus().connect(QString(), QString(), "org.kde.KDirNotify", "FilesRemoved",
00966 this, SLOT(slotKioFilesDeleted(const QStringList&)));
00967
00968
00969 d->rootPAlbum = new PAlbum(i18n("My Albums"));
00970 insertPAlbum(d->rootPAlbum, 0);
00971
00972 d->rootTAlbum = new TAlbum(i18n("My Tags"), 0, true);
00973 insertTAlbum(d->rootTAlbum, 0);
00974
00975 d->rootSAlbum = new SAlbum(i18n("My Searches"), 0, true);
00976
00977 d->rootDAlbum = new DAlbum(QDate(), true);
00978
00979
00980 QList<CollectionLocation> locations = CollectionManager::instance()->allAvailableLocations();
00981 foreach(const CollectionLocation& location, locations)
00982 addAlbumRoot(location);
00983
00984
00985 connect(CollectionManager::instance(), SIGNAL(locationStatusChanged(const CollectionLocation &, int)),
00986 this, SLOT(slotCollectionLocationStatusChanged(const CollectionLocation &, int)));
00987 connect(CollectionManager::instance(), SIGNAL(locationPropertiesChanged(const CollectionLocation &)),
00988 this, SLOT(slotCollectionLocationPropertiesChanged(const CollectionLocation &)));
00989
00990
00991 refresh();
00992
00993
00994 connect(DatabaseAccess::databaseWatch(), SIGNAL(albumChange(const AlbumChangeset &)),
00995 this, SLOT(slotAlbumChange(const AlbumChangeset &)));
00996 connect(DatabaseAccess::databaseWatch(), SIGNAL(tagChange(const TagChangeset &)),
00997 this, SLOT(slotTagChange(const TagChangeset &)));
00998 connect(DatabaseAccess::databaseWatch(), SIGNAL(searchChange(const SearchChangeset &)),
00999 this, SLOT(slotSearchChange(const SearchChangeset &)));
01000
01001 connect(DatabaseAccess::databaseWatch(), SIGNAL(collectionImageChange(const CollectionImageChangeset &)),
01002 this, SLOT(slotCollectionImageChange(const CollectionImageChangeset &)));
01003 connect(DatabaseAccess::databaseWatch(), SIGNAL(imageTagChange(const ImageTagChangeset &)),
01004 this, SLOT(slotImageTagChange(const ImageTagChangeset &)));
01005
01006 emit signalAllAlbumsLoaded();
01007 }
01008
01009 void AlbumManager::slotCollectionLocationStatusChanged(const CollectionLocation& location, int oldStatus)
01010 {
01011
01012 if (!d->rootPAlbum)
01013 return;
01014
01015 if (location.status() == CollectionLocation::LocationAvailable
01016 && oldStatus != CollectionLocation::LocationAvailable)
01017 {
01018 addAlbumRoot(location);
01019
01020 scanPAlbums();
01021 }
01022 else if (oldStatus == CollectionLocation::LocationAvailable
01023 && location.status() != CollectionLocation::LocationAvailable)
01024 {
01025 removeAlbumRoot(location);
01026
01027 scanPAlbums();
01028 }
01029 }
01030
01031 void AlbumManager::slotCollectionLocationPropertiesChanged(const CollectionLocation& location)
01032 {
01033 PAlbum *album = d->albumRootAlbumHash.value(location.id());
01034 if (album)
01035 {
01036 QString newLabel = d->labelForAlbumRootAlbum(location);
01037 kDebug() << newLabel << album->title();
01038 if (album->title() != newLabel)
01039 {
01040 album->setTitle(newLabel);
01041 emit signalAlbumRenamed(album);
01042 }
01043 }
01044 }
01045
01046 void AlbumManager::addAlbumRoot(const CollectionLocation& location)
01047 {
01048 if (!d->dirWatch->contains(location.albumRootPath()))
01049 d->dirWatch->addDir(location.albumRootPath(), KDirWatch::WatchSubDirs);
01050
01051 PAlbum *album = d->albumRootAlbumHash.value(location.id());
01052 if (!album)
01053 {
01054
01055 QString label = d->labelForAlbumRootAlbum(location);
01056 album = new PAlbum(location.id(), label);
01057
01058
01059 d->albumRootAlbumHash.insert(location.id(), album);
01060 }
01061 }
01062
01063 void AlbumManager::removeAlbumRoot(const CollectionLocation& location)
01064 {
01065 d->dirWatch->removeDir(location.albumRootPath());
01066
01067 PAlbum *album = d->albumRootAlbumHash.take(location.id());
01068 if (album)
01069 {
01070
01071 removePAlbum(album);
01072 }
01073 }
01074
01075 void AlbumManager::refresh()
01076 {
01077 scanPAlbums();
01078 scanTAlbums();
01079 scanSAlbums();
01080 scanDAlbums();
01081 }
01082
01083 void AlbumManager::prepareItemCounts()
01084 {
01085
01086
01087 scanDAlbums();
01088 getAlbumItemsCount();
01089 getTagItemsCount();
01090 }
01091
01092 void AlbumManager::scanPAlbums()
01093 {
01094 d->scanPAlbumsTimer->stop();
01095
01096
01097 QHash<int, PAlbum *> oldAlbums;
01098 AlbumIterator it(d->rootPAlbum);
01099 while (it.current())
01100 {
01101 PAlbum* a = (PAlbum*)(*it);
01102 oldAlbums[a->id()] = a;
01103 ++it;
01104 }
01105
01106
01107 QList<AlbumInfo> currentAlbums = DatabaseAccess().db()->scanAlbums();
01108
01109
01110 qSort(currentAlbums);
01111
01112 QList<AlbumInfo> newAlbums;
01113
01114
01115 foreach (const AlbumInfo& info, currentAlbums)
01116 {
01117
01118 if (CollectionManager::instance()->locationForAlbumRootId(info.albumRootId).isAvailable())
01119 {
01120 if (oldAlbums.contains(info.id))
01121 oldAlbums.remove(info.id);
01122 else
01123 newAlbums << info;
01124 }
01125 }
01126
01127
01128
01129
01130
01131
01132
01133
01134
01135 QSet<PAlbum *> topMostOldAlbums;
01136 foreach (PAlbum *album, oldAlbums)
01137 {
01138 if (!album->parent() || !oldAlbums.contains(album->parent()->id()))
01139 topMostOldAlbums << album;
01140 }
01141
01142 foreach(PAlbum *album, topMostOldAlbums)
01143 {
01144
01145
01146
01147 removePAlbum(album);
01148 }
01149
01150
01151 qSort(newAlbums);
01152
01153
01154 foreach (const AlbumInfo& info, newAlbums)
01155 {
01156 if (info.relativePath.isEmpty())
01157 continue;
01158
01159 PAlbum *album, *parent;
01160 if (info.relativePath == "/")
01161 {
01162
01163
01164
01165 parent = d->rootPAlbum;
01166 album = d->albumRootAlbumHash.value(info.albumRootId);
01167
01168 if (!album)
01169 {
01170 kError() << "Did not find album root album in hash";
01171 continue;
01172 }
01173
01174
01175
01176 album->m_id = info.id;
01177 }
01178 else
01179 {
01180
01181 QString name = info.relativePath.section('/', -1, -1);
01182
01183 QString parentPath = info.relativePath.section('/', 0, -2);
01184
01185 if (parentPath.isEmpty())
01186 parent = d->albumRootAlbumHash.value(info.albumRootId);
01187 else
01188 parent = d->albumPathHash.value(PAlbumPath(info.albumRootId, parentPath));
01189
01190 if (!parent)
01191 {
01192 kError() << "Could not find parent with url: "
01193 << parentPath << " for: " << info.relativePath;
01194 continue;
01195 }
01196
01197
01198 album = new PAlbum(info.albumRootId, parentPath, name, info.id);
01199 }
01200
01201 album->m_caption = info.caption;
01202 album->m_category = info.category;
01203 album->m_date = info.date;
01204
01205 if (info.iconAlbumRootId)
01206 {
01207 QString albumRootPath = CollectionManager::instance()->albumRootPath(info.iconAlbumRootId);
01208 if (!albumRootPath.isNull())
01209 album->m_icon = albumRootPath + info.iconRelativePath;
01210 }
01211
01212 insertPAlbum(album, parent);
01213 }
01214
01215 getAlbumItemsCount();
01216 }
01217
01218 void AlbumManager::updateChangedPAlbums()
01219 {
01220 d->updatePAlbumsTimer->stop();
01221
01222
01223 QList<AlbumInfo> currentAlbums = DatabaseAccess().db()->scanAlbums();
01224
01225 bool needScanPAlbums = false;
01226
01227
01228 foreach (int id, d->changedPAlbums)
01229 {
01230 foreach (const AlbumInfo& info, currentAlbums)
01231 {
01232 if (info.id == id)
01233 {
01234 d->changedPAlbums.remove(info.id);
01235
01236 PAlbum *album = findPAlbum(info.id);
01237 if (album)
01238 {
01239
01240 if (info.relativePath != "/")
01241 {
01242
01243
01244 QString name = info.relativePath.section('/', -1, -1);
01245 QString parentPath = info.relativePath;
01246 parentPath.chop(name.length());
01247 if (parentPath != album->m_parentPath || info.albumRootId != album->albumRootId())
01248 {
01249
01250 needScanPAlbums = true;
01251 removePAlbum(album);
01252 break;
01253 }
01254 else if (name != album->title())
01255 {
01256 album->setTitle(name);
01257 updateAlbumPathHash();
01258 emit signalAlbumRenamed(album);
01259 }
01260 }
01261
01262
01263 album->m_caption = info.caption;
01264 album->m_category = info.category;
01265 album->m_date = info.date;
01266
01267
01268 QString icon;
01269 if (info.iconAlbumRootId)
01270 {
01271 QString albumRootPath = CollectionManager::instance()->albumRootPath(info.iconAlbumRootId);
01272 if (!albumRootPath.isNull())
01273 icon = albumRootPath + info.iconRelativePath;
01274 }
01275 if (icon != album->m_icon)
01276 {
01277 album->m_icon = icon;
01278 emit signalAlbumIconChanged(album);
01279 }
01280 }
01281 }
01282 }
01283 }
01284
01285 if (needScanPAlbums)
01286 scanPAlbums();
01287 }
01288
01289 void AlbumManager::getAlbumItemsCount()
01290 {
01291 d->albumItemCountTimer->stop();
01292
01293 if (!AlbumSettings::instance()->getShowFolderTreeViewItemsCount())
01294 return;
01295
01296
01297
01298 if (d->albumListJob)
01299 {
01300 d->albumListJob->kill();
01301 d->albumListJob = 0;
01302 }
01303
01304 DatabaseUrl u = DatabaseUrl::albumUrl();
01305
01306 d->albumListJob = ImageLister::startListJob(u);
01307 d->albumListJob->addMetaData("folders", "true");
01308
01309 connect(d->albumListJob, SIGNAL(result(KJob*)),
01310 this, SLOT(slotAlbumsJobResult(KJob*)));
01311
01312 connect(d->albumListJob, SIGNAL(data(KIO::Job*, const QByteArray&)),
01313 this, SLOT(slotAlbumsJobData(KIO::Job*, const QByteArray&)));
01314 }
01315
01316 void AlbumManager::scanTAlbums()
01317 {
01318 d->scanTAlbumsTimer->stop();
01319
01320
01321
01322 typedef QMap<int, TAlbum*> TagMap;
01323 TagMap tmap;
01324
01325 tmap.insert(0, d->rootTAlbum);
01326
01327 AlbumIterator it(d->rootTAlbum);
01328 while (it.current())
01329 {
01330 TAlbum* t = (TAlbum*)(*it);
01331 tmap.insert(t->id(), t);
01332 ++it;
01333 }
01334
01335
01336 TagInfo::List tList = DatabaseAccess().db()->scanTags();
01337
01338
01339
01340
01341
01342 {
01343 QHash<int, TAlbum*> tagHash;
01344
01345
01346 for (TagInfo::List::const_iterator iter = tList.constBegin(); iter != tList.constEnd(); ++iter)
01347 {
01348 TagInfo info = *iter;
01349 TAlbum* album = new TAlbum(info.name, info.id);
01350 if (info.icon.isNull())
01351 {
01352
01353 QString albumRootPath = CollectionManager::instance()->albumRootPath(info.iconAlbumRootId);
01354 album->m_icon = albumRootPath + info.iconRelativePath;
01355 }
01356 else
01357 {
01358
01359 album->m_icon = info.icon;
01360 }
01361 album->m_pid = info.pid;
01362 tagHash.insert(info.id, album);
01363 }
01364 tList.clear();
01365
01366
01367 TAlbum* rootTag = new TAlbum("root", 0, true);
01368 tagHash.insert(0, rootTag);
01369
01370
01371 for (QHash<int, TAlbum*>::const_iterator iter = tagHash.constBegin();
01372 iter != tagHash.constEnd(); ++iter )
01373 {
01374 TAlbum* album = *iter;
01375 if (album->m_id == 0)
01376 continue;
01377
01378 TAlbum* parent = tagHash.value(album->m_pid);
01379 if (parent)
01380 {
01381 album->setParent(parent);
01382 }
01383 else
01384 {
01385 kWarning() << "Failed to find parent tag for tag "
01386 << album->m_title
01387 << " with pid "
01388 << album->m_pid;
01389 }
01390 }
01391
01392
01393 AlbumIterator it(rootTag);
01394 while (it.current())
01395 {
01396 TagInfo info;
01397 TAlbum* album = (TAlbum*)it.current();
01398 info.id = album->m_id;
01399 info.pid = album->m_pid;
01400 info.name = album->m_title;
01401 info.icon = album->m_icon;
01402 tList.append(info);
01403 ++it;
01404 }
01405
01406
01407 delete rootTag;
01408 }
01409
01410 for (TagInfo::List::const_iterator it = tList.constBegin(); it != tList.constEnd(); ++it)
01411 {
01412 TagInfo info = *it;
01413
01414
01415 if (tmap.contains(info.id))
01416 continue;
01417
01418
01419 TagMap::iterator iter = tmap.find(info.pid);
01420 if (iter == tmap.end())
01421 {
01422 kWarning() << "Failed to find parent tag for tag "
01423 << info.name
01424 << " with pid "
01425 << info.pid;
01426 continue;
01427 }
01428
01429 TAlbum* parent = iter.value();
01430
01431
01432 TAlbum* album = new TAlbum(info.name, info.id, false);
01433 album->m_icon = info.icon;
01434 insertTAlbum(album, parent);
01435
01436
01437 tmap.insert(info.id, album);
01438 }
01439
01440 getTagItemsCount();
01441 }
01442
01443 void AlbumManager::getTagItemsCount()
01444 {
01445 d->tagItemCountTimer->stop();
01446
01447 if (!AlbumSettings::instance()->getShowFolderTreeViewItemsCount())
01448 return;
01449
01450
01451
01452 if (d->tagListJob)
01453 {
01454 d->tagListJob->kill();
01455 d->tagListJob = 0;
01456 }
01457
01458 DatabaseUrl u = DatabaseUrl::fromTagIds(QList<int>());
01459
01460 d->tagListJob = ImageLister::startListJob(u);
01461 d->tagListJob->addMetaData("folders", "true");
01462
01463 connect(d->tagListJob, SIGNAL(result(KJob*)),
01464 this, SLOT(slotTagsJobResult(KJob*)));
01465
01466 connect(d->tagListJob, SIGNAL(data(KIO::Job*, const QByteArray&)),
01467 this, SLOT(slotTagsJobData(KIO::Job*, const QByteArray&)));
01468 }
01469
01470 void AlbumManager::scanSAlbums()
01471 {
01472 d->scanSAlbumsTimer->stop();
01473
01474
01475
01476 QMap<int, SAlbum*> oldSearches;
01477
01478 AlbumIterator it(d->rootSAlbum);
01479 while (it.current())
01480 {
01481 SAlbum* search = (SAlbum*)(*it);
01482 oldSearches[search->id()] = search;
01483 ++it;
01484 }
01485
01486
01487 QList<SearchInfo> currentSearches = DatabaseAccess().db()->scanSearches();
01488
01489 QList<SearchInfo> newSearches;
01490
01491
01492 foreach (const SearchInfo& info, currentSearches)
01493 {
01494 if (oldSearches.contains(info.id))
01495 {
01496 SAlbum *album = oldSearches[info.id];
01497 if (info.name != album->title()
01498 || info.type != album->type()
01499 || info.query != album->query())
01500 {
01501 QString oldName = album->title();
01502
01503 album->setSearch(info.type, info.query);
01504 album->setTitle(info.name);
01505 if (oldName != album->title())
01506 emit signalAlbumRenamed(album);
01507 emit signalSearchUpdated(album);
01508 }
01509
01510 oldSearches.remove(info.id);
01511 }
01512 else
01513 newSearches << info;
01514 }
01515
01516
01517 foreach (SAlbum *album, oldSearches)
01518 {
01519 emit signalAlbumAboutToBeDeleted(album);
01520 d->allAlbumsIdHash.remove(album->globalID());
01521 emit signalAlbumDeleted(album);
01522 delete album;
01523 emit signalAlbumHasBeenDeleted(album);
01524 }
01525
01526
01527 foreach (const SearchInfo& info, newSearches)
01528 {
01529 SAlbum* album = new SAlbum(info.name, info.id);
01530 emit signalAlbumAboutToBeAdded(album, d->rootSAlbum, d->rootSAlbum->lastChild());
01531 album->setSearch(info.type, info.query);
01532 album->setParent(d->rootSAlbum);
01533 d->allAlbumsIdHash[album->globalID()] = album;
01534 emit signalAlbumAdded(album);
01535 }
01536 }
01537
01538 void AlbumManager::scanDAlbums()
01539 {
01540 d->scanDAlbumsTimer->stop();
01541
01542
01543
01544
01545
01546 if (d->dateListJob)
01547 {
01548 d->dateListJob->kill();
01549 d->dateListJob = 0;
01550 }
01551
01552 DatabaseUrl u = DatabaseUrl::dateUrl();
01553
01554 d->dateListJob = ImageLister::startListJob(u);
01555 d->dateListJob->addMetaData("folders", "true");
01556
01557 connect(d->dateListJob, SIGNAL(result(KJob*)),
01558 this, SLOT(slotDatesJobResult(KJob*)));
01559
01560 connect(d->dateListJob, SIGNAL(data(KIO::Job*, const QByteArray&)),
01561 this, SLOT(slotDatesJobData(KIO::Job*, const QByteArray&)));
01562 }
01563
01564 AlbumList AlbumManager::allPAlbums() const
01565 {
01566 AlbumList list;
01567 if (d->rootPAlbum)
01568 list.append(d->rootPAlbum);
01569
01570 AlbumIterator it(d->rootPAlbum);
01571 while (it.current())
01572 {
01573 list.append(*it);
01574 ++it;
01575 }
01576
01577 return list;
01578 }
01579
01580 AlbumList AlbumManager::allTAlbums() const
01581 {
01582 AlbumList list;
01583 if (d->rootTAlbum)
01584 list.append(d->rootTAlbum);
01585
01586 AlbumIterator it(d->rootTAlbum);
01587 while (it.current())
01588 {
01589 list.append(*it);
01590 ++it;
01591 }
01592
01593 return list;
01594 }
01595
01596 AlbumList AlbumManager::allSAlbums() const
01597 {
01598 AlbumList list;
01599 if (d->rootSAlbum)
01600 list.append(d->rootSAlbum);
01601
01602 AlbumIterator it(d->rootSAlbum);
01603 while (it.current())
01604 {
01605 list.append(*it);
01606 ++it;
01607 }
01608
01609 return list;
01610 }
01611
01612 AlbumList AlbumManager::allDAlbums() const
01613 {
01614 AlbumList list;
01615 if (d->rootDAlbum)
01616 list.append(d->rootDAlbum);
01617
01618 AlbumIterator it(d->rootDAlbum);
01619 while (it.current())
01620 {
01621 list.append(*it);
01622 ++it;
01623 }
01624
01625 return list;
01626 }
01627
01628 void AlbumManager::setCurrentAlbum(Album *album)
01629 {
01630 d->currentAlbum = album;
01631 emit signalAlbumCurrentChanged(album);
01632 }
01633
01634 Album* AlbumManager::currentAlbum() const
01635 {
01636 return d->currentAlbum;
01637 }
01638
01639 PAlbum* AlbumManager::findPAlbum(const KUrl& url) const
01640 {
01641 CollectionLocation location = CollectionManager::instance()->locationForUrl(url);
01642 if (location.isNull())
01643 return 0;
01644 return d->albumPathHash.value(PAlbumPath(location.id(), CollectionManager::instance()->album(location, url)));
01645 }
01646
01647 PAlbum* AlbumManager::findPAlbum(int id) const
01648 {
01649 if (!d->rootPAlbum)
01650 return 0;
01651
01652 int gid = d->rootPAlbum->globalID() + id;
01653
01654 return (PAlbum*)(d->allAlbumsIdHash.value(gid));
01655 }
01656
01657 TAlbum* AlbumManager::findTAlbum(int id) const
01658 {
01659 if (!d->rootTAlbum)
01660 return 0;
01661
01662 int gid = d->rootTAlbum->globalID() + id;
01663
01664 return (TAlbum*)(d->allAlbumsIdHash.value(gid));
01665 }
01666
01667 SAlbum* AlbumManager::findSAlbum(int id) const
01668 {
01669 if (!d->rootSAlbum)
01670 return 0;
01671
01672 int gid = d->rootSAlbum->globalID() + id;
01673
01674 return (SAlbum*)(d->allAlbumsIdHash.value(gid));
01675 }
01676
01677 DAlbum* AlbumManager::findDAlbum(int id) const
01678 {
01679 if (!d->rootDAlbum)
01680 return 0;
01681
01682 int gid = d->rootDAlbum->globalID() + id;
01683
01684 return (DAlbum*)(d->allAlbumsIdHash.value(gid));
01685 }
01686
01687 Album* AlbumManager::findAlbum(int gid) const
01688 {
01689 return d->allAlbumsIdHash.value(gid);
01690 }
01691
01692 TAlbum* AlbumManager::findTAlbum(const QString& tagPath) const
01693 {
01694
01695 bool withLeadingSlash = tagPath.startsWith('/');
01696 AlbumIterator it(d->rootTAlbum);
01697 while (it.current())
01698 {
01699 TAlbum *talbum = static_cast<TAlbum *>(*it);
01700 if (talbum->tagPath(withLeadingSlash) == tagPath)
01701 return talbum;
01702 ++it;
01703 }
01704 return 0;
01705
01706 }
01707
01708 SAlbum* AlbumManager::findSAlbum(const QString& name) const
01709 {
01710 for (Album* album = d->rootSAlbum->firstChild(); album; album = album->next())
01711 {
01712 if (album->title() == name)
01713 return (SAlbum*)album;
01714 }
01715 return 0;
01716 }
01717
01718 void AlbumManager::addGuardedPointer(Album *album, Album **pointer)
01719 {
01720 if (album)
01721 d->guardedPointers.insert(album, pointer);
01722 }
01723
01724 void AlbumManager::removeGuardedPointer(Album *album, Album **pointer)
01725 {
01726 if (album)
01727 d->guardedPointers.remove(album, pointer);
01728 }
01729
01730 void AlbumManager::changeGuardedPointer(Album *oldAlbum, Album *album, Album **pointer)
01731 {
01732 if (oldAlbum)
01733 d->guardedPointers.remove(oldAlbum, pointer);
01734 if (album)
01735 d->guardedPointers.insert(album, pointer);
01736 }
01737
01738 void AlbumManager::invalidateGuardedPointers(Album *album)
01739 {
01740 if (!album)
01741 return;
01742 QMultiHash<Album*, Album**>::iterator it = d->guardedPointers.find(album);
01743 for( ; it != d->guardedPointers.end() && it.key() == album; ++it)
01744 {
01745 if (it.value())
01746 *(it.value()) = 0;
01747 }
01748 }
01749
01750 PAlbum* AlbumManager::createPAlbum(const QString& albumRootPath, const QString& name,
01751 const QString& caption, const QDate& date,
01752 const QString& category,
01753 QString& errMsg)
01754 {
01755 CollectionLocation location = CollectionManager::instance()->locationForAlbumRootPath(albumRootPath);
01756 return createPAlbum(location, name, caption, date, category, errMsg);
01757 }
01758
01759 PAlbum* AlbumManager::createPAlbum(const CollectionLocation& location, const QString& name,
01760 const QString& caption, const QDate& date,
01761 const QString& category,
01762 QString& errMsg)
01763 {
01764 if (location.isNull() || !location.isAvailable())
01765 {
01766 errMsg = i18n("The collection location supplied is invalid or currently not available.");
01767 return 0;
01768 }
01769
01770 PAlbum *album = d->albumRootAlbumHash.value(location.id());
01771
01772 if (!album)
01773 {
01774 errMsg = "No album for collection location: Internal error";
01775 return 0;
01776 }
01777
01778 return createPAlbum(album, name, caption, date, category, errMsg);
01779 }
01780
01781
01782 PAlbum* AlbumManager::createPAlbum(PAlbum* parent,
01783 const QString& name,
01784 const QString& caption,
01785 const QDate& date,
01786 const QString& category,
01787 QString& errMsg)
01788 {
01789 if (!parent)
01790 {
01791 errMsg = i18n("No parent found for album.");
01792 return 0;
01793 }
01794
01795
01796 if (name.isEmpty())
01797 {
01798 errMsg = i18n("Album name cannot be empty.");
01799 return 0;
01800 }
01801
01802 if (name.contains("/"))
01803 {
01804 errMsg = i18n("Album name cannot contain '/'.");
01805 return 0;
01806 }
01807
01808 if (parent->isRoot())
01809 {
01810 errMsg = i18n("createPAlbum does not accept the root album as parent.");
01811 return 0;
01812 }
01813
01814 QString albumPath = parent->isAlbumRoot() ? ('/' + name) : (parent->albumPath() + '/' + name);
01815 int albumRootId = parent->albumRootId();
01816
01817
01818 PAlbum *child = (PAlbum *)parent->m_firstChild;
01819 while (child)
01820 {
01821 if (child->albumRootId() == albumRootId && child->albumPath() == albumPath)
01822 {
01823 errMsg = i18n("An existing album has the same name.");
01824 return 0;
01825 }
01826 child = (PAlbum *)child->m_next;
01827 }
01828
01829 DatabaseUrl url = parent->databaseUrl();
01830 url.addPath(name);
01831 KUrl fileUrl = url.fileUrl();
01832
01833 if (!KIO::NetAccess::mkdir(fileUrl, qApp->activeWindow()))
01834 {
01835 errMsg = i18n("Failed to create directory,");
01836 return 0;
01837 }
01838
01839 ChangingDB changing(d);
01840 int id = DatabaseAccess().db()->addAlbum(albumRootId, albumPath, caption, date, category);
01841
01842 if (id == -1)
01843 {
01844 errMsg = i18n("Failed to add album to database");
01845 return 0;
01846 }
01847
01848 QString parentPath;
01849 if (!parent->isAlbumRoot())
01850 parentPath = parent->albumPath();
01851
01852 PAlbum *album = new PAlbum(albumRootId, parentPath, name, id);
01853 album->m_caption = caption;
01854 album->m_category = category;
01855 album->m_date = date;
01856
01857 insertPAlbum(album, parent);
01858
01859 return album;
01860 }
01861
01862 bool AlbumManager::renamePAlbum(PAlbum* album, const QString& newName,
01863 QString& errMsg)
01864 {
01865 if (!album)
01866 {
01867 errMsg = i18n("No such album");
01868 return false;
01869 }
01870
01871 if (album == d->rootPAlbum)
01872 {
01873 errMsg = i18n("Cannot rename root album");
01874 return false;
01875 }
01876
01877 if (album->isAlbumRoot())
01878 {
01879 errMsg = i18n("Cannot rename album root album");
01880 return false;
01881 }
01882
01883 if (newName.contains("/"))
01884 {
01885 errMsg = i18n("Album name cannot contain '/'");
01886 return false;
01887 }
01888
01889
01890 Album *sibling = album->m_parent->m_firstChild;
01891 while (sibling)
01892 {
01893 if (sibling->title() == newName)
01894 {
01895 errMsg = i18n("Another album with the same name already exists.\n"
01896 "Please choose another name.");
01897 return false;
01898 }
01899 sibling = sibling->m_next;
01900 }
01901
01902 QString oldAlbumPath = album->albumPath();
01903
01904 KUrl oldUrl = album->fileUrl();
01905 album->setTitle(newName);
01906 album->m_path = newName;
01907 KUrl newUrl = album->fileUrl();
01908
01909 QString newAlbumPath = album->albumPath();
01910
01911
01912
01913 ScanController::instance()->suspendCollectionScan();
01914
01915 KIO::Job *job = KIO::rename(oldUrl, newUrl, KIO::HideProgressInfo);
01916 if (!KIO::NetAccess::synchronousRun(job, 0))
01917 {
01918 errMsg = i18n("Failed to rename Album");
01919 return false;
01920 }
01921
01922
01923
01924 {
01925 DatabaseAccess access;
01926 ChangingDB changing(d);
01927 access.db()->renameAlbum(album->id(), album->albumRootId(), album->albumPath());
01928
01929 PAlbum* subAlbum = 0;
01930 AlbumIterator it(album);
01931 while ((subAlbum = static_cast<PAlbum*>(it.current())) != 0)
01932 {
01933 subAlbum->m_parentPath = newAlbumPath + subAlbum->m_parentPath.mid(oldAlbumPath.length());
01934 access.db()->renameAlbum(subAlbum->id(), album->albumRootId(), subAlbum->albumPath());
01935 ++it;
01936 }
01937 }
01938
01939 updateAlbumPathHash();
01940 emit signalAlbumRenamed(album);
01941
01942 ScanController::instance()->resumeCollectionScan();
01943
01944 return true;
01945 }
01946
01947 void AlbumManager::updateAlbumPathHash()
01948 {
01949
01950 {
01951 d->albumPathHash.clear();
01952 AlbumIterator it(d->rootPAlbum);
01953 PAlbum* subAlbum = 0;
01954 while ((subAlbum = (PAlbum*)it.current()) != 0)
01955 {
01956 d->albumPathHash[subAlbum] = subAlbum;
01957 ++it;
01958 }
01959 }
01960
01961 }
01962
01963 bool AlbumManager::updatePAlbumIcon(PAlbum *album, qlonglong iconID, QString& errMsg)
01964 {
01965 if (!album)
01966 {
01967 errMsg = i18n("No such album");
01968 return false;
01969 }
01970
01971 if (album == d->rootPAlbum)
01972 {
01973 errMsg = i18n("Cannot edit root album");
01974 return false;
01975 }
01976
01977 {
01978 DatabaseAccess access;
01979 ChangingDB changing(d);
01980 access.db()->setAlbumIcon(album->id(), iconID);
01981 QString iconRelativePath;
01982 int iconAlbumRootId;
01983 if (access.db()->getAlbumIcon(album->id(), &iconAlbumRootId, &iconRelativePath))
01984 {
01985 QString albumRootPath = CollectionManager::instance()->albumRootPath(iconAlbumRootId);
01986 album->m_icon = albumRootPath + iconRelativePath;
01987 }
01988 else
01989 album->m_icon.clear();
01990 }
01991
01992 emit signalAlbumIconChanged(album);
01993
01994 return true;
01995 }
01996
01997 TAlbum* AlbumManager::createTAlbum(TAlbum* parent, const QString& name,
01998 const QString& iconkde, QString& errMsg)
01999 {
02000 if (!parent)
02001 {
02002 errMsg = i18n("No parent found for tag");
02003 return 0;
02004 }
02005
02006
02007 if (name.isEmpty())
02008 {
02009 errMsg = i18n("Tag name cannot be empty");
02010 return 0;
02011 }
02012
02013 if (name.contains("/"))
02014 {
02015 errMsg = i18n("Tag name cannot contain '/'");
02016 return 0;
02017 }
02018
02019
02020 Album *child = parent->m_firstChild;
02021 while (child)
02022 {
02023 if (child->title() == name)
02024 {
02025 errMsg = i18n("Tag name already exists");
02026 return 0;
02027 }
02028 child = child->m_next;
02029 }
02030
02031 ChangingDB changing(d);
02032 int id = DatabaseAccess().db()->addTag(parent->id(), name, iconkde, 0);
02033 if (id == -1)
02034 {
02035 errMsg = i18n("Failed to add tag to database");
02036 return 0;
02037 }
02038
02039 TAlbum *album = new TAlbum(name, id, false);
02040 album->m_icon = iconkde;
02041
02042 insertTAlbum(album, parent);
02043
02044 return album;
02045 }
02046
02047 AlbumList AlbumManager::findOrCreateTAlbums(const QStringList& tagPaths)
02048 {
02049
02050 QList<int> tagIDs = DatabaseAccess().db()->getTagsFromTagPaths(tagPaths, true);
02051
02052
02053 scanTAlbums();
02054
02055 AlbumList resultList;
02056
02057 for (QList<int>::const_iterator it = tagIDs.constBegin() ; it != tagIDs.constEnd() ; ++it)
02058 {
02059 resultList.append(findTAlbum(*it));
02060 }
02061
02062 return resultList;
02063 }
02064
02065 bool AlbumManager::deleteTAlbum(TAlbum* album, QString& errMsg)
02066 {
02067 if (!album)
02068 {
02069 errMsg = i18n("No such album");
02070 return false;
02071 }
02072
02073 if (album == d->rootTAlbum)
02074 {
02075 errMsg = i18n("Cannot delete Root Tag");
02076 return false;
02077 }
02078
02079 {
02080 DatabaseAccess access;
02081 ChangingDB changing(d);
02082 access.db()->deleteTag(album->id());
02083
02084 Album* subAlbum = 0;
02085 AlbumIterator it(album);
02086 while ((subAlbum = it.current()) != 0)
02087 {
02088 access.db()->deleteTag(subAlbum->id());
02089 ++it;
02090 }
02091 }
02092
02093 removeTAlbum(album);
02094
02095 return true;
02096 }
02097
02098 bool AlbumManager::renameTAlbum(TAlbum* album, const QString& name,
02099 QString& errMsg)
02100 {
02101 if (!album)
02102 {
02103 errMsg = i18n("No such album");
02104 return false;
02105 }
02106
02107 if (album == d->rootTAlbum)
02108 {
02109 errMsg = i18n("Cannot edit root tag");
02110 return false;
02111 }
02112
02113 if (name.contains("/"))
02114 {
02115 errMsg = i18n("Tag name cannot contain '/'");
02116 return false;
02117 }
02118
02119
02120 Album *sibling = album->m_parent->m_firstChild;
02121 while (sibling)
02122 {
02123 if (sibling->title() == name)
02124 {
02125 errMsg = i18n("Another tag with the same name already exists.\n"
02126 "Please choose another name.");
02127 return false;
02128 }
02129 sibling = sibling->m_next;
02130 }
02131
02132 ChangingDB changing(d);
02133 DatabaseAccess().db()->setTagName(album->id(), name);
02134 album->setTitle(name);
02135 emit signalAlbumRenamed(album);
02136
02137 return true;
02138 }
02139
02140 bool AlbumManager::moveTAlbum(TAlbum* album, TAlbum *newParent, QString& errMsg)
02141 {
02142 if (!album)
02143 {
02144 errMsg = i18n("No such album");
02145 return false;
02146 }
02147
02148 if (album == d->rootTAlbum)
02149 {
02150 errMsg = i18n("Cannot move root tag");
02151 return false;
02152 }
02153
02154 ChangingDB changing(d);
02155 DatabaseAccess().db()->setTagParentID(album->id(), newParent->id());
02156 if (album->parent())
02157 album->parent()->removeChild(album);
02158 album->setParent(newParent);
02159
02160 emit signalTAlbumMoved(album, newParent);
02161
02162 return true;
02163 }
02164
02165 bool AlbumManager::updateTAlbumIcon(TAlbum* album, const QString& iconKDE,
02166 qlonglong iconID, QString& errMsg)
02167 {
02168 if (!album)
02169 {
02170 errMsg = i18n("No such tag");
02171 return false;
02172 }
02173
02174 if (album == d->rootTAlbum)
02175 {
02176 errMsg = i18n("Cannot edit root tag");
02177 return false;
02178 }
02179
02180 {
02181 DatabaseAccess access;
02182 ChangingDB changing(d);
02183 access.db()->setTagIcon(album->id(), iconKDE, iconID);
02184 QString albumRelativePath, iconKDE;
02185 int albumRootId;
02186 if (access.db()->getTagIcon(album->id(), &albumRootId, &albumRelativePath, &iconKDE))
02187 {
02188 if (iconKDE.isEmpty())
02189 {
02190 QString albumRootPath = CollectionManager::instance()->albumRootPath(albumRootId);
02191 album->m_icon = albumRootPath + albumRelativePath;
02192 }
02193 else
02194 {
02195 album->m_icon = iconKDE;
02196 }
02197 }
02198 else
02199 album->m_icon.clear();
02200 }
02201
02202 emit signalAlbumIconChanged(album);
02203
02204 return true;
02205 }
02206
02207 AlbumList AlbumManager::getRecentlyAssignedTags() const
02208 {
02209 QList<int> tagIDs = DatabaseAccess().db()->getRecentlyAssignedTags();
02210
02211 AlbumList resultList;
02212
02213 for (QList<int>::const_iterator it = tagIDs.constBegin() ; it != tagIDs.constEnd() ; ++it)
02214 {
02215 resultList.append(findTAlbum(*it));
02216 }
02217
02218 return resultList;
02219 }
02220
02221 QStringList AlbumManager::tagPaths(const QList<int>& tagIDs, bool leadingSlash) const
02222 {
02223 QStringList tagPaths;
02224
02225 for (QList<int>::const_iterator it = tagIDs.constBegin(); it != tagIDs.constEnd(); ++it)
02226 {
02227 TAlbum *album = findTAlbum(*it);
02228 if (album)
02229 {
02230 tagPaths.append(album->tagPath(leadingSlash));
02231 }
02232 }
02233
02234 return tagPaths;
02235 }
02236
02237 QStringList AlbumManager::tagNames(const QList<int>& tagIDs) const
02238 {
02239 QStringList tagNames;
02240
02241 foreach(int id, tagIDs)
02242 {
02243 TAlbum *album = findTAlbum(id);
02244 if (album)
02245 {
02246 tagNames << album->title();
02247 }
02248 }
02249
02250 return tagNames;
02251 }
02252
02253 QHash<int, QString> AlbumManager::tagPaths(bool leadingSlash) const
02254 {
02255 QHash<int, QString> hash;
02256 AlbumIterator it(d->rootTAlbum);
02257 while (it.current())
02258 {
02259 TAlbum* t = (TAlbum*)(*it);
02260 hash.insert(t->id(), t->tagPath(leadingSlash));
02261 ++it;
02262 }
02263 return hash;
02264 }
02265
02266 QHash<int, QString> AlbumManager::tagNames() const
02267 {
02268 QHash<int, QString> hash;
02269 AlbumIterator it(d->rootTAlbum);
02270 while (it.current())
02271 {
02272 TAlbum* t = (TAlbum*)(*it);
02273 hash.insert(t->id(), t->title());
02274 ++it;
02275 }
02276 return hash;
02277 }
02278
02279 QHash<int, QString> AlbumManager::albumTitles() const
02280 {
02281 QHash<int, QString> hash;
02282 AlbumIterator it(d->rootPAlbum);
02283 while (it.current())
02284 {
02285 PAlbum* a = (PAlbum*)(*it);
02286 hash.insert(a->id(), a->title());
02287 ++it;
02288 }
02289 return hash;
02290 }
02291
02292 SAlbum* AlbumManager::createSAlbum(const QString& name, DatabaseSearch::Type type, const QString& query)
02293 {
02294
02295
02296 SAlbum *album = findSAlbum(name);
02297 ChangingDB changing(d);
02298 if (album)
02299 {
02300 album->setSearch(type, query);
02301 DatabaseAccess access;
02302 access.db()->updateSearch(album->id(), album->m_type, name, query);
02303 return album;
02304 }
02305
02306 int id = DatabaseAccess().db()->addSearch(type, name, query);
02307
02308 if (id == -1)
02309 return 0;
02310
02311 album = new SAlbum(name, id);
02312 emit signalAlbumAboutToBeAdded(album, d->rootSAlbum, d->rootSAlbum->lastChild());
02313 album->setSearch(type, query);
02314 album->setParent(d->rootSAlbum);
02315
02316 d->allAlbumsIdHash.insert(album->globalID(), album);
02317 emit signalAlbumAdded(album);
02318
02319 return album;
02320 }
02321
02322 bool AlbumManager::updateSAlbum(SAlbum* album, const QString& changedQuery,
02323 const QString& changedName, DatabaseSearch::Type type)
02324 {
02325 if (!album)
02326 return false;
02327
02328 QString newName = changedName.isNull() ? album->title() : changedName;
02329 DatabaseSearch::Type newType = (type == DatabaseSearch::UndefinedType) ? album->type() : type;
02330
02331 ChangingDB changing(d);
02332 DatabaseAccess().db()->updateSearch(album->id(), newType, newName, changedQuery);
02333
02334 QString oldName = album->title();
02335
02336 album->setSearch(newType, changedQuery);
02337 album->setTitle(newName);
02338 if (oldName != album->title())
02339 emit signalAlbumRenamed(album);
02340
02341 return true;
02342 }
02343
02344 bool AlbumManager::deleteSAlbum(SAlbum* album)
02345 {
02346 if (!album)
02347 return false;
02348
02349 emit signalAlbumAboutToBeDeleted(album);
02350
02351 ChangingDB changing(d);
02352 DatabaseAccess().db()->deleteSearch(album->id());
02353
02354 d->allAlbumsIdHash.remove(album->globalID());
02355 emit signalAlbumDeleted(album);
02356 delete album;
02357 emit signalAlbumHasBeenDeleted(album);
02358
02359 return true;
02360 }
02361
02362 void AlbumManager::insertPAlbum(PAlbum *album, PAlbum *parent)
02363 {
02364 if (!album)
02365 return;
02366
02367 emit signalAlbumAboutToBeAdded(album, parent, parent ? parent->lastChild() : 0);
02368
02369 if (parent)
02370 album->setParent(parent);
02371
02372 d->albumPathHash[album] = album;
02373 d->allAlbumsIdHash[album->globalID()] = album;
02374
02375 emit signalAlbumAdded(album);
02376 }
02377
02378 void AlbumManager::removePAlbum(PAlbum *album)
02379 {
02380 if (!album)
02381 return;
02382
02383
02384 Album* child = album->m_firstChild;
02385 while (child)
02386 {
02387 Album *next = child->m_next;
02388 removePAlbum((PAlbum*)child);
02389 child = next;
02390 }
02391
02392 emit signalAlbumAboutToBeDeleted(album);
02393 d->albumPathHash.remove(album);
02394 d->allAlbumsIdHash.remove(album->globalID());
02395
02396 DatabaseUrl url = album->databaseUrl();
02397
02398 if (album == d->currentAlbum)
02399 {
02400 d->currentAlbum = 0;
02401 emit signalAlbumCurrentChanged(0);
02402 }
02403
02404 emit signalAlbumDeleted(album);
02405 delete album;
02406 emit signalAlbumHasBeenDeleted(album);
02407 }
02408
02409 void AlbumManager::insertTAlbum(TAlbum *album, TAlbum *parent)
02410 {
02411 if (!album)
02412 return;
02413
02414 emit signalAlbumAboutToBeAdded(album, parent, parent ? parent->lastChild() : 0);
02415
02416 if (parent)
02417 album->setParent(parent);
02418
02419 d->allAlbumsIdHash.insert(album->globalID(), album);
02420
02421 emit signalAlbumAdded(album);
02422 }
02423
02424 void AlbumManager::removeTAlbum(TAlbum *album)
02425 {
02426 if (!album)
02427 return;
02428
02429
02430 Album* child = album->m_firstChild;
02431 while (child)
02432 {
02433 Album *next = child->m_next;
02434 removeTAlbum((TAlbum*)child);
02435 child = next;
02436 }
02437
02438 emit signalAlbumAboutToBeDeleted(album);
02439 d->allAlbumsIdHash.remove(album->globalID());
02440
02441 if (album == d->currentAlbum)
02442 {
02443 d->currentAlbum = 0;
02444 emit signalAlbumCurrentChanged(0);
02445 }
02446
02447 emit signalAlbumDeleted(album);
02448 delete album;
02449 emit signalAlbumHasBeenDeleted(album);
02450 }
02451
02452 void AlbumManager::notifyAlbumDeletion(Album *album)
02453 {
02454 invalidateGuardedPointers(album);
02455 }
02456
02457 void AlbumManager::slotAlbumsJobResult(KJob* job)
02458 {
02459 d->albumListJob = 0;
02460
02461 if (job->error())
02462 {
02463 kWarning() << k_funcinfo << "Failed to list albums";
02464 return;
02465 }
02466 }
02467
02468 void AlbumManager::slotAlbumsJobData(KIO::Job*, const QByteArray& data)
02469 {
02470 if (data.isEmpty())
02471 return;
02472
02473 QMap<int, int> albumsStatMap;
02474 QByteArray di(data);
02475 QDataStream ds(&di, QIODevice::ReadOnly);
02476 ds >> albumsStatMap;
02477
02478 emit signalPAlbumsDirty(albumsStatMap);
02479 }
02480
02481 void AlbumManager::slotTagsJobResult(KJob* job)
02482 {
02483 d->tagListJob = 0;
02484
02485 if (job->error())
02486 {
02487 kWarning() << k_funcinfo << "Failed to list tags";
02488 return;
02489 }
02490 }
02491
02492 void AlbumManager::slotTagsJobData(KIO::Job*, const QByteArray& data)
02493 {
02494 if (data.isEmpty())
02495 return;
02496
02497 QMap<int, int> tagsStatMap;
02498 QByteArray di(data);
02499 QDataStream ds(&di, QIODevice::ReadOnly);
02500 ds >> tagsStatMap;
02501
02502 emit signalTAlbumsDirty(tagsStatMap);
02503 }
02504
02505 void AlbumManager::slotDatesJobResult(KJob* job)
02506 {
02507 d->dateListJob = 0;
02508
02509 if (job->error())
02510 {
02511 kWarning() << "Failed to list dates";
02512 return;
02513 }
02514
02515 emit signalAllDAlbumsLoaded();
02516 }
02517
02518 void AlbumManager::slotDatesJobData(KIO::Job*, const QByteArray& data)
02519 {
02520 if (data.isEmpty())
02521 return;
02522
02523
02524 QMap<QDate, DAlbum*> mAlbumMap;
02525 QMap<int, DAlbum*> yAlbumMap;
02526
02527 AlbumIterator it(d->rootDAlbum);
02528 while (it.current())
02529 {
02530 DAlbum* a = (DAlbum*)(*it);
02531 if (a->range() == DAlbum::Month)
02532 mAlbumMap.insert(a->date(), a);
02533 else
02534 yAlbumMap.insert(a->date().year(), a);
02535 ++it;
02536 }
02537
02538 QMap<QDateTime, int> datesStatMap;
02539 QByteArray di(data);
02540 QDataStream ds(&di, QIODevice::ReadOnly);
02541 ds >> datesStatMap;
02542
02543 QMap<YearMonth, int> yearMonthMap;
02544 for (QMap<QDateTime, int>::const_iterator it = datesStatMap.constBegin(); it != datesStatMap.constEnd(); ++it)
02545 {
02546 YearMonth yearMonth = YearMonth(it.key().date().year(), it.key().date().month());
02547
02548 QMap<YearMonth, int>::iterator it2 = yearMonthMap.find(yearMonth);
02549 if ( it2 == yearMonthMap.end() )
02550 {
02551 yearMonthMap.insert( yearMonth, *it );
02552 }
02553 else
02554 {
02555 *it2 += *it;
02556 }
02557 }
02558
02559 int year, month;
02560 for (QMap<YearMonth, int>::const_iterator iter = yearMonthMap.constBegin();
02561 iter != yearMonthMap.constEnd(); ++iter)
02562 {
02563 year = iter.key().first;
02564 month = iter.key().second;
02565
02566 QDate md(year, month, 1);
02567
02568
02569 if (mAlbumMap.contains(md))
02570 {
02571
02572 mAlbumMap.remove(md);
02573
02574 if (yAlbumMap.contains(year))
02575 {
02576
02577 yAlbumMap.remove(year);
02578 }
02579
02580 continue;
02581 }
02582
02583
02584 DAlbum *yAlbum = 0;
02585 AlbumIterator it(d->rootDAlbum);
02586 while (it.current())
02587 {
02588 DAlbum* a = (DAlbum*)(*it);
02589 if (a->date() == QDate(year, 1, 1) && a->range() == DAlbum::Year)
02590 {
02591 yAlbum = a;
02592 break;
02593 }
02594 ++it;
02595 }
02596
02597
02598 if (!yAlbum)
02599 {
02600 yAlbum = new DAlbum(QDate(year, 1, 1), false, DAlbum::Year);
02601 emit signalAlbumAboutToBeAdded(yAlbum, d->rootDAlbum, d->rootDAlbum->lastChild());
02602 yAlbum->setParent(d->rootDAlbum);
02603 d->allAlbumsIdHash.insert(yAlbum->globalID(), yAlbum);
02604 emit signalAlbumAdded(yAlbum);
02605 }
02606
02607
02608 DAlbum *mAlbum = new DAlbum(md);
02609 emit signalAlbumAboutToBeAdded(mAlbum, yAlbum, yAlbum->lastChild());
02610 mAlbum->setParent(yAlbum);
02611 d->allAlbumsIdHash.insert(mAlbum->globalID(), mAlbum);
02612 emit signalAlbumAdded(mAlbum);
02613 }
02614
02615
02616
02617 for (QMap<QDate, DAlbum*>::const_iterator it = mAlbumMap.constBegin();
02618 it != mAlbumMap.constEnd(); ++it)
02619 {
02620 DAlbum* album = it.value();
02621 emit signalAlbumAboutToBeDeleted(album);
02622 d->allAlbumsIdHash.remove(album->globalID());
02623 emit signalAlbumDeleted(album);
02624 delete album;
02625 emit signalAlbumHasBeenDeleted(album);
02626 }
02627
02628 for (QMap<int, DAlbum*>::const_iterator it = yAlbumMap.constBegin();
02629 it != yAlbumMap.constEnd(); ++it)
02630 {
02631 DAlbum* album = it.value();
02632 emit signalAlbumAboutToBeDeleted(album);
02633 d->allAlbumsIdHash.remove(album->globalID());
02634 emit signalAlbumDeleted(album);
02635 delete album;
02636 emit signalAlbumHasBeenDeleted(album);
02637 }
02638
02639 emit signalDAlbumsDirty(yearMonthMap);
02640 emit signalDatesMapDirty(datesStatMap);
02641 }
02642
02643 void AlbumManager::slotAlbumChange(const AlbumChangeset& changeset)
02644 {
02645 if (d->changingDB || !d->rootPAlbum)
02646 return;
02647
02648 switch(changeset.operation())
02649 {
02650 case AlbumChangeset::Added:
02651 case AlbumChangeset::Deleted:
02652 if (!d->scanPAlbumsTimer->isActive())
02653 d->scanPAlbumsTimer->start();
02654 break;
02655 case AlbumChangeset::Renamed:
02656 case AlbumChangeset::PropertiesChanged:
02657
02658 d->changedPAlbums << changeset.albumId();
02659 if (!d->updatePAlbumsTimer->isActive())
02660 d->updatePAlbumsTimer->start();
02661 break;
02662 case AlbumChangeset::Unknown:
02663 break;
02664 }
02665 }
02666
02667 void AlbumManager::slotTagChange(const TagChangeset& changeset)
02668 {
02669 if (d->changingDB || !d->rootTAlbum)
02670 return;
02671
02672 switch(changeset.operation())
02673 {
02674 case TagChangeset::Added:
02675 case TagChangeset::Deleted:
02676 case TagChangeset::Reparented:
02677 if (!d->scanTAlbumsTimer->isActive())
02678 d->scanTAlbumsTimer->start();
02679 break;
02680 case TagChangeset::Renamed:
02681 case TagChangeset::IconChanged:
02682
02683 break;
02684 case TagChangeset::Unknown:
02685 break;
02686 }
02687 }
02688
02689 void AlbumManager::slotSearchChange(const SearchChangeset& changeset)
02690 {
02691 if (d->changingDB || !d->rootSAlbum)
02692 return;
02693
02694 switch(changeset.operation())
02695 {
02696 case SearchChangeset::Added:
02697 case SearchChangeset::Deleted:
02698 if (!d->scanSAlbumsTimer->isActive())
02699 d->scanSAlbumsTimer->start();
02700 break;
02701 case SearchChangeset::Changed:
02702 break;
02703 case SearchChangeset::Unknown:
02704 break;
02705 }
02706 }
02707
02708 void AlbumManager::slotCollectionImageChange(const CollectionImageChangeset& changeset)
02709 {
02710 if (!d->rootDAlbum)
02711 return;
02712
02713 switch (changeset.operation())
02714 {
02715 case CollectionImageChangeset::Added:
02716 case CollectionImageChangeset::Removed:
02717 case CollectionImageChangeset::RemovedAll:
02718 if (!d->scanDAlbumsTimer->isActive())
02719 d->scanDAlbumsTimer->start();
02720 if (!d->albumItemCountTimer->isActive())
02721 d->albumItemCountTimer->start();
02722 break;
02723 default:
02724 break;
02725 }
02726 }
02727
02728 void AlbumManager::slotImageTagChange(const ImageTagChangeset& changeset)
02729 {
02730 if (!d->rootTAlbum)
02731 return;
02732 switch (changeset.operation())
02733 {
02734 case ImageTagChangeset::Added:
02735 case ImageTagChangeset::Removed:
02736 case ImageTagChangeset::RemovedAll:
02737 if (!d->tagItemCountTimer->isActive())
02738 d->tagItemCountTimer->start();
02739 break;
02740 default:
02741 break;
02742 }
02743 }
02744
02745 void AlbumManager::slotNotifyFileChange(const QString& path)
02746 {
02747
02748 ScanController::instance()->scheduleCollectionScanRelaxed(path);
02749 }
02750
02751 void AlbumManager::slotDirWatchDirty(const QString& path)
02752 {
02753
02754 foreach (const QString &bannedFile, d->dirWatchBlackList)
02755 if (path.endsWith(bannedFile))
02756 return;
02757
02758 DatabaseParameters params = DatabaseAccess::parameters();
02759 if (params.isSQLite())
02760 {
02761 QDir dir(path);
02762 QFileInfo dbFile(params.SQLiteDatabaseFile());
02763
02764
02765 if (path.startsWith(dbFile.filePath()))
02766 return;
02767
02768
02769 if (dbFile.dir() == dir)
02770 {
02771
02772 QList<QDateTime> modList = d->buildDirectoryModList(dbFile);
02773
02774
02775 if (modList == d->dbPathModificationDateList)
02776 {
02777
02778
02779 return;
02780 }
02781
02782
02783 d->dbPathModificationDateList = modList;
02784 }
02785 }
02786
02787 kDebug() << "KDirWatch detected change at" << path;
02788
02789 slotNotifyFileChange(path);
02790 }
02791
02792 void AlbumManager::slotKioFileMoved(const QString& urlFrom, const QString& urlTo)
02793 {
02794 kDebug() << urlFrom << urlTo;
02795 handleKioNotification(KUrl(urlFrom));
02796 handleKioNotification(KUrl(urlTo));
02797 }
02798
02799 void AlbumManager::slotKioFilesAdded(const QString& url)
02800 {
02801 kDebug() << url;
02802 handleKioNotification(KUrl(url));
02803 }
02804
02805 void AlbumManager::slotKioFilesDeleted(const QStringList& urls)
02806 {
02807 kDebug() << urls;
02808 foreach (const QString& url, urls)
02809 handleKioNotification(KUrl(url));
02810 }
02811
02812 void AlbumManager::handleKioNotification(const KUrl& url)
02813 {
02814 if (url.isLocalFile())
02815 {
02816 QString path = url.directory();
02817
02818
02819 if (CollectionManager::instance()->albumRootPath(path).isNull())
02820 return;
02821
02822 kDebug() << "KDirNotify detected file change at" << path;
02823
02824 slotNotifyFileChange(path);
02825 }
02826 else
02827 {
02828 DatabaseUrl dbUrl(url);
02829 if (dbUrl.isAlbumUrl())
02830 {
02831 QString path = dbUrl.fileUrl().directory();
02832 kDebug() << "KDirNotify detected file change at" << path;
02833 slotNotifyFileChange(path);
02834 }
02835 }
02836 }
02837
02838
02839 }