File indexing completed on 2025-01-19 03:55:43

0001 /* ============================================================
0002  *
0003  * This file is a part of digiKam project
0004  * https://www.digikam.org
0005  *
0006  * Date        : 2009-12-13
0007  * Description : a widget to preview image effect.
0008  *
0009  * SPDX-FileCopyrightText: 2009-2024 by Gilles Caulier <caulier dot gilles at gmail dot com>
0010  * SPDX-FileCopyrightText: 2008      by Kare Sars <kare dot sars at iki dot fi>
0011  * SPDX-FileCopyrightText: 2012      by Benjamin Girault <benjamin dot girault at gmail dot com>
0012  *
0013  * SPDX-License-Identifier: GPL-2.0-or-later
0014  *
0015  * ============================================================ */
0016 
0017 #include "dpreviewimage.h"
0018 
0019 // Qt includes
0020 
0021 #include <QAction>
0022 #include <QLabel>
0023 #include <QTimer>
0024 #include <QImage>
0025 #include <QPaintEvent>
0026 #include <QPainter>
0027 #include <QPixmap>
0028 #include <QGraphicsScene>
0029 #include <QWheelEvent>
0030 #include <QScrollBar>
0031 #include <QToolBar>
0032 #include <QIcon>
0033 
0034 // KDE includes
0035 
0036 #include <klocalizedstring.h>
0037 
0038 // Local includes
0039 
0040 #include "digikam_debug.h"
0041 #include "dimg.h"
0042 #include "previewloadthread.h"
0043 
0044 namespace Digikam
0045 {
0046 
0047 static const qreal   selMargin = 8.0;
0048 static const QPointF boundMargin(selMargin, selMargin);
0049 
0050 class Q_DECL_HIDDEN DSelectionItem::Private
0051 {
0052 public:
0053 
0054     explicit Private()
0055       : maxX        (0.0),
0056         maxY        (0.0),
0057         hasMaxX     (false),
0058         hasMaxY     (false),
0059         hasMax      (false),
0060         invZoom     (1.0),
0061         selMargin   (0.0),
0062         showAnchors (true)
0063     {
0064     }
0065 
0066     QPen        penDark;
0067     QPen        penLight;
0068     QPen        penAnchors;
0069     QRectF      rect;
0070     qreal       maxX;
0071     qreal       maxY;
0072     bool        hasMaxX;
0073     bool        hasMaxY;
0074     bool        hasMax;
0075     qreal       invZoom;
0076     qreal       selMargin;
0077     QRectF      anchorTopLeft;
0078     QRectF      anchorTopRight;
0079     QRectF      anchorBottomLeft;
0080     QRectF      anchorBottomRight;
0081     QLineF      anchorTop;
0082     QLineF      anchorBottom;
0083     QLineF      anchorLeft;
0084     QLineF      anchorRight;
0085     bool        showAnchors;
0086 };
0087 
0088 DSelectionItem::DSelectionItem(const QRectF& rect)
0089     : QGraphicsItem(),
0090       d            (new Private)
0091 {
0092     d->selMargin = selMargin;
0093     setRect(rect);
0094 
0095     d->penDark.setColor(Qt::black);
0096     d->penDark.setStyle(Qt::SolidLine);
0097     d->penLight.setColor(Qt::white);
0098     d->penLight.setStyle(Qt::DashLine);
0099     d->penAnchors.setColor(Qt::white);
0100     d->penAnchors.setStyle(Qt::SolidLine);
0101 }
0102 
0103 DSelectionItem::~DSelectionItem()
0104 {
0105     delete d;
0106 }
0107 
0108 void DSelectionItem::saveZoom(qreal zoom)
0109 {
0110     if (zoom < 0.00001)
0111     {
0112         zoom = 0.00001;
0113     }
0114 
0115     d->invZoom   = 1 / zoom;
0116     d->selMargin = selMargin * d->invZoom;
0117 
0118     updateAnchors();
0119 }
0120 
0121 void DSelectionItem::setMaxRight(qreal maxX)
0122 {
0123     d->maxX    = maxX;
0124     d->hasMaxX = true;
0125 
0126     if (d->hasMaxY)
0127     {
0128         d->hasMax = true;
0129     }
0130 }
0131 
0132 void DSelectionItem::setMaxBottom(qreal maxY)
0133 {
0134     d->maxY    = maxY;
0135     d->hasMaxY = true;
0136 
0137     if (d->hasMaxX)
0138     {
0139         d->hasMax = true;
0140     }
0141 }
0142 
0143 DSelectionItem::Intersects DSelectionItem::intersects(QPointF& point)
0144 {
0145 
0146     if ((point.x() < (d->rect.left()   - d->selMargin)) ||
0147         (point.x() > (d->rect.right()  + d->selMargin)) ||
0148         (point.y() < (d->rect.top()    - d->selMargin)) ||
0149         (point.y() > (d->rect.bottom() + d->selMargin)))
0150     {
0151         d->showAnchors = false;
0152         update();
0153         return None;
0154     }
0155 
0156     d->showAnchors = true;
0157     update();
0158 
0159     if (point.x() < (d->rect.left() + d->selMargin))
0160     {
0161         if (point.y() < (d->rect.top()    + d->selMargin))
0162         {
0163             return TopLeft;
0164         }
0165 
0166         if (point.y() > (d->rect.bottom() - d->selMargin))
0167         {
0168             return BottomLeft;
0169         }
0170 
0171         return Left;
0172     }
0173 
0174     if (point.x() > (d->rect.right() - d->selMargin))
0175     {
0176         if (point.y() < (d->rect.top()    + d->selMargin))
0177         {
0178             return TopRight;
0179         }
0180 
0181         if (point.y() > (d->rect.bottom() - d->selMargin))
0182         {
0183             return BottomRight;
0184         }
0185 
0186         return Right;
0187     }
0188 
0189     if (point.y() < (d->rect.top() + d->selMargin))
0190     {
0191         return Top;
0192     }
0193 
0194     if (point.y() > (d->rect.bottom()-d->selMargin))
0195     {
0196         return Bottom;
0197     }
0198 
0199     return Move;
0200 }
0201 
0202 void DSelectionItem::setRect(const QRectF& rect)
0203 {
0204     prepareGeometryChange();
0205     d->rect = rect;
0206     d->rect = d->rect.normalized();
0207 
0208     if (d->hasMax)
0209     {
0210         if (d->rect.top() < 0)
0211         {
0212             d->rect.setTop(0);
0213         }
0214 
0215         if (d->rect.left() < 0)
0216         {
0217             d->rect.setLeft(0);
0218         }
0219 
0220         if (d->rect.right() > d->maxX)
0221         {
0222             d->rect.setRight(d->maxX);
0223         }
0224 
0225         if (d->rect.bottom() > d->maxY)
0226         {
0227             d->rect.setBottom(d->maxY);
0228         }
0229     }
0230 
0231     updateAnchors();
0232 }
0233 
0234 QPointF DSelectionItem::fixTranslation(QPointF dp) const
0235 {
0236     if ((d->rect.left()   + dp.x()) < 0)
0237     {
0238         dp.setX(-d->rect.left());
0239     }
0240 
0241     if ((d->rect.top()    + dp.y()) < 0)
0242     {
0243         dp.setY(-d->rect.top());
0244     }
0245 
0246     if ((d->rect.right()  + dp.x()) > d->maxX)
0247     {
0248         dp.setX(d->maxX - d->rect.right());
0249     }
0250 
0251     if ((d->rect.bottom() + dp.y()) > d->maxY)
0252     {
0253         dp.setY(d->maxY - d->rect.bottom());
0254     }
0255 
0256     return dp;
0257 }
0258 
0259 QRectF DSelectionItem::rect() const
0260 {
0261     return d->rect;
0262 }
0263 
0264 QRectF DSelectionItem::boundingRect() const
0265 {
0266     return QRectF(d->rect.topLeft() - boundMargin, d->rect.bottomRight() + boundMargin);
0267 }
0268 
0269 void DSelectionItem::paint(QPainter* painter, const QStyleOptionGraphicsItem*, QWidget*)
0270 {
0271     painter->setPen(d->penDark);
0272     painter->drawRect(d->rect);
0273 
0274     painter->setPen(d->penLight);
0275     painter->drawRect(d->rect);
0276 
0277     if (d->showAnchors)
0278     {
0279         painter->setPen(d->penAnchors);
0280         painter->setOpacity(0.4);
0281 
0282         if (!d->anchorTop.isNull())
0283         {
0284             painter->drawLine(d->anchorTop);
0285         }
0286 
0287         if (!d->anchorBottom.isNull())
0288         {
0289             painter->drawLine(d->anchorBottom);
0290         }
0291 
0292         if (!d->anchorLeft.isNull())
0293         {
0294             painter->drawLine(d->anchorLeft);
0295         }
0296 
0297         if (!d->anchorRight.isNull())
0298         {
0299             painter->drawLine(d->anchorRight);
0300         }
0301 
0302         painter->setOpacity(0.4);
0303 
0304         if (!d->anchorTopLeft.isNull())
0305         {
0306             painter->fillRect(d->anchorTopLeft, Qt::white);
0307         }
0308 
0309         if (!d->anchorTopRight.isNull())
0310         {
0311             painter->fillRect(d->anchorTopRight, Qt::white);
0312         }
0313 
0314         if (!d->anchorBottomLeft.isNull())
0315         {
0316             painter->fillRect(d->anchorBottomLeft, Qt::white);
0317         }
0318 
0319         if (!d->anchorBottomRight.isNull())
0320         {
0321             painter->fillRect(d->anchorBottomRight, Qt::white);
0322         }
0323     }
0324 }
0325 
0326 void DSelectionItem::updateAnchors()
0327 {
0328     QPointF moveDown(0.0, d->selMargin);
0329     QPointF moveRight(d->selMargin, 0.0);
0330     bool verticalCondition   = (d->rect.height() - 3 * d->selMargin) > 0;
0331     bool horizontalCondition = (d->rect.width()  - 3 * d->selMargin) > 0;
0332 
0333     if (verticalCondition)
0334     {
0335         if (horizontalCondition)
0336         {
0337             d->anchorTop    = QLineF(d->rect.topLeft() + moveDown,
0338                                      d->rect.topRight() + moveDown);
0339             d->anchorBottom = QLineF(d->rect.bottomLeft() - moveDown,
0340                                      d->rect.bottomRight() - moveDown);
0341             d->anchorLeft   = QLineF(d->rect.topLeft() + moveRight,
0342                                      d->rect.bottomLeft() + moveRight);
0343             d->anchorRight  = QLineF(d->rect.topRight() - moveRight,
0344                                      d->rect.bottomRight() - moveRight);
0345 
0346             d->anchorTopLeft        = QRectF(d->rect.topLeft(),
0347                                              d->rect.topLeft() + moveDown + moveRight);
0348             d->anchorTopRight       = QRectF(d->rect.topRight() - moveRight,
0349                                              d->rect.topRight() + moveDown);
0350             d->anchorBottomLeft     = QRectF(d->rect.bottomLeft() - moveDown,
0351                                              d->rect.bottomLeft() + moveRight);
0352             d->anchorBottomRight    = QRectF(d->rect.bottomRight() - moveDown - moveRight,
0353                                              d->rect.bottomRight());
0354         }
0355         else
0356         {
0357             // Only the top & bottom lines & middle line plus two corners are drawn
0358 
0359             d->anchorTop    = QLineF(d->rect.topLeft() + moveDown,
0360                                      d->rect.topRight() + moveDown);
0361             d->anchorBottom = QLineF(d->rect.bottomLeft() - moveDown,
0362                                      d->rect.bottomRight() - moveDown);
0363             d->anchorLeft   = QLineF(d->rect.topLeft() + QPointF(d->rect.width() / 2.0, 0.0),
0364                                      d->rect.bottomLeft() + QPointF(d->rect.width() / 2.0, 0.0));
0365             d->anchorRight  = QLineF();
0366 
0367             d->anchorTopLeft        = QRectF(d->rect.topLeft(),
0368                                              d->rect.topRight() + moveDown);
0369             d->anchorTopRight       = QRectF();
0370             d->anchorBottomLeft     = QRectF(d->rect.bottomLeft() - moveDown,
0371                                              d->rect.bottomRight());
0372             d->anchorBottomRight    = QRectF();
0373         }
0374     }
0375     else
0376     {
0377         if (horizontalCondition)
0378         {
0379             // Only the left & right lines & middle line plus two corners are drawn
0380 
0381             d->anchorTop            = QLineF(d->rect.topLeft() + QPointF(0.0, d->rect.height() / 2.0),
0382                                              d->rect.topRight() + QPointF(0.0, d->rect.height() / 2.0));
0383             d->anchorBottom         = QLineF();
0384             d->anchorLeft           = QLineF(d->rect.topLeft() + moveRight,
0385                                              d->rect.bottomLeft() + moveRight);
0386             d->anchorRight          = QLineF(d->rect.topRight() - moveRight,
0387                                              d->rect.bottomRight() - moveRight);
0388 
0389             d->anchorTopLeft        = QRectF(d->rect.topLeft(),
0390                                              d->rect.bottomLeft() + moveRight);
0391             d->anchorTopRight       = QRectF(d->rect.topRight() - moveRight,
0392                                              d->rect.bottomRight());
0393             d->anchorBottomLeft     = QRectF();
0394             d->anchorBottomRight    = QRectF();
0395         }
0396         else
0397         {
0398             d->anchorTop            = QLineF();
0399             d->anchorBottom         = QLineF();
0400             d->anchorLeft           = QLineF();
0401             d->anchorRight          = QLineF();
0402 
0403             d->anchorTopLeft        = QRectF();
0404             d->anchorTopRight       = QRectF();
0405             d->anchorBottomLeft     = QRectF();
0406             d->anchorBottomRight    = QRectF();
0407         }
0408     }
0409 }
0410 
0411 // -------------------------------------------------------------------------------------------------
0412 
0413 class Q_DECL_HIDDEN DPreviewImage::Private
0414 {
0415 public:
0416 
0417     enum
0418     {
0419         NONE,
0420         LOOKAROUND,
0421         DRAWSELECTION,
0422         EXPANDORSHRINK,
0423         MOVESELECTION
0424     }
0425     mouseDragAction;
0426 
0427 public:
0428 
0429     explicit Private()
0430         : mouseDragAction(NONE),
0431           lastdx         (0),
0432           lastdy         (0),
0433           scene          (nullptr),
0434           pixmapItem     (nullptr),
0435           selection      (nullptr),
0436           enableSelection(false),
0437           mouseZone      (DSelectionItem::None),
0438           zoomInAction   (nullptr),
0439           zoomOutAction  (nullptr),
0440           zoom2FitAction (nullptr),
0441           toolBar        (nullptr),
0442           highLightLeft  (nullptr),
0443           highLightRight (nullptr),
0444           highLightTop   (nullptr),
0445           highLightBottom(nullptr),
0446           highLightArea  (nullptr)
0447     {
0448     }
0449 
0450     int                        lastdx;
0451     int                        lastdy;
0452 
0453     QGraphicsScene*            scene;
0454     QGraphicsPixmapItem*       pixmapItem;
0455     DSelectionItem*            selection;
0456     bool                       enableSelection;
0457     DSelectionItem::Intersects mouseZone;
0458     QPointF                    lastMousePoint;
0459 
0460     QAction*                   zoomInAction;
0461     QAction*                   zoomOutAction;
0462     QAction*                   zoom2FitAction;
0463 
0464     QToolBar*                  toolBar;
0465 
0466     QGraphicsRectItem*         highLightLeft;
0467     QGraphicsRectItem*         highLightRight;
0468     QGraphicsRectItem*         highLightTop;
0469     QGraphicsRectItem*         highLightBottom;
0470     QGraphicsRectItem*         highLightArea;
0471 };
0472 
0473 DPreviewImage::DPreviewImage(QWidget* const parent)
0474     : QGraphicsView(parent),
0475       d            (new Private)
0476 {
0477     setAttribute(Qt::WA_DeleteOnClose);
0478     setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
0479     setMouseTracking(true);
0480     setCacheMode(QGraphicsView::CacheBackground);
0481 
0482     d->scene           = new QGraphicsScene;
0483     d->pixmapItem      = new QGraphicsPixmapItem;
0484 
0485     d->selection       = new DSelectionItem(QRectF());
0486     d->selection->setZValue(10);
0487     d->selection->setVisible(false);
0488     d->enableSelection = false;
0489 
0490     d->scene->addItem(d->pixmapItem);
0491     setScene(d->scene);
0492 
0493     d->highLightTop    = new QGraphicsRectItem;
0494     d->highLightBottom = new QGraphicsRectItem;
0495     d->highLightRight  = new QGraphicsRectItem;
0496     d->highLightLeft   = new QGraphicsRectItem;
0497     d->highLightArea   = new QGraphicsRectItem;
0498 
0499     d->highLightTop->setOpacity(0.4);
0500     d->highLightBottom->setOpacity(0.4);
0501     d->highLightRight->setOpacity(0.4);
0502     d->highLightLeft->setOpacity(0.4);
0503     d->highLightArea->setOpacity(0.6);
0504 
0505     d->highLightTop->setPen(Qt::NoPen);
0506     d->highLightBottom->setPen(Qt::NoPen);
0507     d->highLightRight->setPen(Qt::NoPen);
0508     d->highLightLeft->setPen(Qt::NoPen);
0509     d->highLightArea->setPen(Qt::NoPen);
0510 
0511     d->highLightTop->setBrush(QBrush(Qt::black));
0512     d->highLightBottom->setBrush(QBrush(Qt::black));
0513     d->highLightRight->setBrush(QBrush(Qt::black));
0514     d->highLightLeft->setBrush(QBrush(Qt::black));
0515 
0516     d->scene->addItem(d->selection);
0517     d->scene->addItem(d->highLightTop);
0518     d->scene->addItem(d->highLightBottom);
0519     d->scene->addItem(d->highLightRight);
0520     d->scene->addItem(d->highLightLeft);
0521     d->scene->addItem(d->highLightArea);
0522 
0523     d->mouseZone = DSelectionItem::None;
0524 
0525     // create context menu
0526 
0527     d->zoomInAction = new QAction(QIcon::fromTheme(QLatin1String("zoom-in")), i18nc("@action", "Zoom In"), this);
0528     d->zoomInAction->setToolTip(i18nc("@info", "Zoom In"));
0529     d->zoomInAction->setShortcut(Qt::Key_Plus);
0530 
0531     connect(d->zoomInAction, &QAction::triggered,
0532             this, &DPreviewImage::slotZoomIn);
0533 
0534     d->zoomOutAction = new QAction(QIcon::fromTheme(QLatin1String("zoom-out")), i18nc("@action", "Zoom Out"), this);
0535     d->zoomOutAction->setToolTip(i18nc("@info", "Zoom Out"));
0536     d->zoomOutAction->setShortcut(Qt::Key_Minus);
0537 
0538     connect(d->zoomOutAction, &QAction::triggered,
0539             this, &DPreviewImage::slotZoomOut);
0540 
0541     d->zoom2FitAction = new QAction(QIcon::fromTheme(QLatin1String("zoom-fit-best")), i18nc("@action", "Zoom to Fit"), this);
0542     d->zoom2FitAction->setToolTip(i18nc("@info", "Zoom to Fit"));
0543     d->zoom2FitAction->setShortcut(Qt::Key_Asterisk);
0544 
0545     connect(d->zoom2FitAction, &QAction::triggered,
0546             this, &DPreviewImage::slotZoom2Fit);
0547 
0548     addAction(d->zoomInAction);
0549     addAction(d->zoomOutAction);
0550     addAction(d->zoom2FitAction);
0551     setContextMenuPolicy(Qt::ActionsContextMenu);
0552 
0553     // Create ToolBar
0554 
0555     d->toolBar = new QToolBar(this);
0556     d->toolBar->addAction(d->zoomInAction);
0557     d->toolBar->addAction(d->zoomOutAction);
0558     d->toolBar->addAction(d->zoom2FitAction);
0559     d->toolBar->hide();
0560     d->toolBar->installEventFilter(this);
0561 
0562     horizontalScrollBar()->installEventFilter(this);
0563     verticalScrollBar()->installEventFilter(this);
0564 }
0565 
0566 DPreviewImage::~DPreviewImage()
0567 {
0568     delete d;
0569 }
0570 
0571 bool DPreviewImage::setImage(const QImage& img) const
0572 {
0573     if (!img.isNull())
0574     {
0575         d->pixmapItem->setPixmap(QPixmap::fromImage(img));
0576         d->pixmapItem->setShapeMode(QGraphicsPixmapItem::BoundingRectShape);
0577         d->scene->setSceneRect(0, 0, img.width(), img.height());
0578         return true;
0579     }
0580 
0581     return false;
0582 }
0583 
0584 void DPreviewImage::enableSelectionArea(bool b)
0585 {
0586     d->enableSelection = b;
0587 }
0588 
0589 QRectF DPreviewImage::getSelectionArea() const
0590 {
0591     return d->selection->rect();
0592 }
0593 
0594 void DPreviewImage::setSelectionArea(const QRectF& rectangle)
0595 {
0596     d->selection->setRect(rectangle);
0597 
0598     if (!d->selection->isVisible())
0599         d->selection->setVisible(true);
0600 }
0601 
0602 bool DPreviewImage::load(const QUrl& file) const
0603 {
0604     DImg dimg = PreviewLoadThread::loadHighQualitySynchronously(file.toLocalFile());
0605     bool ret  = setImage(dimg.copyQImage());
0606 
0607     if (ret && d->enableSelection)
0608     {
0609         qCDebug(DIGIKAM_GENERAL_LOG) << d->scene->height() << " " << d->scene->width();
0610         d->selection->setMaxRight(d->scene->width());
0611         d->selection->setMaxBottom(d->scene->height());
0612         d->selection->setRect(d->scene->sceneRect());
0613     }
0614 
0615     return ret;
0616 }
0617 
0618 void DPreviewImage::slotZoomIn()
0619 {
0620     scale(1.5, 1.5);
0621     d->selection->saveZoom(transform().m11());
0622     d->zoom2FitAction->setDisabled(false);
0623 }
0624 
0625 void DPreviewImage::slotZoomOut()
0626 {
0627     scale(1.0 / 1.5, 1.0 / 1.5);
0628     d->selection->saveZoom(transform().m11());
0629     d->zoom2FitAction->setDisabled(false);
0630 }
0631 
0632 void DPreviewImage::slotZoom2Fit()
0633 {
0634     fitInView(d->pixmapItem->boundingRect(), Qt::KeepAspectRatio);
0635     d->selection->saveZoom(transform().m11());
0636     d->zoom2FitAction->setDisabled(true);
0637 }
0638 
0639 void DPreviewImage::slotSetTLX(float ratio)
0640 {
0641     if (!d->selection->isVisible())
0642     {
0643         return; // only correct the selection if it is visible
0644     }
0645 
0646     QRectF rect = d->selection->rect();
0647     rect.setLeft(ratio * d->scene->width());
0648     d->selection->setRect(rect);
0649     updateSelVisibility();
0650 }
0651 
0652 void DPreviewImage::slotSetTLY(float ratio)
0653 {
0654     if (!d->selection->isVisible())
0655     {
0656         return; // only correct the selection if it is visible
0657     }
0658 
0659     QRectF rect = d->selection->rect();
0660     rect.setTop(ratio * d->scene->height());
0661     d->selection->setRect(rect);
0662     updateSelVisibility();
0663 }
0664 
0665 void DPreviewImage::slotSetBRX(float ratio)
0666 {
0667     if (!d->selection->isVisible())
0668     {
0669         return; // only correct the selection if it is visible
0670     }
0671 
0672     QRectF rect = d->selection->rect();
0673     rect.setRight(ratio * d->scene->width());
0674     d->selection->setRect(rect);
0675     updateSelVisibility();
0676 }
0677 
0678 void DPreviewImage::slotSetBRY(float ratio)
0679 {
0680     if (!d->selection->isVisible())
0681     {
0682         return; // only correct the selection if it is visible
0683     }
0684 
0685     QRectF rect = d->selection->rect();
0686     rect.setBottom(ratio * d->scene->height());
0687     d->selection->setRect(rect);
0688     updateSelVisibility();
0689 }
0690 
0691 void DPreviewImage::slotSetSelection(float tl_x, float tl_y, float br_x, float br_y)
0692 {
0693     QRectF rect;
0694     rect.setCoords(tl_x * d->scene->width(),
0695                    tl_y * d->scene->height(),
0696                    br_x * d->scene->width(),
0697                    br_y * d->scene->height());
0698 
0699     d->selection->setRect(rect);
0700     updateSelVisibility();
0701 }
0702 
0703 void DPreviewImage::slotClearActiveSelection()
0704 {
0705     d->selection->setRect(QRectF(0, 0, 0, 0));
0706     d->selection->setVisible(false);
0707 }
0708 
0709 void DPreviewImage::slotSetHighlightArea(float tl_x, float tl_y, float br_x, float br_y)
0710 {
0711     QRectF rect;
0712 
0713     // Left  reason for rect: setCoords(x1,y1,x2,y2) != setRect(x1,x2, width, height)
0714 
0715     rect.setCoords(0,
0716                    0,
0717                    tl_x * d->scene->width(),
0718                    d->scene->height());
0719     d->highLightLeft->setRect(rect);
0720 
0721     // Right
0722 
0723     rect.setCoords(br_x * d->scene->width(),
0724                    0,
0725                    d->scene->width(),
0726                    d->scene->height());
0727     d->highLightRight->setRect(rect);
0728 
0729     // Top
0730 
0731     rect.setCoords(tl_x * d->scene->width(),
0732                    0,
0733                    br_x * d->scene->width(),
0734                    tl_y * d->scene->height());
0735     d->highLightTop->setRect(rect);
0736 
0737     // Bottom
0738 
0739     rect.setCoords(tl_x * d->scene->width(),
0740                    br_y * d->scene->height(),
0741                    br_x * d->scene->width(),
0742                    d->scene->height());
0743     d->highLightBottom->setRect(rect);
0744 
0745     // Area
0746 
0747     rect.setCoords(tl_x * d->scene->width(),
0748                    tl_y* d->scene->height(),
0749                    br_x * d->scene->width(),
0750                    br_y* d->scene->height());
0751 
0752     d->highLightArea->setRect(rect);
0753 
0754     d->highLightLeft->show();
0755     d->highLightRight->show();
0756     d->highLightTop->show();
0757     d->highLightBottom->show();
0758 
0759     // the highlight area is hidden until setHighlightShown is called.
0760 
0761     d->highLightArea->hide();
0762 }
0763 
0764 void DPreviewImage::slotSetHighlightShown(int percentage, const QColor& highLightColor)
0765 {
0766     if (percentage >= 100)
0767     {
0768         d->highLightArea->hide();
0769         return;
0770     }
0771 
0772     d->highLightArea->setBrush(highLightColor);
0773 
0774     qreal diff  = d->highLightBottom->rect().top() - d->highLightTop->rect().bottom();
0775     diff       -= (diff * percentage) / 100;
0776 
0777     QRectF rect = d->highLightArea->rect();
0778     rect.setTop(d->highLightBottom->rect().top() - diff);
0779 
0780     d->highLightArea->setRect(rect);
0781 
0782     d->highLightArea->show();
0783 }
0784 
0785 void DPreviewImage::slotClearHighlight()
0786 {
0787     d->highLightLeft->hide();
0788     d->highLightRight->hide();
0789     d->highLightTop->hide();
0790     d->highLightBottom->hide();
0791     d->highLightArea->hide();
0792 }
0793 
0794 void DPreviewImage::wheelEvent(QWheelEvent* e)
0795 {
0796     if (e->modifiers() == Qt::ControlModifier)
0797     {
0798         if (e->angleDelta().y() > 0)
0799         {
0800             slotZoomIn();
0801         }
0802         else
0803         {
0804             slotZoomOut();
0805         }
0806     }
0807     else
0808     {
0809         QGraphicsView::wheelEvent(e);
0810     }
0811 }
0812 
0813 void DPreviewImage::mousePressEvent(QMouseEvent* e)
0814 {
0815     if (e->button() & Qt::LeftButton)
0816     {
0817 
0818 #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
0819 
0820         d->lastdx          = e->position().toPoint().x();
0821         d->lastdy          = e->position().toPoint().y();
0822         QPointF scenePoint = mapToScene(e->position().toPoint());
0823 
0824 #else
0825 
0826         d->lastdx          = e->x();
0827         d->lastdy          = e->y();
0828         QPointF scenePoint = mapToScene(e->pos());
0829 
0830 #endif
0831 
0832         d->lastMousePoint  = scenePoint;
0833 
0834         if (e->modifiers() != Qt::ControlModifier && d->enableSelection)
0835         {
0836             if      (!d->selection->isVisible() || !d->selection->contains(scenePoint))
0837             {
0838                 // Beginning of a selection area change
0839 
0840                 d->mouseDragAction = Private::DRAWSELECTION;
0841                 d->selection->setVisible(true);
0842                 d->selection->setRect(QRectF(scenePoint, QSizeF(0, 0)));
0843                 d->mouseZone = DSelectionItem::BottomRight;
0844             }
0845             else if (d->selection->isVisible() &&
0846                      d->mouseZone != DSelectionItem::None &&
0847                      d->mouseZone != DSelectionItem::Move)
0848             {
0849                 // Modification of the selection area
0850 
0851                 d->mouseDragAction = Private::EXPANDORSHRINK;
0852             }
0853             else
0854             {
0855                 // Selection movement called by QGraphicsView
0856 
0857                 d->mouseDragAction = Private::MOVESELECTION;
0858             }
0859 
0860             updateHighlight();
0861         }
0862         else
0863         {
0864             // Beginning of moving around the picture
0865 
0866             d->mouseDragAction = Private::LOOKAROUND;
0867             setCursor(Qt::ClosedHandCursor);
0868         }
0869     }
0870 
0871     QGraphicsView::mousePressEvent(e);
0872 }
0873 
0874 void DPreviewImage::mouseReleaseEvent(QMouseEvent* e)
0875 {
0876     if (e->button() & Qt::LeftButton)
0877     {
0878         if (d->mouseDragAction == Private::DRAWSELECTION)
0879         {
0880             // Stop and setup the selection area
0881             // Only one case: small rectangle that we drop
0882 
0883             if ((d->selection->rect().width()  < 0.001) ||
0884                 (d->selection->rect().height() < 0.001))
0885             {
0886                 slotClearActiveSelection();
0887             }
0888         }
0889 
0890 #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
0891 
0892         if (!d->selection->isVisible() || !d->selection->contains(e->position().toPoint()))
0893 
0894 #else
0895 
0896         if (!d->selection->isVisible() || !d->selection->contains(e->pos()))
0897 
0898 #endif
0899 
0900         {
0901             setCursor(Qt::CrossCursor);
0902         }
0903     }
0904 
0905     d->mouseDragAction = Private::NONE;
0906     updateHighlight();
0907 
0908     QGraphicsView::mouseReleaseEvent(e);
0909 }
0910 
0911 void DPreviewImage::mouseMoveEvent(QMouseEvent* e)
0912 {
0913 
0914 #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
0915 
0916     QPointF scenePoint = mapToScene(e->position().toPoint());
0917 
0918 #else
0919 
0920     QPointF scenePoint = mapToScene(e->pos());
0921 
0922 #endif
0923 
0924     if (e->buttons() & Qt::LeftButton)
0925     {
0926         if (d->mouseDragAction == Private::LOOKAROUND)
0927         {
0928 
0929 #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
0930 
0931             int dx    = e->position().toPoint().x() - d->lastdx;
0932             int dy    = e->position().toPoint().y() - d->lastdy;
0933 
0934 #else
0935 
0936             int dx    = e->x() - d->lastdx;
0937             int dy    = e->y() - d->lastdy;
0938 
0939 #endif
0940 
0941             verticalScrollBar()->setValue(verticalScrollBar()->value() - dy);
0942             horizontalScrollBar()->setValue(horizontalScrollBar()->value() - dx);
0943 
0944 #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
0945 
0946             d->lastdx = e->position().toPoint().x();
0947             d->lastdy = e->position().toPoint().y();
0948 
0949 #else
0950 
0951             d->lastdx = e->x();
0952             d->lastdy = e->y();
0953 
0954 #endif
0955 
0956         }
0957         else if (d->mouseDragAction == Private::DRAWSELECTION  ||
0958                  d->mouseDragAction == Private::EXPANDORSHRINK ||
0959                  d->mouseDragAction == Private::MOVESELECTION)
0960         {
0961             ensureVisible(QRectF(scenePoint, QSizeF(0, 0)), 1, 1);
0962             QRectF rect = d->selection->rect();
0963 
0964             switch (d->mouseZone)
0965             {
0966                 case DSelectionItem::None:
0967                 {
0968                     // should not be here :)
0969                     break;
0970                 }
0971 
0972                 case DSelectionItem::Top:
0973                 {
0974                     if (scenePoint.y() < rect.bottom())
0975                     {
0976                         rect.setTop(scenePoint.y());
0977                     }
0978                     else
0979                     {
0980                         d->mouseZone = DSelectionItem::Bottom;
0981                         rect.setTop(rect.bottom());
0982                     }
0983 
0984                     break;
0985                 }
0986 
0987                 case DSelectionItem::TopRight:
0988                 {
0989                     if (scenePoint.x() > rect.left())
0990                     {
0991                         rect.setRight(scenePoint.x());
0992                     }
0993                     else
0994                     {
0995                         d->mouseZone = DSelectionItem::TopLeft;
0996                         setCursor(Qt::SizeFDiagCursor);
0997                         rect.setRight(rect.left());
0998                     }
0999 
1000                     if (scenePoint.y() < rect.bottom())
1001                     {
1002                         rect.setTop(scenePoint.y());
1003                     }
1004                     else
1005                     {
1006                         if (d->mouseZone != DSelectionItem::TopLeft)
1007                         {
1008                             d->mouseZone = DSelectionItem::BottomRight;
1009                             setCursor(Qt::SizeFDiagCursor);
1010                         }
1011                         else
1012                         {
1013                             d->mouseZone = DSelectionItem::BottomLeft;
1014                             setCursor(Qt::SizeBDiagCursor);
1015                         }
1016 
1017                         rect.setTop(rect.bottom());
1018                     }
1019 
1020                     break;
1021                 }
1022 
1023                 case DSelectionItem::Right:
1024                 {
1025                     if (scenePoint.x() > rect.left())
1026                     {
1027                         rect.setRight(scenePoint.x());
1028                     }
1029                     else
1030                     {
1031                         d->mouseZone = DSelectionItem::Left;
1032                         rect.setRight(rect.left());
1033                     }
1034 
1035                     break;
1036                 }
1037 
1038                 case DSelectionItem::BottomRight:
1039                 {
1040                     if (scenePoint.x() > rect.left())
1041                     {
1042                         rect.setRight(scenePoint.x());
1043                     }
1044                     else
1045                     {
1046                         d->mouseZone = DSelectionItem::BottomLeft;
1047                         setCursor(Qt::SizeBDiagCursor);
1048                         rect.setRight(rect.left());
1049                     }
1050 
1051                     if (scenePoint.y() > rect.top())
1052                     {
1053                         rect.setBottom(scenePoint.y());
1054                     }
1055                     else
1056                     {
1057                         if (d->mouseZone != DSelectionItem::BottomLeft)
1058                         {
1059                             d->mouseZone = DSelectionItem::TopRight;
1060                             setCursor(Qt::SizeBDiagCursor);
1061                         }
1062                         else
1063                         {
1064                             d->mouseZone = DSelectionItem::TopLeft;
1065                             setCursor(Qt::SizeFDiagCursor);
1066                         }
1067 
1068                         rect.setBottom(rect.top());
1069                     }
1070 
1071                     break;
1072                 }
1073 
1074                 case DSelectionItem::Bottom:
1075                 {
1076                     if (scenePoint.y() > rect.top())
1077                     {
1078                         rect.setBottom(scenePoint.y());
1079                     }
1080                     else
1081                     {
1082                         d->mouseZone = DSelectionItem::Top;
1083                         rect.setBottom(rect.top());
1084                     }
1085 
1086                     break;
1087                 }
1088 
1089                 case DSelectionItem::BottomLeft:
1090                 {
1091                     if (scenePoint.x() < rect.right())
1092                     {
1093                         rect.setLeft(scenePoint.x());
1094                     }
1095                     else
1096                     {
1097                         d->mouseZone = DSelectionItem::BottomRight;
1098                         setCursor(Qt::SizeFDiagCursor);
1099                         rect.setLeft(rect.right());
1100                     }
1101 
1102                     if (scenePoint.y() > rect.top())
1103                     {
1104                         rect.setBottom(scenePoint.y());
1105                     }
1106                     else
1107                     {
1108                         if (d->mouseZone != DSelectionItem::BottomRight)
1109                         {
1110                             d->mouseZone = DSelectionItem::TopLeft;
1111                             setCursor(Qt::SizeFDiagCursor);
1112                         }
1113                         else
1114                         {
1115                             d->mouseZone = DSelectionItem::TopRight;
1116                             setCursor(Qt::SizeBDiagCursor);
1117                         }
1118 
1119                         rect.setBottom(rect.top());
1120                     }
1121 
1122                     break;
1123                 }
1124 
1125                 case DSelectionItem::Left:
1126                 {
1127                     if (scenePoint.x() < rect.right())
1128                     {
1129                         rect.setLeft(scenePoint.x());
1130                     }
1131                     else
1132                     {
1133                         d->mouseZone = DSelectionItem::Right;
1134                         rect.setLeft(rect.right());
1135                     }
1136 
1137                     break;
1138                 }
1139 
1140                 case DSelectionItem::TopLeft:
1141                 {
1142                     if (scenePoint.x() < rect.right())
1143                     {
1144                         rect.setLeft(scenePoint.x());
1145                     }
1146                     else
1147                     {
1148                         d->mouseZone = DSelectionItem::TopRight;
1149                         setCursor(Qt::SizeBDiagCursor);
1150                         rect.setLeft(rect.right());
1151                     }
1152 
1153                     if (scenePoint.y() < rect.bottom())
1154                     {
1155                         rect.setTop(scenePoint.y());
1156                     }
1157                     else
1158                     {
1159                         if (d->mouseZone != DSelectionItem::TopRight)
1160                         {
1161                             d->mouseZone = DSelectionItem::BottomLeft;
1162                             setCursor(Qt::SizeBDiagCursor);
1163                         }
1164                         else
1165                         {
1166                             d->mouseZone = DSelectionItem::BottomRight;
1167                             setCursor(Qt::SizeFDiagCursor);
1168                         }
1169 
1170                         rect.setTop(rect.bottom());
1171                     }
1172 
1173                     break;
1174                 }
1175 
1176                 case DSelectionItem::Move:
1177                 {
1178                     rect.translate(d->selection->fixTranslation(scenePoint - d->lastMousePoint));
1179                     break;
1180                 }
1181             }
1182 
1183             d->selection->setRect(rect);
1184         }
1185     }
1186     else if (d->selection->isVisible())
1187     {
1188         d->mouseZone = d->selection->intersects(scenePoint);
1189 
1190         switch (d->mouseZone)
1191         {
1192             case DSelectionItem::None:
1193             {
1194                 setCursor(Qt::CrossCursor);
1195                 break;
1196             }
1197 
1198             case DSelectionItem::Top:
1199             {
1200                 setCursor(Qt::SizeVerCursor);
1201                 break;
1202             }
1203 
1204             case DSelectionItem::TopRight:
1205             {
1206                 setCursor(Qt::SizeBDiagCursor);
1207                 break;
1208             }
1209 
1210             case DSelectionItem::Right:
1211             {
1212                 setCursor(Qt::SizeHorCursor);
1213                 break;
1214             }
1215 
1216             case DSelectionItem::BottomRight:
1217             {
1218                 setCursor(Qt::SizeFDiagCursor);
1219                 break;
1220             }
1221 
1222             case DSelectionItem::Bottom:
1223             {
1224                 setCursor(Qt::SizeVerCursor);
1225                 break;
1226             }
1227 
1228             case DSelectionItem::BottomLeft:
1229             {
1230                 setCursor(Qt::SizeBDiagCursor);
1231                 break;
1232             }
1233 
1234             case DSelectionItem::Left:
1235             {
1236                 setCursor(Qt::SizeHorCursor);
1237                 break;
1238             }
1239 
1240             case DSelectionItem::TopLeft:
1241             {
1242                 setCursor(Qt::SizeFDiagCursor);
1243                 break;
1244             }
1245 
1246             case DSelectionItem::Move:
1247             {
1248                 setCursor(Qt::SizeAllCursor);
1249                 break;
1250             }
1251         }
1252     }
1253     else
1254     {
1255         setCursor(Qt::CrossCursor);
1256     }
1257 
1258     d->lastMousePoint = scenePoint;
1259     updateHighlight();
1260     QGraphicsView::mouseMoveEvent(e);
1261 }
1262 
1263 #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
1264 
1265 void DPreviewImage::enterEvent(QEnterEvent* /*event*/)
1266 
1267 #else
1268 
1269 void DPreviewImage::enterEvent(QEvent* /*event*/)
1270 
1271 #endif
1272 
1273 {
1274     d->toolBar->show();
1275 }
1276 
1277 void DPreviewImage::leaveEvent(QEvent*)
1278 {
1279     d->toolBar->hide();
1280 }
1281 
1282 bool DPreviewImage::eventFilter(QObject* obj, QEvent* ev)
1283 {
1284     if (obj == d->toolBar)
1285     {
1286         if      (ev->type() == QEvent::Enter)
1287         {
1288             setCursor(Qt::ArrowCursor);
1289         }
1290         else if (ev->type() == QEvent::Leave)
1291         {
1292             unsetCursor();
1293         }
1294 
1295         return false;
1296     }
1297     else if (obj == verticalScrollBar() && verticalScrollBar()->isVisible())
1298     {
1299         if      (ev->type() == QEvent::Enter)
1300         {
1301             setCursor(Qt::ArrowCursor);
1302         }
1303         else if (ev->type() == QEvent::Leave)
1304         {
1305             unsetCursor();
1306         }
1307 
1308         return false;
1309     }
1310     else if (obj == horizontalScrollBar() && horizontalScrollBar()->isVisible())
1311     {
1312         if      (ev->type() == QEvent::Enter)
1313         {
1314             setCursor(Qt::ArrowCursor);
1315         }
1316         else if (ev->type() == QEvent::Leave)
1317         {
1318             unsetCursor();
1319         }
1320 
1321         return false;
1322     }
1323 
1324     return QGraphicsView::eventFilter(obj, ev);
1325 }
1326 
1327 void DPreviewImage::resizeEvent(QResizeEvent* e)
1328 {
1329     if (!d->zoom2FitAction->isEnabled())
1330     {
1331         // Fit the image to the new size...
1332 
1333         fitInView(d->pixmapItem->boundingRect(), Qt::KeepAspectRatio);
1334         d->selection->saveZoom(transform().m11());
1335     }
1336 
1337     QGraphicsView::resizeEvent(e);
1338 }
1339 
1340 void DPreviewImage::updateSelVisibility()
1341 {
1342     if ((d->selection->rect().width()                       > 0.001) &&
1343         (d->selection->rect().height()                      > 0.001) &&
1344         ((d->scene->width() - d->selection->rect().width()  > 0.1) ||
1345         (d->scene->height() - d->selection->rect().height() > 0.1)))
1346     {
1347         d->selection->setVisible(true);
1348     }
1349     else
1350     {
1351         d->selection->setVisible(false);
1352     }
1353 
1354     updateHighlight();
1355 }
1356 
1357 void DPreviewImage::updateHighlight()
1358 {
1359     if (d->selection->isVisible())
1360     {
1361         QRectF rect;
1362 
1363         // Left
1364 
1365         rect.setCoords(0,
1366                        0,
1367                        d->selection->rect().left(),
1368                        d->scene->height());
1369         d->highLightLeft->setRect(rect);
1370 
1371         // Right
1372 
1373         rect.setCoords(d->selection->rect().right(),
1374                        0,
1375                        d->scene->width(),
1376                        d->scene->height());
1377         d->highLightRight->setRect(rect);
1378 
1379         // Top
1380 
1381         rect.setCoords(d->selection->rect().left(),
1382                        0,
1383                        d->selection->rect().right(),
1384                        d->selection->rect().top());
1385         d->highLightTop->setRect(rect);
1386 
1387         // Bottom
1388 
1389         rect.setCoords(d->selection->rect().left(),
1390                        d->selection->rect().bottom(),
1391                        d->selection->rect().right(),
1392                        d->scene->height());
1393         d->highLightBottom->setRect(rect);
1394 
1395         d->highLightLeft->show();
1396         d->highLightRight->show();
1397         d->highLightTop->show();
1398         d->highLightBottom->show();
1399         d->highLightArea->hide();
1400     }
1401     else
1402     {
1403         d->highLightLeft->hide();
1404         d->highLightRight->hide();
1405         d->highLightTop->hide();
1406         d->highLightBottom->hide();
1407         d->highLightArea->hide();
1408     }
1409 }
1410 
1411 } // namespace Digikam
1412 
1413 #include "moc_dpreviewimage.cpp"