abstractimageview.cpp 17 KB
Newer Older
1
// vim: set tabstop=4 shiftwidth=4 expandtab:
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
/*
Gwenview: an image viewer
Copyright 2011 Aurélien Gâteau <agateau@kde.org>

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Cambridge, MA 02110-1301, USA.

*/
// Self
David Edmundson's avatar
David Edmundson committed
22
#include "abstractimageview.h"
23 24 25 26 27 28

// Local

// KDE

// Qt
Laurent Montel's avatar
Laurent Montel committed
29
#include "gwenview_lib_debug.h"
rkflx's avatar
rkflx committed
30
#include <QGuiApplication>
Aurélien Gâteau's avatar
Aurélien Gâteau committed
31 32
#include <QCursor>
#include <QGraphicsSceneMouseEvent>
33
#include <QStandardPaths>
34
#include <QPainter>
Alexander Volkov's avatar
Alexander Volkov committed
35
#include <QApplication>
36

37 38
namespace Gwenview
{
39

40
static const int UNIT_STEP = 16;
41

Aurélien Gâteau's avatar
Aurélien Gâteau committed
42 43
struct AbstractImageViewPrivate
{
44 45 46 47 48
    enum Verbosity {
        Silent,
        Notify
    };
    AbstractImageView* q;
49
    QCursor mZoomCursor;
50 51
    Document::Ptr mDocument;

52
    bool mControlKeyIsDown;
53 54 55 56
    bool mEnlargeSmallerImages;

    qreal mZoom;
    bool mZoomToFit;
57
    bool mZoomToFill;
58 59
    QPointF mImageOffset;
    QPointF mScrollPos;
60
    QPointF mLastDragPos;
61
    QSizeF mDocumentSize;
62

63
    const QPixmap mAlphaBackgroundTexture;
64

65 66
    void adjustImageOffset(Verbosity verbosity = Notify)
    {
Alexander Volkov's avatar
Alexander Volkov committed
67
        QSizeF zoomedDocSize = q->dipDocumentSize() * mZoom;
68 69
        QSizeF viewSize = q->boundingRect().size();
        QPointF offset(
Scott Kitterman's avatar
Scott Kitterman committed
70 71
            qMax((viewSize.width() - zoomedDocSize.width()) / 2, qreal(0.)),
            qMax((viewSize.height() - zoomedDocSize.height()) / 2, qreal(0.))
72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87
        );
        if (offset != mImageOffset) {
            mImageOffset = offset;
            if (verbosity == Notify) {
                q->onImageOffsetChanged();
            }
        }
    }

    void adjustScrollPos(Verbosity verbosity = Notify)
    {
        setScrollPos(mScrollPos, verbosity);
    }

    void setScrollPos(const QPointF& _newPos, Verbosity verbosity = Notify)
    {
88 89 90 91
        if (!mDocument) {
            mScrollPos = _newPos;
            return;
        }
Alexander Volkov's avatar
Alexander Volkov committed
92
        QSizeF zoomedDocSize = q->dipDocumentSize() * mZoom;
93 94
        QSizeF viewSize = q->boundingRect().size();
        QPointF newPos(
Scott Kitterman's avatar
Scott Kitterman committed
95 96
            qBound(qreal(0.), _newPos.x(), zoomedDocSize.width() - viewSize.width()),
            qBound(qreal(0.), _newPos.y(), zoomedDocSize.height() - viewSize.height())
97 98 99 100 101 102 103 104 105
        );
        if (newPos != mScrollPos) {
            QPointF oldPos = mScrollPos;
            mScrollPos = newPos;
            if (verbosity == Notify) {
                q->onScrollPosChanged(oldPos);
            }
            // No verbosity test: we always notify the outside world about
            // scrollPos changes
106
            emit q->scrollPosChanged();
107 108
        }
    }
109 110 111

    void setupZoomCursor()
    {
112 113
        // We do not use "appdata" here because that does not work when this
        // code is called from a KPart.
114
        const QString path = QStandardPaths::locate(QStandardPaths::GenericDataLocation, QStringLiteral("gwenview/cursors/zoom.png"));
115
        QPixmap cursorPixmap = QPixmap(path);
Aurélien Gâteau's avatar
Aurélien Gâteau committed
116
        mZoomCursor = QCursor(cursorPixmap, 11, 11);
117
    }
118 119 120 121 122 123 124 125 126

