File indexing completed on 2025-04-27 03:58:25

0001 /* ============================================================
0002  *
0003  * This file is a part of digiKam project
0004  * https://www.digikam.org
0005  *
0006  * Date        : 2010-04-30
0007  * Description : Layout for an item on image preview
0008  *
0009  * SPDX-FileCopyrightText: 2010-2011 by Marcel Wiesweg <marcel dot wiesweg at gmx dot de>
0010  *
0011  * SPDX-License-Identifier: GPL-2.0-or-later
0012  *
0013  * ============================================================ */
0014 
0015 #include "previewlayout.h"
0016 
0017 // Qt includes
0018 
0019 #include <QGraphicsView>
0020 #include <QGraphicsScene>
0021 
0022 // Local includes
0023 
0024 #include "graphicsdimgitem.h"
0025 #include "graphicsdimgview.h"
0026 #include "imagezoomsettings.h"
0027 
0028 namespace Digikam
0029 {
0030 
0031 class Q_DECL_HIDDEN SinglePhotoPreviewLayout::Private
0032 {
0033 public:
0034 
0035     explicit Private()
0036       : view          (nullptr),
0037         item          (nullptr),
0038         fitToSizeMode (ImageZoomSettings::OnlyScaleDown),
0039         isFitToWindow (true),
0040         previousZoom  (1.0),
0041         zoomMultiplier(1.2),
0042         maxZoom       (12.0),
0043         minZoom       (0.1)
0044     {
0045     }
0046 
0047     ImageZoomSettings* zoomSettings() const
0048     {
0049         return item->zoomSettings();
0050     }
0051 
0052     QSizeF frameSize() const
0053     {
0054         return view->maximumViewportSize();
0055     }
0056 
0057 public:
0058 
0059     GraphicsDImgView*                view;
0060     GraphicsDImgItem*                item;
0061 
0062     ImageZoomSettings::FitToSizeMode fitToSizeMode;
0063 
0064     bool                             isFitToWindow;
0065     double                           previousZoom;
0066 
0067     double                           zoomMultiplier;
0068     double                           maxZoom;
0069     double                           minZoom;
0070 };
0071 
0072 SinglePhotoPreviewLayout::SinglePhotoPreviewLayout(QObject* const parent)
0073     : QObject(parent),
0074       d(new Private)
0075 {
0076 }
0077 
0078 SinglePhotoPreviewLayout::~SinglePhotoPreviewLayout()
0079 {
0080     delete d;
0081 }
0082 
0083 void SinglePhotoPreviewLayout::setGraphicsView(GraphicsDImgView* const view)
0084 {
0085     d->view = view;
0086 }
0087 
0088 void SinglePhotoPreviewLayout::setScaleFitToWindow(bool value)
0089 {
0090     if (value)
0091     {
0092         d->fitToSizeMode = ImageZoomSettings::AlwaysFit;
0093     }
0094     else
0095     {
0096         d->fitToSizeMode = ImageZoomSettings::OnlyScaleDown;
0097     }
0098 }
0099 
0100 void SinglePhotoPreviewLayout::addItem(GraphicsDImgItem* const item)
0101 {
0102     if (d->item)
0103     {
0104         disconnect(d->item, SIGNAL(imageChanged()),
0105                    this, SLOT(updateZoomAndSize()));
0106     }
0107 
0108     d->item = item;
0109 
0110     if (d->item)
0111     {
0112         connect(d->item, SIGNAL(imageChanged()),
0113                 this, SLOT(updateZoomAndSize()));
0114     }
0115 }
0116 
0117 bool SinglePhotoPreviewLayout::isFitToWindow() const
0118 {
0119     return d->isFitToWindow;
0120 }
0121 
0122 double SinglePhotoPreviewLayout::zoomFactor() const
0123 {
0124     if (!d->item || !d->view)
0125     {
0126         return 1.0;
0127     }
0128 
0129     return d->zoomSettings()->zoomFactor();
0130 }
0131 
0132 double SinglePhotoPreviewLayout::realZoomFactor() const
0133 {
0134     if (!d->item || !d->view)
0135     {
0136         return 1.0;
0137     }
0138 
0139     return d->zoomSettings()->realZoomFactor();
0140 }
0141 
0142 double SinglePhotoPreviewLayout::maxZoomFactor() const
0143 {
0144     return d->maxZoom;
0145 }
0146 
0147 double SinglePhotoPreviewLayout::minZoomFactor() const
0148 {
0149     return d->minZoom;
0150 }
0151 
0152 bool SinglePhotoPreviewLayout::atMaxZoom() const
0153 {
0154     return (zoomFactor() >= d->maxZoom);
0155 }
0156 
0157 bool SinglePhotoPreviewLayout::atMinZoom() const
0158 {
0159     return (zoomFactor() <= d->minZoom);
0160 }
0161 
0162 void SinglePhotoPreviewLayout::setMaxZoomFactor(double z)
0163 {
0164     d->maxZoom = z;
0165 }
0166 
0167 void SinglePhotoPreviewLayout::setMinZoomFactor(double z)
0168 {
0169     d->minZoom = z;
0170 }
0171 
0172 void SinglePhotoPreviewLayout::increaseZoom(const QPoint& viewportAnchor)
0173 {
0174     if (!d->item || !d->view)
0175     {
0176         return;
0177     }
0178 
0179     double zoom = d->zoomSettings()->zoomFactor() * d->zoomMultiplier;
0180     zoom        = qMin(zoom, d->maxZoom);
0181     zoom        = d->zoomSettings()->snappedZoomStep(zoom, d->frameSize());
0182     setZoomFactor(zoom, viewportAnchor);
0183 }
0184 
0185 void SinglePhotoPreviewLayout::decreaseZoom(const QPoint& viewportAnchor)
0186 {
0187     if (!d->item || !d->view)
0188     {
0189         return;
0190     }
0191 
0192     double zoom = d->zoomSettings()->zoomFactor() / d->zoomMultiplier;
0193     zoom        = qMax(zoom, d->minZoom);
0194     zoom        = d->zoomSettings()->snappedZoomStep(zoom, d->frameSize());
0195     setZoomFactor(zoom, viewportAnchor);
0196 }
0197 
0198 void SinglePhotoPreviewLayout::setZoomFactorSnapped(double z)
0199 {
0200     setZoomFactor(z, SnapZoomFactor);
0201 }
0202 
0203 void SinglePhotoPreviewLayout::setZoomFactor(double z, const QPoint& givenAnchor, SetZoomFlags flags)
0204 {
0205     if (!d->item || !d->view)
0206     {
0207         return;
0208     }
0209 
0210     QPoint  viewportAnchor = givenAnchor.isNull() ? d->view->viewport()->rect().center() : givenAnchor;
0211     QPointF sceneAnchor    = d->view->mapToScene(viewportAnchor);
0212     QPointF imageAnchor    = d->zoomSettings()->mapZoomToImage(sceneAnchor);
0213 
0214     if (flags & SnapZoomFactor)
0215     {
0216         z = d->zoomSettings()->snappedZoomFactor(z, d->frameSize());
0217     }
0218 
0219     d->isFitToWindow = false;
0220     double minZoom   = qMin(z, 0.1);
0221     double maxZoom   = qMax(z, maxZoomFactor());
0222     minZoom          = qMin(minZoom, minZoomFactor());
0223 
0224     setMinZoomFactor(minZoom);
0225     setMaxZoomFactor(maxZoom);
0226 
0227     d->zoomSettings()->setZoomFactor(z);
0228     d->item->sizeHasChanged();
0229 
0230     updateLayout();
0231     d->item->update();
0232 
0233     Q_EMIT fitToWindowToggled(d->isFitToWindow);
0234     Q_EMIT zoomFactorChanged(d->zoomSettings()->zoomFactor());
0235 
0236     if (flags & CenterView)
0237     {
0238         d->view->centerOn(d->view->scene()->sceneRect().width()  / 2.0,
0239                           d->view->scene()->sceneRect().height() / 2.0);
0240     }
0241 
0242     d->view->scrollPointOnPoint(d->zoomSettings()->mapImageToZoom(imageAnchor), viewportAnchor);
0243 }
0244 
0245 void SinglePhotoPreviewLayout::setZoomFactor(double z, SetZoomFlags flags)
0246 {
0247     if (!d->item || !d->view)
0248     {
0249         return;
0250     }
0251 
0252     setZoomFactor(z, QPoint(), flags);
0253 }
0254 
0255 void SinglePhotoPreviewLayout::fitToWindow()
0256 {
0257     if (!d->item || !d->view)
0258     {
0259         return;
0260     }
0261 
0262     if (!d->isFitToWindow)
0263     {
0264         d->previousZoom = d->zoomSettings()->zoomFactor();
0265     }
0266 
0267     d->isFitToWindow = true;
0268 
0269     d->zoomSettings()->fitToSize(d->frameSize(), d->fitToSizeMode);
0270     d->item->sizeHasChanged();
0271 
0272     updateLayout();
0273     d->item->update();
0274 
0275     Q_EMIT fitToWindowToggled(d->isFitToWindow);
0276     Q_EMIT zoomFactorChanged(d->zoomSettings()->zoomFactor());
0277 }
0278 
0279 void SinglePhotoPreviewLayout::toggleFitToWindow()
0280 {
0281     if (!d->item || !d->view)
0282     {
0283         return;
0284     }
0285 
0286     if (d->isFitToWindow)
0287     {
0288         setZoomFactor(d->previousZoom);
0289     }
0290     else
0291     {
0292         fitToWindow();
0293     }
0294 }
0295 
0296 void SinglePhotoPreviewLayout::toggleFitToWindowOr100()
0297 {
0298     if (!d->item || !d->view)
0299     {
0300         return;
0301     }
0302 
0303     if (d->isFitToWindow || ((qRound(zoomFactor() * 1000) / 1000.0) != 1.0))
0304     {
0305         setZoomFactor(1.0);
0306     }
0307     else
0308     {
0309         fitToWindow();
0310     }
0311 }
0312 
0313 void SinglePhotoPreviewLayout::updateLayout()
0314 {
0315     if (!d->item || !d->view)
0316     {
0317         return;
0318     }
0319 
0320     d->view->scene()->setSceneRect(d->item->boundingRect());
0321     d->item->setPos(0, 0);
0322 }
0323 
0324 void SinglePhotoPreviewLayout::updateZoomAndSize()
0325 {
0326     if (!d->item || !d->view)
0327     {
0328         return;
0329     }
0330 
0331     double fitZoom = d->zoomSettings()->fitToSizeZoomFactor(d->frameSize(), d->fitToSizeMode);
0332     double minZoom = qBound(0.01, fitZoom - 0.01, 0.1);
0333 
0334     setMinZoomFactor(minZoom);
0335     setMaxZoomFactor(12.0);
0336 
0337     // Is currently the zoom factor set to fit to window? Then set it again to fit the new size.
0338 
0339     if (!d->zoomSettings()->imageSize().isNull() && d->isFitToWindow)
0340     {
0341         fitToWindow();
0342     }
0343 
0344     updateLayout();
0345 }
0346 
0347 } // namespace Digikam
0348 
0349 #include "moc_previewlayout.cpp"