diff --git a/CMakeLists.txt b/CMakeLists.txt --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -30,6 +30,7 @@ add_definitions(-DTRANSLATION_DOMAIN="svgpart") set(svgpart_SRCS + svgbrowserextension.cpp svgpart.cpp ) diff --git a/svgpart.h b/svgbrowserextension.h copy from svgpart.h copy to svgbrowserextension.h --- a/svgpart.h +++ b/svgbrowserextension.h @@ -1,5 +1,5 @@ /* -Copyright 2007 Aurélien Gâteau +Copyright (C) 2017 by Friedrich W. H. Kossebau This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License @@ -16,43 +16,29 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -#ifndef SVGPART_H -#define SVGPART_H +#ifndef SVGBROWSEREXTENSION_H +#define SVGBROWSEREXTENSION_H // KF -#include +#include -class QGraphicsScene; -class QGraphicsSvgItem; -class QGraphicsView; -class QSvgRenderer; +class SvgPart; -class SvgPart : public KParts::ReadOnlyPart +class SvgBrowserExtension : public KParts::BrowserExtension { Q_OBJECT public: - SvgPart(QWidget* parentWidget, QObject* parent, const QVariantList&); + explicit SvgBrowserExtension(SvgPart* part); - bool closeUrl() override; + int xOffset() override; + int yOffset() override; -protected: - bool openFile() override; - -private Q_SLOTS: - void zoomActualSize(); - void zoomIn(); - void zoomOut(); - -private: - qreal zoom() const; - void setZoom(qreal value); + void saveState(QDataStream& stream) override; + void restoreState(QDataStream& stream) override; private: - QGraphicsScene* mScene; - QGraphicsView* mView; - QGraphicsSvgItem* mItem; - QSvgRenderer* mRenderer; + SvgPart* m_part; }; -#endif /* SVGPART_H */ +#endif diff --git a/svgbrowserextension.cpp b/svgbrowserextension.cpp new file mode 100644 --- /dev/null +++ b/svgbrowserextension.cpp @@ -0,0 +1,60 @@ +/* +Copyright (C) 2017 by Friedrich W. H. Kossebau + +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, Boston, MA 02110-1301, USA. +*/ + +#include "svgbrowserextension.h" + +#include "svgpart.h" + +// KF +#include + +// Qt +#include + + +SvgBrowserExtension::SvgBrowserExtension(SvgPart* part) + : KParts::BrowserExtension(part) + , m_part(part) +{ +} + +int SvgBrowserExtension::xOffset() +{ + return m_part->horizontalScrollPosition(); +} + +int SvgBrowserExtension::yOffset() +{ + return m_part->verticalScrollPosition(); +} + +void SvgBrowserExtension::saveState(QDataStream& stream) +{ + stream << m_part->zoom(); + KParts::BrowserExtension::saveState(stream); +} + +void SvgBrowserExtension::restoreState(QDataStream& stream) +{ + qreal zoom; + stream >> zoom; + + m_part->setExtendedRestoreArguments(zoom); + KParts::BrowserExtension::restoreState(stream); +} + diff --git a/svgpart.h b/svgpart.h --- a/svgpart.h +++ b/svgpart.h @@ -22,6 +22,8 @@ // KF #include +class SvgBrowserExtension; + class QGraphicsScene; class QGraphicsSvgItem; class QGraphicsView; @@ -34,25 +36,52 @@ public: SvgPart(QWidget* parentWidget, QObject* parent, const QVariantList&); + bool openUrl(const QUrl& url) override; bool closeUrl() override; + void setExtendedRestoreArguments(qreal zoom); + + qreal zoom() const; + int horizontalScrollPosition() const; + int verticalScrollPosition() const; + protected: bool openFile() override; + bool doOpenStream(const QString& mimeType) override; + bool doWriteStream(const QByteArray& data) override; + bool doCloseStream() override; private Q_SLOTS: void zoomActualSize(); void zoomIn(); void zoomOut(); + void delayedRestoreViewState(); + private: - qreal zoom() const; void setZoom(qreal value); + void createViewForDocument(); + private: QGraphicsScene* mScene; QGraphicsView* mView; QGraphicsSvgItem* mItem; QSvgRenderer* mRenderer; + + SvgBrowserExtension* m_browserExtension; + + bool mCloseUrlFromOpen = false; + + bool mHasExtendedRestoreArguments = false; + qreal mRestoreZoom; + + QUrl mPreviousUrl; + qreal mPreviousZoom = 1.0; + int mPreviousHorizontalScrollPosition = 0; + int mPreviousVerticalScrollPosition = 0; + + QByteArray mStreamedData; }; #endif /* SVGPART_H */ diff --git a/svgpart.cpp b/svgpart.cpp --- a/svgpart.cpp +++ b/svgpart.cpp @@ -18,6 +18,8 @@ #include "svgpart.h" +#include "svgbrowserextension.h" + // KF #include #include @@ -30,6 +32,9 @@ #include #include #include +#include +#include +#include static KAboutData createAboutData() @@ -49,6 +54,7 @@ SvgPart::SvgPart(QWidget* parentWidget, QObject* parent, const QVariantList&) : KParts::ReadOnlyPart(parent) , mItem(nullptr) + , m_browserExtension(new SvgBrowserExtension(this)) { setComponentData(createAboutData()); @@ -66,22 +72,80 @@ } +bool SvgPart::openUrl(const QUrl& url) +{ + mCloseUrlFromOpen = true; + + const auto success = KParts::ReadOnlyPart::openUrl(url); + + mCloseUrlFromOpen = false; + + return success; +} + + bool SvgPart::openFile() { if (!mRenderer->load(localFilePath())) { return false; } - mItem = new QGraphicsSvgItem(); - mItem->setSharedRenderer(mRenderer); - mScene->addItem(mItem); - // we reuse the scene, whose scenerect though is not properly resetable, so ensure up-to-date one - mScene->setSceneRect(mItem->boundingRect()); + + createViewForDocument(); + + return true; +} + + +bool SvgPart::doOpenStream(const QString& mimeType) +{ + auto mime = QMimeDatabase().mimeTypeForName(mimeType); + if (!mime.inherits(QStringLiteral("image/svg+xml")) + && !mime.inherits(QStringLiteral("image/svg+xml-compressed"))) { + return false; + } + + mStreamedData.clear(); + + return true; +} + + +bool SvgPart::doWriteStream(const QByteArray& data) +{ + mStreamedData.append(data); + return true; +} + + +bool SvgPart::doCloseStream() +{ + // too bad QSvgRenderer supports QXmlStreamReader, but not its incremental parsing + if (!mRenderer->load(mStreamedData)) { + mStreamedData.clear(); + return false; + } + + mStreamedData.clear(); + + createViewForDocument(); + return true; } bool SvgPart::closeUrl() { + // protect against repeated call if already closed + const auto currentUrl = url(); + // remember old view state for a possible reload from same url + if (currentUrl.isValid()) { + mPreviousUrl = currentUrl; + + mPreviousZoom = zoom(); + mPreviousHorizontalScrollPosition = mView->horizontalScrollBar()->value(); + mPreviousVerticalScrollPosition = mView->verticalScrollBar()->value(); + } + mView->resetMatrix(); mView->resetTransform(); // cannot reset the rect completely, as a null QRectF is ignored @@ -91,10 +155,60 @@ delete mItem; mItem = nullptr; + // reset arguments + if (!mCloseUrlFromOpen) { + mHasExtendedRestoreArguments = false; + } + return KParts::ReadOnlyPart::closeUrl(); } +void SvgPart::createViewForDocument() +{ + mItem = new QGraphicsSvgItem(); + mItem->setSharedRenderer(mRenderer); + mScene->addItem(mItem); + // we reuse the scene, whose scenerect though is not properly resetable, so ensure up-to-date one + mScene->setSceneRect(mItem->boundingRect()); + + // ideally the viewstate would be restored here, but at this point in time + // the view has not yet been updated to the scene and is a wrong size, + // so setting the scrollbars etc now will not have any effect + // TODO: this results in flickering, needs to find a better way to hook into + // updating of view state to new content before the first rendering is done + QTimer::singleShot(0, this, &SvgPart::delayedRestoreViewState); +} + +void SvgPart::delayedRestoreViewState() +{ + // arguments set by caller or restore method + KParts::OpenUrlArguments args(arguments()); + qreal zoomValue = mHasExtendedRestoreArguments ? mRestoreZoom : 1.0; + + // reloading same url? + // we can't tell if caller has explicitely set xOffset/yOffset of OpenUrlArguments + // so in case of same url we just assume a reload and ignore the OpenUrlArguments xOffset/yOffset + if (!mHasExtendedRestoreArguments && (url() == mPreviousUrl)) { + // restore last view state instead + zoomValue = mPreviousZoom; + args.setXOffset(mPreviousHorizontalScrollPosition); + args.setYOffset(mPreviousVerticalScrollPosition); + } + + // now restore view state + setZoom(zoomValue); + + mView->horizontalScrollBar()->setValue(args.xOffset()); + mView->verticalScrollBar()->setValue(args.yOffset()); +} + +void SvgPart::setExtendedRestoreArguments(qreal zoom) +{ + mHasExtendedRestoreArguments = true; + mRestoreZoom = zoom; +} + void SvgPart::zoomIn() { setZoom(zoom() * 2); @@ -126,4 +240,16 @@ mView->setMatrix(matrix); } +int SvgPart::horizontalScrollPosition() const +{ + return mView->horizontalScrollBar()->value(); +} + + +int SvgPart::verticalScrollPosition() const +{ + return mView->verticalScrollBar()->value(); +} + + #include "svgpart.moc"