File indexing completed on 2025-03-09 03:50:02

0001 /* ============================================================
0002  *
0003  * This file is a part of digiKam project
0004  * https://www.digikam.org
0005  *
0006  * Date        : 2002-16-10
0007  * Description : main digiKam interface implementation
0008  *
0009  * SPDX-FileCopyrightText: 2002-2005 by Renchi Raju <renchi dot raju at gmail dot com>
0010  * SPDX-FileCopyrightText:      2006 by Tom Albers <tomalbers at kde dot nl>
0011  * SPDX-FileCopyrightText: 2009-2012 by Andi Clemens <andi dot clemens at gmail dot com>
0012  * SPDX-FileCopyrightText:      2013 by Michael G. Hansen <mike at mghansen dot de>
0013  * SPDX-FileCopyrightText: 2014-2015 by Mohamed_Anwer <m_dot_anwer at gmx dot com>
0014  * SPDX-FileCopyrightText: 2002-2024 by Gilles Caulier <caulier dot gilles at gmail dot com>
0015  *
0016  * SPDX-License-Identifier: GPL-2.0-or-later
0017  *
0018  * ============================================================ */
0019 
0020 #include "digikamapp_p.h"
0021 
0022 namespace Digikam
0023 {
0024 
0025 DigikamApp* DigikamApp::m_instance = nullptr;
0026 
0027 DigikamApp::DigikamApp()
0028     : DXmlGuiWindow(nullptr),
0029       d            (new Private)
0030 {
0031     setObjectName(QLatin1String("Digikam"));
0032     setConfigGroupName(ApplicationSettings::instance()->generalConfigGroupName());
0033     setFullScreenOptions(FS_ALBUMGUI);
0034     setXMLFile(QLatin1String("digikamui5.rc"));
0035 
0036     m_instance         = this;
0037     d->config          = KSharedConfig::openConfig();
0038     KConfigGroup group = d->config->group(configGroupName());
0039 
0040 #ifdef HAVE_DBUS
0041 
0042     new DigikamAdaptor(this);
0043     QDBusConnection::sessionBus().registerObject(QLatin1String("/Digikam"), this);
0044     QDBusConnection::sessionBus().registerService(QLatin1String("org.kde.digikam-") +
0045                                                   QString::number(QCoreApplication::instance()->applicationPid()));
0046 #endif
0047 
0048     // collection scan
0049 
0050     if (!CollectionScanner::databaseInitialScanDone())
0051     {
0052         ScanController::instance()->completeCollectionScanDeferFiles();
0053     }
0054 
0055     if (ApplicationSettings::instance()->getShowSplashScreen() &&
0056         !qApp->isSessionRestored())
0057     {
0058         d->splashScreen = new DSplashScreen();
0059         d->splashScreen->show();
0060     }
0061 
0062     // We need here QCoreApplication::processEvents().
0063 
0064     qApp->processEvents();
0065 
0066     if (d->splashScreen)
0067     {
0068         d->splashScreen->setMessage(i18n("Initializing..."));
0069     }
0070 
0071     // Ensure creation
0072 
0073     AlbumManager::instance();
0074     LoadingCacheInterface::initialize();
0075     IccSettings::instance()->loadAllProfilesProperties();
0076     MetaEngineSettings::instance();
0077     DMetadataSettings::instance();
0078     ProgressManager::instance();
0079     ThumbnailLoadThread::setDisplayingWidget(this);
0080     DIO::instance();
0081     LocalizeSettings::instance();
0082     NetworkManager::instance();
0083 
0084 #ifdef HAVE_GEOLOCATION
0085 
0086     connect(GeolocationSettings::instance(), &GeolocationSettings::signalSetupGeolocation,
0087             this, [=](int tab)
0088         {
0089             Setup::execGeolocation(this, tab);
0090         }
0091     );
0092 
0093     qCDebug(DIGIKAM_GENERAL_LOG) << GeolocationSettings::instance()->settings();
0094 
0095 #endif
0096 
0097     connect(LocalizeSettings::instance(), &LocalizeSettings::signalOpenLocalizeSetup,
0098             this, [=]()
0099         {
0100             Setup::execLocalize(this);
0101         }
0102     );
0103 
0104     if (!ExifToolProcess::isCreated())
0105     {
0106         ExifToolThread* const exifToolThread = new ExifToolThread(qApp);
0107         exifToolThread->start();
0108     }
0109 
0110     // creation of the engine on first use - when drawing -
0111     // can take considerable time and cause a noticeable hang in the UI thread.
0112 
0113     QFontMetrics fm(font());
0114     fm.horizontalAdvance(QLatin1String("a"));
0115 
0116     connect(ApplicationSettings::instance(), SIGNAL(setupChanged()),
0117             this, SLOT(slotSetupChanged()));
0118 
0119     connect(IccSettings::instance(), SIGNAL(signalSettingsChanged()),
0120             this, SLOT(slotColorManagementOptionsChanged()));
0121 
0122     d->cameraMenu      = new QMenu(this);
0123     d->usbMediaMenu    = new QMenu(this);
0124     d->cardReaderMenu  = new QMenu(this);
0125     d->quickImportMenu = new QMenu(this);
0126 
0127     d->cameraList = new CameraList(this, QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) +
0128                                          QLatin1String("/cameras.xml"));
0129 
0130     connect(d->cameraList, SIGNAL(signalCameraAdded(CameraType*)),
0131             this, SLOT(slotCameraAdded(CameraType*)));
0132 
0133     connect(d->cameraList, SIGNAL(signalCameraRemoved(QAction*)),
0134             this, SLOT(slotCameraRemoved(QAction*)));
0135 
0136     d->modelCollection = new DModelFactory;
0137 
0138     // This manager must be created after collection setup and before accelerators setup.
0139 
0140     d->tagsActionManager = new TagsActionMngr(this);
0141 
0142     // Load plugins
0143 
0144     if (d->splashScreen)
0145     {
0146         d->splashScreen->setMessage(i18n("Load Plugins..."));
0147     }
0148 
0149     DPluginLoader* const dpl = DPluginLoader::instance();
0150     dpl->init();
0151 
0152     // First create everything, then connect.
0153     // Otherwise some items may send signals and the slots can try
0154     // to access items which were not created yet.
0155 
0156     setupView();
0157     setupAccelerators();
0158     setupActions();
0159     setupStatusBar();
0160 
0161     initGui();
0162 
0163     setupViewConnections();
0164     applyMainWindowSettings(group);
0165     slotColorManagementOptionsChanged();
0166 
0167     // Check ICC profiles repository availability
0168 
0169     if (d->splashScreen)
0170     {
0171         d->splashScreen->setMessage(i18n("Checking ICC repository..."));
0172     }
0173 
0174     d->validIccPath = SetupICC::iccRepositoryIsValid();
0175 
0176     // Clean up database if enabled in the settings
0177 
0178     if (ApplicationSettings::instance()->getCleanAtStart() &&
0179         CollectionScanner::databaseInitialScanDone())
0180     {
0181         if (d->splashScreen)
0182         {
0183             d->splashScreen->setMessage(i18n("Clean up Database..."));
0184         }
0185 
0186         QEventLoop loop;
0187 
0188         DbCleaner* const tool = new DbCleaner(false, false);
0189 
0190         connect(tool, SIGNAL(signalComplete()),
0191                 &loop, SLOT(quit()));
0192 
0193         tool->start();
0194         loop.exec();
0195     }
0196 
0197     // Build trash counters from all collections
0198 
0199     DIO::buildCollectionTrashCounters();
0200 
0201     // Read albums from database
0202 
0203     if (d->splashScreen)
0204     {
0205         d->splashScreen->setMessage(i18n("Loading albums..."));
0206     }
0207 
0208     if (CoreDbAccess().backend()->isOpen())
0209     {
0210         AlbumManager::instance()->startScan();
0211     }
0212 
0213     // Setting the initial menu options after all tools have been loaded
0214 
0215     QList<Album*> albumList = AlbumManager::instance()->currentAlbums();
0216     d->view->slotAlbumSelected(albumList);
0217 
0218     // preload additional windows
0219 
0220     preloadWindows();
0221 
0222     readFullScreenSettings(group);
0223 
0224 #ifdef HAVE_KFILEMETADATA
0225 
0226     // Create BalooWrap object, because it need to register a listener
0227     // to update digiKam data when changes in Baloo occur
0228     BalooWrap* const baloo = BalooWrap::instance();
0229     Q_UNUSED(baloo);
0230 
0231 #endif // HAVE_KFILEMETADATA
0232 
0233     setAutoSaveSettings(group, true);
0234 
0235     LoadSaveThread::setInfoProvider(new DatabaseLoadSaveFileInfoProvider);
0236 
0237     setupSelectToolsAction();
0238 }
0239 
0240 DigikamApp::~DigikamApp()
0241 {
0242     ProgressManager::instance()->slotAbortAll();
0243 
0244     ItemAttributesWatch::shutDown();
0245 
0246     // Close and delete image editor instance.
0247 
0248     if (ImageWindow::imageWindowCreated())
0249     {
0250         ImageWindow::imageWindow()->setAttribute(Qt::WA_DeleteOnClose, true);
0251         ImageWindow::imageWindow()->close();
0252         qApp->processEvents();
0253     }
0254 
0255     // Close and delete light table instance.
0256 
0257     if (LightTableWindow::lightTableWindowCreated())
0258     {
0259         LightTableWindow::lightTableWindow()->setAttribute(Qt::WA_DeleteOnClose, true);
0260         LightTableWindow::lightTableWindow()->close();
0261         qApp->processEvents();
0262     }
0263 
0264     // Close and delete Batch Queue Manager instance.
0265 
0266     if (QueueMgrWindow::queueManagerWindowCreated())
0267     {
0268         QueueMgrWindow::queueManagerWindow()->setAttribute(Qt::WA_DeleteOnClose, true);
0269         QueueMgrWindow::queueManagerWindow()->close();
0270         qApp->processEvents();
0271     }
0272 
0273     // Close and delete Tags Manager instance.
0274 
0275     if (TagsManager::isCreated())
0276     {
0277         TagsManager::instance()->setAttribute(Qt::WA_DeleteOnClose, true);
0278         TagsManager::instance()->close();
0279     }
0280 
0281     if (MetadataHubMngr::isCreated())
0282     {
0283         delete MetadataHubMngr::internalPtr;
0284     }
0285 
0286 #ifdef HAVE_KFILEMETADATA
0287 
0288     if (BalooWrap::isCreated())
0289     {
0290         delete BalooWrap::internalPtr;
0291     }
0292 
0293 #endif
0294 
0295     delete d->view;
0296     d->view = nullptr;
0297 
0298     DPluginLoader::instance()->cleanUp();
0299 
0300     ApplicationSettings::instance()->setRecurseAlbums(d->recurseAlbumsAction->isChecked());
0301     ApplicationSettings::instance()->setRecurseTags(d->recurseTagsAction->isChecked());
0302     ApplicationSettings::instance()->setShowThumbbar(d->showBarAction->isChecked());
0303     ApplicationSettings::instance()->saveSettings();
0304 
0305     ScanController::instance()->shutDown();
0306     AlbumManager::instance()->cleanUp();
0307     ItemAttributesWatch::cleanUp();
0308     AlbumThumbnailLoader::instance()->cleanUp();
0309     ThumbnailLoadThread::cleanUp();
0310     LoadingCacheInterface::cleanUp();
0311     DIO::cleanUp();
0312 
0313     // close database server
0314 
0315     DatabaseServerStarter::instance()->stopServerManagerProcess();
0316 
0317     delete d->modelCollection;
0318 
0319     m_instance = nullptr;
0320 
0321     delete d;
0322 }
0323 
0324 DigikamApp* DigikamApp::instance()
0325 {
0326     return m_instance;
0327 }
0328 
0329 ItemIconView* DigikamApp::view() const
0330 {
0331     return d->view;
0332 }
0333 
0334 void DigikamApp::show()
0335 {
0336     // Remove Splashscreen.
0337 
0338     if (d->splashScreen)
0339     {
0340         d->splashScreen->finish(this);
0341     }
0342 
0343     // Display application window.
0344 
0345     KMainWindow::show();
0346 
0347     // Report errors from ICC repository path.
0348 
0349     if (!d->validIccPath)
0350     {
0351         QString message = i18n("<p>The ICC profiles folder seems to be invalid.</p>"
0352                                "<p>If you want to try setting it again, choose \"Yes\" here, otherwise "
0353                                "choose \"No\", and the \"Color Management\" feature "
0354                                "will be disabled until you solve this issue.</p>");
0355 
0356         if (QMessageBox::warning(this, qApp->applicationName(), message, QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes)
0357         {
0358             if (!setupICC())
0359             {
0360                 d->config->group(QLatin1String("Color Management")).writeEntry(QLatin1String("EnableCM"), false);
0361                 d->config->sync();
0362             }
0363         }
0364         else
0365         {
0366             d->config->group(QLatin1String("Color Management")).writeEntry(QLatin1String("EnableCM"), false);
0367             d->config->sync();
0368         }
0369     }
0370 
0371     // Init album icon view zoom factor.
0372 
0373     ApplicationSettings* const settings = ApplicationSettings::instance();
0374 
0375     slotThumbSizeChanged(settings->getDefaultIconSize());
0376     slotZoomSliderChanged(settings->getDefaultIconSize());
0377     d->autoShowZoomToolTip = true;
0378 
0379     // Enable finished the collection scan as deferred process
0380 
0381     if (settings->getScanAtStart()                  ||
0382         !CollectionScanner::databaseInitialScanDone())
0383     {
0384         NewItemsFinder* const tool = new NewItemsFinder(NewItemsFinder::ScanDeferredFiles);
0385 
0386         if (settings->getDetectFacesInNewImages())
0387         {
0388             connect(tool, SIGNAL(signalComplete()),
0389                     this, SLOT(slotDetectFaces()));
0390         }
0391 
0392         QTimer::singleShot(1000, tool, SLOT(start()));
0393     }
0394 
0395     if (d->splashScreen)
0396     {
0397         delete d->splashScreen;
0398         d->splashScreen = nullptr;
0399     }
0400 }
0401 
0402 void DigikamApp::restoreSession()
0403 {
0404     // TODO: show and restore ImageEditor, Lighttable, and Batch Queue Manager main windows
0405 
0406     if (qApp->isSessionRestored())
0407     {
0408         int n = 1;
0409 
0410         while (KMainWindow::canBeRestored(n))
0411         {
0412             const QString className = KMainWindow::classNameOfToplevel(n);
0413 
0414             if (className == QLatin1String(Digikam::DigikamApp::staticMetaObject.className()))
0415             {
0416                 restore(n, false);
0417                 break;
0418             }
0419 
0420             ++n;
0421         }
0422     }
0423 }
0424 
0425 void DigikamApp::closeEvent(QCloseEvent* e)
0426 {
0427     // may show a progress dialog to finish actions
0428 
0429     FileActionMngr::instance()->requestShutDown();
0430 
0431     // may show a progress dialog to apply pending metadata
0432 
0433     if (MetadataHubMngr::isCreated())
0434     {
0435         MetadataHubMngr::instance()->requestShutDown();
0436     }
0437 
0438     DXmlGuiWindow::closeEvent(e);
0439 }
0440 
0441 bool DigikamApp::queryClose()
0442 {
0443     bool ret = true;
0444 
0445     if (ImageWindow::imageWindowCreated())
0446     {
0447         ret &= ImageWindow::imageWindow()->queryClose();
0448     }
0449 
0450     if (QueueMgrWindow::queueManagerWindowCreated())
0451     {
0452         ret &= QueueMgrWindow::queueManagerWindow()->queryClose();
0453     }
0454 
0455     return ret;
0456 }
0457 
0458 void DigikamApp::enableZoomPlusAction(bool val)
0459 {
0460     d->zoomPlusAction->setEnabled(val);
0461 }
0462 
0463 void DigikamApp::enableZoomMinusAction(bool val)
0464 {
0465     d->zoomMinusAction->setEnabled(val);
0466 }
0467 
0468 void DigikamApp::enableAlbumBackwardHistory(bool enable)
0469 {
0470     d->backwardActionMenu->setEnabled(enable);
0471 }
0472 
0473 void DigikamApp::enableAlbumForwardHistory(bool enable)
0474 {
0475     d->forwardActionMenu->setEnabled(enable);
0476 }
0477 
0478 void DigikamApp::slotAboutToShowBackwardMenu()
0479 {
0480 
0481 #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
0482 
0483     d->backwardActionMenu->popupMenu()->clear();
0484 
0485 #else
0486 
0487     d->backwardActionMenu->menu()->clear();
0488 
0489 #endif
0490 
0491     QStringList titles;
0492     d->view->getBackwardHistory(titles);
0493 
0494     for (int i = 0 ; i < titles.size() ; ++i)
0495     {
0496 
0497 #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
0498 
0499         QAction* const action = d->backwardActionMenu->popupMenu()->addAction(titles.at(i));
0500 
0501 #else
0502 
0503         QAction* const action = d->backwardActionMenu->menu()->addAction(titles.at(i));
0504 
0505 #endif
0506 
0507         int id                = i + 1;
0508 
0509         connect(action, &QAction::triggered,
0510                 this, [this, id]() { d->view->slotAlbumHistoryBack(id); });
0511     }
0512 }
0513 
0514 void DigikamApp::slotAboutToShowForwardMenu()
0515 {
0516 
0517 #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
0518 
0519     d->forwardActionMenu->popupMenu()->clear();
0520 
0521 #else
0522 
0523     d->forwardActionMenu->menu()->clear();
0524 
0525 #endif
0526 
0527     QStringList titles;
0528     d->view->getForwardHistory(titles);
0529 
0530     for (int i = 0 ; i < titles.size() ; ++i)
0531     {
0532 
0533 #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
0534 
0535         QAction* const action = d->forwardActionMenu->popupMenu()->addAction(titles.at(i));
0536 
0537 #else
0538 
0539         QAction* const action = d->forwardActionMenu->menu()->addAction(titles.at(i));
0540 
0541 #endif
0542 
0543         int id                = i + 1;
0544 
0545         connect(action, &QAction::triggered,
0546                 this, [this, id]() { d->view->slotAlbumHistoryForward(id); });
0547     }
0548 }
0549 
0550 void DigikamApp::slotAlbumSelected(Album* album)
0551 {
0552     if (album && !album->isTrashAlbum())
0553     {
0554         PAlbum* const palbum = dynamic_cast<PAlbum*>(album);
0555 
0556         if (album->type() != Album::PHYSICAL || !palbum)
0557         {
0558             // Rules if not Physical album.
0559 
0560             d->deleteAction->setEnabled(false);
0561             d->renameAction->setEnabled(false);
0562             d->propsEditAction->setEnabled(false);
0563             d->openInFileManagerAction->setEnabled(false);
0564             d->newAction->setEnabled(false);
0565             d->writeAlbumMetadataAction->setEnabled(true);
0566             d->readAlbumMetadataAction->setEnabled(true);
0567 
0568             d->pasteItemsAction->setEnabled(!album->isRoot());
0569 
0570             d->selectInvertAction->setEnabled(!album->isRoot());
0571             d->selectNoneAction->setEnabled(!album->isRoot());
0572             d->selectAllAction->setEnabled(!album->isRoot());
0573 
0574             // Special case if Tag album.
0575 
0576             bool enabled = (
0577                             (album->type() == Album::TAG)                       &&
0578                             !album->isRoot()                                    &&
0579                             (album->id() != FaceTags::unconfirmedPersonTagId()) &&
0580                             (album->id() != FaceTags::unknownPersonTagId())
0581                            );
0582 
0583             d->newTagAction->setEnabled(enabled);
0584             d->deleteTagAction->setEnabled(enabled);
0585             d->editTagAction->setEnabled(enabled);
0586         }
0587         else
0588         {
0589             // Rules if Physical album.
0590 
0591             // We have either the abstract root album,
0592             // the album root album for collection base dirs, or normal albums.
0593 
0594             bool isRoot          = palbum->isRoot();
0595             bool isAlbumRoot     = palbum->isAlbumRoot();
0596             bool isNormalAlbum   = !isRoot && !isAlbumRoot;
0597 
0598             d->deleteAction->setEnabled(isNormalAlbum);
0599             d->renameAction->setEnabled(isNormalAlbum || isAlbumRoot);
0600             d->propsEditAction->setEnabled(isNormalAlbum);
0601             d->openInFileManagerAction->setEnabled(isNormalAlbum || isAlbumRoot);
0602             d->newAction->setEnabled(isNormalAlbum || isAlbumRoot);
0603             d->writeAlbumMetadataAction->setEnabled(isNormalAlbum || isAlbumRoot);
0604             d->readAlbumMetadataAction->setEnabled(isNormalAlbum || isAlbumRoot);
0605 
0606             d->pasteItemsAction->setEnabled(isNormalAlbum || isAlbumRoot);
0607 
0608             d->selectInvertAction->setEnabled(isNormalAlbum || isAlbumRoot);
0609             d->selectNoneAction->setEnabled(isNormalAlbum || isAlbumRoot);
0610             d->selectAllAction->setEnabled(isNormalAlbum || isAlbumRoot);
0611         }
0612     }
0613     else
0614     {
0615         // Rules if album is trash or no current album.
0616 
0617         d->deleteAction->setEnabled(false);
0618         d->renameAction->setEnabled(false);
0619         d->propsEditAction->setEnabled(false);
0620         d->openInFileManagerAction->setEnabled(false);
0621         d->newAction->setEnabled(false);
0622         d->writeAlbumMetadataAction->setEnabled(false);
0623         d->readAlbumMetadataAction->setEnabled(false);
0624 
0625         d->pasteItemsAction->setEnabled(false);
0626         d->copyItemsAction->setEnabled(false);
0627         d->cutItemsAction->setEnabled(false);
0628 
0629         d->selectInvertAction->setEnabled(false);
0630         d->selectNoneAction->setEnabled(false);
0631         d->selectAllAction->setEnabled(false);
0632 
0633         d->newTagAction->setEnabled(false);
0634         d->deleteTagAction->setEnabled(false);
0635         d->editTagAction->setEnabled(false);
0636     }
0637 }
0638 
0639 void DigikamApp::slotImageSelected(const ItemInfoList& selection, const ItemInfoList& listAll)
0640 {
0641     qint64 listAllFileSize               = 0;
0642     qint64 selectionFileSize             = 0;
0643 
0644     int numOfImagesInModel               = 0;
0645     int numImagesWithGrouped             = listAll.count();
0646     int numImagesWithoutGrouped          = d->view->allUrls(false).count();
0647 
0648     ItemInfoList selectionWithoutGrouped = d->view->selectedInfoList(true, false);
0649 
0650     Q_FOREACH (const ItemInfo& info, selection)
0651     {
0652         // cppcheck-suppress useStlAlgorithm
0653         selectionFileSize += info.fileSize();
0654     }
0655 
0656     Q_FOREACH (const ItemInfo& info, listAll)
0657     {
0658         // cppcheck-suppress useStlAlgorithm
0659         listAllFileSize += info.fileSize();
0660     }
0661 
0662     Album* const album = d->view->currentAlbum();
0663 
0664     if (album && (album->type() == Album::PHYSICAL))
0665     {
0666         numOfImagesInModel = d->view->itemCount();
0667     }
0668 
0669     QString statusBarSelectionText;
0670     QString statusBarSelectionToolTip;
0671 
0672     switch (selection.count())
0673     {
0674         case 0:
0675         {
0676             if (numImagesWithGrouped == numImagesWithoutGrouped)
0677             {
0678                 statusBarSelectionText = i18np("No item selected (%1 item)",
0679                                                "No item selected (%1 items)",
0680                                                numImagesWithoutGrouped);
0681                 break;
0682             }
0683 
0684             statusBarSelectionText    = i18np("No item selected (%1 [%2] item)",
0685                                               "No item selected (%1 [%2] items)",
0686                                               numImagesWithoutGrouped,
0687                                               numImagesWithGrouped);
0688 
0689             statusBarSelectionToolTip = i18np("No item selected (%1 item. With grouped items: %2)",
0690                                               "No item selected (%1 items. With grouped items: %2)",
0691                                               numImagesWithoutGrouped,
0692                                               numImagesWithGrouped);
0693             break;
0694         }
0695 
0696         default:
0697         {
0698             if (numImagesWithGrouped == numImagesWithoutGrouped)
0699             {
0700                 statusBarSelectionText = i18np("1/%2 item selected (%3/%4)",
0701                                                "%1/%2 items selected (%3/%4)",
0702                                                selection.count(),
0703                                                numImagesWithoutGrouped,
0704                                                ItemPropertiesTab::humanReadableBytesCount(selectionFileSize),
0705                                                ItemPropertiesTab::humanReadableBytesCount(listAllFileSize));
0706                 break;
0707             }
0708 
0709             if (selectionWithoutGrouped.count() > 1)
0710             {
0711                 if (selection.count() == selectionWithoutGrouped.count())
0712                 {
0713                     statusBarSelectionText    = i18np("1/%2 [%3] item selected (%4/%5)",
0714                                                       "%1/%2 [%3] items selected (%4/%5)",
0715                                                       selectionWithoutGrouped.count(),
0716                                                       numImagesWithoutGrouped,
0717                                                       numImagesWithGrouped,
0718                                                       ItemPropertiesTab::humanReadableBytesCount(selectionFileSize),
0719                                                       ItemPropertiesTab::humanReadableBytesCount(listAllFileSize));
0720 
0721                     statusBarSelectionToolTip = i18np("1/%2 item selected. Total with grouped items: %3",
0722                                                       "%1/%2 items selected. Total with grouped items: %3",
0723                                                       selectionWithoutGrouped.count(),
0724                                                       numImagesWithoutGrouped,
0725                                                       numImagesWithGrouped);
0726                 }
0727                 else
0728                 {
0729                     statusBarSelectionText    = i18np("1/%2 [%3/%4] item selected (%5/%6)",
0730                                                       "%1/%2 [%3/%4] items selected (%5/%6)",
0731                                                       selectionWithoutGrouped.count(),
0732                                                       numImagesWithoutGrouped,
0733                                                       selection.count(),
0734                                                       numImagesWithGrouped,
0735                                                       ItemPropertiesTab::humanReadableBytesCount(selectionFileSize),
0736                                                       ItemPropertiesTab::humanReadableBytesCount(listAllFileSize));
0737 
0738                     statusBarSelectionToolTip = i18np("1/%2 item selected. With grouped items: %3/%4",
0739                                                       "%1/%2 items selected. With grouped items: %3/%4",
0740                                                       selectionWithoutGrouped.count(),
0741                                                       numImagesWithoutGrouped,
0742                                                       selection.count(),
0743                                                       numImagesWithGrouped);
0744                 }
0745 
0746                 break;
0747             }
0748 
0749 #if __GNUC__ >= 7   // krazy:exclude=cpp
0750 
0751             // no break; is completely intentional, arriving here is equivalent to case 1:
0752             [[fallthrough]];
0753 
0754 #endif
0755         }
0756 
0757         case 1:
0758         {
0759             if (!selectionWithoutGrouped.isEmpty())
0760             {
0761                 slotSetCheckedExifOrientationAction(selectionWithoutGrouped.first());
0762             }
0763 
0764             int index = listAll.indexOf(selection.first()) + 1;
0765 
0766             if (numImagesWithGrouped == numImagesWithoutGrouped)
0767             {
0768                 statusBarSelectionText = selection.first().fileUrl().fileName()
0769                                             + i18n(" (%1 of %2)",
0770                                                    index, numImagesWithoutGrouped);
0771             }
0772             else
0773             {
0774                 int indexWithoutGrouped = 0;
0775 
0776                 if (!selectionWithoutGrouped.isEmpty())
0777                 {
0778                     indexWithoutGrouped = d->view->allInfo(false).indexOf(selectionWithoutGrouped.first()) + 1;
0779                 }
0780 
0781                 statusBarSelectionText
0782                         = selection.first().fileUrl().fileName()
0783                           + i18n(" (%1 of %2 [%3] )", indexWithoutGrouped,
0784                                  numImagesWithoutGrouped, numImagesWithGrouped);
0785                 statusBarSelectionToolTip
0786                         = selection.first().fileUrl().fileName()
0787                           + i18n(" (%1 of %2. Total with grouped items: %3)", indexWithoutGrouped,
0788                                  numImagesWithoutGrouped, numImagesWithGrouped);
0789             }
0790 
0791             break;
0792         }
0793     }
0794 
0795     if (numImagesWithGrouped < numOfImagesInModel)
0796     {
0797         statusBarSelectionText += QLatin1String(" - ");
0798         statusBarSelectionText += i18np("%1 item hidden", "%1 items hidden",
0799                                         numOfImagesInModel - numImagesWithGrouped);
0800     }
0801 
0802     d->statusLabel->setAdjustedText(statusBarSelectionText);
0803     d->statusLabel->setToolTip(statusBarSelectionToolTip);
0804 }
0805 
0806 void DigikamApp::slotTrashSelectionChanged(const QString& text)
0807 {
0808     d->statusLabel->setAdjustedText(text);
0809     d->statusLabel->setToolTip(text);
0810 }
0811 
0812 void DigikamApp::slotSelectionChanged(int selectionCount)
0813 {
0814     // The preview can either be activated when only one image is selected,
0815     // or if multiple images are selected, but one image is the 'current image'.
0816 
0817     bool hasAtLeastCurrent = (selectionCount == 1) || ((selectionCount > 0) && d->view->hasCurrentItem());
0818 
0819     d->imagePreviewAction->setEnabled(hasAtLeastCurrent);
0820     d->imageViewAction->setEnabled(hasAtLeastCurrent);
0821     d->imageScanForFacesAction->setEnabled(selectionCount > 0);
0822     d->imageRemoveAllFacesAction->setEnabled(selectionCount > 0);
0823     d->imageFindSimilarAction->setEnabled(selectionCount == 1);
0824     d->imageRenameAction->setEnabled(selectionCount > 0);
0825     d->imageLightTableAction->setEnabled(selectionCount > 0);
0826     d->imageAddLightTableAction->setEnabled(selectionCount > 0);
0827     d->imageAddCurrentQueueAction->setEnabled((selectionCount > 0) && !QueueMgrWindow::queueManagerWindow()->isBusy());
0828     d->imageAddNewQueueAction->setEnabled((selectionCount > 0) && !QueueMgrWindow::queueManagerWindow()->isBusy());
0829     d->imageWriteMetadataAction->setEnabled(selectionCount > 0);
0830     d->imageReadMetadataAction->setEnabled(selectionCount > 0);
0831     d->imageDeleteAction->setEnabled(selectionCount > 0);
0832     d->imageRotateActionMenu->setEnabled(selectionCount > 0);
0833     d->imageFlipActionMenu->setEnabled(selectionCount > 0);
0834     d->imageExifOrientationActionMenu->setEnabled(selectionCount > 0);
0835     d->moveSelectionToAlbumAction->setEnabled(selectionCount > 0);
0836     d->copySelectionToAction->setEnabled(selectionCount > 0);
0837     d->cutItemsAction->setEnabled(selectionCount > 0);
0838     d->copyItemsAction->setEnabled(selectionCount > 0);
0839     d->openWithAction->setEnabled(selectionCount > 0);
0840     d->imageAutoExifActionMenu->setEnabled(selectionCount > 0);
0841 
0842     Q_FOREACH (DPluginAction* const ac, DPluginLoader::instance()->pluginsActions(DPluginAction::GenericMetadata, this))
0843     {
0844         ac->setEnabled(selectionCount > 0);
0845     }
0846 
0847     if (selectionCount > 0)
0848     {
0849         d->imageWriteMetadataAction->setText(i18np("Write Metadata to File",
0850                                                    "Write Metadata to Selected Files", selectionCount));
0851         d->imageReadMetadataAction->setText(i18np("Reread Metadata From File",
0852                                                   "Reread Metadata From Selected Files", selectionCount));
0853 
0854         slotResetExifOrientationActions();
0855     }
0856 }
0857 
0858 void DigikamApp::slotExit()
0859 {
0860     close();
0861 }
0862 
0863 void DigikamApp::slotDBStat()
0864 {
0865     showDigikamDatabaseStat();
0866 }
0867 
0868 void DigikamApp::slotRecurseAlbums(bool checked)
0869 {
0870     d->view->setRecurseAlbums(checked);
0871 }
0872 
0873 void DigikamApp::slotRecurseTags(bool checked)
0874 {
0875     d->view->setRecurseTags(checked);
0876 }
0877 
0878 void DigikamApp::slotAllGroupsOpen(bool checked)
0879 {
0880     d->view->setAllGroupsOpen(checked);
0881 }
0882 
0883 void DigikamApp::slotZoomSliderChanged(int size)
0884 {
0885     d->view->setThumbSize(size);
0886 }
0887 
0888 void DigikamApp::slotThumbSizeChanged(int size)
0889 {
0890     d->zoomBar->setThumbsSize(size);
0891 
0892     if (!fullScreenIsActive() && d->autoShowZoomToolTip)
0893     {
0894         d->zoomBar->triggerZoomTrackerToolTip();
0895     }
0896 }
0897 
0898 void DigikamApp::slotZoomChanged(double zoom)
0899 {
0900     double zmin = d->view->zoomMin();
0901     double zmax = d->view->zoomMax();
0902     d->zoomBar->setZoom(zoom, zmin, zmax);
0903 
0904     if (!fullScreenIsActive() && d->autoShowZoomToolTip)
0905     {
0906         d->zoomBar->triggerZoomTrackerToolTip();
0907     }
0908 }
0909 
0910 void DigikamApp::slotToggleShowBar()
0911 {
0912     d->view->toggleShowBar(d->showBarAction->isChecked());
0913 }
0914 
0915 void DigikamApp::moveEvent(QMoveEvent*)
0916 {
0917     Q_EMIT signalWindowHasMoved();
0918 }
0919 
0920 void DigikamApp::slotTransformAction()
0921 {
0922     if      (sender()->objectName() == QLatin1String("rotate_ccw"))
0923     {
0924         d->view->imageTransform(MetaEngineRotation::Rotate270);
0925     }
0926     else if (sender()->objectName() == QLatin1String("rotate_cw"))
0927     {
0928         d->view->imageTransform(MetaEngineRotation::Rotate90);
0929     }
0930     else if (sender()->objectName() == QLatin1String("flip_horizontal"))
0931     {
0932         d->view->imageTransform(MetaEngineRotation::FlipHorizontal);
0933     }
0934     else if (sender()->objectName() == QLatin1String("flip_vertical"))
0935     {
0936         d->view->imageTransform(MetaEngineRotation::FlipVertical);
0937     }
0938     else if (sender()->objectName() == QLatin1String("image_transform_exif"))
0939     {
0940         // special value for FileActionMngr
0941 
0942         d->view->imageTransform(MetaEngineRotation::NoTransformation);
0943     }
0944 }
0945 
0946 void DigikamApp::slotResetExifOrientationActions()
0947 {
0948     d->imageSetExifOrientation1Action->setChecked(false);
0949     d->imageSetExifOrientation2Action->setChecked(false);
0950     d->imageSetExifOrientation3Action->setChecked(false);
0951     d->imageSetExifOrientation4Action->setChecked(false);
0952     d->imageSetExifOrientation5Action->setChecked(false);
0953     d->imageSetExifOrientation6Action->setChecked(false);
0954     d->imageSetExifOrientation7Action->setChecked(false);
0955     d->imageSetExifOrientation8Action->setChecked(false);
0956 }
0957 
0958 void DigikamApp::slotSetCheckedExifOrientationAction(const ItemInfo& info)
0959 {
0960 /*
0961     QScopedPointer<DMetadata> meta(new DMetadata(info.fileUrl().toLocalFile()));
0962     int orientation = (meta->isEmpty()) ? 0 : meta->getItemOrientation();
0963 */
0964     int orientation = info.orientation();
0965 
0966     switch (orientation)
0967     {
0968         case 1:
0969             d->imageSetExifOrientation1Action->setChecked(true);
0970             break;
0971 
0972         case 2:
0973             d->imageSetExifOrientation2Action->setChecked(true);
0974             break;
0975 
0976         case 3:
0977             d->imageSetExifOrientation3Action->setChecked(true);
0978             break;
0979 
0980         case 4:
0981             d->imageSetExifOrientation4Action->setChecked(true);
0982             break;
0983 
0984         case 5:
0985             d->imageSetExifOrientation5Action->setChecked(true);
0986             break;
0987 
0988         case 6:
0989             d->imageSetExifOrientation6Action->setChecked(true);
0990             break;
0991 
0992         case 7:
0993             d->imageSetExifOrientation7Action->setChecked(true);
0994             break;
0995 
0996         case 8:
0997             d->imageSetExifOrientation8Action->setChecked(true);
0998             break;
0999 
1000         default:
1001             slotResetExifOrientationActions();
1002             break;
1003     }
1004 }
1005 
1006 void DigikamApp::showSideBars(bool visible)
1007 {
1008     visible ? d->view->showSideBars()
1009             : d->view->hideSideBars();
1010 }
1011 
1012 void DigikamApp::slotToggleLeftSideBar()
1013 {
1014     d->view->toggleLeftSidebar();
1015 }
1016 
1017 void DigikamApp::slotToggleRightSideBar()
1018 {
1019     d->view->toggleRightSidebar();
1020 }
1021 
1022 void DigikamApp::slotPreviousLeftSideBarTab()
1023 {
1024     d->view->previousLeftSideBarTab();
1025 }
1026 
1027 void DigikamApp::slotNextLeftSideBarTab()
1028 {
1029     d->view->nextLeftSideBarTab();
1030 }
1031 
1032 void DigikamApp::slotNextRightSideBarTab()
1033 {
1034     d->view->nextRightSideBarTab();
1035 }
1036 
1037 void DigikamApp::slotPreviousRightSideBarTab()
1038 {
1039     d->view->previousRightSideBarTab();
1040 }
1041 
1042 void DigikamApp::showThumbBar(bool visible)
1043 {
1044     view()->toggleShowBar(visible);
1045 }
1046 
1047 bool DigikamApp::thumbbarVisibility() const
1048 {
1049     return d->showBarAction->isChecked();
1050 }
1051 
1052 void DigikamApp::slotSwitchedToPreview()
1053 {
1054     d->zoomBar->setBarMode(DZoomBar::PreviewZoomCtrl);
1055     d->imagePreviewAction->setChecked(true);
1056     customizedTrashView(true);
1057     toggleShowBar();
1058 }
1059 
1060 void DigikamApp::slotSwitchedToIconView()
1061 {
1062     d->zoomBar->setBarMode(DZoomBar::ThumbsSizeCtrl);
1063     d->imageIconViewAction->setChecked(true);
1064     customizedTrashView(true);
1065     toggleShowBar();
1066 }
1067 
1068 void DigikamApp::slotSwitchedToMapView()
1069 {
1070     // TODO: Link to map view's zoom actions
1071 
1072     d->zoomBar->setBarMode(DZoomBar::ThumbsSizeCtrl);
1073 
1074 #ifdef HAVE_GEOLOCATION
1075 
1076     d->imageMapViewAction->setChecked(true);
1077 
1078 #endif // HAVE_GEOLOCATION
1079 
1080     customizedTrashView(true);
1081     toggleShowBar();
1082 }
1083 
1084 void DigikamApp::slotSwitchedToTableView()
1085 {
1086     d->zoomBar->setBarMode(DZoomBar::ThumbsSizeCtrl);
1087     d->imageTableViewAction->setChecked(true);
1088     customizedTrashView(true);
1089     toggleShowBar();
1090 }
1091 
1092 void DigikamApp::slotSwitchedToTrashView()
1093 {
1094     d->zoomBar->setBarMode(DZoomBar::ThumbsSizeCtrl);
1095     customizedTrashView(false);
1096     toggleShowBar();
1097 }
1098 
1099 void DigikamApp::customizedFullScreenMode(bool set)
1100 {
1101     toolBarMenuAction()->setEnabled(!set);
1102     showMenuBarAction()->setEnabled(!set);
1103     showStatusBarAction()->setEnabled(!set);
1104 
1105     set ? d->showBarAction->setEnabled(false)
1106         : toggleShowBar();
1107 
1108     d->view->toggleFullScreen(set);
1109 }
1110 
1111 void DigikamApp::customizedTrashView(bool set)
1112 {
1113     d->imageTableViewAction->setEnabled(set);
1114     d->imageIconViewAction->setEnabled(set);
1115 
1116 #ifdef HAVE_GEOLOCATION
1117 
1118     d->imageMapViewAction->setEnabled(set);
1119 
1120 #endif
1121 
1122     d->imagePreviewAction->setEnabled(set);
1123     d->bqmAction->setEnabled(set);
1124     d->ltAction->setEnabled(set);
1125     d->ieAction->setEnabled(set);
1126 
1127     d->imageSeparationSortOrderAction->setEnabled(set);
1128     d->imageSeparationAction->setEnabled(set);
1129     d->imageSortOrderAction->setEnabled(set);
1130     d->imageSortAction->setEnabled(set);
1131     d->albumSortAction->setEnabled(set);
1132 
1133     d->zoomFitToWindowAction->setEnabled(set);
1134     d->recurseAlbumsAction->setEnabled(set);
1135     d->recurseTagsAction->setEnabled(set);
1136     d->refreshAction->setEnabled(set);
1137 
1138     Q_FOREACH (DPluginAction* const ac, DPluginLoader::instance()->pluginsActions(DPluginAction::GenericView, this))
1139     {
1140         ac->setEnabled(set);
1141     }
1142 }
1143 
1144 void DigikamApp::toggleShowBar()
1145 {
1146     switch (d->view->viewMode())
1147     {
1148         case StackedView::PreviewImageMode:
1149         case StackedView::MediaPlayerMode:
1150             d->showBarAction->setEnabled(true);
1151             break;
1152 
1153         default:
1154             d->showBarAction->setEnabled(false);
1155             break;
1156     }
1157 }
1158 
1159 void DigikamApp::slotComponentsInfo()
1160 {
1161     showDigikamComponentsInfo();
1162 }
1163 
1164 void DigikamApp::slotOnlineVersionCheck()
1165 {
1166     Setup::onlineVersionCheck();
1167 }
1168 
1169 void DigikamApp::slotToggleColorManagedView()
1170 {
1171     if (!IccSettings::instance()->isEnabled())
1172     {
1173         return;
1174     }
1175 
1176     bool cmv = !IccSettings::instance()->settings().useManagedPreviews;
1177     IccSettings::instance()->setUseManagedPreviews(cmv);
1178 }
1179 
1180 void DigikamApp::slotColorManagementOptionsChanged()
1181 {
1182     ICCSettingsContainer settings = IccSettings::instance()->settings();
1183 
1184     ThumbnailLoadThread::setDisplayingWidget(this);
1185 
1186     d->viewCMViewAction->blockSignals(true);
1187     d->viewCMViewAction->setEnabled(settings.enableCM);
1188     d->viewCMViewAction->setChecked(settings.useManagedPreviews);
1189     d->viewCMViewAction->blockSignals(false);
1190 }
1191 
1192 DInfoInterface* DigikamApp::infoIface(DPluginAction* const ac)
1193 {
1194     ApplicationSettings::OperationType aset = ApplicationSettings::Unspecified;
1195 
1196     switch (ac->actionCategory())
1197     {
1198         case DPluginAction::GenericExport:
1199         case DPluginAction::GenericImport:
1200             aset = ApplicationSettings::ImportExport;
1201             break;
1202 
1203         case DPluginAction::GenericMetadata:
1204             aset = ApplicationSettings::Metadata;
1205             break;
1206 
1207         case DPluginAction::GenericTool:
1208             aset = ApplicationSettings::Tools;
1209             break;
1210 
1211         case DPluginAction::GenericView:
1212             aset = ApplicationSettings::Slideshow;
1213             break;
1214 
1215         default:
1216             break;
1217     }
1218 
1219     DBInfoIface* const iface = new DBInfoIface(this, QList<QUrl>(), aset);
1220 
1221     connect(iface, SIGNAL(signalImportedImage(QUrl)),
1222             this, SLOT(slotImportedImagefromScanner(QUrl)));
1223 
1224     if (aset == ApplicationSettings::Slideshow)
1225     {
1226         connect(iface, SIGNAL(signalLastItemUrl(QUrl)),
1227                 d->view, SLOT(slotSetCurrentUrlWhenAvailable(QUrl)));
1228     }
1229 
1230     return iface;
1231 }
1232 
1233 } // namespace Digikam
1234 
1235 #include "moc_digikamapp.cpp"