File indexing completed on 2025-01-19 03:50:42

0001 /* ============================================================
0002  *
0003  * This file is a part of digiKam project
0004  * https://www.digikam.org
0005  *
0006  * Date        : 2002-07-28
0007  * Description : main program from digiKam
0008  *
0009  * SPDX-FileCopyrightText: 2002-2006 by Renchi Raju <renchi dot raju at gmail dot com>
0010  * SPDX-FileCopyrightText: 2002-2024 by Gilles Caulier <caulier dot gilles at gmail dot com>
0011  *
0012  * SPDX-License-Identifier: GPL-2.0-or-later
0013  *
0014  * ============================================================ */
0015 
0016 #include "digikam_config.h"
0017 
0018 // Qt includes
0019 
0020 #include <QDir>
0021 #include <QFile>
0022 #include <QString>
0023 #include <QFileInfo>
0024 #include <QSettings>
0025 #include <QStringList>
0026 #include <QMessageBox>
0027 #include <QSqlDatabase>
0028 #include <QApplication>
0029 #include <QImageReader>
0030 #include <QStandardPaths>
0031 #include <QCommandLineParser>
0032 #include <QCommandLineOption>
0033 
0034 // KDE includes
0035 
0036 #include <klocalizedstring.h>
0037 #include <ksharedconfig.h>
0038 #include <kconfiggroup.h>
0039 #include <kaboutdata.h>
0040 
0041 // ImageMagick includes
0042 
0043 #ifdef HAVE_IMAGE_MAGICK
0044 
0045 // Pragma directives to reduce warnings from ImageMagick header files.
0046 #   if !defined(Q_OS_DARWIN) && defined(Q_CC_GNU)
0047 #       pragma GCC diagnostic push
0048 #       pragma GCC diagnostic ignored "-Wignored-qualifiers"
0049 #       pragma GCC diagnostic ignored "-Wzero-as-null-pointer-constant"
0050 #   endif
0051 
0052 #   if defined(Q_CC_CLANG)
0053 #       pragma clang diagnostic push
0054 #       pragma clang diagnostic ignored "-Wignored-qualifiers"
0055 #       pragma clang diagnostic ignored "-Wkeyword-macro"
0056 #   endif
0057 
0058 #   include <Magick++.h>
0059 using namespace Magick;
0060 
0061 // Restore warnings
0062 #   if !defined(Q_OS_DARWIN) && defined(Q_CC_GNU)
0063 #       pragma GCC diagnostic pop
0064 #   endif
0065 
0066 #   if defined(Q_CC_CLANG)
0067 #       pragma clang diagnostic pop
0068 #   endif
0069 
0070 #endif // HAVE_IMAGE_MAGICK
0071 
0072 // Local includes
0073 
0074 #include "digikam_debug.h"
0075 #include "digikam_version.h"
0076 #include "digikam_globals.h"
0077 #include "systemsettings.h"
0078 #include "metaengine.h"
0079 #include "dmessagebox.h"
0080 #include "albummanager.h"
0081 #include "firstrundlg.h"
0082 #include "collectionlocation.h"
0083 #include "collectionmanager.h"
0084 #include "daboutdata.h"
0085 #include "dbengineparameters.h"
0086 #include "digikamapp.h"
0087 #include "scancontroller.h"
0088 #include "coredbaccess.h"
0089 #include "thumbsdbaccess.h"
0090 #include "facedbaccess.h"
0091 #include "dxmlguiwindow.h"
0092 #include "applicationsettings.h"
0093 #include "similaritydbaccess.h"
0094 #include "databaseserverstarter.h"
0095 #include "filesdownloader.h"
0096 #include "dfileoperations.h"
0097 
0098 #ifdef Q_OS_WIN
0099 #   include <windows.h>
0100 #   include <shellapi.h>
0101 #   include <objbase.h>
0102 #endif
0103 
0104 #if defined Q_OS_WIN
0105 #   define MAIN_EXPORT __declspec(dllexport)
0106 #   define MAIN_FN digikam_main
0107 #else
0108 #   define MAIN_EXPORT
0109 #   define MAIN_FN main
0110 #endif
0111 
0112 using namespace Digikam;
0113 
0114 extern "C" MAIN_EXPORT int MAIN_FN(int argc, char** argv)
0115 {
0116     SystemSettings system(QLatin1String("digikam"));
0117 
0118 #if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
0119 
0120     // These settings has no effect with Qt6 (always enabled)
0121 
0122     QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps,
0123                                    system.useHighDpiPixmaps);
0124 
0125 
0126     if (system.useHighDpiScaling)
0127     {
0128         QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
0129     }
0130     else
0131     {
0132         QCoreApplication::setAttribute(Qt::AA_DisableHighDpiScaling);
0133     }
0134 
0135 #else
0136 
0137     QImageReader::setAllocationLimit(2048);
0138 
0139 #endif
0140 
0141     if (system.softwareOpenGL)
0142     {
0143         QCoreApplication::setAttribute(Qt::AA_UseSoftwareOpenGL);
0144     }
0145 
0146     // OpenCV crash with face engine with OpenCL support
0147     // https://bugs.kde.org/show_bug.cgi?id=423632
0148     // https://bugs.kde.org/show_bug.cgi?id=426175
0149 
0150     if (system.disableOpenCL)
0151     {
0152         qputenv("OPENCV_OPENCL_RUNTIME", "disabled");
0153         qputenv("OPENCV_OPENCL_DEVICE",  "disabled");
0154     }
0155 
0156     QGuiApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy::RoundPreferFloor);
0157 
0158 #ifdef HAVE_QWEBENGINE
0159 
0160     QCoreApplication::setAttribute(Qt::AA_ShareOpenGLContexts);
0161 
0162 #endif
0163 
0164     QApplication app(argc, argv);
0165 
0166     digikamSetDebugFilterRules(system.enableLogging);
0167 
0168     tryInitDrMingw();
0169 
0170 #ifdef HAVE_IMAGE_MAGICK
0171 
0172 #ifdef Q_CC_MSVC
0173 
0174     qputenv("MAGICK_CODER_MODULE_PATH", qApp->applicationDirPath().toUtf8());
0175     qputenv("MAGICK_CODER_FILTER_PATH", qApp->applicationDirPath().toUtf8());
0176 
0177 #endif
0178 
0179     InitializeMagick(nullptr);
0180 
0181 #endif
0182 
0183 #ifdef Q_OS_MACOS
0184 
0185     // See bug #461734
0186     app.setAttribute(Qt::AA_DontShowIconsInMenus, true);
0187 
0188 #endif
0189 
0190     // if we have some local breeze icon resource, prefer it
0191 
0192     DXmlGuiWindow::setupIconTheme();
0193 
0194     KLocalizedString::setApplicationDomain("digikam");
0195 
0196     KAboutData aboutData(QLatin1String("digikam"), // component name
0197                          i18n("digiKam"),          // display name
0198                          digiKamVersion());
0199 
0200     aboutData.setShortDescription(QString::fromUtf8("%1 - %2").arg(DAboutData::digiKamSlogan())
0201                                                               .arg(DAboutData::digiKamFamily()));
0202     aboutData.setLicense(KAboutLicense::GPL);
0203     aboutData.setCopyrightStatement(DAboutData::copyright());
0204     aboutData.setOtherText(additionalInformation());
0205     aboutData.setHomepage(DAboutData::webProjectUrl().url());
0206     aboutData.setTranslator(i18nc("NAME OF TRANSLATORS", "Your names"),
0207                             i18nc("EMAIL OF TRANSLATORS", "Your emails"));
0208 
0209     DAboutData::authorsRegistration(aboutData);
0210 
0211     QCommandLineParser parser;
0212     KAboutData::setApplicationData(aboutData);
0213     aboutData.setupCommandLine(&parser);
0214     parser.addOption(QCommandLineOption(QStringList() << QLatin1String("download-from"),
0215                                         i18n("Open camera dialog at \"path\""),
0216                                         QLatin1String("path")));
0217     parser.addOption(QCommandLineOption(QStringList() << QLatin1String("download-from-udi"),
0218                                         i18n("Open camera dialog for the device with Solid UDI \"udi\""),
0219                                         QLatin1String("udi")));
0220     parser.addOption(QCommandLineOption(QStringList() << QLatin1String("detect-camera"),
0221                                         i18n("Automatically detect and open a connected gphoto2 camera")));
0222     parser.addOption(QCommandLineOption(QStringList() << QLatin1String("database-directory"),
0223                                         i18n("Start digikam with the SQLite database file found in the directory \"dir\""),
0224                                         QLatin1String("dir")));
0225     parser.addOption(QCommandLineOption(QStringList() << QLatin1String("config"),
0226                                         i18n("Start digikam with the configuration file \"config\""),
0227                                         QLatin1String("config")));
0228 
0229     parser.process(app);
0230     aboutData.processCommandLine(&parser);
0231 
0232     setupKSycocaDatabaseFile();
0233 
0234     // See bug #438701
0235 
0236     installQtTranslationFiles(app);
0237 
0238     // ---
0239 
0240     MetaEngine::initializeExiv2();
0241 
0242     // Force to use application icon for non plasma desktop as Unity for ex.
0243 
0244     QApplication::setWindowIcon(QIcon::fromTheme(QLatin1String("digikam"), app.windowIcon()));
0245 
0246 #ifdef Q_OS_WIN
0247 
0248     if (QSysInfo::currentCpuArchitecture().contains(QLatin1String("64")) &&
0249         !QSysInfo::buildCpuArchitecture().contains(QLatin1String("64")))
0250     {
0251         QMessageBox::critical(qApp->activeWindow(),
0252                               qApp->applicationName(),
0253                               i18n("<p>You are running digiKam as a 32-bit version on a 64-bit Windows.</p>"
0254                                    "<p>Please install the 64-bit version of digiKam to get "
0255                                    "a better experience with digiKam.</p>"));
0256     }
0257 
0258     QString appPath = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);
0259     QUrl    appUrl  = QUrl::fromLocalFile(appPath).adjusted(QUrl::RemoveFilename);
0260     appUrl.setPath(appUrl.path() + QLatin1String("digikam/facesengine"));
0261 
0262     QFileInfo appInfo(appUrl.toLocalFile());
0263 
0264     if (appInfo.exists() && appInfo.isDir())
0265     {
0266         QString appLocalPath = QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation);
0267         QUrl    appLocalUrl  = QUrl::fromLocalFile(appLocalPath).adjusted(QUrl::RemoveFilename);
0268         appLocalUrl.setPath(appLocalUrl.path() + QLatin1String("digikam"));
0269 
0270         qCDebug(DIGIKAM_GENERAL_LOG) << "Copy faces engine data from"
0271                                      << appUrl.toLocalFile() << "to"
0272                                      << appLocalUrl.toLocalFile();
0273 
0274         if (DFileOperations::copyFolderRecursively(appUrl.toLocalFile(), appLocalUrl.toLocalFile()))
0275         {
0276             qCDebug(DIGIKAM_GENERAL_LOG) << "Delete faces engine data from"
0277                                          << appUrl.toLocalFile();
0278 
0279             QDir rmAppPath(appUrl.toLocalFile());
0280             rmAppPath.removeRecursively();
0281         }
0282     }
0283 
0284 #endif
0285 
0286     // Check if Qt database plugins are available.
0287 
0288     if (!QSqlDatabase::isDriverAvailable(DbEngineParameters::SQLiteDatabaseType()) &&
0289         !QSqlDatabase::isDriverAvailable(DbEngineParameters::MySQLDatabaseType()))
0290     {
0291         if (QSqlDatabase::drivers().isEmpty())
0292         {
0293             QMessageBox::critical(qApp->activeWindow(),
0294                                   qApp->applicationName(),
0295                                   i18n("Run-time Qt SQLite or MySQL database plugin is not available. "
0296                                        "please install it.\n"
0297                                        "There is no database plugin installed on your computer."));
0298         }
0299         else
0300         {
0301             DMessageBox::showInformationList(QMessageBox::Warning,
0302                                              qApp->activeWindow(),
0303                                              qApp->applicationName(),
0304                                              i18n("Run-time Qt SQLite or MySQL database plugin are not available. "
0305                                                   "Please install it.\n"
0306                                                   "Database plugins installed on your computer are listed below."),
0307                                              QSqlDatabase::drivers());
0308         }
0309 
0310         qCDebug(DIGIKAM_GENERAL_LOG) << "QT Sql drivers list: " << QSqlDatabase::drivers();
0311 
0312         return 1;
0313     }
0314 
0315     QString commandLineDBPath;
0316 
0317     if (parser.isSet(QLatin1String("database-directory")))
0318     {
0319         QDir commandLineDBDir(parser.value(QLatin1String("database-directory")));
0320 
0321         if (!commandLineDBDir.exists())
0322         {
0323             qCDebug(DIGIKAM_GENERAL_LOG) << "The given database-directory does not exist or is not readable. Ignoring."
0324                                          << commandLineDBDir.absolutePath();
0325         }
0326         else
0327         {
0328             commandLineDBPath = commandLineDBDir.absolutePath();
0329         }
0330     }
0331 
0332     if (parser.isSet(QLatin1String("config")))
0333     {
0334         QString configFilename = parser.value(QLatin1String("config"));
0335         QFileInfo configFile(configFilename);
0336 
0337         if (configFile.isDir()       || !configFile.dir().exists() ||
0338             !configFile.isReadable() || !configFile.isWritable())
0339         {
0340             QMessageBox::critical(qApp->activeWindow(),
0341                                   qApp->applicationName(),
0342                                   QLatin1String("--config ") +
0343                                   configFilename             +
0344                                   i18n("<p>The given path for the config file "
0345                                        "is not valid. Either its parent "
0346                                        "directory does not exist, it is a "
0347                                        "directory itself or it cannot be read/"
0348                                        "written to.</p>"));
0349             qCDebug(DIGIKAM_GENERAL_LOG) << "Invalid path: --config"
0350                                          << configFilename;
0351             return 1;
0352         }
0353     }
0354 
0355     KSharedConfig::Ptr config = KSharedConfig::openConfig();
0356     KConfigGroup group        = config->group(QLatin1String("General Settings"));
0357     QString version           = group.readEntry(QLatin1String("Version"), QString());
0358     QString iconTheme         = group.readEntry(QLatin1String("Icon Theme"), QString());
0359     KConfigGroup mainConfig   = config->group(QLatin1String("Album Settings"));
0360 
0361     QString            firstAlbumPath;
0362     DbEngineParameters params;
0363 
0364     // Run the first run assistant if we have no or very old config
0365 
0366     if (!mainConfig.exists() || (version.startsWith(QLatin1String("0.5"))))
0367     {
0368         FirstRunDlg firstRun;
0369         firstRun.show();
0370 
0371         if (firstRun.exec() == QDialog::Rejected)
0372         {
0373             return 1;
0374         }
0375 
0376         // parameters are written to config
0377 
0378         firstAlbumPath = firstRun.firstAlbumPath();
0379 
0380         if (firstRun.getDbEngineParameters().isSQLite())
0381         {
0382             AlbumManager::checkDatabaseDirsAfterFirstRun(firstRun.getDbEngineParameters().getCoreDatabaseNameOrDir(), firstAlbumPath);
0383         }
0384     }
0385 
0386     if (!commandLineDBPath.isNull())
0387     {
0388         // command line option set?
0389 
0390         params = DbEngineParameters::parametersForSQLiteDefaultFile(commandLineDBPath);
0391         ApplicationSettings::instance()->setDatabaseDirSetAtCmd(true);
0392         ApplicationSettings::instance()->setDbEngineParameters(params);
0393     }
0394     else
0395     {
0396         params = DbEngineParameters::parametersFromConfig();
0397         params.legacyAndDefaultChecks(firstAlbumPath);
0398 
0399         // sync to config, for all first-run or upgrade situations
0400 
0401         params.writeToConfig();
0402         ApplicationSettings::instance()->setDbEngineParameters(params);
0403     }
0404 
0405     // initialize database
0406 
0407     if (!AlbumManager::instance()->setDatabase(params, !commandLineDBPath.isNull(), firstAlbumPath))
0408     {
0409         DatabaseServerStarter::instance()->stopServerManagerProcess();
0410 
0411         CoreDbAccess::cleanUpDatabase();
0412         ThumbsDbAccess::cleanUpDatabase();
0413         FaceDbAccess::cleanUpDatabase();
0414         SimilarityDbAccess::cleanUpDatabase();
0415 
0416         return 0;
0417     }
0418 
0419     if (!iconTheme.isEmpty())
0420     {
0421         QIcon::setThemeName(iconTheme);
0422     }
0423 
0424 #ifdef Q_OS_WIN
0425 
0426     // Necessary to open native open with dialog on windows
0427 
0428     CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);
0429 
0430 #endif
0431 
0432     // create main window
0433 
0434     DigikamApp* const digikam = new DigikamApp();
0435 
0436     // If application storage place in home directory to save customized XML settings files do not exist, create it,
0437     // else QFile will not able to create new files as well.
0438 
0439     if (!QFile::exists(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation)))
0440     {
0441         QDir().mkpath(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation));
0442     }
0443 
0444     // If application cache place in home directory to save cached files do not exist, create it.
0445     if (!QFile::exists(QStandardPaths::writableLocation(QStandardPaths::CacheLocation)))
0446     {
0447         QDir().mkpath(QStandardPaths::writableLocation(QStandardPaths::CacheLocation));
0448     }
0449 
0450     // Bug #247175:
0451     // Add a connection to the destroyed() signal when the digiKam mainwindow has been
0452     // closed. This should prevent digiKam from staying open in the background.
0453     //
0454     // Right now this is the easiest and cleanest fix for the described problem, but we might re-think the
0455     // solution later on, just in case there are better ways to do it.
0456 
0457     QObject::connect(digikam, SIGNAL(destroyed(QObject*)),
0458                      &app, SLOT(quit()));
0459 
0460     digikam->restoreSession();
0461     digikam->show();
0462 
0463     if (system.enableFaceEngine || system.enableAesthetic || system.enableAutoTags)
0464     {
0465         QPointer<FilesDownloader> floader = new FilesDownloader(qApp->activeWindow());
0466 
0467         if (!floader->checkDownloadFiles())
0468         {
0469             floader->startDownload();
0470         }
0471 
0472         delete floader;
0473     }
0474 
0475     if      (parser.isSet(QLatin1String("download-from")))
0476     {
0477         digikam->downloadFrom(parser.value(QLatin1String("download-from")));
0478     }
0479     else if (parser.isSet(QLatin1String("download-from-udi")))
0480     {
0481         digikam->downloadFromUdi(parser.value(QLatin1String("download-from-udi")));
0482     }
0483     else if (parser.isSet(QLatin1String("detect-camera")))
0484     {
0485         digikam->autoDetect();
0486     }
0487 
0488     int ret = app.exec();
0489 
0490     CoreDbAccess::cleanUpDatabase();
0491     ThumbsDbAccess::cleanUpDatabase();
0492     FaceDbAccess::cleanUpDatabase();
0493     SimilarityDbAccess::cleanUpDatabase();
0494 
0495 #ifdef Q_OS_WIN
0496 
0497     // Necessary to open native open with dialog on windows
0498     CoUninitialize();
0499 
0500 #endif
0501 
0502 #ifdef HAVE_IMAGE_MAGICK
0503 #   if MagickLibVersion >= 0x693
0504 
0505     TerminateMagick();
0506 
0507 #   endif
0508 #endif
0509 
0510     return ret;
0511 }