diff --git a/lib/documentview/abstractdocumentviewadapter.h b/lib/documentview/abstractdocumentviewadapter.h index 75c79093..75cfd765 100644 --- a/lib/documentview/abstractdocumentviewadapter.h +++ b/lib/documentview/abstractdocumentviewadapter.h @@ -1,203 +1,203 @@ // vim: set tabstop=4 shiftwidth=4 expandtab: /* Gwenview: an image viewer Copyright 2008 Aurélien Gâteau 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. */ #ifndef ABSTRACTDOCUMENTVIEWADAPTER_H #define ABSTRACTDOCUMENTVIEWADAPTER_H #include // Qt #include #include // KDE // Local #include class QCursor; class QGraphicsWidget; class QRectF; namespace Gwenview { -class ImageView; +class AbstractImageView; class RasterImageView; /** * Classes inherit from this class so that they can be used inside the * DocumentPanel. */ class GWENVIEWLIB_EXPORT AbstractDocumentViewAdapter : public QObject { Q_OBJECT public: AbstractDocumentViewAdapter(); virtual ~AbstractDocumentViewAdapter(); QGraphicsWidget* widget() const { return mWidget; } virtual MimeTypeUtils::Kind kind() const = 0; - virtual ImageView* imageView() const + virtual AbstractImageView* imageView() const { return 0; } virtual RasterImageView* rasterImageView() const { return 0; } virtual QCursor cursor() const; virtual void setCursor(const QCursor&); /** * @defgroup zooming functions * @{ */ virtual bool canZoom() const { return false; } // Implementation must emit zoomToFitChanged() virtual void setZoomToFit(bool) {} virtual bool zoomToFit() const { return false; } // Implementation must emit zoomToFillChanged() virtual void setZoomToFill(bool) {} virtual bool zoomToFill() const { return false; } virtual qreal zoom() const { return 0; } virtual void setZoom(qreal /*zoom*/, const QPointF& /*center*/ = QPointF(-1, -1)) {} virtual qreal computeZoomToFit() const { return 1.; } virtual qreal computeZoomToFill() const { return 1.; } /** @} */ virtual Document::Ptr document() const = 0; virtual void setDocument(Document::Ptr) = 0; virtual void loadConfig() {} virtual QPointF scrollPos() const { return QPointF(0, 0); } virtual void setScrollPos(const QPointF& /*pos*/) {} /** * Rectangle within the item which is actually used to show the document. * In item coordinates. */ virtual QRectF visibleDocumentRect() const; protected: void setWidget(QGraphicsWidget* widget) { mWidget = widget; } Q_SIGNALS: /** * @addgroup zooming functions * @{ */ void zoomChanged(qreal); void zoomToFitChanged(bool); void zoomToFillChanged(bool); void zoomInRequested(const QPointF&); void zoomOutRequested(const QPointF&); /** @} */ void scrollPosChanged(); /** * Emitted when the adapter is done showing the document for the first time */ void completed(); void previousImageRequested(); void nextImageRequested(); void toggleFullScreenRequested(); private: QGraphicsWidget* mWidget; }; /** * An empty adapter, used when no document is displayed */ class EmptyAdapter : public AbstractDocumentViewAdapter { Q_OBJECT public: EmptyAdapter(); virtual MimeTypeUtils::Kind kind() const Q_DECL_OVERRIDE { return MimeTypeUtils::KIND_UNKNOWN; } virtual Document::Ptr document() const Q_DECL_OVERRIDE { return Document::Ptr(); } virtual void setDocument(Document::Ptr) Q_DECL_OVERRIDE {} }; } // namespace #endif /* ABSTRACTDOCUMENTVIEWADAPTER_H */ diff --git a/lib/documentview/abstractimageview.cpp b/lib/documentview/abstractimageview.cpp index 506fd991..54424a88 100644 --- a/lib/documentview/abstractimageview.cpp +++ b/lib/documentview/abstractimageview.cpp @@ -1,599 +1,605 @@ // vim: set tabstop=4 shiftwidth=4 expandtab: /* Gwenview: an image viewer Copyright 2011 Aurélien Gâteau 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 #include "abstractimageview.h" // Local // KDE // Qt #include #include #include #include #include namespace Gwenview { static const int UNIT_STEP = 16; struct AbstractImageViewPrivate { enum Verbosity { Silent, Notify }; AbstractImageView* q; QCursor mZoomCursor; Document::Ptr mDocument; bool mControlKeyIsDown; bool mEnlargeSmallerImages; qreal mZoom; bool mZoomToFit; bool mZoomToFill; QPointF mImageOffset; QPointF mScrollPos; QPointF mLastDragPos; QSizeF mDocumentSize; const QPixmap mAlphaBackgroundTexture = createAlphaBackgroundTexture(); void adjustImageOffset(Verbosity verbosity = Notify) { QSizeF zoomedDocSize = q->documentSize() * mZoom; QSizeF viewSize = q->boundingRect().size(); QPointF offset( qMax((viewSize.width() - zoomedDocSize.width()) / 2, qreal(0.)), qMax((viewSize.height() - zoomedDocSize.height()) / 2, qreal(0.)) ); 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) { if (!mDocument) { mScrollPos = _newPos; return; } QSizeF zoomedDocSize = q->documentSize() * mZoom; QSizeF viewSize = q->boundingRect().size(); QPointF newPos( qBound(qreal(0.), _newPos.x(), zoomedDocSize.width() - viewSize.width()), qBound(qreal(0.), _newPos.y(), zoomedDocSize.height() - viewSize.height()) ); 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 QMetaObject::invokeMethod(q, "scrollPosChanged"); } } void setupZoomCursor() { // We do not use "appdata" here because that does not work when this // code is called from a KPart. const QString path = QStandardPaths::locate(QStandardPaths::GenericDataLocation, QStringLiteral("gwenview/cursors/zoom.png")); QPixmap cursorPixmap = QPixmap(path); mZoomCursor = QCursor(cursorPixmap, 11, 11); } 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); return pix; } }; AbstractImageView::AbstractImageView(QGraphicsItem* parent) : QGraphicsWidget(parent) , d(new AbstractImageViewPrivate) { d->q = this; d->mControlKeyIsDown = false; d->mEnlargeSmallerImages = false; d->mZoom = 1; d->mZoomToFit = true; d->mZoomToFill = false; d->mImageOffset = QPointF(0, 0); d->mScrollPos = QPointF(0, 0); setFocusPolicy(Qt::WheelFocus); setFlag(ItemIsSelectable); setFlag(ItemClipsChildrenToShape); setAcceptHoverEvents(true); d->setupZoomCursor(); updateCursor(); } AbstractImageView::~AbstractImageView() { if (d->mDocument) { d->mDocument->stopAnimation(); } delete d; } Document::Ptr AbstractImageView::document() const { return d->mDocument; } void AbstractImageView::setDocument(Document::Ptr doc) { if (d->mDocument) { disconnect(d->mDocument.data(), 0, this, 0); } d->mDocument = doc; loadFromDocument(); } QSizeF AbstractImageView::documentSize() const { return d->mDocument ? d->mDocument->size() : QSizeF(); } qreal AbstractImageView::zoom() const { return d->mZoom; } void AbstractImageView::setZoom(qreal zoom, const QPointF& _center, AbstractImageView::UpdateType updateType) { if (!d->mDocument) { d->mZoom = zoom; return; } if (updateType == UpdateIfNecessary && qFuzzyCompare(zoom, d->mZoom) && documentSize() == d->mDocumentSize) { return; } qreal oldZoom = d->mZoom; d->mZoom = zoom; d->mDocumentSize = documentSize(); 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(); zoomChanged(d->mZoom); } bool AbstractImageView::zoomToFit() const { return d->mZoomToFit; } bool AbstractImageView::zoomToFill() const { return d->mZoomToFill; } void AbstractImageView::setZoomToFit(bool on) { d->mZoomToFit = on; if (on) { 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 zoomToFitChanged(d->mZoomToFit); } void AbstractImageView::setZoomToFill(bool on) { d->mZoomToFill = on; if (on) { setZoom(computeZoomToFill()); } // 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 zoomToFillChanged(d->mZoomToFill); } const QPixmap& AbstractImageView::alphaBackgroundTexture() const { return d->mAlphaBackgroundTexture; } void AbstractImageView::resizeEvent(QGraphicsSceneResizeEvent* event) { QGraphicsWidget::resizeEvent(event); if (d->mZoomToFit) { // 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); } } else if (d->mZoomToFill) { const qreal newZoom = computeZoomToFill(); if (qFuzzyCompare(zoom(), newZoom)) { d->adjustImageOffset(AbstractImageViewPrivate::Notify); } else { setZoom(newZoom); } } else { d->adjustImageOffset(); d->adjustScrollPos(); } } qreal AbstractImageView::computeZoomToFit() const { QSizeF docSize = documentSize(); 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) { fit = qMin(fit, qreal(1.)); } return fit; } qreal AbstractImageView::computeZoomToFill() const { QSizeF docSize = documentSize(); if (docSize.isEmpty()) { return 1; } QSizeF viewSize = boundingRect().size(); qreal fitWidth = viewSize.width() / docSize.width(); qreal fitHeight = viewSize.height() / docSize.height(); qreal fill = qMax(fitWidth, fitHeight); if (!d->mEnlargeSmallerImages) { fill = qMin(fill, qreal(1.)); } return fill; } void AbstractImageView::mousePressEvent(QGraphicsSceneMouseEvent* event) { QGraphicsItem::mousePressEvent(event); if (event->button() == Qt::MiddleButton) { bool value = !zoomToFit(); setZoomToFit(value); if (!value) { setZoom(1., event->pos()); } return; } if (event->modifiers() & Qt::ControlModifier) { if (event->button() == Qt::LeftButton) { zoomInRequested(event->pos()); return; } else if (event->button() == Qt::RightButton) { zoomOutRequested(event->pos()); return; } } d->mLastDragPos = event->pos(); updateCursor(); } void AbstractImageView::mouseMoveEvent(QGraphicsSceneMouseEvent* event) { QGraphicsItem::mouseMoveEvent(event); QPointF mousePos = event->pos(); QPointF newScrollPos = d->mScrollPos + d->mLastDragPos - mousePos; #if 0 // commented out due to mouse pointer warping around, bug in Qt? // 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()); #endif d->mLastDragPos = mousePos; d->setScrollPos(newScrollPos); } void AbstractImageView::mouseReleaseEvent(QGraphicsSceneMouseEvent* event) { QGraphicsItem::mouseReleaseEvent(event); if (!d->mLastDragPos.isNull()) { d->mLastDragPos = QPointF(); } updateCursor(); } void AbstractImageView::keyPressEvent(QKeyEvent* event) { if (event->key() == Qt::Key_Control) { d->mControlKeyIsDown = true; updateCursor(); return; } if (zoomToFit() || qFuzzyCompare(computeZoomToFit(), zoom())) { if (event->modifiers() != Qt::NoModifier) { return; } switch (event->key()) { case Qt::Key_Left: case Qt::Key_Up: previousImageRequested(); break; case Qt::Key_Right: case Qt::Key_Down: nextImageRequested(); break; default: break; } return; } QPointF delta(0, 0); qreal pageStep = boundingRect().height(); qreal unitStep; 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: d->setScrollPos(QPointF(d->mScrollPos.x(), documentSize().height() * zoom())); return; default: return; } d->setScrollPos(d->mScrollPos + delta); } void AbstractImageView::keyReleaseEvent(QKeyEvent* event) { if (event->key() == Qt::Key_Control) { d->mControlKeyIsDown = false; updateCursor(); } } void AbstractImageView::mouseDoubleClickEvent(QGraphicsSceneMouseEvent* event) { if (event->modifiers() == Qt::NoModifier) { toggleFullScreenRequested(); } } QPointF AbstractImageView::imageOffset() const { return d->mImageOffset; } QPointF AbstractImageView::scrollPos() const { return d->mScrollPos; } void AbstractImageView::setScrollPos(const QPointF& pos) { d->setScrollPos(pos); } QPointF AbstractImageView::mapToView(const QPointF& imagePos) const { return imagePos * d->mZoom + d->mImageOffset - d->mScrollPos; } QPoint AbstractImageView::mapToView(const QPoint& imagePos) const { return mapToView(QPointF(imagePos)).toPoint(); } QRectF AbstractImageView::mapToView(const QRectF& imageRect) const { return QRectF( mapToView(imageRect.topLeft()), imageRect.size() * zoom() ); } QRect AbstractImageView::mapToView(const QRect& imageRect) const { return QRect( mapToView(imageRect.topLeft()), imageRect.size() * zoom() ); } QPointF AbstractImageView::mapToImage(const QPointF& viewPos) const { return (viewPos - d->mImageOffset + d->mScrollPos) / d->mZoom; } QPoint AbstractImageView::mapToImage(const QPoint& viewPos) const { return mapToImage(QPointF(viewPos)).toPoint(); } QRectF AbstractImageView::mapToImage(const QRectF& viewRect) const { return QRectF( mapToImage(viewRect.topLeft()), viewRect.size() / zoom() ); } QRect AbstractImageView::mapToImage(const QRect& viewRect) const { return QRect( mapToImage(viewRect.topLeft()), viewRect.size() / zoom() ); } void AbstractImageView::setEnlargeSmallerImages(bool value) { d->mEnlargeSmallerImages = value; if (zoomToFit()) { setZoom(computeZoomToFit()); } } void AbstractImageView::updateCursor() { if (d->mControlKeyIsDown) { setCursor(d->mZoomCursor); } else { if (d->mLastDragPos.isNull()) { setCursor(Qt::OpenHandCursor); } else { setCursor(Qt::ClosedHandCursor); } } } QSizeF AbstractImageView::visibleImageSize() const { if (!document()) { return QSizeF(); } QSizeF size = documentSize() * zoom(); return size.boundedTo(boundingRect().size()); } void AbstractImageView::applyPendingScrollPos() { d->adjustImageOffset(); d->adjustScrollPos(); } +void AbstractImageView::resetDragCursor() +{ + d->mLastDragPos = QPointF(); + updateCursor(); +} + } // namespace diff --git a/lib/documentview/abstractimageview.h b/lib/documentview/abstractimageview.h index d15a1e6d..3e6597f8 100644 --- a/lib/documentview/abstractimageview.h +++ b/lib/documentview/abstractimageview.h @@ -1,157 +1,159 @@ // vim: set tabstop=4 shiftwidth=4 expandtab: /* Gwenview: an image viewer Copyright 2011 Aurélien Gâteau 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. */ #ifndef ABSTRACTIMAGEVIEW_H #define ABSTRACTIMAGEVIEW_H // Local #include // KDE // Qt #include namespace Gwenview { struct AbstractImageViewPrivate; /** * */ class AbstractImageView : public QGraphicsWidget { Q_OBJECT public: enum UpdateType { UpdateIfNecessary, ForceUpdate }; enum AlphaBackgroundMode { AlphaBackgroundNone, AlphaBackgroundCheckBoard, AlphaBackgroundSolid }; AbstractImageView(QGraphicsItem* parent); ~AbstractImageView(); qreal zoom() const; virtual void setZoom(qreal zoom, const QPointF& center = QPointF(-1, -1), UpdateType updateType = UpdateIfNecessary); bool zoomToFit() const; bool zoomToFill() const; virtual void setZoomToFit(bool value); virtual void setZoomToFill(bool value); virtual void setDocument(Document::Ptr doc); Document::Ptr document() const; qreal computeZoomToFit() const; qreal computeZoomToFill() const; QSizeF documentSize() const; QSizeF visibleImageSize() const; /** * If the image is smaller than the view, imageOffset is the distance from * the topleft corner of the view to the topleft corner of the image. * Neither x nor y can be negative. */ QPointF imageOffset() const; /** * The scroll position, in zoomed image coordinates. * x and y are always between 0 and (docsize * zoom - viewsize) */ QPointF scrollPos() const; void setScrollPos(const QPointF& pos); QPointF mapToView(const QPointF& imagePos) const; QPoint mapToView(const QPoint& imagePos) const; QRectF mapToView(const QRectF& imageRect) const; QRect mapToView(const QRect& imageRect) const; QPointF mapToImage(const QPointF& viewPos) const; QPoint mapToImage(const QPoint& viewPos) const; QRectF mapToImage(const QRectF& viewRect) const; QRect mapToImage(const QRect& viewRect) const; void setEnlargeSmallerImages(bool value); void applyPendingScrollPos(); + void resetDragCursor(); + public Q_SLOTS: void updateCursor(); Q_SIGNALS: void zoomToFitChanged(bool); void zoomToFillChanged(bool); void zoomChanged(qreal); void zoomInRequested(const QPointF&); void zoomOutRequested(const QPointF&); void scrollPosChanged(); void completed(); void previousImageRequested(); void nextImageRequested(); void toggleFullScreenRequested(); protected: virtual void setAlphaBackgroundMode(AlphaBackgroundMode mode) = 0; virtual void setAlphaBackgroundColor(const QColor& color) = 0; const QPixmap& alphaBackgroundTexture() const; virtual void loadFromDocument() = 0; virtual void onZoomChanged() = 0; /** * Called when the offset changes. * Note: to avoid multiple adjustments, this is not called if zoom changes! */ virtual void onImageOffsetChanged() = 0; /** * Called when the scrollPos changes. * Note: to avoid multiple adjustments, this is not called if zoom changes! */ virtual void onScrollPosChanged(const QPointF& oldPos) = 0; void resizeEvent(QGraphicsSceneResizeEvent* event) Q_DECL_OVERRIDE; void keyPressEvent(QKeyEvent* event) Q_DECL_OVERRIDE; void keyReleaseEvent(QKeyEvent* event) Q_DECL_OVERRIDE; void mousePressEvent(QGraphicsSceneMouseEvent* event) Q_DECL_OVERRIDE; void mouseMoveEvent(QGraphicsSceneMouseEvent* event) Q_DECL_OVERRIDE; void mouseReleaseEvent(QGraphicsSceneMouseEvent* event) Q_DECL_OVERRIDE; void mouseDoubleClickEvent(QGraphicsSceneMouseEvent* event) Q_DECL_OVERRIDE; private: friend struct AbstractImageViewPrivate; AbstractImageViewPrivate* const d; }; } // namespace #endif /* ABSTRACTIMAGEVIEW_H */ diff --git a/lib/documentview/documentview.cpp b/lib/documentview/documentview.cpp index 75e1e04a..95fd1e0f 100644 --- a/lib/documentview/documentview.cpp +++ b/lib/documentview/documentview.cpp @@ -1,852 +1,958 @@ // vim: set tabstop=4 shiftwidth=4 expandtab: /* Gwenview: an image viewer Copyright 2008 Aurélien Gâteau 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 #include "documentview.h" // C++ Standard library #include // Qt #include #include #include #include #include #include #include #include #include #include #include #include #include +#include #include +#include // KDE #include +#include // Local #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include +#include +#include namespace Gwenview { #undef ENABLE_LOG #undef LOG //#define ENABLE_LOG #ifdef ENABLE_LOG #define LOG(x) //qDebug() << x #else #define LOG(x) ; #endif static const qreal REAL_DELTA = 0.001; static const qreal MAXIMUM_ZOOM_VALUE = qreal(DocumentView::MaximumZoom); static const auto MINSTEP = sqrt(0.5); static const auto MAXSTEP = sqrt(2.0); static const int COMPARE_MARGIN = 4; const int DocumentView::MaximumZoom = 16; const int DocumentView::AnimDuration = 250; struct DocumentViewPrivate { DocumentView* q; int mSortKey; // Used to sort views when displayed in compare mode HudWidget* mHud; BirdEyeView* mBirdEyeView; QPointer mMoveAnimation; QPointer mFadeAnimation; QGraphicsOpacityEffect* mOpacityEffect; LoadingIndicator* mLoadingIndicator; QScopedPointer mAdapter; QList mZoomSnapValues; Document::Ptr mDocument; DocumentView::Setup mSetup; bool mCurrent; bool mCompareMode; int controlWheelAccumulatedDelta; + QPointF mDragStartPosition; + QPointer mDragThumbnailProvider; + QPointer mDrag; + void setCurrentAdapter(AbstractDocumentViewAdapter* adapter) { Q_ASSERT(adapter); mAdapter.reset(adapter); adapter->widget()->setParentItem(q); resizeAdapterWidget(); if (adapter->canZoom()) { QObject::connect(adapter, SIGNAL(zoomChanged(qreal)), q, SLOT(slotZoomChanged(qreal))); QObject::connect(adapter, SIGNAL(zoomInRequested(QPointF)), q, SLOT(zoomIn(QPointF))); QObject::connect(adapter, SIGNAL(zoomOutRequested(QPointF)), q, SLOT(zoomOut(QPointF))); QObject::connect(adapter, SIGNAL(zoomToFitChanged(bool)), q, SIGNAL(zoomToFitChanged(bool))); QObject::connect(adapter, SIGNAL(zoomToFillChanged(bool)), q, SIGNAL(zoomToFillChanged(bool))); } QObject::connect(adapter, SIGNAL(scrollPosChanged()), q, SIGNAL(positionChanged())); QObject::connect(adapter, SIGNAL(previousImageRequested()), q, SIGNAL(previousImageRequested())); QObject::connect(adapter, SIGNAL(nextImageRequested()), q, SIGNAL(nextImageRequested())); QObject::connect(adapter, SIGNAL(toggleFullScreenRequested()), q, SIGNAL(toggleFullScreenRequested())); QObject::connect(adapter, SIGNAL(completed()), q, SLOT(slotCompleted())); adapter->loadConfig(); adapter->widget()->installSceneEventFilter(q); if (mCurrent) { adapter->widget()->setFocus(); } if (mSetup.valid && adapter->canZoom()) { adapter->setZoomToFit(mSetup.zoomToFit); adapter->setZoomToFill(mSetup.zoomToFill); if (!mSetup.zoomToFit && !mSetup.zoomToFill) { adapter->setZoom(mSetup.zoom); adapter->setScrollPos(mSetup.position); } } q->adapterChanged(); q->positionChanged(); if (adapter->canZoom()) { if (adapter->zoomToFit()) { q->zoomToFitChanged(true); } else if (adapter->zoomToFill()) { q->zoomToFillChanged(true); } else { q->zoomChanged(adapter->zoom()); } } if (adapter->rasterImageView()) { QObject::connect(adapter->rasterImageView(), SIGNAL(currentToolChanged(AbstractRasterImageViewTool*)), q, SIGNAL(currentToolChanged(AbstractRasterImageViewTool*))); } } void setupLoadingIndicator() { mLoadingIndicator = new LoadingIndicator(q); GraphicsWidgetFloater* floater = new GraphicsWidgetFloater(q); floater->setChildWidget(mLoadingIndicator); } HudButton* createHudButton(const QString& text, const char* iconName, bool showText) { HudButton* button = new HudButton; if (showText) { button->setText(text); } else { button->setToolTip(text); } button->setIcon(QIcon::fromTheme(iconName)); return button; } void setupHud() { HudButton* trashButton = createHudButton(i18nc("@info:tooltip", "Trash"), "user-trash", false); HudButton* deselectButton = createHudButton(i18nc("@action:button", "Deselect"), "list-remove", true); QGraphicsWidget* content = new QGraphicsWidget; QGraphicsLinearLayout* layout = new QGraphicsLinearLayout(content); layout->addItem(trashButton); layout->addItem(deselectButton); mHud = new HudWidget(q); mHud->init(content, HudWidget::OptionNone); GraphicsWidgetFloater* floater = new GraphicsWidgetFloater(q); floater->setChildWidget(mHud); floater->setAlignment(Qt::AlignBottom | Qt::AlignHCenter); QObject::connect(trashButton, SIGNAL(clicked()), q, SLOT(emitHudTrashClicked())); QObject::connect(deselectButton, SIGNAL(clicked()), q, SLOT(emitHudDeselectClicked())); mHud->hide(); } void setupBirdEyeView() { if (mBirdEyeView) { delete mBirdEyeView; } mBirdEyeView = new BirdEyeView(q); mBirdEyeView->setZValue(1); } void updateCaption() { if (!mCurrent) { return; } QString caption; Document::Ptr doc = mAdapter->document(); if (!doc) { emit q->captionUpdateRequested(caption); return; } caption = doc->url().fileName(); QSize size = doc->size(); if (size.isValid()) { caption += QString(" - %1x%2") .arg(size.width()) .arg(size.height()); if (mAdapter->canZoom()) { int intZoom = qRound(mAdapter->zoom() * 100); caption += QString(" - %1%") .arg(intZoom); } } emit q->captionUpdateRequested(caption); } void uncheckZoomToFit() { if (mAdapter->zoomToFit()) { mAdapter->setZoomToFit(false); } } void uncheckZoomToFill() { if (mAdapter->zoomToFill()) { mAdapter->setZoomToFill(false); } } void setZoom(qreal zoom, const QPointF& center = QPointF(-1, -1)) { uncheckZoomToFit(); uncheckZoomToFill(); zoom = qBound(q->minimumZoom(), zoom, MAXIMUM_ZOOM_VALUE); mAdapter->setZoom(zoom, center); } void updateZoomSnapValues() { qreal min = q->minimumZoom(); mZoomSnapValues.clear(); for (qreal zoom = MINSTEP; zoom > min; zoom *= MINSTEP) { mZoomSnapValues << zoom; } mZoomSnapValues << min; std::reverse(mZoomSnapValues.begin(), mZoomSnapValues.end()); for (qreal zoom = 1; zoom < MAXIMUM_ZOOM_VALUE; zoom *= MAXSTEP) { mZoomSnapValues << zoom; } mZoomSnapValues << MAXIMUM_ZOOM_VALUE; q->minimumZoomChanged(min); } void showLoadingIndicator() { if (!mLoadingIndicator) { setupLoadingIndicator(); } mLoadingIndicator->show(); mLoadingIndicator->setZValue(1); } void hideLoadingIndicator() { if (!mLoadingIndicator) { return; } mLoadingIndicator->hide(); } void resizeAdapterWidget() { QRectF rect = QRectF(QPointF(0, 0), q->boundingRect().size()); if (mCompareMode) { rect.adjust(COMPARE_MARGIN, COMPARE_MARGIN, -COMPARE_MARGIN, -COMPARE_MARGIN); } mAdapter->widget()->setGeometry(rect); } void fadeTo(qreal value) { if (mFadeAnimation.data()) { qreal endValue = mFadeAnimation.data()->endValue().toReal(); if (qFuzzyCompare(value, endValue)) { // Same end value, don't change the actual animation return; } } // Create a new fade animation QPropertyAnimation* anim = new QPropertyAnimation(mOpacityEffect, "opacity"); anim->setStartValue(mOpacityEffect->opacity()); anim->setEndValue(value); if (qFuzzyCompare(value, 1)) { QObject::connect(anim, SIGNAL(finished()), q, SLOT(slotFadeInFinished())); } QObject::connect(anim, SIGNAL(finished()), q, SIGNAL(isAnimatedChanged())); anim->setDuration(DocumentView::AnimDuration); mFadeAnimation = anim; q->isAnimatedChanged(); anim->start(QAbstractAnimation::DeleteWhenStopped); } + + bool canPan() const + { + if (!q->canZoom()) { + return false; + } + + const QSize zoomedImageSize = mDocument->size() * q->zoom(); + const QSize viewPortSize = q->boundingRect().size().toSize(); + const bool imageWiderThanViewport = zoomedImageSize.width() > viewPortSize.width(); + const bool imageTallerThanViewport = zoomedImageSize.height() > viewPortSize.height(); + return (imageWiderThanViewport || imageTallerThanViewport); + } + + void setDragPixmap(const QPixmap& pix) + { + if (mDrag) { + DragPixmapGenerator::DragPixmap dragPixmap = DragPixmapGenerator::generate({pix}, 1); + mDrag->setPixmap(dragPixmap.pix); + mDrag->setHotSpot(dragPixmap.hotSpot); + } + } + + void executeDrag() + { + if (mDrag) { + mDrag->exec(); + if (mAdapter->imageView()) { + mAdapter->imageView()->resetDragCursor(); + } + } + } + + void initDragThumbnailProvider() { + mDragThumbnailProvider = new ThumbnailProvider(); + QObject::connect(mDragThumbnailProvider, &ThumbnailProvider::thumbnailLoaded, + q, &DocumentView::dragThumbnailLoaded); + QObject::connect(mDragThumbnailProvider, &ThumbnailProvider::thumbnailLoadingFailed, + q, &DocumentView::dragThumbnailLoadingFailed); + } + + void startDragIfSensible() + { + if (q->document()->loadingState() == Document::LoadingFailed) { + return; + } + + if (q->currentTool()) { + return; + } + + if (mDrag) { + mDrag->deleteLater(); + } + mDrag = new QDrag(q); + const auto itemList = KFileItemList({q->document()->url()}); + mDrag->setMimeData(MimeTypeUtils::selectionMimeData(itemList)); + + if (q->document()->isModified()) { + setDragPixmap(QPixmap::fromImage(q->document()->image())); + executeDrag(); + } else { + // Drag is triggered on success or failure of thumbnail generation + if (mDragThumbnailProvider.isNull()) { + initDragThumbnailProvider(); + } + mDragThumbnailProvider->appendItems(itemList); + } + } }; DocumentView::DocumentView(QGraphicsScene* scene) : d(new DocumentViewPrivate) { setFlag(ItemIsFocusable); setFlag(ItemIsSelectable); setFlag(ItemClipsChildrenToShape); d->q = this; d->mLoadingIndicator = 0; d->mBirdEyeView = 0; d->mCurrent = false; d->mCompareMode = false; d->controlWheelAccumulatedDelta = 0; + d->mDragStartPosition = QPointF(0, 0); + d->mDrag = nullptr; // We use an opacity effect instead of using the opacity property directly, because the latter operates at // the painter level, which means if you draw multiple layers in paint(), all layers get the specified // opacity, resulting in all layers being visible when 0 < opacity < 1. // QGraphicsEffects on the other hand, operate after all painting is done, therefore 'flattening' all layers. // This is important for fade effects, where we don't want any background layers visible during the fade. d->mOpacityEffect = new QGraphicsOpacityEffect(this); d->mOpacityEffect->setOpacity(0); setGraphicsEffect(d->mOpacityEffect); scene->addItem(this); d->setupHud(); d->setCurrentAdapter(new EmptyAdapter); setAcceptDrops(true); } DocumentView::~DocumentView() { + delete d->mDragThumbnailProvider; + delete d->mDrag; delete d; } void DocumentView::createAdapterForDocument() { const MimeTypeUtils::Kind documentKind = d->mDocument->kind(); if (d->mAdapter && documentKind == d->mAdapter->kind() && documentKind != MimeTypeUtils::KIND_UNKNOWN) { // Do not reuse for KIND_UNKNOWN: we may need to change the message LOG("Reusing current adapter"); return; } AbstractDocumentViewAdapter* adapter = 0; switch (documentKind) { case MimeTypeUtils::KIND_RASTER_IMAGE: adapter = new RasterImageViewAdapter; break; case MimeTypeUtils::KIND_SVG_IMAGE: adapter = new SvgViewAdapter; break; case MimeTypeUtils::KIND_VIDEO: adapter = new VideoViewAdapter; connect(adapter, SIGNAL(videoFinished()), SIGNAL(videoFinished())); break; case MimeTypeUtils::KIND_UNKNOWN: adapter = new MessageViewAdapter; static_cast(adapter)->setErrorMessage(i18n("Gwenview does not know how to display this kind of document")); break; default: qWarning() << "should not be called for documentKind=" << documentKind; adapter = new MessageViewAdapter; break; } d->setCurrentAdapter(adapter); } void DocumentView::openUrl(const QUrl &url, const DocumentView::Setup& setup) { if (d->mDocument) { if (url == d->mDocument->url()) { return; } disconnect(d->mDocument.data(), 0, this, 0); } d->mSetup = setup; d->mDocument = DocumentFactory::instance()->load(url); connect(d->mDocument.data(), SIGNAL(busyChanged(QUrl,bool)), SLOT(slotBusyChanged(QUrl,bool))); connect(d->mDocument.data(), &Document::modified, this, [this]() { d->updateZoomSnapValues(); }); if (d->mDocument->loadingState() < Document::KindDetermined) { MessageViewAdapter* messageViewAdapter = qobject_cast(d->mAdapter.data()); if (messageViewAdapter) { messageViewAdapter->setInfoMessage(QString()); } d->showLoadingIndicator(); connect(d->mDocument.data(), SIGNAL(kindDetermined(QUrl)), SLOT(finishOpenUrl())); } else { QMetaObject::invokeMethod(this, "finishOpenUrl", Qt::QueuedConnection); } d->setupBirdEyeView(); } void DocumentView::finishOpenUrl() { disconnect(d->mDocument.data(), SIGNAL(kindDetermined(QUrl)), this, SLOT(finishOpenUrl())); GV_RETURN_IF_FAIL(d->mDocument->loadingState() >= Document::KindDetermined); if (d->mDocument->loadingState() == Document::LoadingFailed) { slotLoadingFailed(); return; } createAdapterForDocument(); connect(d->mDocument.data(), SIGNAL(loadingFailed(QUrl)), SLOT(slotLoadingFailed())); d->mAdapter->setDocument(d->mDocument); d->updateCaption(); } void DocumentView::loadAdapterConfig() { d->mAdapter->loadConfig(); } RasterImageView* DocumentView::imageView() const { return d->mAdapter->rasterImageView(); } void DocumentView::slotCompleted() { d->hideLoadingIndicator(); d->updateCaption(); d->updateZoomSnapValues(); if (!d->mAdapter->zoomToFit() || !d->mAdapter->zoomToFill()) { qreal min = minimumZoom(); if (d->mAdapter->zoom() < min) { d->mAdapter->setZoom(min); } } emit completed(); } DocumentView::Setup DocumentView::setup() const { Setup setup; if (d->mAdapter->canZoom()) { setup.valid = true; setup.zoomToFit = zoomToFit(); setup.zoomToFill = zoomToFill(); if (!setup.zoomToFit && !setup.zoomToFill) { setup.zoom = zoom(); setup.position = position(); } } return setup; } void DocumentView::slotLoadingFailed() { d->hideLoadingIndicator(); MessageViewAdapter* adapter = new MessageViewAdapter; adapter->setDocument(d->mDocument); QString message = xi18n("Loading %1 failed", d->mDocument->url().fileName()); adapter->setErrorMessage(message, d->mDocument->errorString()); d->setCurrentAdapter(adapter); emit completed(); } bool DocumentView::canZoom() const { return d->mAdapter->canZoom(); } void DocumentView::setZoomToFit(bool on) { if (on == d->mAdapter->zoomToFit()) { return; } d->mAdapter->setZoomToFit(on); } void DocumentView::setZoomToFill(bool on) { if (on == d->mAdapter->zoomToFill()) { return; } d->mAdapter->setZoomToFill(on); } bool DocumentView::zoomToFit() const { return d->mAdapter->zoomToFit(); } bool DocumentView::zoomToFill() const { return d->mAdapter->zoomToFill(); } void DocumentView::zoomActualSize() { d->uncheckZoomToFit(); d->uncheckZoomToFill(); d->mAdapter->setZoom(1.); } void DocumentView::zoomIn(const QPointF& center) { qreal currentZoom = d->mAdapter->zoom(); Q_FOREACH(qreal zoom, d->mZoomSnapValues) { if (zoom > currentZoom + REAL_DELTA) { d->setZoom(zoom, center); return; } } } void DocumentView::zoomOut(const QPointF& center) { qreal currentZoom = d->mAdapter->zoom(); QListIterator it(d->mZoomSnapValues); it.toBack(); while (it.hasPrevious()) { qreal zoom = it.previous(); if (zoom < currentZoom - REAL_DELTA) { d->setZoom(zoom, center); return; } } } void DocumentView::slotZoomChanged(qreal zoom) { d->updateCaption(); zoomChanged(zoom); } void DocumentView::setZoom(qreal zoom) { d->setZoom(zoom); } qreal DocumentView::zoom() const { return d->mAdapter->zoom(); } void DocumentView::resizeEvent(QGraphicsSceneResizeEvent *event) { d->resizeAdapterWidget(); d->updateZoomSnapValues(); QGraphicsWidget::resizeEvent(event); } void DocumentView::wheelEvent(QGraphicsSceneWheelEvent* event) { if (d->mAdapter->canZoom() && event->modifiers() & Qt::ControlModifier) { d->controlWheelAccumulatedDelta += event->delta(); // Ctrl + wheel => zoom in or out if (d->controlWheelAccumulatedDelta >= QWheelEvent::DefaultDeltasPerStep) { zoomIn(event->pos()); d->controlWheelAccumulatedDelta = 0; } else if (d->controlWheelAccumulatedDelta <= -QWheelEvent::DefaultDeltasPerStep) { zoomOut(event->pos()); d->controlWheelAccumulatedDelta = 0; } return; } if (GwenviewConfig::mouseWheelBehavior() == MouseWheelBehavior::Browse && event->modifiers() == Qt::NoModifier) { d->controlWheelAccumulatedDelta += event->delta(); // Browse with mouse wheel if (d->controlWheelAccumulatedDelta >= QWheelEvent::DefaultDeltasPerStep) { previousImageRequested(); d->controlWheelAccumulatedDelta = 0; } else if (d->controlWheelAccumulatedDelta <= -QWheelEvent::DefaultDeltasPerStep) { nextImageRequested(); d->controlWheelAccumulatedDelta = 0; } return; } // Scroll qreal dx = 0; // 16 = pixels for one line // 120: see QWheelEvent::delta() doc qreal dy = -qApp->wheelScrollLines() * 16 * event->delta() / 120; if (event->orientation() == Qt::Horizontal) { qSwap(dx, dy); } d->mAdapter->setScrollPos(d->mAdapter->scrollPos() + QPointF(dx, dy)); } void DocumentView::contextMenuEvent(QGraphicsSceneContextMenuEvent* event) { // Filter out context menu if Ctrl is down to avoid showing it when // zooming out with Ctrl + Right button if (event->modifiers() != Qt::ControlModifier) { contextMenuRequested(); } } void DocumentView::paint(QPainter* painter, const QStyleOptionGraphicsItem* /*option*/, QWidget* /*widget*/) { // Fill background manually, because setAutoFillBackground(true) fill with QPalette::Window, // but our palettes use QPalette::Base for the background color/texture painter->fillRect(rect(), palette().base()); // Selection indicator/highlight if (d->mCompareMode && d->mCurrent) { painter->save(); painter->setBrush(Qt::NoBrush); painter->setPen(QPen(palette().highlight().color(), 2)); painter->setRenderHint(QPainter::Antialiasing); const QRectF visibleRectF = mapRectFromItem(d->mAdapter->widget(), d->mAdapter->visibleDocumentRect()); // Round the point and size independently. This is different than calling toRect(), // and is necessary to keep consistent rects, otherwise the selection rect can be // drawn 1 pixel too big or small. const QRect visibleRect = QRect(visibleRectF.topLeft().toPoint(), visibleRectF.size().toSize()); const QRect selectionRect = visibleRect.adjusted(-1, -1, 1, 1); painter->drawRoundedRect(selectionRect, 3, 3); painter->restore(); } } void DocumentView::slotBusyChanged(const QUrl&, bool busy) { if (busy) { d->showLoadingIndicator(); } else { d->hideLoadingIndicator(); } } qreal DocumentView::minimumZoom() const { // There is no point zooming out less than zoomToFit, but make sure it does // not get too small either return qBound(qreal(0.001), d->mAdapter->computeZoomToFit(), qreal(1.)); } void DocumentView::setCompareMode(bool compare) { d->mCompareMode = compare; if (compare) { d->mHud->show(); d->mHud->setZValue(1); } else { d->mHud->hide(); } } void DocumentView::setCurrent(bool value) { d->mCurrent = value; if (value) { d->mAdapter->widget()->setFocus(); d->updateCaption(); } update(); } bool DocumentView::isCurrent() const { return d->mCurrent; } QPoint DocumentView::position() const { return d->mAdapter->scrollPos().toPoint(); } void DocumentView::setPosition(const QPoint& pos) { d->mAdapter->setScrollPos(pos); } Document::Ptr DocumentView::document() const { return d->mDocument; } QUrl DocumentView::url() const { Document::Ptr doc = d->mDocument; return doc ? doc->url() : QUrl(); } void DocumentView::emitHudDeselectClicked() { hudDeselectClicked(this); } void DocumentView::emitHudTrashClicked() { hudTrashClicked(this); } void DocumentView::emitFocused() { focused(this); } void DocumentView::setGeometry(const QRectF& rect) { QGraphicsWidget::setGeometry(rect); if (d->mBirdEyeView) { d->mBirdEyeView->slotZoomOrSizeChanged(); } } void DocumentView::moveTo(const QRect& rect) { if (d->mMoveAnimation) { d->mMoveAnimation.data()->setEndValue(rect); } else { setGeometry(rect); } } void DocumentView::moveToAnimated(const QRect& rect) { QPropertyAnimation* anim = new QPropertyAnimation(this, "geometry"); anim->setStartValue(geometry()); anim->setEndValue(rect); anim->setDuration(DocumentView::AnimDuration); connect(anim, SIGNAL(finished()), SIGNAL(isAnimatedChanged())); d->mMoveAnimation = anim; isAnimatedChanged(); anim->start(QAbstractAnimation::DeleteWhenStopped); } QPropertyAnimation* DocumentView::fadeIn() { d->fadeTo(1); return d->mFadeAnimation.data(); } void DocumentView::fadeOut() { d->fadeTo(0); } void DocumentView::slotFadeInFinished() { fadeInFinished(this); } bool DocumentView::isAnimated() const { return d->mMoveAnimation || d->mFadeAnimation; } bool DocumentView::sceneEventFilter(QGraphicsItem*, QEvent* event) { if (event->type() == QEvent::GraphicsSceneMousePress) { + const QGraphicsSceneMouseEvent* mouseEvent = static_cast(event); + if (mouseEvent->button() == Qt::LeftButton) { + d->mDragStartPosition = mouseEvent->pos(); + } QMetaObject::invokeMethod(this, "emitFocused", Qt::QueuedConnection); } else if (event->type() == QEvent::GraphicsSceneHoverMove) { if (d->mBirdEyeView) { d->mBirdEyeView->onMouseMoved(); } + } else if (event->type() == QEvent::GraphicsSceneMouseMove) { + const QGraphicsSceneMouseEvent* mouseEvent = static_cast(event); + const qreal dragDistance = (mouseEvent->pos() - d->mDragStartPosition).manhattanLength(); + const qreal minDistanceToStartDrag = QGuiApplication::styleHints()->startDragDistance(); + if (!d->canPan() && dragDistance >= minDistanceToStartDrag) { + d->startDragIfSensible(); + } } return false; } AbstractRasterImageViewTool* DocumentView::currentTool() const { return imageView() ? imageView()->currentTool() : 0; } int DocumentView::sortKey() const { return d->mSortKey; } void DocumentView::setSortKey(int sortKey) { d->mSortKey = sortKey; } void DocumentView::hideAndDeleteLater() { hide(); deleteLater(); } void DocumentView::setGraphicsEffectOpacity(qreal opacity) { d->mOpacityEffect->setOpacity(opacity); } void DocumentView::dragEnterEvent(QGraphicsSceneDragDropEvent* event) { QGraphicsWidget::dragEnterEvent(event); event->setAccepted(event->mimeData()->hasUrls()); } void DocumentView::dropEvent(QGraphicsSceneDragDropEvent* event) { QGraphicsWidget::dropEvent(event); // Since we're capturing drops in View mode, we only support one url const QUrl url = event->mimeData()->urls().first(); if (UrlUtils::urlIsDirectory(url)) { emit openDirUrlRequested(url); } else { emit openUrlRequested(url); } } +void DocumentView::dragThumbnailLoaded(const KFileItem& item, const QPixmap& pix) +{ + d->setDragPixmap(pix); + d->executeDrag(); + d->mDragThumbnailProvider->removeItems(KFileItemList({item})); +} + +void DocumentView::dragThumbnailLoadingFailed(const KFileItem& item) +{ + d->executeDrag(); + d->mDragThumbnailProvider->removeItems(KFileItemList({item})); +} + } // namespace diff --git a/lib/documentview/documentview.h b/lib/documentview/documentview.h index 590d932d..5a6aab28 100644 --- a/lib/documentview/documentview.h +++ b/lib/documentview/documentview.h @@ -1,240 +1,243 @@ // vim: set tabstop=4 shiftwidth=4 expandtab: /* Gwenview: an image viewer Copyright 2008 Aurélien Gâteau 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. */ #ifndef DOCUMENTVIEW_H #define DOCUMENTVIEW_H #include // Qt #include // KDE // Local #include class QPropertyAnimation; class QUrl; namespace Gwenview { class AbstractRasterImageViewTool; class RasterImageView; struct DocumentViewPrivate; /** * This widget can display various documents, using an instance of * AbstractDocumentViewAdapter */ class GWENVIEWLIB_EXPORT DocumentView : public QGraphicsWidget { Q_OBJECT Q_PROPERTY(qreal zoom READ zoom WRITE setZoom NOTIFY zoomChanged) Q_PROPERTY(bool zoomToFit READ zoomToFit WRITE setZoomToFit NOTIFY zoomToFitChanged) Q_PROPERTY(bool zoomToFill READ zoomToFill WRITE setZoomToFill NOTIFY zoomToFillChanged) Q_PROPERTY(QPoint position READ position WRITE setPosition NOTIFY positionChanged) public: static const int MaximumZoom; static const int AnimDuration; struct Setup { Setup() : valid(false) , zoomToFit(true) , zoomToFill(false) , zoom(0) {} bool valid:1; bool zoomToFit:1; bool zoomToFill:1; qreal zoom; QPointF position; }; enum AnimationMethod { NoAnimation, SoftwareAnimation, GLAnimation }; /** * Create a new view attached to scene. We need the scene to be able to * install scene event filters. */ DocumentView(QGraphicsScene* scene); ~DocumentView(); Document::Ptr document() const; QUrl url() const; void openUrl(const QUrl&, const Setup&); Setup setup() const; /** * Tells the current adapter to load its config. Used when the user changed * the config while the view was visible. */ void loadAdapterConfig(); bool canZoom() const; qreal minimumZoom() const; qreal zoom() const; bool isCurrent() const; void setCurrent(bool); void setCompareMode(bool); bool zoomToFit() const; bool zoomToFill() const; QPoint position() const; /** * Returns the RasterImageView of the current adapter, if it has one */ RasterImageView* imageView() const; AbstractRasterImageViewTool* currentTool() const; void moveTo(const QRect&); void moveToAnimated(const QRect&); QPropertyAnimation* fadeIn(); void fadeOut(); void fakeFadeOut(); void setGeometry(const QRectF& rect) Q_DECL_OVERRIDE; int sortKey() const; void setSortKey(int sortKey); bool isAnimated() const; /** * Sets the opacity on the installed QGraphicsOpacityEffect. * Use this instead of setOpacity(). */ void setGraphicsEffectOpacity(qreal opacity); public Q_SLOTS: void setZoom(qreal); void setZoomToFit(bool); void setZoomToFill(bool); void setPosition(const QPoint&); void hideAndDeleteLater(); Q_SIGNALS: /** * Emitted when the part has finished loading */ void completed(); void previousImageRequested(); void nextImageRequested(); void openUrlRequested(const QUrl&); void openDirUrlRequested(const QUrl&); void captionUpdateRequested(const QString&); void toggleFullScreenRequested(); void videoFinished(); void minimumZoomChanged(qreal); void zoomChanged(qreal); void adapterChanged(); void focused(DocumentView*); void zoomToFitChanged(bool); void zoomToFillChanged(bool); void positionChanged(); void hudTrashClicked(DocumentView*); void hudDeselectClicked(DocumentView*); void fadeInFinished(DocumentView*); void contextMenuRequested(); void currentToolChanged(AbstractRasterImageViewTool*); void isAnimatedChanged(); protected: void paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget = 0) Q_DECL_OVERRIDE; void resizeEvent(QGraphicsSceneResizeEvent* event) Q_DECL_OVERRIDE; void wheelEvent(QGraphicsSceneWheelEvent* event) Q_DECL_OVERRIDE; void contextMenuEvent(QGraphicsSceneContextMenuEvent* event) Q_DECL_OVERRIDE; bool sceneEventFilter(QGraphicsItem*, QEvent*) Q_DECL_OVERRIDE; void dragEnterEvent(QGraphicsSceneDragDropEvent* event) override; void dropEvent(QGraphicsSceneDragDropEvent* event) override; private Q_SLOTS: void finishOpenUrl(); void slotCompleted(); void slotLoadingFailed(); void zoomActualSize(); void zoomIn(const QPointF& center = QPointF(-1, -1)); void zoomOut(const QPointF& center = QPointF(-1, -1)); void slotZoomChanged(qreal); void slotBusyChanged(const QUrl&, bool); void emitHudTrashClicked(); void emitHudDeselectClicked(); void emitFocused(); void slotFadeInFinished(); + void dragThumbnailLoaded(const KFileItem&, const QPixmap&); + void dragThumbnailLoadingFailed(const KFileItem&); + private: friend struct DocumentViewPrivate; DocumentViewPrivate* const d; void createAdapterForDocument(); }; } // namespace #endif /* DOCUMENTVIEW_H */ diff --git a/lib/documentview/rasterimageviewadapter.cpp b/lib/documentview/rasterimageviewadapter.cpp index d2154db9..19a84f30 100644 --- a/lib/documentview/rasterimageviewadapter.cpp +++ b/lib/documentview/rasterimageviewadapter.cpp @@ -1,166 +1,171 @@ // vim: set tabstop=4 shiftwidth=4 expandtab: /* Gwenview: an image viewer Copyright 2008 Aurélien Gâteau 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 #include "rasterimageviewadapter.h" // Local #include #include #include // KDE // Qt #include namespace Gwenview { //// RasterImageViewAdapter //// struct RasterImageViewAdapterPrivate { RasterImageViewAdapter* q; RasterImageView* mView; }; RasterImageViewAdapter::RasterImageViewAdapter() : d(new RasterImageViewAdapterPrivate) { d->q = this; d->mView = new RasterImageView; connect(d->mView, &RasterImageView::zoomChanged, this, &RasterImageViewAdapter::zoomChanged); connect(d->mView, &RasterImageView::zoomToFitChanged, this, &RasterImageViewAdapter::zoomToFitChanged); connect(d->mView, &RasterImageView::zoomToFillChanged, this, &RasterImageViewAdapter::zoomToFillChanged); connect(d->mView, &RasterImageView::zoomInRequested, this, &RasterImageViewAdapter::zoomInRequested); connect(d->mView, &RasterImageView::zoomOutRequested, this, &RasterImageViewAdapter::zoomOutRequested); connect(d->mView, &RasterImageView::scrollPosChanged, this, &RasterImageViewAdapter::scrollPosChanged); connect(d->mView, &RasterImageView::completed, this, &RasterImageViewAdapter::completed); connect(d->mView, &RasterImageView::previousImageRequested, this, &RasterImageViewAdapter::previousImageRequested); connect(d->mView, &RasterImageView::nextImageRequested, this, &RasterImageViewAdapter::nextImageRequested); connect(d->mView, &RasterImageView::toggleFullScreenRequested, this, &RasterImageViewAdapter::toggleFullScreenRequested); setWidget(d->mView); } RasterImageViewAdapter::~RasterImageViewAdapter() { delete d; } QCursor RasterImageViewAdapter::cursor() const { return d->mView->cursor(); } void RasterImageViewAdapter::setCursor(const QCursor& cursor) { d->mView->setCursor(cursor); } void RasterImageViewAdapter::setDocument(Document::Ptr doc) { d->mView->setDocument(doc); connect(doc.data(), SIGNAL(loadingFailed(QUrl)), SLOT(slotLoadingFailed())); if (doc->loadingState() == Document::LoadingFailed) { slotLoadingFailed(); } } qreal RasterImageViewAdapter::zoom() const { return d->mView->zoom(); } void RasterImageViewAdapter::setZoomToFit(bool on) { d->mView->setZoomToFit(on); } void RasterImageViewAdapter::setZoomToFill(bool on) { d->mView->setZoomToFill(on); } bool RasterImageViewAdapter::zoomToFit() const { return d->mView->zoomToFit(); } bool RasterImageViewAdapter::zoomToFill() const { return d->mView->zoomToFill(); } void RasterImageViewAdapter::setZoom(qreal zoom, const QPointF& center) { d->mView->setZoom(zoom, center); } qreal RasterImageViewAdapter::computeZoomToFit() const { return d->mView->computeZoomToFit(); } qreal RasterImageViewAdapter::computeZoomToFill() const { return d->mView->computeZoomToFill(); } Document::Ptr RasterImageViewAdapter::document() const { return d->mView->document(); } void RasterImageViewAdapter::slotLoadingFailed() { d->mView->setDocument(Document::Ptr()); } void RasterImageViewAdapter::loadConfig() { d->mView->setAlphaBackgroundMode(GwenviewConfig::alphaBackgroundMode()); d->mView->setAlphaBackgroundColor(GwenviewConfig::alphaBackgroundColor()); d->mView->setRenderingIntent(GwenviewConfig::renderingIntent()); d->mView->setEnlargeSmallerImages(GwenviewConfig::enlargeSmallerImages()); } RasterImageView* RasterImageViewAdapter::rasterImageView() const { return d->mView; } +AbstractImageView* RasterImageViewAdapter::imageView() const +{ + return d->mView; +} + QPointF RasterImageViewAdapter::scrollPos() const { return d->mView->scrollPos(); } void RasterImageViewAdapter::setScrollPos(const QPointF& pos) { d->mView->setScrollPos(pos); } QRectF RasterImageViewAdapter::visibleDocumentRect() const { return QRectF(d->mView->imageOffset(), d->mView->visibleImageSize()); } } // namespace diff --git a/lib/documentview/rasterimageviewadapter.h b/lib/documentview/rasterimageviewadapter.h index 36943a41..091ea7dc 100644 --- a/lib/documentview/rasterimageviewadapter.h +++ b/lib/documentview/rasterimageviewadapter.h @@ -1,96 +1,97 @@ // vim: set tabstop=4 shiftwidth=4 expandtab: /* Gwenview: an image viewer Copyright 2008 Aurélien Gâteau 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. */ #ifndef RASTERIMAGEVIEWADAPTER_H #define RASTERIMAGEVIEWADAPTER_H #include // Qt // KDE // Local #include namespace Gwenview { struct RasterImageViewAdapterPrivate; class GWENVIEWLIB_EXPORT RasterImageViewAdapter : public AbstractDocumentViewAdapter { Q_OBJECT public: RasterImageViewAdapter(); ~RasterImageViewAdapter(); virtual QCursor cursor() const Q_DECL_OVERRIDE; virtual void setCursor(const QCursor&) Q_DECL_OVERRIDE; virtual MimeTypeUtils::Kind kind() const Q_DECL_OVERRIDE { return MimeTypeUtils::KIND_RASTER_IMAGE; } virtual bool canZoom() const Q_DECL_OVERRIDE { return true; } virtual void setZoomToFit(bool) Q_DECL_OVERRIDE; virtual void setZoomToFill(bool) Q_DECL_OVERRIDE; virtual bool zoomToFit() const Q_DECL_OVERRIDE; virtual bool zoomToFill() const Q_DECL_OVERRIDE; virtual qreal zoom() const Q_DECL_OVERRIDE; virtual void setZoom(qreal zoom, const QPointF& center) Q_DECL_OVERRIDE; virtual qreal computeZoomToFit() const Q_DECL_OVERRIDE; virtual qreal computeZoomToFill() const Q_DECL_OVERRIDE; virtual Document::Ptr document() const Q_DECL_OVERRIDE; virtual void setDocument(Document::Ptr) Q_DECL_OVERRIDE; virtual void loadConfig() Q_DECL_OVERRIDE; virtual RasterImageView* rasterImageView() const Q_DECL_OVERRIDE; + virtual AbstractImageView* imageView() const override; virtual QPointF scrollPos() const Q_DECL_OVERRIDE; virtual void setScrollPos(const QPointF& pos) Q_DECL_OVERRIDE; virtual QRectF visibleDocumentRect() const Q_DECL_OVERRIDE; private Q_SLOTS: void slotLoadingFailed(); private: RasterImageViewAdapterPrivate* const d; }; } // namespace #endif /* RASTERIMAGEVIEWADAPTER_H */ diff --git a/lib/documentview/svgviewadapter.cpp b/lib/documentview/svgviewadapter.cpp index 3852c7ff..7a484f13 100644 --- a/lib/documentview/svgviewadapter.cpp +++ b/lib/documentview/svgviewadapter.cpp @@ -1,264 +1,269 @@ // vim: set tabstop=4 shiftwidth=4 expandtab: /* Gwenview: an image viewer Copyright 2008 Aurélien Gâteau 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 #include "svgviewadapter.h" // Qt #include #include #include #include #include #include #include // KDE // Local #include "document/documentfactory.h" #include #include #include namespace Gwenview { /// SvgImageView //// SvgImageView::SvgImageView(QGraphicsItem* parent) : AbstractImageView(parent) , mSvgItem(new QGraphicsSvgItem(this)) , mAlphaBackgroundMode(AbstractImageView::AlphaBackgroundCheckBoard) , mAlphaBackgroundColor(Qt::black) , mImageFullyLoaded(false) { // At certain scales, the SVG can render outside its own bounds up to 1 pixel // This clips it so it isn't drawn outside the background or over the selection rect mSvgItem->setFlag(ItemClipsToShape); // So we aren't unnecessarily drawing the background for every paint() setCacheMode(QGraphicsItem::DeviceCoordinateCache); } void SvgImageView::loadFromDocument() { Document::Ptr doc = document(); GV_RETURN_IF_FAIL(doc); if (doc->loadingState() == Document::Loaded) { QMetaObject::invokeMethod(this, "finishLoadFromDocument", Qt::QueuedConnection); } // Ensure finishLoadFromDocument is also called when // - loadFromDocument was called before the document was fully loaded // - reloading is triggered (e.g. via F5) connect(doc.data(), &Document::loaded, this, &SvgImageView::finishLoadFromDocument); } void SvgImageView::finishLoadFromDocument() { QSvgRenderer* renderer = document()->svgRenderer(); GV_RETURN_IF_FAIL(renderer); mSvgItem->setSharedRenderer(renderer); if (zoomToFit()) { setZoom(computeZoomToFit(), QPointF(-1, -1), ForceUpdate); } else if (zoomToFill()) { setZoom(computeZoomToFill(), QPointF(-1, -1), ForceUpdate); } else { mSvgItem->setScale(zoom()); } applyPendingScrollPos(); completed(); mImageFullyLoaded = true; } void SvgImageView::onZoomChanged() { mSvgItem->setScale(zoom()); adjustItemPos(); } void SvgImageView::onImageOffsetChanged() { adjustItemPos(); } void SvgImageView::onScrollPosChanged(const QPointF& /* oldPos */) { adjustItemPos(); } void SvgImageView::adjustItemPos() { mSvgItem->setPos((imageOffset() - scrollPos()).toPoint()); update(); } void SvgImageView::setAlphaBackgroundMode(AbstractImageView::AlphaBackgroundMode mode) { mAlphaBackgroundMode = mode; update(); } void SvgImageView::setAlphaBackgroundColor(const QColor& color) { mAlphaBackgroundColor = color; update(); } void SvgImageView::drawAlphaBackground(QPainter* painter) { // The point and size must be rounded to integers independently, to keep consistency with RasterImageView const QRect imageRect = QRect(imageOffset().toPoint(), visibleImageSize().toSize()); switch (mAlphaBackgroundMode) { case AbstractImageView::AlphaBackgroundNone: // Unlike RasterImageView, SVGs are rendered directly on the image view, // therefore we can simply not draw a background break; case AbstractImageView::AlphaBackgroundCheckBoard: painter->drawTiledPixmap(imageRect, alphaBackgroundTexture(), scrollPos()); break; case AbstractImageView::AlphaBackgroundSolid: painter->fillRect(imageRect, mAlphaBackgroundColor); break; default: Q_ASSERT(0); } } void SvgImageView::paint(QPainter* painter, const QStyleOptionGraphicsItem* /*option*/, QWidget* /*widget*/) { if (mImageFullyLoaded) { drawAlphaBackground(painter); } } //// SvgViewAdapter //// struct SvgViewAdapterPrivate { SvgImageView* mView; }; SvgViewAdapter::SvgViewAdapter() : d(new SvgViewAdapterPrivate) { d->mView = new SvgImageView; setWidget(d->mView); connect(d->mView, &SvgImageView::zoomChanged, this, &SvgViewAdapter::zoomChanged); connect(d->mView, &SvgImageView::zoomToFitChanged, this, &SvgViewAdapter::zoomToFitChanged); connect(d->mView, &SvgImageView::zoomToFillChanged, this, &SvgViewAdapter::zoomToFillChanged); connect(d->mView, &SvgImageView::zoomInRequested, this, &SvgViewAdapter::zoomInRequested); connect(d->mView, &SvgImageView::zoomOutRequested, this, &SvgViewAdapter::zoomOutRequested); connect(d->mView, &SvgImageView::scrollPosChanged, this, &SvgViewAdapter::scrollPosChanged); connect(d->mView, &SvgImageView::completed, this, &SvgViewAdapter::completed); connect(d->mView, &SvgImageView::previousImageRequested, this, &SvgViewAdapter::previousImageRequested); connect(d->mView, &SvgImageView::nextImageRequested, this, &SvgViewAdapter::nextImageRequested); connect(d->mView, &SvgImageView::toggleFullScreenRequested, this, &SvgViewAdapter::toggleFullScreenRequested); } SvgViewAdapter::~SvgViewAdapter() { delete d; } QCursor SvgViewAdapter::cursor() const { return widget()->cursor(); } void SvgViewAdapter::setCursor(const QCursor& cursor) { widget()->setCursor(cursor); } void SvgViewAdapter::setDocument(Document::Ptr doc) { d->mView->setDocument(doc); } Document::Ptr SvgViewAdapter::document() const { return d->mView->document(); } void SvgViewAdapter::loadConfig() { d->mView->setAlphaBackgroundMode(GwenviewConfig::alphaBackgroundMode()); d->mView->setAlphaBackgroundColor(GwenviewConfig::alphaBackgroundColor()); d->mView->setEnlargeSmallerImages(GwenviewConfig::enlargeSmallerImages()); } void SvgViewAdapter::setZoomToFit(bool on) { d->mView->setZoomToFit(on); } void SvgViewAdapter::setZoomToFill(bool on) { d->mView->setZoomToFill(on); } bool SvgViewAdapter::zoomToFit() const { return d->mView->zoomToFit(); } bool SvgViewAdapter::zoomToFill() const { return d->mView->zoomToFill(); } qreal SvgViewAdapter::zoom() const { return d->mView->zoom(); } void SvgViewAdapter::setZoom(qreal zoom, const QPointF& center) { d->mView->setZoom(zoom, center); } qreal SvgViewAdapter::computeZoomToFit() const { return d->mView->computeZoomToFit(); } qreal SvgViewAdapter::computeZoomToFill() const { return d->mView->computeZoomToFill(); } QPointF SvgViewAdapter::scrollPos() const { return d->mView->scrollPos(); } void SvgViewAdapter::setScrollPos(const QPointF& pos) { d->mView->setScrollPos(pos); } QRectF SvgViewAdapter::visibleDocumentRect() const { return QRectF(d->mView->imageOffset(), d->mView->visibleImageSize()); } +AbstractImageView* SvgViewAdapter::imageView() const +{ + return d->mView; +} + } // namespace diff --git a/lib/documentview/svgviewadapter.h b/lib/documentview/svgviewadapter.h index 026dfb95..2f2b7cba 100644 --- a/lib/documentview/svgviewadapter.h +++ b/lib/documentview/svgviewadapter.h @@ -1,123 +1,125 @@ // vim: set tabstop=4 shiftwidth=4 expandtab: /* Gwenview: an image viewer Copyright 2008 Aurélien Gâteau 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. */ #ifndef SVGVIEWADAPTER_H #define SVGVIEWADAPTER_H #include // Qt #include // KDE // Local #include #include class QGraphicsSvgItem; namespace Gwenview { class SvgImageView : public AbstractImageView { Q_OBJECT public: SvgImageView(QGraphicsItem* parent = 0); void setAlphaBackgroundMode(AlphaBackgroundMode mode) Q_DECL_OVERRIDE; void setAlphaBackgroundColor(const QColor& color) Q_DECL_OVERRIDE; protected: void loadFromDocument() Q_DECL_OVERRIDE; void onZoomChanged() Q_DECL_OVERRIDE; void onImageOffsetChanged() Q_DECL_OVERRIDE; void onScrollPosChanged(const QPointF& oldPos) Q_DECL_OVERRIDE; private Q_SLOTS: void finishLoadFromDocument(); private: QGraphicsSvgItem* mSvgItem; AbstractImageView::AlphaBackgroundMode mAlphaBackgroundMode; QColor mAlphaBackgroundColor; bool mImageFullyLoaded; void adjustItemPos(); void drawAlphaBackground(QPainter* painter); void paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) override; }; struct SvgViewAdapterPrivate; class GWENVIEWLIB_EXPORT SvgViewAdapter : public AbstractDocumentViewAdapter { Q_OBJECT public: SvgViewAdapter(); ~SvgViewAdapter(); virtual QCursor cursor() const Q_DECL_OVERRIDE; virtual void setCursor(const QCursor&) Q_DECL_OVERRIDE; virtual void setDocument(Document::Ptr) Q_DECL_OVERRIDE; virtual Document::Ptr document() const Q_DECL_OVERRIDE; virtual void loadConfig() Q_DECL_OVERRIDE; virtual MimeTypeUtils::Kind kind() const Q_DECL_OVERRIDE { return MimeTypeUtils::KIND_SVG_IMAGE; } virtual bool canZoom() const Q_DECL_OVERRIDE { return true; } virtual void setZoomToFit(bool) Q_DECL_OVERRIDE; virtual void setZoomToFill(bool) Q_DECL_OVERRIDE; virtual bool zoomToFit() const Q_DECL_OVERRIDE; virtual bool zoomToFill() const Q_DECL_OVERRIDE; virtual qreal zoom() const Q_DECL_OVERRIDE; virtual void setZoom(qreal /*zoom*/, const QPointF& /*center*/ = QPointF(-1, -1)) Q_DECL_OVERRIDE; virtual qreal computeZoomToFit() const Q_DECL_OVERRIDE; virtual qreal computeZoomToFill() const Q_DECL_OVERRIDE; virtual QPointF scrollPos() const Q_DECL_OVERRIDE; virtual void setScrollPos(const QPointF& pos) Q_DECL_OVERRIDE; virtual QRectF visibleDocumentRect() const override; + virtual AbstractImageView* imageView() const override; + private: SvgViewAdapterPrivate* const d; }; } // namespace #endif /* SVGVIEWADAPTER_H */