File indexing completed on 2025-03-09 03:58:43

0001 /* ============================================================
0002  *
0003  * This file is a part of digiKam project
0004  * https://www.digikam.org
0005  *
0006  * Date        : 2012-05-07
0007  * Description : QStackedWidget to handle different types of views
0008  *               (icon view, image preview, media view)
0009  *
0010  * SPDX-FileCopyrightText: 2012      by Islam Wazery <wazery at ubuntu dot com>
0011  * SPDX-FileCopyrightText: 2012-2024 by Gilles Caulier <caulier dot gilles at gmail dot com>
0012  *
0013  * SPDX-License-Identifier: GPL-2.0-or-later
0014  *
0015  * ============================================================ */
0016 
0017 #include "importstackedview.h"
0018 
0019 // Qt includes
0020 
0021 #include <QSplitter>
0022 
0023 // KDE includes
0024 
0025 #include <klocalizedstring.h>
0026 
0027 // Local includes
0028 
0029 #include "digikam_debug.h"
0030 #include "previewlayout.h"
0031 #include "importsettings.h"
0032 
0033 namespace Digikam
0034 {
0035 
0036 class MediaPlayerView;
0037 
0038 class Q_DECL_HIDDEN ImportStackedView::Private
0039 {
0040 
0041 public:
0042 
0043     explicit Private()
0044       : dockArea(nullptr),
0045         splitter(nullptr),
0046         thumbBar(nullptr),
0047         thumbBarDock(nullptr),
0048         importIconView(nullptr),
0049         importPreviewView(nullptr),
0050 
0051 #ifdef HAVE_GEOLOCATION
0052 
0053         mapWidgetView(nullptr),
0054 
0055 #endif // HAVE_GEOLOCATION
0056 
0057 #ifdef HAVE_MEDIAPLAYER
0058 
0059         mediaPlayerView(nullptr),
0060 
0061 #endif // HAVE_MEDIAPLAYER
0062 
0063         syncingSelection(false)
0064     {
0065     }
0066 
0067     QMainWindow*        dockArea;
0068     QSplitter*          splitter;
0069 
0070     ImportThumbnailBar* thumbBar;
0071     ThumbBarDock*       thumbBarDock;
0072     ImportIconView*     importIconView;
0073     ImportPreviewView*  importPreviewView;
0074 
0075 #ifdef HAVE_GEOLOCATION
0076 
0077     MapWidgetView*      mapWidgetView;
0078 
0079 #endif // HAVE_GEOLOCATION
0080 
0081 #ifdef HAVE_MEDIAPLAYER
0082 
0083     MediaPlayerView*    mediaPlayerView; ///< Reuse of albumgui mediaplayer view.
0084 
0085 #endif // HAVE_MEDIAPLAYER
0086 
0087     bool                syncingSelection;
0088 };
0089 
0090 ImportStackedView::ImportStackedView(QWidget* const parent)
0091     : QStackedWidget(parent),
0092       d(new Private)
0093 {
0094     d->importIconView    = new ImportIconView(this);
0095     d->importPreviewView = new ImportPreviewView(this);
0096     d->thumbBarDock      = new ThumbBarDock();
0097     d->thumbBar          = new ImportThumbnailBar(d->thumbBarDock);
0098     d->thumbBar->setModelsFiltered(d->importIconView->importItemModel(),
0099                                    d->importIconView->importFilterModel());
0100     d->thumbBar->installOverlays();
0101     d->thumbBarDock->setWidget(d->thumbBar);
0102     d->thumbBarDock->setObjectName(QLatin1String("import_thumbbar"));
0103 
0104 #ifdef HAVE_GEOLOCATION
0105 
0106     // TODO refactor MapWidgetView not to require the models on startup?
0107 
0108     d->mapWidgetView     = new MapWidgetView(d->importIconView->getSelectionModel(),
0109                                              d->importIconView->importFilterModel(), this,
0110                                              MapWidgetView::ApplicationImportUI);
0111     d->mapWidgetView->setObjectName(QLatin1String("import_mapwidgetview"));
0112 
0113 #endif // HAVE_GEOLOCATION
0114 
0115 #ifdef HAVE_MEDIAPLAYER
0116 
0117     d->mediaPlayerView   = new MediaPlayerView(this);
0118 
0119 #endif //HAVE_MEDIAPLAYER
0120 
0121     insertWidget(PreviewCameraMode, d->importIconView);
0122     insertWidget(PreviewImageMode,  d->importPreviewView);
0123 
0124 #ifdef HAVE_GEOLOCATION
0125 
0126     insertWidget(MapWidgetMode,     d->mapWidgetView);
0127 
0128 #endif // HAVE_GEOLOCATION
0129 
0130 #ifdef HAVE_MEDIAPLAYER
0131 
0132     insertWidget(MediaPlayerMode,   d->mediaPlayerView);
0133 
0134 #endif //HAVE_MEDIAPLAYER
0135 
0136     setAttribute(Qt::WA_DeleteOnClose);
0137 
0138     readSettings();
0139 
0140     // -----------------------------------------------------------------
0141 /*
0142     FIXME
0143     connect(d->importPreviewView, SIGNAL(signalPopupTagsView()),
0144             d->importIconView, SIGNAL(signalPopupTagsView()));
0145 
0146     connect(d->importPreviewView, SIGNAL(signalGotoFolderAndItem(CamItemInfo)),
0147             this, SIGNAL(signalGotoFolderAndItem(CamItemInfo)));
0148 
0149     connect(d->importPreviewView, SIGNAL(signalGotoDateAndItem(CamItemInfo)),
0150             this, SIGNAL(signalGotoDateAndItem(CamItemInfo)));
0151 
0152     connect(d->importPreviewView, SIGNAL(signalGotoTagAndItem(int)),
0153             this, SIGNAL(signalGotoTagAndItem(int)));
0154 */
0155     connect(d->importPreviewView, SIGNAL(signalNextItem()),
0156             this, SIGNAL(signalNextItem()));
0157 
0158     connect(d->importPreviewView, SIGNAL(signalPrevItem()),
0159             this, SIGNAL(signalPrevItem()));
0160 /*
0161     FIXME
0162     connect(d->importPreviewView, SIGNAL(signalDeleteItem()),
0163             this, SIGNAL(signalDeleteItem()));
0164 */
0165     connect(d->importPreviewView, SIGNAL(signalEscapePreview()),
0166             this, SIGNAL(signalEscapePreview()));
0167 
0168     // A workaround to assign pickLabel, colorLabel, and rating in the preview view.
0169 
0170     connect(d->importPreviewView, SIGNAL(signalAssignPickLabel(int)),
0171             d->importIconView, SLOT(assignPickLabelToSelected(int)));
0172 
0173     connect(d->importPreviewView, SIGNAL(signalAssignColorLabel(int)),
0174             d->importIconView, SLOT(assignColorLabelToSelected(int)));
0175 
0176     connect(d->importPreviewView, SIGNAL(signalAssignRating(int)),
0177             d->importIconView, SLOT(assignRatingToSelected(int)));
0178 
0179     connect(d->importPreviewView->layout(), SIGNAL(zoomFactorChanged(double)),
0180             this, SLOT(slotZoomFactorChanged(double)));
0181 /*
0182     FIXME
0183     connect(d->importPreviewView, SIGNAL(signalAddToExistingQueue(int)),
0184             this, SIGNAL(signalAddToExistingQueue(int)));
0185 */
0186     connect(d->thumbBar, SIGNAL(selectionChanged()),
0187             this, SLOT(slotThumbBarSelectionChanged()));
0188 
0189     connect(d->importIconView, SIGNAL(selectionChanged()),
0190             this, SLOT(slotIconViewSelectionChanged()));
0191 
0192     connect(d->thumbBarDock, SIGNAL(dockLocationChanged(Qt::DockWidgetArea)),
0193             d->thumbBar, SLOT(slotDockLocationChanged(Qt::DockWidgetArea)));
0194 
0195     connect(d->importPreviewView, SIGNAL(signalPreviewLoaded(bool)),
0196             this, SLOT(slotPreviewLoaded(bool)));
0197 
0198 #ifdef HAVE_MEDIAPLAYER
0199 
0200     connect(d->mediaPlayerView, SIGNAL(signalNextItem()),
0201             this, SIGNAL(signalNextItem()));
0202 
0203     connect(d->mediaPlayerView, SIGNAL(signalPrevItem()),
0204             this, SIGNAL(signalPrevItem()));
0205 
0206     connect(d->mediaPlayerView, SIGNAL(signalEscapePreview()),
0207             this, SIGNAL(signalEscapePreview()));
0208 
0209 #endif //HAVE_MEDIAPLAYER
0210 
0211 }
0212 
0213 ImportStackedView::~ImportStackedView()
0214 {
0215     delete d;
0216 }
0217 
0218 void ImportStackedView::readSettings()
0219 {
0220     ImportSettings* const settings = ImportSettings::instance();
0221     bool showThumbbar              = settings->getShowThumbbar();
0222     d->thumbBarDock->setShouldBeVisible(showThumbbar);
0223 }
0224 
0225 void ImportStackedView::setDockArea(QMainWindow* dockArea)
0226 {
0227     // Attach the thumbbar dock to the given dock area and place it initially on top.
0228 
0229     d->dockArea = dockArea;
0230     d->thumbBarDock->setParent(d->dockArea);
0231     d->dockArea->addDockWidget(Qt::TopDockWidgetArea, d->thumbBarDock);
0232     d->thumbBarDock->setFloating(false);
0233 }
0234 
0235 ThumbBarDock* ImportStackedView::thumbBarDock() const
0236 {
0237     return d->thumbBarDock;
0238 }
0239 
0240 ImportThumbnailBar* ImportStackedView::thumbBar() const
0241 {
0242     return d->thumbBar;
0243 }
0244 
0245 ImportIconView* ImportStackedView::importIconView() const
0246 {
0247     return d->importIconView;
0248 }
0249 
0250 ImportPreviewView* ImportStackedView::importPreviewView() const
0251 {
0252     return d->importPreviewView;
0253 }
0254 
0255 #ifdef HAVE_GEOLOCATION
0256 
0257 MapWidgetView* ImportStackedView::mapWidgetView() const
0258 {
0259     return d->mapWidgetView;
0260 }
0261 
0262 #endif // HAVE_GEOLOCATION
0263 
0264 #ifdef HAVE_MEDIAPLAYER
0265 
0266 MediaPlayerView* ImportStackedView::mediaPlayerView() const
0267 {
0268     return d->mediaPlayerView;
0269 }
0270 
0271 #endif //HAVE_MEDIAPLAYER
0272 
0273 bool ImportStackedView::isInSingleFileMode() const
0274 {
0275     return ((currentIndex() == PreviewImageMode) || (currentIndex() == MediaPlayerMode));
0276 }
0277 
0278 bool ImportStackedView::isInMultipleFileMode() const
0279 {
0280     return ((currentIndex() == PreviewCameraMode) || (currentIndex() == MapWidgetMode));
0281 }
0282 
0283 void ImportStackedView::setPreviewItem(const CamItemInfo& info, const CamItemInfo& previous, const CamItemInfo& next)
0284 {
0285     if (info.isNull())
0286     {
0287         if (viewMode() == MediaPlayerMode)
0288         {
0289 
0290 #ifdef HAVE_MEDIAPLAYER
0291 
0292             d->mediaPlayerView->setCurrentItem();
0293 
0294 #endif //HAVE_MEDIAPLAYER
0295 
0296         }
0297         else if (viewMode() == PreviewImageMode)
0298         {
0299             d->importPreviewView->setCamItemInfo();
0300         }
0301     }
0302     else
0303     {
0304         if (identifyCategoryforMime(info.mime) == QLatin1String("audio") ||
0305             identifyCategoryforMime(info.mime) == QLatin1String("video"))
0306         {
0307             // Stop image viewer
0308 
0309             if (viewMode() == PreviewImageMode)
0310             {
0311                 d->importPreviewView->setCamItemInfo();
0312             }
0313 
0314 #ifdef HAVE_MEDIAPLAYER
0315 
0316             setViewMode(MediaPlayerMode);
0317             d->mediaPlayerView->setCurrentItem(info.url(), !previous.isNull(), !next.isNull());
0318 
0319 #endif //HAVE_MEDIAPLAYER
0320 
0321         }
0322         else
0323         {
0324             // Stop media player if running...
0325 
0326             if (viewMode() == MediaPlayerMode)
0327             {
0328 
0329 #ifdef HAVE_MEDIAPLAYER
0330 
0331                 d->mediaPlayerView->setCurrentItem();
0332 
0333 #endif //HAVE_MEDIAPLAYER
0334 
0335             }
0336 
0337             d->importPreviewView->setCamItemInfo(info, previous, next);
0338 
0339             // NOTE: No need to toggle immediately in PreviewImageMode here,
0340             // because we will receive a signal for that when the image preview will be loaded.
0341             // This will prevent a flicker effect with the old image preview loaded in stack.
0342         }
0343 
0344         // do not touch the selection, only adjust current info
0345 
0346         QModelIndex currentIndex = d->thumbBar->importSortFilterModel()->indexForCamItemInfo(info);
0347         d->thumbBar->selectionModel()->setCurrentIndex(currentIndex, QItemSelectionModel::NoUpdate);
0348     }
0349 }
0350 
0351 QString ImportStackedView::identifyCategoryforMime(const QString& mime) const
0352 {
0353     return mime.split(QLatin1Char('/')).at(0);
0354 }
0355 
0356 ImportStackedView::StackedViewMode ImportStackedView::viewMode() const
0357 {
0358     return (StackedViewMode)(indexOf(currentWidget()));
0359 }
0360 
0361 void ImportStackedView::setViewMode(const StackedViewMode mode)
0362 {
0363 
0364 #ifdef HAVE_MEDIAPLAYER
0365 
0366     if ((viewMode() == MediaPlayerMode) && (mode != MediaPlayerMode))
0367     {
0368         d->mediaPlayerView->escapePreview();
0369     }
0370 
0371 #endif // HAVE_MEDIAPLAYER
0372 
0373     if ((mode != PreviewCameraMode) && (mode != PreviewImageMode) &&
0374         (mode != MediaPlayerMode)   && (mode != MapWidgetMode))
0375     {
0376         return;
0377     }
0378 
0379     if ((mode == PreviewImageMode) || (mode == MediaPlayerMode))
0380     {
0381         d->thumbBarDock->restoreVisibility();
0382         syncSelection(d->importIconView, d->thumbBar);
0383     }
0384     else
0385     {
0386         d->thumbBarDock->hide();
0387     }
0388 
0389     if ((mode == PreviewCameraMode) || (mode == MapWidgetMode))
0390     {
0391         setPreviewItem();
0392         setCurrentIndex(mode);
0393     }
0394     else
0395     {
0396         setCurrentIndex(mode);
0397     }
0398 
0399 #ifdef HAVE_GEOLOCATION
0400 
0401     d->mapWidgetView->setActive(mode == MapWidgetMode);
0402 
0403 #endif // HAVE_GEOLOCATION
0404 
0405     if      (mode == PreviewCameraMode)
0406     {
0407         d->importIconView->setFocus();
0408     }
0409 
0410 #ifdef HAVE_GEOLOCATION
0411 
0412     else if (mode == MapWidgetMode)
0413     {
0414         d->mapWidgetView->setFocus();
0415     }
0416 
0417 #endif // HAVE_GEOLOCATION
0418 
0419     Q_EMIT signalViewModeChanged();
0420 }
0421 
0422 void ImportStackedView::syncSelection(ImportCategorizedView* const from, ImportCategorizedView* const to)
0423 {
0424     ImportSortFilterModel* const fromModel = from->importSortFilterModel();
0425     ImportSortFilterModel* const toModel   = to->importSortFilterModel();
0426     QModelIndex currentIndex               = toModel->indexForCamItemInfo(from->currentInfo());
0427 
0428     // sync selection
0429 
0430     QItemSelection selection               = from->selectionModel()->selection();
0431     QItemSelection newSelection;
0432 
0433     Q_FOREACH (const QItemSelectionRange& range, selection)
0434     {
0435         QModelIndex topLeft     = toModel->indexForCamItemInfo(fromModel->camItemInfo(range.topLeft()));
0436         QModelIndex bottomRight = toModel->indexForCamItemInfo(fromModel->camItemInfo(range.bottomRight()));
0437         newSelection.select(topLeft, bottomRight);
0438     }
0439 
0440     d->syncingSelection = true;
0441 
0442     if (currentIndex.isValid())
0443     {
0444         // set current info
0445 
0446         to->setCurrentIndex(currentIndex);
0447     }
0448 
0449     to->selectionModel()->select(newSelection, QItemSelectionModel::ClearAndSelect);
0450     d->syncingSelection = false;
0451 }
0452 
0453 void ImportStackedView::slotThumbBarSelectionChanged()
0454 {
0455     if ((currentIndex() != PreviewImageMode) && (currentIndex() != MediaPlayerMode))
0456     {
0457         return;
0458     }
0459 
0460     if (d->syncingSelection)
0461     {
0462         return;
0463     }
0464 
0465     syncSelection(d->thumbBar, d->importIconView);
0466 }
0467 
0468 void ImportStackedView::slotIconViewSelectionChanged()
0469 {
0470     if (currentIndex() != PreviewCameraMode)
0471     {
0472         return;
0473     }
0474 
0475     if (d->syncingSelection)
0476     {
0477         return;
0478     }
0479 
0480     syncSelection(d->importIconView, d->thumbBar);
0481 }
0482 
0483 void ImportStackedView::previewLoaded()
0484 {
0485     Q_EMIT signalViewModeChanged();
0486 }
0487 
0488 void ImportStackedView::slotZoomFactorChanged(double z)
0489 {
0490     if (viewMode() == PreviewImageMode)
0491     {
0492         Q_EMIT signalZoomFactorChanged(z);
0493     }
0494 }
0495 
0496 void ImportStackedView::increaseZoom()
0497 {
0498     d->importPreviewView->layout()->increaseZoom();
0499 }
0500 
0501 void ImportStackedView::decreaseZoom()
0502 {
0503     d->importPreviewView->layout()->decreaseZoom();
0504 }
0505 
0506 void ImportStackedView::zoomTo100Percents()
0507 {
0508     d->importPreviewView->layout()->setZoomFactor(1.0);
0509 }
0510 
0511 void ImportStackedView::fitToWindow()
0512 {
0513     d->importPreviewView->layout()->fitToWindow();
0514 }
0515 
0516 void ImportStackedView::toggleFitToWindowOr100()
0517 {
0518     d->importPreviewView->layout()->toggleFitToWindowOr100();
0519 }
0520 
0521 bool ImportStackedView::maxZoom() const
0522 {
0523     return d->importPreviewView->layout()->atMaxZoom();
0524 }
0525 
0526 bool ImportStackedView::minZoom() const
0527 {
0528     return d->importPreviewView->layout()->atMinZoom();
0529 }
0530 
0531 void ImportStackedView::setZoomFactor(double z)
0532 {
0533     // Giving a null anchor means to use the current view center
0534 
0535     d->importPreviewView->layout()->setZoomFactor(z, QPoint());
0536 }
0537 
0538 void ImportStackedView::setZoomFactorSnapped(double z)
0539 {
0540     d->importPreviewView->layout()->setZoomFactor(z, QPoint(), SinglePhotoPreviewLayout::SnapZoomFactor);
0541 }
0542 
0543 double ImportStackedView::zoomFactor() const
0544 {
0545     return d->importPreviewView->layout()->zoomFactor();
0546 }
0547 
0548 double ImportStackedView::zoomMin() const
0549 {
0550     return d->importPreviewView->layout()->minZoomFactor();
0551 }
0552 
0553 double ImportStackedView::zoomMax() const
0554 {
0555     return d->importPreviewView->layout()->maxZoomFactor();
0556 }
0557 
0558 void ImportStackedView::slotPreviewLoaded(bool)
0559 {
0560     setViewMode(ImportStackedView::PreviewImageMode);
0561     previewLoaded();
0562 }
0563 
0564 } // namespace Digikam
0565 
0566 #include "moc_importstackedview.cpp"