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

0001 /* ============================================================
0002  *
0003  * This file is a part of digiKam project
0004  * https://www.digikam.org
0005  *
0006  * Date        : 2007-03-05
0007  * Description : a widget to display 2 preview image on
0008  *               lightable to compare pictures.
0009  *
0010  * SPDX-FileCopyrightText: 2007-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 "lighttableview.h"
0017 
0018 // Qt includes
0019 
0020 #include <QGridLayout>
0021 #include <QLabel>
0022 #include <QApplication>
0023 
0024 // Local includes
0025 
0026 #include "digikam_debug.h"
0027 #include "dimg.h"
0028 #include "dzoombar.h"
0029 #include "thumbnailsize.h"
0030 #include "lighttablepreview.h"
0031 #include "previewlayout.h"
0032 #include "dimgpreviewitem.h"
0033 
0034 namespace Digikam
0035 {
0036 
0037 class Q_DECL_HIDDEN LightTableView::Private
0038 {
0039 public:
0040 
0041     explicit Private()
0042       : syncPreview (false),
0043         grid        (nullptr),
0044         leftFrame   (nullptr),
0045         rightFrame  (nullptr),
0046         leftPreview (nullptr),
0047         rightPreview(nullptr)
0048     {
0049     }
0050 
0051     bool               syncPreview;
0052 
0053     QGridLayout*       grid;
0054 
0055     /// These labels are used to draw a frame around preview views to identify easily which item has the focus.
0056     QLabel*            leftFrame;
0057     QLabel*            rightFrame;
0058 
0059     LightTablePreview* leftPreview;
0060     LightTablePreview* rightPreview;
0061 };
0062 
0063 LightTableView::LightTableView(QWidget* const parent)
0064     : QFrame(parent),
0065       d     (new Private)
0066 {
0067     setAttribute(Qt::WA_DeleteOnClose);
0068     setFrameStyle(QFrame::NoFrame);
0069     setLineWidth(0);
0070 
0071     d->grid                 = new QGridLayout();
0072     setLayout(d->grid);
0073 
0074     d->leftFrame            = new QLabel(this);
0075     d->leftPreview          = new LightTablePreview(this);
0076     QVBoxLayout* const llay = new QVBoxLayout(d->leftFrame);
0077     llay->addWidget(d->leftPreview);
0078     llay->setContentsMargins(3, 3, 3, 3);
0079     llay->setSpacing(0);
0080 
0081     d->rightFrame           = new QLabel(this);
0082     d->rightPreview         = new LightTablePreview(this);
0083     QVBoxLayout* const rlay = new QVBoxLayout(d->rightFrame);
0084     rlay->addWidget(d->rightPreview);
0085     rlay->setContentsMargins(3, 3, 3, 3);
0086     rlay->setSpacing(0);
0087 
0088     d->grid->addWidget(d->leftFrame,  0, 0, 1, 1);
0089     d->grid->addWidget(d->rightFrame, 0, 1, 1, 1);
0090     d->grid->setColumnStretch(0, 10);
0091     d->grid->setColumnStretch(1, 10);
0092     d->grid->setRowStretch(0, 10);
0093 
0094     // Left panel connections ------------------------------------------------
0095 
0096     connect(d->leftPreview, SIGNAL(signalPopupTagsView()),
0097             this, SIGNAL(signalLeftPopupTagsView()));
0098 
0099     connect(d->leftPreview->layout(), SIGNAL(zoomFactorChanged(double)),
0100             this, SLOT(slotLeftZoomFactorChanged(double)));
0101 
0102     connect(d->leftPreview, SIGNAL(contentsMoving(int,int)),
0103             this, SLOT(slotLeftContentsMoved(int,int)));
0104 
0105     connect(d->leftPreview, SIGNAL(signalSlideShowCurrent()),
0106             this, SIGNAL(signalLeftSlideShowCurrent()));
0107 
0108     connect(d->leftPreview, SIGNAL(signalDroppedItems(ItemInfoList)),
0109             this, SIGNAL(signalLeftDroppedItems(ItemInfoList)));
0110 
0111     connect(d->leftPreview, SIGNAL(signalPreviewLoaded(bool)),
0112             this, SLOT(slotLeftPreviewLoaded(bool)));
0113 
0114     connect(d->leftPreview, SIGNAL(leftButtonClicked()),
0115             this, SIGNAL(signalLeftPanelLeftButtonClicked()));
0116 
0117     connect(d->leftPreview, SIGNAL(signalDeleteItem()),
0118             this, SLOT(slotDeleteLeftItem()));
0119 
0120     // Right panel connections ------------------------------------------------
0121 
0122     connect(d->rightPreview, SIGNAL(signalPopupTagsView()),
0123             this, SIGNAL(signalRightPopupTagsView()));
0124 
0125     connect(d->rightPreview->layout(), SIGNAL(zoomFactorChanged(double)),
0126             this, SLOT(slotRightZoomFactorChanged(double)));
0127 
0128     connect(d->rightPreview, SIGNAL(contentsMoving(int,int)),
0129             this, SLOT(slotRightContentsMoved(int,int)));
0130 
0131     connect(d->rightPreview, SIGNAL(signalDroppedItems(ItemInfoList)),
0132             this, SIGNAL(signalRightDroppedItems(ItemInfoList)));
0133 
0134     connect(d->rightPreview, SIGNAL(signalSlideShowCurrent()),
0135             this, SIGNAL(signalRightSlideShowCurrent()));
0136 
0137     connect(d->rightPreview, SIGNAL(signalPreviewLoaded(bool)),
0138             this, SLOT(slotRightPreviewLoaded(bool)));
0139 
0140     connect(d->rightPreview, SIGNAL(leftButtonClicked()),
0141             this, SIGNAL(signalRightPanelLeftButtonClicked()));
0142 
0143     connect(d->rightPreview, SIGNAL(signalDeleteItem()),
0144             this, SLOT(slotDeleteRightItem()));
0145 }
0146 
0147 LightTableView::~LightTableView()
0148 {
0149     delete d;
0150 }
0151 
0152 void LightTableView::setPreviewSettings(const PreviewSettings& settings)
0153 {
0154     d->leftPreview->previewItem()->setPreviewSettings(settings);
0155     d->rightPreview->previewItem()->setPreviewSettings(settings);
0156 }
0157 
0158 void LightTableView::setSyncPreview(bool sync)
0159 {
0160     d->syncPreview = sync;
0161 
0162     // Left panel like a reference to resync preview.
0163 
0164     if (d->syncPreview)
0165     {
0166         slotLeftZoomFactorChanged(d->leftPreview->layout()->zoomFactor());
0167         slotLeftContentsMoved(d->leftPreview->contentsX(),
0168                               d->leftPreview->contentsY());
0169     }
0170 }
0171 
0172 void LightTableView::setNavigateByPair(bool b)
0173 {
0174     d->leftPreview->setDragAndDropEnabled(!b);
0175     d->rightPreview->setDragAndDropEnabled(!b);
0176 }
0177 
0178 void LightTableView::slotDecreaseLeftZoom()
0179 {
0180     d->leftPreview->layout()->decreaseZoom();
0181 }
0182 
0183 void LightTableView::slotIncreaseLeftZoom()
0184 {
0185     d->leftPreview->layout()->increaseZoom();
0186 }
0187 
0188 void LightTableView::slotDecreaseRightZoom()
0189 {
0190     d->rightPreview->layout()->decreaseZoom();
0191 }
0192 
0193 void LightTableView::slotIncreaseRightZoom()
0194 {
0195     d->rightPreview->layout()->increaseZoom();
0196 }
0197 
0198 void LightTableView::setLeftZoomFactor(double z)
0199 {
0200     d->leftPreview->layout()->setZoomFactor(z);
0201 }
0202 
0203 void LightTableView::setRightZoomFactor(double z)
0204 {
0205     d->rightPreview->layout()->setZoomFactor(z);
0206 }
0207 
0208 void LightTableView::slotLeftZoomTo100()
0209 {
0210     d->leftPreview->layout()->toggleFitToWindowOr100();
0211 }
0212 
0213 void LightTableView::slotRightZoomTo100()
0214 {
0215     d->rightPreview->layout()->toggleFitToWindowOr100();
0216 }
0217 
0218 void LightTableView::slotLeftFitToWindow()
0219 {
0220     d->leftPreview->layout()->fitToWindow();
0221 }
0222 
0223 void LightTableView::slotRightFitToWindow()
0224 {
0225     d->rightPreview->layout()->fitToWindow();
0226 }
0227 
0228 double LightTableView::leftZoomMax() const
0229 {
0230     return d->leftPreview->layout()->maxZoomFactor();
0231 }
0232 
0233 double LightTableView::leftZoomMin() const
0234 {
0235     return d->leftPreview->layout()->minZoomFactor();
0236 }
0237 
0238 bool LightTableView::leftMaxZoom() const
0239 {
0240     return d->leftPreview->layout()->atMaxZoom();
0241 }
0242 
0243 bool LightTableView::leftMinZoom() const
0244 {
0245     return d->leftPreview->layout()->atMinZoom();
0246 }
0247 
0248 double LightTableView::rightZoomMax() const
0249 {
0250     return d->rightPreview->layout()->maxZoomFactor();
0251 }
0252 
0253 double LightTableView::rightZoomMin() const
0254 {
0255     return d->rightPreview->layout()->minZoomFactor();
0256 }
0257 
0258 bool LightTableView::rightMaxZoom() const
0259 {
0260     return d->rightPreview->layout()->atMaxZoom();
0261 }
0262 
0263 bool LightTableView::rightMinZoom() const
0264 {
0265     return d->rightPreview->layout()->atMinZoom();
0266 }
0267 
0268 void LightTableView::slotLeftZoomSliderChanged(int size)
0269 {
0270     double zmin = d->leftPreview->layout()->minZoomFactor();
0271     double zmax = d->leftPreview->layout()->maxZoomFactor();
0272     double z    = DZoomBar::zoomFromSize(size, zmin, zmax);
0273     d->leftPreview->layout()->setZoomFactorSnapped(z);
0274 }
0275 
0276 void LightTableView::slotRightZoomSliderChanged(int size)
0277 {
0278     double zmin = d->rightPreview->layout()->minZoomFactor();
0279     double zmax = d->rightPreview->layout()->maxZoomFactor();
0280     double z    = DZoomBar::zoomFromSize(size, zmin, zmax);
0281     d->rightPreview->layout()->setZoomFactorSnapped(z);
0282 }
0283 
0284 void LightTableView::leftReload()
0285 {
0286     d->leftPreview->previewItem()->reload();
0287 }
0288 
0289 void LightTableView::rightReload()
0290 {
0291     d->rightPreview->previewItem()->reload();
0292 }
0293 
0294 void LightTableView::slotLeftContentsMoved(int x, int y)
0295 {
0296     if (d->syncPreview && !leftPreviewLoading())
0297     {
0298         d->rightPreview->blockSignals(true);
0299         d->rightPreview->setContentsPos(x, y);
0300         d->rightPreview->blockSignals(false);
0301     }
0302 }
0303 
0304 void LightTableView::slotRightContentsMoved(int x, int y)
0305 {
0306     if (d->syncPreview && !rightPreviewLoading())
0307     {
0308         d->leftPreview->blockSignals(true);
0309         d->leftPreview->setContentsPos(x, y);
0310         d->leftPreview->blockSignals(false);
0311     }
0312 }
0313 
0314 void LightTableView::slotLeftZoomFactorChanged(double zoom)
0315 {
0316     if (d->syncPreview && !leftPreviewLoading())
0317     {
0318         d->rightPreview->layout()->blockSignals(true);
0319         d->rightPreview->blockSignals(true);
0320 
0321         setRightZoomFactor(zoom);
0322 
0323         Q_EMIT signalRightZoomFactorChanged(zoom);
0324 
0325         d->rightPreview->blockSignals(false);
0326         d->rightPreview->layout()->blockSignals(false);
0327     }
0328 
0329     Q_EMIT signalLeftZoomFactorChanged(zoom);
0330 }
0331 
0332 void LightTableView::slotRightZoomFactorChanged(double zoom)
0333 {
0334     if (d->syncPreview && !rightPreviewLoading())
0335     {
0336         d->leftPreview->layout()->blockSignals(true);
0337         d->leftPreview->blockSignals(true);
0338 
0339         setLeftZoomFactor(zoom);
0340 
0341         Q_EMIT signalLeftZoomFactorChanged(zoom);
0342 
0343         d->leftPreview->blockSignals(false);
0344         d->leftPreview->layout()->blockSignals(false);
0345     }
0346 
0347     Q_EMIT signalRightZoomFactorChanged(zoom);
0348 }
0349 
0350 ItemInfo LightTableView::leftItemInfo() const
0351 {
0352     return d->leftPreview->getItemInfo();
0353 }
0354 
0355 ItemInfo LightTableView::rightItemInfo() const
0356 {
0357     return d->rightPreview->getItemInfo();
0358 }
0359 
0360 void LightTableView::setLeftItemInfo(const ItemInfo& info)
0361 {
0362     d->leftPreview->setItemInfo(info);
0363 
0364     if (info.isNull())
0365     {
0366         d->leftPreview->showDragAndDropMessage();
0367     }
0368 }
0369 
0370 void LightTableView::setRightItemInfo(const ItemInfo& info)
0371 {
0372     d->rightPreview->setItemInfo(info);
0373 
0374     if (info.isNull())
0375     {
0376         d->rightPreview->showDragAndDropMessage();
0377     }
0378 }
0379 
0380 void LightTableView::slotLeftPreviewLoaded(bool success)
0381 {
0382     checkForSyncPreview();
0383     slotRightContentsMoved(d->rightPreview->contentsX(), d->rightPreview->contentsY());
0384 
0385     Q_EMIT signalLeftPreviewLoaded(success);
0386 }
0387 
0388 void LightTableView::slotRightPreviewLoaded(bool success)
0389 {
0390     checkForSyncPreview();
0391     slotLeftContentsMoved(d->leftPreview->contentsX(), d->leftPreview->contentsY());
0392 
0393     Q_EMIT signalRightPreviewLoaded(success);
0394 }
0395 
0396 void LightTableView::checkForSyncPreview()
0397 {
0398     if (!d->leftPreview->getItemInfo().isNull()  &&
0399         !d->rightPreview->getItemInfo().isNull() &&
0400         (d->leftPreview->previewItem()->image().size() == d->rightPreview->previewItem()->image().size()))
0401     {
0402         d->syncPreview = true;
0403     }
0404     else
0405     {
0406         d->syncPreview = false;
0407     }
0408 
0409     Q_EMIT signalToggleOnSyncPreview(d->syncPreview);
0410 }
0411 
0412 void LightTableView::checkForSelection(const ItemInfo& info)
0413 {
0414     QString selected    = QString::fromUtf8("QLabel { background-color: %1; }")
0415                           .arg(qApp->palette().color(QPalette::Highlight).name());
0416 
0417     QString notSelected = QString::fromUtf8("QLabel { background-color: %1; }")
0418                           .arg(qApp->palette().color(QPalette::Base).name());
0419 
0420     if (info.isNull())
0421     {
0422         d->leftFrame->setStyleSheet(notSelected);
0423         d->rightFrame->setStyleSheet(notSelected);
0424 
0425         Q_EMIT signalLeftPreviewSelected(false);
0426         Q_EMIT signalRightPreviewSelected(false);
0427 
0428         return;
0429     }
0430 
0431     if (!d->leftPreview->getItemInfo().isNull())
0432     {
0433         bool onLeft = (d->leftPreview->getItemInfo() == info);
0434         d->leftFrame->setStyleSheet(onLeft ? selected : notSelected);
0435 
0436         Q_EMIT signalLeftPreviewSelected(onLeft);
0437     }
0438 
0439     if (!d->rightPreview->getItemInfo().isNull())
0440     {
0441         bool onRight = (d->rightPreview->getItemInfo() == info);
0442         d->rightFrame->setStyleSheet(onRight ? selected : notSelected);
0443 
0444         Q_EMIT signalRightPreviewSelected(onRight);
0445     }
0446 }
0447 
0448 void LightTableView::slotDeleteLeftItem()
0449 {
0450     Q_EMIT signalDeleteItem(d->leftPreview->getItemInfo());
0451 }
0452 
0453 void LightTableView::slotDeleteRightItem()
0454 {
0455     Q_EMIT signalDeleteItem(d->rightPreview->getItemInfo());
0456 }
0457 
0458 bool LightTableView::leftPreviewLoading() const
0459 {
0460     return (d->leftPreview->previewItem()->state() == DImgPreviewItem::Loading);
0461 }
0462 
0463 bool LightTableView::rightPreviewLoading() const
0464 {
0465     return (d->rightPreview->previewItem()->state() == DImgPreviewItem::Loading);
0466 }
0467 
0468 void LightTableView::toggleFullScreen(bool set)
0469 {
0470     d->leftPreview->toggleFullScreen(set);
0471     d->rightPreview->toggleFullScreen(set);
0472 }
0473 
0474 } // namespace Digikam
0475 
0476 #include "moc_lighttableview.cpp"