File indexing completed on 2025-04-27 03:58:24
0001 /* ============================================================ 0002 * 0003 * This file is a part of digiKam project 0004 * https://www.digikam.org 0005 * 0006 * Date : 2010-04-30 0007 * Description : Graphics View for DImg preview 0008 * 0009 * SPDX-FileCopyrightText: 2010-2012 by Marcel Wiesweg <marcel dot wiesweg at gmx dot de> 0010 * SPDX-FileCopyrightText: 2011-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 "graphicsdimgview.h" 0017 0018 // Qt includes 0019 0020 #include <QApplication> 0021 #include <QGraphicsScene> 0022 #include <QScrollBar> 0023 #include <QToolButton> 0024 #include <QStyle> 0025 0026 // Local includes 0027 0028 #include "digikam_debug.h" 0029 #include "dimgpreviewitem.h" 0030 #include "imagezoomsettings.h" 0031 #include "paniconwidget.h" 0032 #include "previewlayout.h" 0033 #include "dimgchilditem.h" 0034 0035 namespace Digikam 0036 { 0037 0038 class Q_DECL_HIDDEN GraphicsDImgView::Private 0039 { 0040 public: 0041 0042 explicit Private() 0043 : scene (nullptr), 0044 item (nullptr), 0045 layout (nullptr), 0046 cornerButton (nullptr), 0047 panIconPopup (nullptr), 0048 movingInProgress(false), 0049 showText (true) 0050 { 0051 } 0052 0053 QGraphicsScene* scene; 0054 GraphicsDImgItem* item; 0055 SinglePhotoPreviewLayout* layout; 0056 0057 QToolButton* cornerButton; 0058 PanIconFrame* panIconPopup; 0059 0060 QPoint mousePressPos; 0061 QPoint panningScrollPos; 0062 bool movingInProgress; 0063 bool showText; 0064 }; 0065 0066 GraphicsDImgView::GraphicsDImgView(QWidget* const parent) 0067 : QGraphicsView(parent), 0068 d(new Private) 0069 { 0070 d->scene = new QGraphicsScene(this); 0071 d->scene->setItemIndexMethod(QGraphicsScene::NoIndex); 0072 0073 setScene(d->scene); 0074 d->layout = new SinglePhotoPreviewLayout(this); 0075 d->layout->setGraphicsView(this); 0076 0077 setViewportUpdateMode(QGraphicsView::MinimalViewportUpdate); 0078 0079 horizontalScrollBar()->setSingleStep(1); 0080 horizontalScrollBar()->setPageStep(1); 0081 verticalScrollBar()->setSingleStep(1); 0082 verticalScrollBar()->setPageStep(1); 0083 0084 connect(horizontalScrollBar(), SIGNAL(valueChanged(int)), 0085 this, SLOT(slotContentsMoved())); 0086 0087 connect(verticalScrollBar(), SIGNAL(valueChanged(int)), 0088 this, SLOT(slotContentsMoved())); 0089 } 0090 0091 GraphicsDImgView::~GraphicsDImgView() 0092 { 0093 delete d; 0094 } 0095 0096 void GraphicsDImgView::setItem(GraphicsDImgItem* const item) 0097 { 0098 d->item = item; 0099 d->item->setDisplayWidget(this); 0100 0101 d->scene->addItem(d->item); 0102 d->layout->addItem(d->item); 0103 } 0104 0105 GraphicsDImgItem* GraphicsDImgView::item() const 0106 { 0107 return d->item; 0108 } 0109 0110 DImgPreviewItem* GraphicsDImgView::previewItem() const 0111 { 0112 return dynamic_cast<DImgPreviewItem*>(item()); 0113 } 0114 0115 SinglePhotoPreviewLayout* GraphicsDImgView::layout() const 0116 { 0117 return d->layout; 0118 } 0119 0120 void GraphicsDImgView::installPanIcon() 0121 { 0122 d->cornerButton = PanIconWidget::button(); 0123 setCornerWidget(d->cornerButton); 0124 0125 connect(d->cornerButton, SIGNAL(pressed()), 0126 this, SLOT(slotCornerButtonPressed())); 0127 } 0128 0129 void GraphicsDImgView::drawForeground(QPainter* p, const QRectF& rect) 0130 { 0131 QGraphicsView::drawForeground(p, rect); 0132 0133 QString text = d->item->userLoadingHint(); 0134 0135 if (text.isNull() || !d->showText) 0136 { 0137 return; 0138 } 0139 0140 QRect viewportRect = viewport()->rect(); 0141 QRect fontRect = p->fontMetrics().boundingRect(viewportRect, 0, text); 0142 QPoint drawingPoint(viewportRect.topRight().x() - fontRect.width() - 10, 0143 viewportRect.topRight().y() + 5); 0144 0145 QPointF sceneDrawingPoint = mapToScene(drawingPoint); 0146 QRectF sceneDrawingRect(sceneDrawingPoint, fontRect.size()); 0147 0148 if (!rect.intersects(sceneDrawingRect)) 0149 { 0150 return; 0151 } 0152 0153 drawText(p, sceneDrawingRect, text); 0154 } 0155 0156 void GraphicsDImgView::drawText(QPainter* p, const QRectF& rect, const QString& text) 0157 { 0158 p->save(); 0159 0160 p->setRenderHint(QPainter::Antialiasing, true); 0161 p->setBackgroundMode(Qt::TransparentMode); 0162 0163 // increase width by 5 and height by 2 0164 0165 QRectF textRect = rect.adjusted(0, 0, 5, 2); 0166 0167 // Draw background 0168 0169 p->setPen(Qt::black); 0170 QColor semiTransBg = palette().color(QPalette::Window); 0171 semiTransBg.setAlpha(190); 0172 p->setBrush(semiTransBg); 0173 /* 0174 p->translate(0.5, 0.5); 0175 */ 0176 p->drawRoundedRect(textRect, 10.0, 10.0); 0177 0178 // Draw shadow and text 0179 0180 p->setPen(palette().color(QPalette::Window).darker(115)); 0181 p->drawText(textRect.translated(3, 1), text); 0182 p->setPen(palette().color(QPalette::WindowText)); 0183 p->drawText(textRect.translated(2, 0), text); 0184 0185 p->restore(); 0186 } 0187 0188 void GraphicsDImgView::mouseDoubleClickEvent(QMouseEvent* e) 0189 { 0190 QGraphicsView::mouseDoubleClickEvent(e); 0191 0192 if (!acceptsMouseClick(e)) 0193 { 0194 return; 0195 } 0196 0197 if (e->button() == Qt::LeftButton) 0198 { 0199 Q_EMIT leftButtonDoubleClicked(); 0200 0201 if (!qApp->style()->styleHint(QStyle::SH_ItemView_ActivateItemOnSingleClick)) 0202 { 0203 Q_EMIT activated(); 0204 } 0205 } 0206 } 0207 0208 void GraphicsDImgView::mousePressEvent(QMouseEvent* e) 0209 { 0210 QGraphicsView::mousePressEvent(e); 0211 0212 d->mousePressPos = QPoint(); 0213 d->movingInProgress = false; 0214 0215 if (!acceptsMouseClick(e)) 0216 { 0217 return; 0218 } 0219 0220 if (e->button() == Qt::LeftButton) 0221 { 0222 Q_EMIT leftButtonClicked(); 0223 } 0224 0225 if ((e->button() == Qt::LeftButton) || (e->button() == Qt::MiddleButton)) 0226 { 0227 d->mousePressPos = e->pos(); 0228 0229 if (!qApp->style()->styleHint(QStyle::SH_ItemView_ActivateItemOnSingleClick) || (e->button() == Qt::MiddleButton)) 0230 { 0231 startPanning(e->pos()); 0232 } 0233 0234 return; 0235 } 0236 0237 if (e->button() == Qt::RightButton) 0238 { 0239 Q_EMIT rightButtonClicked(); 0240 } 0241 } 0242 0243 void GraphicsDImgView::mouseMoveEvent(QMouseEvent* e) 0244 { 0245 QGraphicsView::mouseMoveEvent(e); 0246 0247 if (((e->buttons() & Qt::LeftButton) || (e->buttons() & Qt::MiddleButton)) && !d->mousePressPos.isNull()) 0248 { 0249 if (!d->movingInProgress && (e->buttons() & Qt::LeftButton)) 0250 { 0251 if ((d->mousePressPos - e->pos()).manhattanLength() > QApplication::startDragDistance()) 0252 { 0253 startPanning(d->mousePressPos); 0254 } 0255 } 0256 0257 if (d->movingInProgress) 0258 { 0259 continuePanning(e->pos()); 0260 } 0261 } 0262 } 0263 0264 void GraphicsDImgView::mouseReleaseEvent(QMouseEvent* e) 0265 { 0266 QGraphicsView::mouseReleaseEvent(e); 0267 0268 // Do not call acceptsMouseClick() here, only on press. Seems that release event are accepted per default. 0269 0270 if (((e->button() == Qt::LeftButton) || (e->button() == Qt::MiddleButton)) && !d->mousePressPos.isNull()) 0271 { 0272 if (!d->movingInProgress && (e->button() == Qt::LeftButton)) 0273 { 0274 if (qApp->style()->styleHint(QStyle::SH_ItemView_ActivateItemOnSingleClick)) 0275 { 0276 Q_EMIT activated(); 0277 } 0278 } 0279 else 0280 { 0281 finishPanning(); 0282 } 0283 } 0284 0285 d->movingInProgress = false; 0286 d->mousePressPos = QPoint(); 0287 } 0288 0289 bool GraphicsDImgView::acceptsMouseClick(QMouseEvent* e) 0290 { 0291 // the basic condition is that now item ate the event 0292 0293 if (e->isAccepted()) 0294 { 0295 return false; 0296 } 0297 0298 return true; 0299 } 0300 0301 void GraphicsDImgView::resizeEvent(QResizeEvent* e) 0302 { 0303 QGraphicsView::resizeEvent(e); 0304 d->layout->updateZoomAndSize(); 0305 Q_EMIT resized(); 0306 Q_EMIT viewportRectChanged(mapToScene(viewport()->rect()).boundingRect()); 0307 } 0308 0309 void GraphicsDImgView::scrollContentsBy(int dx, int dy) 0310 { 0311 QGraphicsView::scrollContentsBy(dx, dy); 0312 Q_EMIT viewportRectChanged(mapToScene(viewport()->rect()).boundingRect()); 0313 } 0314 0315 void GraphicsDImgView::startPanning(const QPoint& pos) 0316 { 0317 if (horizontalScrollBar()->maximum() || verticalScrollBar()->maximum()) 0318 { 0319 d->movingInProgress = true; 0320 d->mousePressPos = pos; 0321 d->panningScrollPos = QPoint(horizontalScrollBar()->value(), 0322 verticalScrollBar()->value()); 0323 viewport()->setCursor(Qt::SizeAllCursor); 0324 } 0325 } 0326 0327 void GraphicsDImgView::continuePanning(const QPoint& pos) 0328 { 0329 QPoint delta = pos - d->mousePressPos; 0330 horizontalScrollBar()->setValue(d->panningScrollPos.x() + (isRightToLeft() ? delta.x() : -delta.x())); 0331 verticalScrollBar()->setValue(d->panningScrollPos.y() - delta.y()); 0332 Q_EMIT contentsMoved(false); 0333 viewport()->update(); 0334 } 0335 0336 void GraphicsDImgView::finishPanning() 0337 { 0338 Q_EMIT contentsMoved(true); 0339 viewport()->unsetCursor(); 0340 } 0341 0342 void GraphicsDImgView::scrollPointOnPoint(const QPointF& scenePos, const QPoint& viewportPos) 0343 { 0344 // This is inspired from QGraphicsView's centerOn() 0345 QPointF viewPoint = transform().map(scenePos); 0346 0347 if (horizontalScrollBar()->maximum()) 0348 { 0349 if (isRightToLeft()) 0350 { 0351 qint64 horizontal = 0; 0352 horizontal += horizontalScrollBar()->minimum(); 0353 horizontal += horizontalScrollBar()->maximum(); 0354 horizontal -= int(viewPoint.x() - viewportPos.x()); 0355 horizontalScrollBar()->setValue(horizontal); 0356 } 0357 else 0358 { 0359 horizontalScrollBar()->setValue(int(viewPoint.x() - viewportPos.x())); 0360 } 0361 } 0362 0363 if (verticalScrollBar()->maximum()) 0364 { 0365 verticalScrollBar()->setValue(int(viewPoint.y() - viewportPos.y())); 0366 } 0367 0368 viewport()->update(); 0369 } 0370 0371 void GraphicsDImgView::wheelEvent(QWheelEvent* e) 0372 { 0373 int p = this->verticalScrollBar()->sliderPosition(); 0374 0375 if (e->modifiers() & Qt::ShiftModifier) 0376 { 0377 e->accept(); 0378 0379 if (e->angleDelta().y() < 0) 0380 { 0381 Q_EMIT toNextImage(); 0382 } 0383 else if (e->angleDelta().y() > 0) 0384 { 0385 Q_EMIT toPreviousImage(); 0386 } 0387 0388 return; 0389 } 0390 else if (e->modifiers() & Qt::ControlModifier) 0391 { 0392 // When zooming with the mouse-wheel, the image center is kept fixed. 0393 0394 QPoint point = e->position().toPoint(); 0395 0396 if (e->angleDelta().y() < 0) 0397 { 0398 d->layout->decreaseZoom(point); 0399 } 0400 else if (e->angleDelta().y() > 0) 0401 { 0402 d->layout->increaseZoom(point); 0403 } 0404 0405 return; 0406 } 0407 0408 else if ( 0409 ((p == this->verticalScrollBar()->maximum()) && (e->angleDelta().y() < 0)) || 0410 ((p == this->verticalScrollBar()->minimum()) && (e->angleDelta().y() > 0)) 0411 ) 0412 { 0413 // I had to add this condition for "ImageBrushGuideWidget" that subclasses ImageRegionWidget, used 0414 // in the healingclone tool. 0415 // If I remove that condition, this event handler gets called recursively and the program 0416 // crashes.T I couldn't figure out the reason. [Ahmed Fathy] 0417 0418 return; 0419 } 0420 else 0421 { 0422 QGraphicsView::wheelEvent(e); 0423 } 0424 } 0425 0426 void GraphicsDImgView::slotCornerButtonPressed() 0427 { 0428 if (d->panIconPopup) 0429 { 0430 d->panIconPopup->hide(); 0431 d->panIconPopup->deleteLater(); 0432 d->panIconPopup = nullptr; 0433 } 0434 0435 d->panIconPopup = new PanIconFrame(this); 0436 PanIconWidget* const pan = new PanIconWidget(d->panIconPopup); 0437 /* 0438 connect(pan, SIGNAL(signalSelectionTakeFocus()), 0439 this, SIGNAL(signalContentTakeFocus())); 0440 */ 0441 connect(pan, SIGNAL(signalSelectionMoved(QRect,bool)), 0442 this, SLOT(slotPanIconSelectionMoved(QRect,bool))); 0443 0444 connect(pan, SIGNAL(signalHidden()), 0445 this, SLOT(slotPanIconHidden())); 0446 0447 pan->setImage(180, 120, item()->image()); 0448 QRectF sceneRect(mapToScene(viewport()->rect().topLeft()), mapToScene(viewport()->rect().bottomRight())); 0449 pan->setRegionSelection(item()->zoomSettings()->sourceRect(sceneRect).toRect()); 0450 pan->setMouseFocus(); 0451 d->panIconPopup->setMainWidget(pan); 0452 /* 0453 slotContentTakeFocus(); 0454 */ 0455 QPoint g = mapToGlobal(viewport()->pos()); 0456 g.setX(g.x()+ viewport()->size().width()); 0457 g.setY(g.y()+ viewport()->size().height()); 0458 d->panIconPopup->popup(QPoint(g.x() - d->panIconPopup->width(), 0459 g.y() - d->panIconPopup->height())); 0460 0461 pan->setCursorToLocalRegionSelectionCenter(); 0462 } 0463 0464 void GraphicsDImgView::slotPanIconHidden() 0465 { 0466 d->cornerButton->blockSignals(true); 0467 d->cornerButton->animateClick(); 0468 d->cornerButton->blockSignals(false); 0469 } 0470 0471 void GraphicsDImgView::slotPanIconSelectionMoved(const QRect& imageRect, bool b) 0472 { 0473 QRectF zoomRect = item()->zoomSettings()->mapImageToZoom(imageRect); 0474 qCDebug(DIGIKAM_WIDGETS_LOG) << imageRect << zoomRect; 0475 centerOn(item()->mapToScene(zoomRect.center())); 0476 viewport()->update(); 0477 0478 if (b) 0479 { 0480 d->panIconPopup->hide(); 0481 d->panIconPopup->deleteLater(); 0482 d->panIconPopup = nullptr; 0483 slotPanIconHidden(); 0484 //slotContentLeaveFocus(); 0485 } 0486 } 0487 0488 void GraphicsDImgView::slotContentsMoved() 0489 { 0490 Q_EMIT contentsMoving(horizontalScrollBar()->value(), 0491 verticalScrollBar()->value()); 0492 viewport()->update(); 0493 } 0494 0495 int GraphicsDImgView::contentsX() const 0496 { 0497 return horizontalScrollBar()->value(); 0498 } 0499 0500 int GraphicsDImgView::contentsY() const 0501 { 0502 return verticalScrollBar()->value(); 0503 } 0504 0505 void GraphicsDImgView::setContentsPos(int x, int y) 0506 { 0507 horizontalScrollBar()->setValue(x); 0508 verticalScrollBar()->setValue(y); 0509 } 0510 0511 void GraphicsDImgView::setShowText(bool val) 0512 { 0513 d->showText = val; 0514 } 0515 0516 void GraphicsDImgView::setScaleFitToWindow(bool value) 0517 { 0518 d->layout->setScaleFitToWindow(value); 0519 } 0520 0521 QRect GraphicsDImgView::visibleArea() const 0522 { 0523 return (mapToScene(viewport()->geometry()).boundingRect().toRect()); 0524 } 0525 0526 void GraphicsDImgView::fitToWindow() 0527 { 0528 d->layout->fitToWindow(); 0529 update(); 0530 } 0531 0532 void GraphicsDImgView::toggleFullScreen(bool set) 0533 { 0534 if (set) 0535 { 0536 d->scene->setBackgroundBrush(Qt::black); 0537 setFrameShape(QFrame::NoFrame); 0538 } 0539 else 0540 { 0541 d->scene->setBackgroundBrush(Qt::NoBrush); 0542 setFrameShape(QFrame::StyledPanel); 0543 } 0544 } 0545 0546 } // namespace Digikam 0547 0548 #include "moc_graphicsdimgview.cpp"