    QPixmap createAlphaBackgroundTexture()
    {
        QPixmap pix = QPixmap(32, 32);
        QPainter painter(&pix);
        painter.fillRect(pix.rect(), QColor(128, 128, 128));
        const QColor light = QColor(192, 192, 192);
        painter.fillRect(0, 0, 16, 16, light);
        painter.fillRect(16, 16, 16, 16, light);
Alexander Volkov's avatar
Alexander Volkov committed
127
        pix.setDevicePixelRatio(q->devicePixelRatio());
128 129
        return pix;
    }
130

131 132 133 134 135
    AbstractImageViewPrivate(AbstractImageView *parent) :
        q(parent),
        mAlphaBackgroundTexture(createAlphaBackgroundTexture())
    { }

136 137 138 139
    void checkAndRequestZoomAction(const QGraphicsSceneMouseEvent* event)
    {
        if (event->modifiers() & Qt::ControlModifier) {
            if (event->button() == Qt::LeftButton) {
Alexander Volkov's avatar
Alexander Volkov committed
140
                emit q->zoomInRequested(event->pos());
141
            } else if (event->button() == Qt::RightButton) {
Alexander Volkov's avatar
Alexander Volkov committed
142
                emit q->zoomOutRequested(event->pos());
143 144 145
            }
        }
    }
146 147 148 149
};

AbstractImageView::AbstractImageView(QGraphicsItem* parent)
: QGraphicsWidget(parent)
150
, d(new AbstractImageViewPrivate(this))
151
{
152
    d->mControlKeyIsDown = false;
153 154 155
    d->mEnlargeSmallerImages = false;
    d->mZoom = 1;
    d->mZoomToFit = true;
156
    d->mZoomToFill = false;
157 158 159 160
    d->mImageOffset = QPointF(0, 0);
    d->mScrollPos = QPointF(0, 0);
    setFocusPolicy(Qt::WheelFocus);
    setFlag(ItemIsSelectable);
161
    setFlag(ItemClipsChildrenToShape);
162
    setAcceptHoverEvents(true);
163 164
    d->setupZoomCursor();
    updateCursor();
165 166
}

167 168
AbstractImageView::~AbstractImageView()
{
169 170 171
    if (d->mDocument) {
        d->mDocument->stopAnimation();
    }
172
    delete d;
173 174
}

175 176 177
Document::Ptr AbstractImageView::document() const
{
    return d->mDocument;
Aurélien Gâteau's avatar
Aurélien Gâteau committed
178
}
179

180
void AbstractImageView::setDocument(const Document::Ptr &doc)
181
{
182
    if (d->mDocument) {
183
        disconnect(d->mDocument.data(), nullptr, this, nullptr);
184
    }
185 186
    d->mDocument = doc;
    loadFromDocument();
Aurélien Gâteau's avatar
Aurélien Gâteau committed
187 188
}

189 190 191
QSizeF AbstractImageView::documentSize() const
{
    return d->mDocument ? d->mDocument->size() : QSizeF();
192 193
}

Alexander Volkov's avatar
Alexander Volkov committed
194 195 196 197 198
QSizeF AbstractImageView::dipDocumentSize() const
{
    return d->mDocument ? d->mDocument->size() / devicePixelRatio(): QSizeF();
}

199 200 201
qreal AbstractImageView::zoom() const
{
    return d->mZoom;
202 203
}

204 205
void AbstractImageView::setZoom(qreal zoom, const QPointF& _center, AbstractImageView::UpdateType updateType)
{
206 207 208 209
    if (!d->mDocument) {
        d->mZoom = zoom;
        return;
    }
210 211 212

    if (updateType == UpdateIfNecessary
            && qFuzzyCompare(zoom, d->mZoom) && documentSize() == d->mDocumentSize) {
213 214 215 216
        return;
    }
    qreal oldZoom = d->mZoom;
    d->mZoom = zoom;
217
    d->mDocumentSize = documentSize();
218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264

    QPointF center;
    if (_center == QPointF(-1, -1)) {
        center = boundingRect().center();
    } else {
        center = _center;
    }

    /*
    We want to keep the point at viewport coordinates "center" at the same
    position after zooming. The coordinates of this point in image coordinates
    can be expressed like this:

                          oldScroll + center
    imagePointAtOldZoom = ------------------
                               oldZoom

                       scroll + center
    imagePointAtZoom = ---------------
                            zoom

    So we want:

        imagePointAtOldZoom = imagePointAtZoom

        oldScroll + center   scroll + center
    <=> ------------------ = ---------------
              oldZoom             zoom

                  zoom
    <=> scroll = ------- (oldScroll + center) - center
                 oldZoom
    */

    /*
    Compute oldScroll
    It's useless to take the new offset in consideration because if a direction
    of the new offset is not 0, we won't be able to center on a specific point
    in that direction.
    */
    QPointF oldScroll = scrollPos() - imageOffset();

    QPointF scroll = (zoom / oldZoom) * (oldScroll + center) - center;

    d->adjustImageOffset(AbstractImageViewPrivate::Silent);
    d->setScrollPos(scroll, AbstractImageViewPrivate::Silent);
    onZoomChanged();
Alexander Volkov's avatar
Alexander Volkov committed
265
    emit zoomChanged(d->mZoom);
266 267
}

268 269 270
bool AbstractImageView::zoomToFit() const
{
    return d->mZoomToFit;
271 272
}

273
bool AbstractImageView::zoomToFill() const
Eldin Gagulić's avatar
Eldin Gagulić committed
274
{
275
    return d->mZoomToFill;
Eldin Gagulić's avatar
Eldin Gagulić committed
276 277
}

278 279 280 281
void AbstractImageView::setZoomToFit(bool on)
{
    d->mZoomToFit = on;
    if (on) {
282
        d->mZoomToFill = false;
283 284 285 286 287
        setZoom(computeZoomToFit());
    }
    // We do not set zoom to 1 if zoomToFit is off, this is up to the code
    // calling us. It may went to zoom to some other level and/or to zoom on
    // a particular position
Alexander Volkov's avatar
Alexander Volkov committed
288
    emit zoomToFitChanged(d->mZoomToFit);
289 290
}

291
void AbstractImageView::setZoomToFill(bool on, const QPointF& center)
Eldin Gagulić's avatar
Eldin Gagulić committed
292
{
293
    d->mZoomToFill = on;
Eldin Gagulić's avatar
Eldin Gagulić committed
294
    if (on) {
295
        d->mZoomToFit = false;
296
        setZoom(computeZoomToFill(), center);
Eldin Gagulić's avatar
Eldin Gagulić committed
297 298 299 300
    }
    // We do not set zoom to 1 if zoomToFit is off, this is up to the code
    // calling us. It may went to zoom to some other level and/or to zoom on
    // a particular position
Alexander Volkov's avatar
Alexander Volkov committed
301
    emit zoomToFillChanged(d->mZoomToFill);
Eldin Gagulić's avatar
Eldin Gagulić committed
302 303
}

304 305 306 307 308
const QPixmap& AbstractImageView::alphaBackgroundTexture() const
{
    return d->mAlphaBackgroundTexture;
}

309 310
void AbstractImageView::resizeEvent(QGraphicsSceneResizeEvent* event)
{
Aurélien Gâteau's avatar
Aurélien Gâteau committed
311
    QGraphicsWidget::resizeEvent(event);
312
    if (d->mZoomToFit) {
313 314 315 316 317 318 319 320 321
        // setZoom() calls adjustImageOffset(), but only if the zoom changes.
        // If the view is resized but does not cause a zoom change, we call
        // adjustImageOffset() ourself.
        const qreal newZoom = computeZoomToFit();
        if (qFuzzyCompare(zoom(), newZoom)) {
            d->adjustImageOffset(AbstractImageViewPrivate::Notify);
        } else {
            setZoom(newZoom);
        }
322 323
    } else if (d->mZoomToFill) {
        const qreal newZoom = computeZoomToFill();
Eldin Gagulić's avatar
Eldin Gagulić committed
324 325 326 327 328
        if (qFuzzyCompare(zoom(), newZoom)) {
            d->adjustImageOffset(AbstractImageViewPrivate::Notify);
        } else {
            setZoom(newZoom);
        }
329 330 331 332
    } else {
        d->adjustImageOffset();
        d->adjustScrollPos();
    }
333 334
}

rkflx's avatar
rkflx committed
335 336 337 338 339 340 341 342 343 344 345 346
void AbstractImageView::focusInEvent(QFocusEvent* event)
{
    QGraphicsWidget::focusInEvent(event);

    // We might have missed a keyReleaseEvent for the control key, e.g. for Ctrl+O
    const bool controlKeyIsCurrentlyDown = QGuiApplication::queryKeyboardModifiers() & Qt::ControlModifier;
    if (d->mControlKeyIsDown != controlKeyIsCurrentlyDown) {
        d->mControlKeyIsDown = controlKeyIsCurrentlyDown;
        updateCursor();
    }
}

347 348
qreal AbstractImageView::computeZoomToFit() const
{
Alexander Volkov's avatar
Alexander Volkov committed
349
    QSizeF docSize = dipDocumentSize();
350 351 352 353 354 355 356 357
    if (docSize.isEmpty()) {
        return 1;
    }
    QSizeF viewSize = boundingRect().size();
    qreal fitWidth = viewSize.width() / docSize.width();
    qreal fitHeight = viewSize.height() / docSize.height();
    qreal fit = qMin(fitWidth, fitHeight);
    if (!d->mEnlargeSmallerImages) {
Scott Kitterman's avatar
Scott Kitterman committed
358
        fit = qMin(fit, qreal(1.));
359 360
    }
    return fit;
361 362
}

363
qreal AbstractImageView::computeZoomToFill() const
Eldin Gagulić's avatar
Eldin Gagulić committed
364
{
Alexander Volkov's avatar
Alexander Volkov committed
365
    QSizeF docSize = dipDocumentSize();
Eldin Gagulić's avatar
Eldin Gagulić committed
366 367 368 369 370
    if (docSize.isEmpty()) {
        return 1;
    }
    QSizeF viewSize = boundingRect().size();
    qreal fitWidth = viewSize.width() / docSize.width();
371 372
    qreal fitHeight = viewSize.height() / docSize.height();
    qreal fill = qMax(fitWidth, fitHeight);
Eldin Gagulić's avatar
Eldin Gagulić committed
373
    if (!d->mEnlargeSmallerImages) {
374
        fill = qMin(fill, qreal(1.));
Eldin Gagulić's avatar
Eldin Gagulić committed
375
    }
376
    return fill;
Eldin Gagulić's avatar
Eldin Gagulić committed
377 378
}

379 380 381
void AbstractImageView::mousePressEvent(QGraphicsSceneMouseEvent* event)
{
    QGraphicsItem::mousePressEvent(event);
382

383
    d->checkAndRequestZoomAction(event);
384

385 386 387 388 389
    // Prepare for panning or dragging
    if (event->button() == Qt::LeftButton) {
        d->mLastDragPos = event->pos();
        updateCursor();
    }
390 391
}

392 393 394 395
void AbstractImageView::mouseMoveEvent(QGraphicsSceneMouseEvent* event)
{
    QGraphicsItem::mouseMoveEvent(event);

396 397 398
    QPointF mousePos = event->pos();
    QPointF newScrollPos = d->mScrollPos + d->mLastDragPos - mousePos;

399
#if 0 // commented out due to mouse pointer warping around, bug in Qt?
400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422
    // Wrap mouse pos
    qreal maxWidth = boundingRect().width();
    qreal maxHeight = boundingRect().height();
    // We need a margin because if the window is maximized, the mouse may not
    // be able to go past the bounding rect.
    // The mouse get placed 1 pixel before/after the margin to avoid getting
    // considered as needing to wrap the other way in next mouseMoveEvent
    // (because we don't check the move vector)
    const int margin = 5;
    if (mousePos.x() <= margin) {
        mousePos.setX(maxWidth - margin - 1);
    } else if (mousePos.x() >= maxWidth - margin) {
        mousePos.setX(margin + 1);
    }
    if (mousePos.y() <= margin) {
        mousePos.setY(maxHeight - margin - 1);
    } else if (mousePos.y() >= maxHeight - margin) {
        mousePos.setY(margin + 1);
    }

    // Set mouse pos (Hackish translation to screen coords!)
    QPointF screenDelta = event->screenPos() - event->pos();
    QCursor::setPos((mousePos + screenDelta).toPoint());
423
#endif
424 425

    d->mLastDragPos = mousePos;
426
    d->setScrollPos(newScrollPos);
427 428
}

429 430 431
void AbstractImageView::mouseReleaseEvent(QGraphicsSceneMouseEvent* event)
{
    QGraphicsItem::mouseReleaseEvent(event);
432 433
    if (!d->mLastDragPos.isNull()) {
        d->mLastDragPos = QPointF();
434 435
    }
    updateCursor();
436 437
}

438 439
void AbstractImageView::keyPressEvent(QKeyEvent* event)
{
440 441 442 443 444
    if (event->key() == Qt::Key_Control) {
        d->mControlKeyIsDown = true;
        updateCursor();
        return;
    }
Aurélien Gâteau's avatar
Aurélien Gâteau committed
445 446 447 448 449 450 451 452
    if (zoomToFit() || qFuzzyCompare(computeZoomToFit(), zoom())) {
        if (event->modifiers() != Qt::NoModifier) {
            return;
        }

        switch (event->key()) {
        case Qt::Key_Left:
        case Qt::Key_Up:
Alexander Volkov's avatar
Alexander Volkov committed
453
            emit previousImageRequested();
Aurélien Gâteau's avatar
Aurélien Gâteau committed
454 455 456
            break;
        case Qt::Key_Right:
        case Qt::Key_Down:
Alexander Volkov's avatar
Alexander Volkov committed
457
            emit nextImageRequested();
Aurélien Gâteau's avatar
Aurélien Gâteau committed
458 459 460 461 462 463 464
            break;
        default:
            break;
        }
        return;
    }

465 466 467
    QPointF delta(0, 0);
    qreal pageStep = boundingRect().height();
    qreal unitStep;
Aurélien Gâteau's avatar
Aurélien Gâteau committed
468

469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496
    if (event->modifiers() & Qt::ShiftModifier) {
        unitStep = pageStep / 2;
    } else {
        unitStep = UNIT_STEP;
    }
    switch (event->key()) {
    case Qt::Key_Left:
        delta.setX(-unitStep);
        break;
    case Qt::Key_Right:
        delta.setX(unitStep);
        break;
    case Qt::Key_Up:
        delta.setY(-unitStep);
        break;
    case Qt::Key_Down:
        delta.setY(unitStep);
        break;
    case Qt::Key_PageUp:
        delta.setY(-pageStep);
        break;
    case Qt::Key_PageDown:
        delta.setY(pageStep);
        break;
    case Qt::Key_Home:
        d->setScrollPos(QPointF(d->mScrollPos.x(), 0));
        return;
    case Qt::Key_End:
Alexander Volkov's avatar
Alexander Volkov committed
497
        d->setScrollPos(QPointF(d->mScrollPos.x(), dipDocumentSize().height() * zoom()));
498 499 500 501 502
        return;
    default:
        return;
    }
    d->setScrollPos(d->mScrollPos + delta);
503 504
}

505 506 507 508 509 510 511 512
void AbstractImageView::keyReleaseEvent(QKeyEvent* event)
{
    if (event->key() == Qt::Key_Control) {
        d->mControlKeyIsDown = false;
        updateCursor();
    }
}

513 514
void AbstractImageView::mouseDoubleClickEvent(QGraphicsSceneMouseEvent* event)
{
515
    if (event->modifiers() == Qt::NoModifier && event->button() == Qt::LeftButton) {
Alexander Volkov's avatar
Alexander Volkov committed
516
        emit toggleFullScreenRequested();
517
    }
518 519

    d->checkAndRequestZoomAction(event);
520 521
}

522 523 524
QPointF AbstractImageView::imageOffset() const
{
    return d->mImageOffset;
525 526
}

527 528 529
QPointF AbstractImageView::scrollPos() const
{
    return d->mScrollPos;
530 531
}

532 533 534
void AbstractImageView::setScrollPos(const QPointF& pos)
{
    d->setScrollPos(pos);
535 536
}

Alexander Volkov's avatar
Alexander Volkov committed
537 538 539 540 541
qreal AbstractImageView::devicePixelRatio() const
{
    return qApp->devicePixelRatio();
}

542 543
QPointF AbstractImageView::mapToView(const QPointF& imagePos) const
{
Alexander Volkov's avatar
Alexander Volkov committed
544
    return imagePos / devicePixelRatio() * d->mZoom + d->mImageOffset - d->mScrollPos;
545 546
}

547 548 549
QPoint AbstractImageView::mapToView(const QPoint& imagePos) const
{
    return mapToView(QPointF(imagePos)).toPoint();
550 551
}

552 553 554 555
QRectF AbstractImageView::mapToView(const QRectF& imageRect) const
{
    return QRectF(
               mapToView(imageRect.topLeft()),
Alexander Volkov's avatar
Alexander Volkov committed
556
               imageRect.size() * zoom() / devicePixelRatio()
557
           );
558 559
}

560 561 562 563
QRect AbstractImageView::mapToView(const QRect& imageRect) const
{
    return QRect(
               mapToView(imageRect.topLeft()),
Alexander Volkov's avatar
Alexander Volkov committed
564
               imageRect.size() * zoom() / devicePixelRatio()
565
           );
566 567
}

568 569
QPointF AbstractImageView::mapToImage(const QPointF& viewPos) const
{
Alexander Volkov's avatar
Alexander Volkov committed
570
    return (viewPos - d->mImageOffset + d->mScrollPos) / d->mZoom * devicePixelRatio();
571 572
}

573 574 575
QPoint AbstractImageView::mapToImage(const QPoint& viewPos) const
{
    return mapToImage(QPointF(viewPos)).toPoint();
576 577
}

578 579 580 581
QRectF AbstractImageView::mapToImage(const QRectF& viewRect) const
{
    return QRectF(
               mapToImage(viewRect.topLeft()),
Alexander Volkov's avatar
Alexander Volkov committed
582
               viewRect.size() / zoom() * devicePixelRatio()
583
           );
584 585
}

586 587 588 589
QRect AbstractImageView::mapToImage(const QRect& viewRect) const
{
    return QRect(
               mapToImage(viewRect.topLeft()),
Alexander Volkov's avatar
Alexander Volkov committed
590
               viewRect.size() / zoom() * devicePixelRatio()
591
           );
592 593
}

594 595 596 597 598 599
void AbstractImageView::setEnlargeSmallerImages(bool value)
{
    d->mEnlargeSmallerImages = value;
    if (zoomToFit()) {
        setZoom(computeZoomToFit());
    }
600 601
}

602 603
void AbstractImageView::updateCursor()
{
604
    if (d->mControlKeyIsDown) {
605 606
        setCursor(d->mZoomCursor);
    } else {
607 608
        if (d->mLastDragPos.isNull()) {
            setCursor(Qt::OpenHandCursor);
609
        } else {
610
            setCursor(Qt::ClosedHandCursor);
611 612 613 614
        }
    }
}

615 616 617 618 619
QSizeF AbstractImageView::visibleImageSize() const
{
    if (!document()) {
        return QSizeF();
    }
Alexander Volkov's avatar
Alexander Volkov committed
620
    QSizeF size = dipDocumentSize() * zoom();
621 622 623
    return size.boundedTo(boundingRect().size());
}

Aurélien Gâteau's avatar
Cleanup  
Aurélien Gâteau committed
624
void AbstractImageView::applyPendingScrollPos()
625
{
Aurélien Gâteau's avatar
Cleanup  
Aurélien Gâteau committed
626 627
    d->adjustImageOffset();
    d->adjustScrollPos();
628 629
}

630 631 632 633 634 635
void AbstractImageView::resetDragCursor()
{
    d->mLastDragPos = QPointF();
    updateCursor();
}

636
} // namespace