File indexing completed on 2025-01-19 03:53:20

0001 /* ============================================================
0002  *
0003  * This file is a part of digiKam project
0004  * https://www.digikam.org
0005  *
0006  * Date        : 2004-06-15
0007  * Description : Albums manager interface - Database helpers.
0008  *
0009  * SPDX-FileCopyrightText: 2006-2024 by Gilles Caulier <caulier dot gilles at gmail dot com>
0010  * SPDX-FileCopyrightText: 2006-2011 by Marcel Wiesweg <marcel dot wiesweg at gmx dot de>
0011  * SPDX-FileCopyrightText: 2015      by Mohamed_Anwer <m_dot_anwer at gmx dot com>
0012  *
0013  * SPDX-License-Identifier: GPL-2.0-or-later
0014  *
0015  * ============================================================ */
0016 
0017 #include "albummanager_p.h"
0018 
0019 // Qt includes
0020 
0021 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
0022     #include <QTextCodec>
0023 #else
0024     #include <QStringConverter>
0025 #endif
0026 
0027 // KDE includes
0028 
0029 #include <kconfiggroup.h>
0030 #include <ksharedconfig.h>
0031 
0032 namespace Digikam
0033 {
0034 
0035 bool AlbumManager::setDatabase(const DbEngineParameters& params, bool priority, const QString& suggestedAlbumRoot, bool ignoreDisappearedLocations)
0036 {
0037     // This is to ensure that the setup does not overrule the command line.
0038     // TODO: there is a bug that setup is showing something different here.
0039 
0040     if      (priority)
0041     {
0042         d->hasPriorizedDbPath = true;
0043     }
0044     else if (d->hasPriorizedDbPath)
0045     {
0046         // ignore change without priority
0047 
0048         return true;
0049     }
0050 
0051     d->changed = true;
0052 
0053     QApplication::setOverrideCursor(Qt::WaitCursor);
0054 
0055     DatabaseServerStarter::instance()->stopServerManagerProcess();
0056 
0057     // Shutdown possibly running collection scans.
0058     // Must call restartCollectionScan further down.
0059 
0060     ScanController::instance()->cancelAllAndSuspendCollectionScan();
0061 
0062     disconnect(CollectionManager::instance(), nullptr, this, nullptr);
0063     CollectionManager::instance()->setWatchDisabled();
0064 
0065     if (CoreDbAccess::databaseWatch())
0066     {
0067         disconnect(CoreDbAccess::databaseWatch(), nullptr, this, nullptr);
0068     }
0069 
0070     ItemAttributesWatch::shutDown();
0071     ItemAttributesWatch::cleanUp();
0072     d->albumWatch->clear();
0073 
0074     cleanUp();
0075 
0076     d->currentAlbums.clear();
0077     Q_EMIT signalAlbumCurrentChanged(d->currentAlbums);
0078     Q_EMIT signalAlbumsCleared();
0079 
0080     d->albumPathHash.clear();
0081     d->allAlbumsIdHash.clear();
0082     d->albumRootAlbumHash.clear();
0083 
0084     // deletes all child albums as well
0085 
0086     delete d->rootPAlbum;
0087     delete d->rootTAlbum;
0088     delete d->rootDAlbum;
0089     delete d->rootSAlbum;
0090 
0091     d->rootPAlbum = nullptr;
0092     d->rootTAlbum = nullptr;
0093     d->rootDAlbum = nullptr;
0094     d->rootSAlbum = nullptr;
0095 
0096     // -- Database initialization -------------------------------------------------
0097 
0098     // ensure, embedded database is loaded
0099 
0100     qCDebug(DIGIKAM_GENERAL_LOG) << params;
0101 
0102     QString databaseError;
0103 
0104     ApplicationSettings* const settings = ApplicationSettings::instance();
0105 
0106     if      (params.internalServer && suggestedAlbumRoot.isEmpty())
0107     {
0108         if      (!QFileInfo::exists(params.internalServerPath()))
0109         {
0110             databaseError = i18n("The MySQL database directory was not found.");
0111         }
0112         else if (
0113                  (!QFileInfo::exists(params.internalServerMysqlUpgradeCmd)                        &&
0114                   QStandardPaths::findExecutable(params.internalServerMysqlUpgradeCmd).isEmpty()) ||
0115                  (!QFileInfo::exists(params.internalServerMysqlServerCmd)                         &&
0116                   QStandardPaths::findExecutable(params.internalServerMysqlServerCmd).isEmpty())  ||
0117                  (!QFileInfo::exists(params.internalServerMysqlAdminCmd)                          &&
0118                   QStandardPaths::findExecutable(params.internalServerMysqlAdminCmd).isEmpty())
0119                 )
0120         {
0121             databaseError = i18n("The MySQL binary tools was not found.");
0122         }
0123     }
0124     else if (params.isSQLite() && suggestedAlbumRoot.isEmpty() && !settings->getDatabaseDirSetAtCmd())
0125     {
0126         if (!QFileInfo::exists(params.databaseNameCore))
0127         {
0128             databaseError = i18n("The SQLite core database was not found.");
0129         }
0130     }
0131 
0132     if (!databaseError.isEmpty())
0133     {
0134         QString configPath = QStandardPaths::locate(QStandardPaths::GenericConfigLocation,
0135                                                     QLatin1String("digikamrc"));
0136 
0137         databaseError     += i18n("<p>If you want to start with a new configuration and "
0138                                   "with a first run wizard, delete the file:<br>%1</p>",
0139                                   QDir::toNativeSeparators(configPath));
0140 
0141         return showDatabaseSetupPage(databaseError, priority, suggestedAlbumRoot);
0142     }
0143 
0144     if (params.internalServer)
0145     {
0146         DatabaseServerError result = DatabaseServerStarter::instance()->startServerManagerProcess(params);
0147 
0148         if (result.getErrorType() != DatabaseServerError::NoErrors)
0149         {
0150             databaseError = i18n("An error occurred during the internal server start."
0151                                  "<p>Details:\n%1</p>", result.getErrorText());
0152 
0153             return showDatabaseSetupPage(databaseError, priority, suggestedAlbumRoot);
0154         }
0155     }
0156 
0157     CoreDbAccess::setParameters(params, CoreDbAccess::MainApplication);
0158 
0159     DbEngineGuiErrorHandler* const handler = new DbEngineGuiErrorHandler(CoreDbAccess::parameters());
0160     CoreDbAccess::initDbEngineErrorHandler(handler);
0161 
0162     QApplication::restoreOverrideCursor();
0163 
0164     if (!handler->checkDatabaseConnection())
0165     {
0166         databaseError = i18n("Failed to open the database.");
0167 
0168         if (!showDatabaseSetupPage(databaseError, priority, suggestedAlbumRoot))
0169         {
0170             if (params.isSQLite())
0171             {
0172                 QMessageBox::critical(qApp->activeWindow(), qApp->applicationName(),
0173                                       i18n("<p>digiKam will attempt to start now, "
0174                                            "but it will <b>not</b> be functional.</p>"));
0175 
0176                 CoreDbAccess::setParameters(DbEngineParameters(), CoreDbAccess::DatabaseSlave);
0177 
0178                 return true;
0179             }
0180 
0181             return false;
0182         }
0183 
0184         return true;
0185     }
0186 
0187     QApplication::setOverrideCursor(Qt::WaitCursor);
0188 
0189     d->albumWatch->setDbEngineParameters(params);
0190 
0191     ScanController::Advice advice = ScanController::instance()->databaseInitialization();
0192 
0193     QApplication::restoreOverrideCursor();
0194 
0195     switch (advice)
0196     {
0197         case ScanController::Success:
0198         {
0199             break;
0200         }
0201 
0202         case ScanController::ContinueWithoutDatabase:
0203         {
0204             QString errorMsg = CoreDbAccess().lastError();
0205 
0206             if (errorMsg.isEmpty())
0207             {
0208                 QMessageBox::critical(qApp->activeWindow(), qApp->applicationName(),
0209                                       i18n("<p>Failed to open the database.</p>"
0210                                            "<p>You cannot use digiKam without a working database. "
0211                                            "digiKam will attempt to start now, but it will <b>not</b> be functional. "
0212                                            "Please check the database settings in the <b>configuration menu</b>.</p>"
0213                                           ));
0214             }
0215             else
0216             {
0217                 QMessageBox::critical(qApp->activeWindow(), qApp->applicationName(),
0218                                       i18n("<p>Failed to open the database. Error message from database:</p>"
0219                                            "<p><b>%1</b></p>"
0220                                            "<p>You cannot use digiKam without a working database. "
0221                                            "digiKam will attempt to start now, but it will <b>not</b> be functional. "
0222                                            "Please check the database settings in the <b>configuration menu</b>.</p>",
0223                                            errorMsg));
0224             }
0225 
0226             return true;
0227         }
0228 
0229         case ScanController::AbortImmediately:
0230         {
0231             databaseError = i18n("Failed to initialize the database.");
0232 
0233             return showDatabaseSetupPage(databaseError, priority, suggestedAlbumRoot);
0234         }
0235     }
0236 
0237     // -- Locale Checking ---------------------------------------------------------
0238 
0239     QString currLocale = CoreDbAccess().db()->getDatabaseEncoding();
0240     QString dbLocale   = CoreDbAccess().db()->getSetting(QLatin1String("Locale"));
0241 
0242     if      (dbLocale.isEmpty())
0243     {
0244         qCDebug(DIGIKAM_GENERAL_LOG) << "No locale found in database";
0245         CoreDbAccess().db()->setSetting(QLatin1String("Locale"), currLocale);
0246     }
0247     else if (dbLocale != currLocale)
0248     {
0249         // TODO it would be better to replace all yes/no confirmation dialogs with ones that has custom
0250         // buttons that denote the actions directly, i.e.:  ["Ignore and Continue"]  ["Adjust locale"]
0251 
0252         int result = QMessageBox::warning(qApp->activeWindow(), qApp->applicationName(),
0253                                  i18n("Your database character set has changed since this "
0254                                       "album was last opened.\n"
0255                                       "Old character set: %1, new character set: %2\n"
0256                                       "If you have recently changed your database character set, "
0257                                       "you need not be concerned.\n"
0258                                       "Please note that if you switched to a database character set "
0259                                       "that does not support some of the filenames in your collection, "
0260                                       "these files may no longer be found in the collection. "
0261                                       "If you are sure that you want to "
0262                                       "continue, click 'Yes'. "
0263                                       "Otherwise, click 'No' and correct your "
0264                                       "database character set setting before restarting digiKam.",
0265                                       dbLocale, currLocale),
0266                                   QMessageBox::Yes | QMessageBox::No);
0267 
0268         if (result != QMessageBox::Yes)
0269         {
0270             return false;
0271         }
0272 
0273         CoreDbAccess().db()->setSetting(QLatin1String("Locale"), currLocale);
0274     }
0275 
0276     // -- UUID Checking ---------------------------------------------------------
0277 
0278     QList<CollectionLocation> disappearedLocations = CollectionManager::instance()->checkHardWiredLocations();
0279 
0280     Q_FOREACH (const CollectionLocation& loc, disappearedLocations)
0281     {
0282         QString locDescription;
0283         QStringList candidateIds, candidateDescriptions;
0284         CollectionManager::instance()->migrationCandidates(loc, &locDescription, &candidateIds, &candidateDescriptions);
0285         qCDebug(DIGIKAM_GENERAL_LOG) << "Migration candidates for" << locDescription << ":" << candidateIds << candidateDescriptions;
0286 
0287         QDialog* const dialog         = new QDialog;
0288         QWidget* const widget         = new QWidget(dialog);
0289         QGridLayout* const mainLayout = new QGridLayout;
0290         mainLayout->setColumnStretch(1, 1);
0291 
0292         QLabel* const deviceIconLabel = new QLabel;
0293         deviceIconLabel->setPixmap(QIcon::fromTheme(QLatin1String("drive-harddisk")).pixmap(64));
0294         mainLayout->addWidget(deviceIconLabel, 0, 0);
0295 
0296         QLabel* const mainLabel       = new QLabel(i18n("<p>The collection </p><p><b>%1</b><br/>(%2)</p><p> is currently "
0297                                                         "not found on your system.<br/> Please choose the most "
0298                                                         "appropriate  option to handle this situation:</p>",
0299                                                    loc.label(), QDir::toNativeSeparators(locDescription)));
0300         mainLabel->setWordWrap(true);
0301         mainLayout->addWidget(mainLabel, 0, 1);
0302 
0303         QGroupBox* const groupBox     = new QGroupBox;
0304         mainLayout->addWidget(groupBox, 1, 0, 1, 2);
0305 
0306         QGridLayout* const layout     = new QGridLayout;
0307         layout->setColumnStretch(1, 1);
0308 
0309         QRadioButton* migrateButton   = nullptr;
0310         QComboBox* migrateChoices     = nullptr;
0311 
0312         if (!candidateIds.isEmpty())
0313         {
0314             migrateButton              = new QRadioButton;
0315             QLabel* const migrateLabel = new QLabel(i18n("<p>The collection is still available, but the identifier changed.<br/>"
0316                                                          "This can be caused by restoring a backup, changing the partition layout "
0317                                                          "or the file system settings.<br/>"
0318                                                          "The collection is now located at this place:</p>"));
0319             migrateLabel->setWordWrap(true);
0320 
0321             migrateChoices             = new QComboBox;
0322 
0323             for (int i = 0 ; i < candidateIds.size() ; ++i)
0324             {
0325                 migrateChoices->addItem(QDir::toNativeSeparators(candidateDescriptions.at(i)), candidateIds.at(i));
0326             }
0327 
0328             layout->addWidget(migrateButton,  0, 0, Qt::AlignTop);
0329             layout->addWidget(migrateLabel,   0, 1);
0330             layout->addWidget(migrateChoices, 1, 1);
0331         }
0332 
0333         QRadioButton* const isRemovableButton = new QRadioButton;
0334         QLabel* const isRemovableLabel        = new QLabel(i18n("The collection is located on a storage device which is not "
0335                                                                 "always attached. Mark the collection as a removable collection."));
0336         isRemovableLabel->setWordWrap(true);
0337         layout->addWidget(isRemovableButton, 2, 0, Qt::AlignTop);
0338         layout->addWidget(isRemovableLabel,  2, 1);
0339 
0340         QRadioButton* const solveManuallyButton = new QRadioButton;
0341         QLabel* const solveManuallyLabel        = new QLabel(i18n("Take no action now. I would like to solve the problem "
0342                                                                   "later using the setup dialog"));
0343         solveManuallyLabel->setWordWrap(true);
0344         layout->addWidget(solveManuallyButton, 3, 0, Qt::AlignTop);
0345         layout->addWidget(solveManuallyLabel,  3, 1);
0346 
0347         groupBox->setLayout(layout);
0348         widget->setLayout(mainLayout);
0349 
0350         QVBoxLayout* const vbx                  = new QVBoxLayout(dialog);
0351         QDialogButtonBox* const buttons         = new QDialogButtonBox(QDialogButtonBox::Ok, dialog);
0352         vbx->addWidget(widget);
0353         vbx->addWidget(buttons);
0354         dialog->setLayout(vbx);
0355         dialog->setWindowTitle(i18nc("@title:window", "Collection not Found"));
0356 
0357         connect(buttons->button(QDialogButtonBox::Ok), SIGNAL(clicked()),
0358                 dialog, SLOT(accept()));
0359 
0360         // Default option: If there is only one candidate, default to migration.
0361         // Otherwise default to do nothing now.
0362 
0363         if (migrateButton && (candidateIds.size() == 1))
0364         {
0365             migrateButton->setChecked(true);
0366         }
0367         else
0368         {
0369             solveManuallyButton->setChecked(true);
0370         }
0371 
0372         if (!ignoreDisappearedLocations && dialog->exec())
0373         {
0374             if      (migrateButton && migrateButton->isChecked())
0375             {
0376                 CollectionManager::instance()->migrateToVolume(loc, migrateChoices->itemData(migrateChoices->currentIndex()).toString());
0377             }
0378             else if (isRemovableButton->isChecked())
0379             {
0380                 CollectionManager::instance()->changeType(loc, CollectionLocation::VolumeRemovable);
0381             }
0382         }
0383 
0384         delete dialog;
0385     }
0386 
0387     // -- ---------------------------------------------------------
0388 
0389     // check that we have one album root
0390 
0391     if (CollectionManager::instance()->allLocations().isEmpty())
0392     {
0393         if (suggestedAlbumRoot.isEmpty())
0394         {
0395             Setup::execSinglePage(Setup::CollectionsPage);
0396         }
0397         else
0398         {
0399             QUrl albumRoot(QUrl::fromLocalFile(suggestedAlbumRoot));
0400             CollectionManager::instance()->addLocation(albumRoot, albumRoot.fileName());
0401 
0402             // Not needed? See bug #188959
0403 /*
0404             ScanController::instance()->completeCollectionScan();
0405 */
0406         }
0407     }
0408 
0409     // -- ---------------------------------------------------------
0410 
0411     QApplication::setOverrideCursor(Qt::WaitCursor);
0412 
0413     ThumbnailLoadThread::initializeThumbnailDatabase(CoreDbAccess::parameters().thumbnailParameters(),
0414                                                      new ThumbsDbInfoProvider());
0415 
0416     DbEngineGuiErrorHandler* const thumbnailsDBHandler = new DbEngineGuiErrorHandler(ThumbsDbAccess::parameters());
0417     ThumbsDbAccess::initDbEngineErrorHandler(thumbnailsDBHandler);
0418 
0419     // Activate the similarity database.
0420 
0421     SimilarityDbAccess::setParameters(params.similarityParameters());
0422 
0423     DbEngineGuiErrorHandler* const similarityHandler   = new DbEngineGuiErrorHandler(SimilarityDbAccess::parameters());
0424     SimilarityDbAccess::initDbEngineErrorHandler(similarityHandler);
0425 
0426     if (SimilarityDbAccess::checkReadyForUse(nullptr))
0427     {
0428         qCDebug(DIGIKAM_SIMILARITYDB_LOG) << "Similarity database ready for use";
0429     }
0430     else
0431     {
0432         qCDebug(DIGIKAM_SIMILARITYDB_LOG) << "Failed to initialize similarity database";
0433     }
0434 
0435     QApplication::restoreOverrideCursor();
0436 
0437     // still suspended from above
0438 
0439     ScanController::instance()->restartCollectionScan();
0440 
0441     return true;
0442 }
0443 
0444 void AlbumManager::checkDatabaseDirsAfterFirstRun(const QString& dbPath, const QString& albumPath)
0445 {
0446     // for bug #193522
0447 
0448     QDir               newDir(dbPath);
0449     QDir               albumDir(albumPath);
0450     DbEngineParameters newParams = DbEngineParameters::parametersForSQLiteDefaultFile(newDir.path());
0451     QFileInfo          digikam4DB(newParams.SQLiteDatabaseFile());
0452 
0453     if (!digikam4DB.exists())
0454     {
0455         QFileInfo digikam3DB(newDir, QLatin1String("digikam3.db"));
0456         QFileInfo digikamVeryOldDB(newDir, QLatin1String("digikam.db"));
0457 
0458         if (digikam3DB.exists() || digikamVeryOldDB.exists())
0459         {
0460             QPointer<QMessageBox> msgBox = new QMessageBox(QMessageBox::Warning,
0461                      i18nc("@title:window", "Database Folder"),
0462                      i18n("<p>You have chosen the folder \"%1\" as the place to store the database. "
0463                           "A database file from an older version of digiKam is found in this folder.</p> "
0464                           "<p>Would you like to upgrade the old database file - confirming "
0465                           "that this database file was indeed created for the pictures located in the folder \"%2\" - "
0466                           "or ignore the old file and start with a new database?</p> ",
0467                           QDir::toNativeSeparators(newDir.path()),
0468                           QDir::toNativeSeparators(albumDir.path())),
0469                       QMessageBox::Yes | QMessageBox::No,
0470                       qApp->activeWindow());
0471 
0472             msgBox->button(QMessageBox::Yes)->setText(i18nc("@action:button", "Upgrade Database"));
0473             msgBox->button(QMessageBox::Yes)->setIcon(QIcon::fromTheme(QLatin1String("view-refresh")));
0474             msgBox->button(QMessageBox::No)->setText(i18nc("@action:button", "Create New Database"));
0475             msgBox->button(QMessageBox::No)->setIcon(QIcon::fromTheme(QLatin1String("document-new")));
0476             msgBox->setDefaultButton(QMessageBox::Yes);
0477 
0478             int result = msgBox->exec();
0479 
0480             if      (result == QMessageBox::Yes)
0481             {
0482                 // CoreDbSchemaUpdater expects Album Path to point to the album root of the 0.9 db file.
0483                 // Restore this situation.
0484 
0485                 KSharedConfigPtr config = KSharedConfig::openConfig();
0486                 KConfigGroup group      = config->group(QLatin1String("Album Settings"));
0487                 group.writeEntry("Album Path", albumDir.path());
0488                 group.sync();
0489             }
0490             else if (result == QMessageBox::No)
0491             {
0492                 moveToBackup(digikam3DB);
0493                 moveToBackup(digikamVeryOldDB);
0494             }
0495 
0496             delete msgBox;
0497         }
0498     }
0499 }
0500 
0501 void AlbumManager::changeDatabase(const DbEngineParameters& newParams)
0502 {
0503     ScanController::instance()->shutDown();
0504 
0505     // if there is no file at the new place, copy old one
0506 
0507     DbEngineParameters params = CoreDbAccess::parameters();
0508 
0509     // New database type SQLITE
0510 
0511     if (newParams.isSQLite())
0512     {
0513         bool walModeChanged = ((params.walMode != newParams.walMode)                                     &&
0514                                (params.getCoreDatabaseNameOrDir() == newParams.getCoreDatabaseNameOrDir()));
0515 
0516         QDir newDir(newParams.getCoreDatabaseNameOrDir());
0517         QFileInfo newFile(newDir, QLatin1String("digikam4.db"));
0518 
0519         if      (!newFile.exists() && !walModeChanged)
0520         {
0521             QFileInfo digikam3DB(newDir, QLatin1String("digikam3.db"));
0522             QFileInfo digikamVeryOldDB(newDir, QLatin1String("digikam.db"));
0523 
0524             if (digikam3DB.exists() || digikamVeryOldDB.exists())
0525             {
0526                 int result = -1;
0527 
0528                 if (params.isSQLite())
0529                 {
0530                     QPointer<QMessageBox> msgBox = new QMessageBox(QMessageBox::Warning,
0531                              i18nc("@title:window", "New Database Folder"),
0532                              i18n("<p>You have chosen the folder \"%1\" as the new place to store the database. "
0533                                   "A database file from an older version of digiKam is found in this folder.</p> "
0534                                   "<p>Would you like to upgrade the old database file, start with a new database, "
0535                                   "or copy the current database to this location and continue using it?</p> ",
0536                                   QDir::toNativeSeparators(newDir.path())),
0537                              QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel,
0538                              qApp->activeWindow());
0539 
0540                     msgBox->button(QMessageBox::Yes)->setText(i18nc("@action:button", "Upgrade Database"));
0541                     msgBox->button(QMessageBox::Yes)->setIcon(QIcon::fromTheme(QLatin1String("view-refresh")));
0542                     msgBox->button(QMessageBox::No)->setText(i18nc("@action:button", "Create New Database"));
0543                     msgBox->button(QMessageBox::No)->setIcon(QIcon::fromTheme(QLatin1String("document-new")));
0544                     msgBox->button(QMessageBox::Cancel)->setText(i18nc("@action:button", "Copy Current Database"));
0545                     msgBox->button(QMessageBox::Cancel)->setIcon(QIcon::fromTheme(QLatin1String("edit-copy")));
0546                     msgBox->setDefaultButton(QMessageBox::Yes);
0547 
0548                     result = msgBox->exec();
0549                     delete msgBox;
0550                 }
0551                 else
0552                 {
0553                     QPointer<QMessageBox> msgBox = new QMessageBox(QMessageBox::Warning,
0554                              i18nc("@title:window", "New Database Folder"),
0555                              i18n("<p>You have chosen the folder \"%1\" as the new place to store the database. "
0556                                   "A database file from an older version of digiKam is found in this folder.</p> "
0557                                   "<p>Would you like to upgrade the old database file or start with a new database?</p>",
0558                                   QDir::toNativeSeparators(newDir.path())),
0559                              QMessageBox::Yes | QMessageBox::No,
0560                              qApp->activeWindow());
0561 
0562                     msgBox->button(QMessageBox::Yes)->setText(i18nc("@action:button", "Upgrade Database"));
0563                     msgBox->button(QMessageBox::Yes)->setIcon(QIcon::fromTheme(QLatin1String("view-refresh")));
0564                     msgBox->button(QMessageBox::No)->setText(i18nc("@action:button", "Create New Database"));
0565                     msgBox->button(QMessageBox::No)->setIcon(QIcon::fromTheme(QLatin1String("document-new")));
0566                     msgBox->setDefaultButton(QMessageBox::Yes);
0567 
0568                     result = msgBox->exec();
0569                     delete msgBox;
0570                 }
0571 
0572                 if      (result == QMessageBox::Yes)
0573                 {
0574                     // CoreDbSchemaUpdater expects Album Path to point to the album root of the 0.9 db file.
0575                     // Restore this situation.
0576 
0577                     KSharedConfigPtr config = KSharedConfig::openConfig();
0578                     KConfigGroup group      = config->group(QLatin1String("Album Settings"));
0579                     group.writeEntry(QLatin1String("Album Path"), newDir.path());
0580                     group.sync();
0581                 }
0582                 else if (result == QMessageBox::No)
0583                 {
0584                     moveToBackup(digikam3DB);
0585                     moveToBackup(digikamVeryOldDB);
0586                 }
0587                 else if (result == QMessageBox::Cancel)
0588                 {
0589                     QFileInfo oldFile(params.SQLiteDatabaseFile());
0590                     copyToNewLocation(oldFile, newFile, i18n("Failed to copy the old database file (\"%1\") "
0591                                                              "to its new location (\"%2\"). "
0592                                                              "Trying to upgrade old databases.",
0593                                                              QDir::toNativeSeparators(oldFile.filePath()),
0594                                                              QDir::toNativeSeparators(newFile.filePath())));
0595                 }
0596             }
0597             else
0598             {
0599                 int result = QMessageBox::Yes;
0600 
0601                 if (params.isSQLite())
0602                 {
0603                     QPointer<QMessageBox> msgBox = new QMessageBox(QMessageBox::Warning,
0604                              i18nc("@title:window", "New Database Folder"),
0605                              i18n("<p>You have chosen the folder \"%1\" as the new place to store the database.</p>"
0606                                   "<p>Would you like to copy the current database to this location "
0607                                   "and continue using it, or start with a new database?</p> ",
0608                                   QDir::toNativeSeparators(newDir.path())),
0609                              QMessageBox::Yes | QMessageBox::No,
0610                              qApp->activeWindow());
0611 
0612                     msgBox->button(QMessageBox::Yes)->setText(i18nc("@action:button", "Create New Database"));
0613                     msgBox->button(QMessageBox::Yes)->setIcon(QIcon::fromTheme(QLatin1String("document-new")));
0614                     msgBox->button(QMessageBox::No)->setText(i18nc("@action:button", "Copy Current Database"));
0615                     msgBox->button(QMessageBox::No)->setIcon(QIcon::fromTheme(QLatin1String("edit-copy")));
0616                     msgBox->setDefaultButton(QMessageBox::Yes);
0617 
0618                     result = msgBox->exec();
0619                     delete msgBox;
0620                 }
0621 
0622                 if (result == QMessageBox::No)
0623                 {
0624                     QFileInfo oldFile(params.SQLiteDatabaseFile());
0625                     copyToNewLocation(oldFile, newFile);
0626                 }
0627             }
0628         }
0629         else if (!walModeChanged)
0630         {
0631             int result = QMessageBox::No;
0632 
0633             if (params.isSQLite())
0634             {
0635                 QPointer<QMessageBox> msgBox = new QMessageBox(QMessageBox::Warning,
0636                          i18nc("@title:window", "New Database Folder"),
0637                          i18n("<p>You have chosen the folder \"%1\" as the new place to store the database. "
0638                               "There is already a database file in this location.</p> "
0639                               "<p>Would you like to use this existing file as the new database, or remove it "
0640                               "and copy the current database to this place?</p> ",
0641                               QDir::toNativeSeparators(newDir.path())),
0642                          QMessageBox::Yes | QMessageBox::No,
0643                          qApp->activeWindow());
0644 
0645                 msgBox->button(QMessageBox::Yes)->setText(i18nc("@action:button", "Copy Current Database"));
0646                 msgBox->button(QMessageBox::Yes)->setIcon(QIcon::fromTheme(QLatin1String("edit-copy")));
0647                 msgBox->button(QMessageBox::No)->setText(i18nc("@action:button", "Use Existing File"));
0648                 msgBox->button(QMessageBox::No)->setIcon(QIcon::fromTheme(QLatin1String("document-open")));
0649                 msgBox->setDefaultButton(QMessageBox::Yes);
0650 
0651                 result = msgBox->exec();
0652                 delete msgBox;
0653             }
0654 
0655             if (result == QMessageBox::Yes)
0656             {
0657                 // first backup
0658 
0659                 if (moveToBackup(newFile))
0660                 {
0661                     QFileInfo oldFile(params.SQLiteDatabaseFile());
0662 
0663                     // then copy
0664 
0665                     copyToNewLocation(oldFile, newFile);
0666                 }
0667             }
0668         }
0669     }
0670 
0671     ScanController::instance()->restart();
0672 
0673     if (setDatabase(newParams, false))
0674     {
0675         QApplication::setOverrideCursor(Qt::WaitCursor);
0676         startScan();
0677         QApplication::restoreOverrideCursor();
0678         ScanController::instance()->completeCollectionScan();
0679     }
0680 }
0681 
0682 bool AlbumManager::databaseEqual(const DbEngineParameters& parameters) const
0683 {
0684     DbEngineParameters params = CoreDbAccess::parameters();
0685 
0686     return (params == parameters);
0687 }
0688 
0689 bool AlbumManager::moveToBackup(const QFileInfo& info)
0690 {
0691     if (info.exists())
0692     {
0693         QFileInfo backup(info.dir(), info.fileName() +
0694                                      QLatin1String("-backup-") +
0695                                      QDateTime::currentDateTime().toString(Qt::ISODate));
0696 
0697         bool ret = QDir().rename(info.filePath(), backup.filePath());
0698 
0699         if (!ret)
0700         {
0701             QMessageBox::critical(qApp->activeWindow(), qApp->applicationName(),
0702                                   i18n("Failed to backup the existing database file (\"%1\"). "
0703                                        "Refusing to replace file without backup, using the existing file.",
0704                                        QDir::toNativeSeparators(info.filePath())));
0705             return false;
0706         }
0707     }
0708 
0709     return true;
0710 }
0711 
0712 bool AlbumManager::copyToNewLocation(const QFileInfo& oldFile,
0713                                      const QFileInfo& newFile,
0714                                      const QString& otherMessage)
0715 {
0716     QString message = otherMessage;
0717 
0718     if (message.isNull())
0719     {
0720         message = i18n("Failed to copy the old database file (\"%1\") "
0721                        "to its new location (\"%2\"). "
0722                        "Starting with an empty database.",
0723                        QDir::toNativeSeparators(oldFile.filePath()),
0724                        QDir::toNativeSeparators(newFile.filePath()));
0725     }
0726 
0727     bool ret = QFile::copy(oldFile.filePath(), newFile.filePath());
0728 
0729     if (!ret)
0730     {
0731         QMessageBox::critical(qApp->activeWindow(), qApp->applicationName(), message);
0732         return false;
0733     }
0734 
0735     return true;
0736 }
0737 
0738 bool AlbumManager::showDatabaseSetupPage(const QString& error, bool priority, const QString& suggestedAlbumRoot)
0739 {
0740     QApplication::restoreOverrideCursor();
0741 
0742     QString errorMsg = error + i18n("<p><b>Please check the database settings in this dialog.</b></p>");
0743 
0744     // We cannot use Setup::execSinglePage() as this already requires a core database.
0745 
0746     QPointer<QDialog> setup                  = new QDialog(qApp->activeWindow());
0747     QVBoxLayout* const layout                = new QVBoxLayout(setup);
0748     QLabel* const errorLabel                 = new QLabel(errorMsg, setup);
0749     errorLabel->setWordWrap(true);
0750     errorLabel->setTextFormat(Qt::RichText);
0751     errorLabel->setAlignment(Qt::AlignCenter);
0752     errorLabel->setFrameStyle(QFrame::StyledPanel | QFrame::Raised);
0753     errorLabel->setStyleSheet(QLatin1String("QLabel { background-color: #FFDDDD; color: black; }"));
0754     DatabaseSettingsWidget* const dbsettings = new DatabaseSettingsWidget(setup);
0755     QDialogButtonBox* const buttons          = new QDialogButtonBox(QDialogButtonBox::Ok    |
0756                                                                     QDialogButtonBox::Reset |
0757                                                                     QDialogButtonBox::Cancel, setup);
0758     buttons->button(QDialogButtonBox::Reset)->setText(i18nc("@action:button", "New database"));
0759     buttons->button(QDialogButtonBox::Ok)->setDefault(true);
0760 
0761     layout->addWidget(errorLabel);
0762     layout->addWidget(dbsettings);
0763     layout->addStretch(10);
0764     layout->addWidget(buttons);
0765 
0766     bool newDatabase = false;
0767 
0768     connect(buttons->button(QDialogButtonBox::Ok), SIGNAL(clicked()),
0769             setup, SLOT(accept()));
0770 
0771     connect(buttons->button(QDialogButtonBox::Cancel), SIGNAL(clicked()),
0772             setup, SLOT(reject()));
0773 
0774     connect(buttons->button(QDialogButtonBox::Reset), &QPushButton::clicked,
0775             this, [&]()
0776             {
0777                 newDatabase = true;
0778                 setup->accept();
0779             }
0780     );
0781 
0782     ApplicationSettings* const settings = ApplicationSettings::instance();
0783     dbsettings->setParametersFromSettings(settings);
0784 
0785     if (setup->exec() != QDialog::Accepted)
0786     {
0787         delete setup;
0788 
0789         return false;
0790     }
0791 
0792     DbEngineParameters dbParams = dbsettings->getDbEngineParameters();
0793     settings->setDbEngineParameters(dbParams);
0794     settings->saveSettings();
0795 
0796     delete setup;
0797 
0798     if (newDatabase)
0799     {
0800         if (dbParams.internalServer)
0801         {
0802             DatabaseServerError result = DatabaseServerStarter::instance()->startServerManagerProcess(dbParams);
0803 
0804             if (result.getErrorType() != DatabaseServerError::NoErrors)
0805             {
0806                 return false;
0807             }
0808         }
0809 
0810         CoreDbAccess::setParameters(dbParams);
0811 
0812         if (!CoreDbAccess::checkReadyForUse())
0813         {
0814             return false;
0815         }
0816     }
0817 
0818     return (setDatabase(dbParams, priority, suggestedAlbumRoot));
0819 }
0820 
0821 } // namespace Digikam