diff --git a/CMakeLists.txt b/CMakeLists.txt index 93e49ab..e996f54 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,44 +1,45 @@ cmake_minimum_required(VERSION 2.8.12 FATAL_ERROR) project(svgpart) set(QT_MIN_VERSION "5.5.0") set(KF_MIN_VERSION "5.18.0") find_package(ECM ${KF_MIN_VERSION} REQUIRED NO_MODULE) set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${ECM_MODULE_PATH} ${ECM_KDE_MODULE_DIR}) include(KDEInstallDirs) include(KDECMakeSettings) include(KDECompilerSettings NO_POLICY_SCOPE) include(FeatureSummary) find_package(Qt5 ${QT_MIN_VERSION} CONFIG REQUIRED COMPONENTS Widgets Svg) find_package(KF5 ${KF_MIN_VERSION} REQUIRED COMPONENTS CoreAddons Parts) add_definitions( -DQT_DEPRECATED_WARNINGS -DQT_DISABLE_DEPRECATED_BEFORE=0x050500 -DQT_NO_SIGNALS_SLOTS_KEYWORDS -DQT_STRICT_ITERATORS -DQT_USE_QSTRINGBUILDER -DQT_NO_CAST_TO_ASCII -DQT_NO_CAST_FROM_ASCII -DQT_NO_CAST_FROM_BYTEARRAY -DQT_NO_URL_CAST_FROM_STRING ) add_definitions(-DTRANSLATION_DOMAIN="svgpart") set(svgpart_SRCS + svgbrowserextension.cpp svgpart.cpp ) add_library(svgpart MODULE ${svgpart_SRCS}) target_link_libraries(svgpart Qt5::Widgets Qt5::Svg KF5::CoreAddons KF5::Parts) install(TARGETS svgpart DESTINATION ${KDE_INSTALL_PLUGINDIR}) install(FILES svgpart.desktop DESTINATION ${KDE_INSTALL_KSERVICES5DIR}) install(FILES svgpart.rc DESTINATION ${KDE_INSTALL_KXMLGUI5DIR}/svgpart) feature_summary(WHAT ALL INCLUDE_QUIET_PACKAGES FATAL_ON_MISSING_REQUIRED_PACKAGES) diff --git a/svgbrowserextension.cpp b/svgbrowserextension.cpp new file mode 100644 index 0000000..2166bff --- /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/svgbrowserextension.h similarity index 52% copy from svgpart.h copy to svgbrowserextension.h index 192c01f..eb9e544 100644 --- a/svgpart.h +++ b/svgbrowserextension.h @@ -1,58 +1,44 @@ /* -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 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. */ -#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/svgpart.cpp b/svgpart.cpp index 62a4eea..b8f4971 100644 --- a/svgpart.cpp +++ b/svgpart.cpp @@ -1,129 +1,255 @@ /* Copyright 2007 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, Boston, MA 02110-1301, USA. */ #include "svgpart.h" +#include "svgbrowserextension.h" + // KF #include #include #include #include #include // Qt #include #include #include #include +#include +#include +#include static KAboutData createAboutData() { KAboutData aboutData(QStringLiteral("svgpart"), i18n("SVG Part"), QStringLiteral("1.0"), i18n("A KPart to display SVG images"), KAboutLicense::KAboutLicense::GPL, i18n("Copyright 2007, Aurélien Gâteau ")); return aboutData; } //Factory Code K_PLUGIN_FACTORY(SvgPartFactory, registerPlugin();) SvgPart::SvgPart(QWidget* parentWidget, QObject* parent, const QVariantList&) : KParts::ReadOnlyPart(parent) , mItem(nullptr) + , m_browserExtension(new SvgBrowserExtension(this)) { setComponentData(createAboutData()); mRenderer = new QSvgRenderer(this); mScene = new QGraphicsScene(this); mView = new QGraphicsView(mScene, parentWidget); mView->setFrameStyle(QFrame::NoFrame); mView->setDragMode(QGraphicsView::ScrollHandDrag); setWidget(mView); KStandardAction::actualSize(this, SLOT(zoomActualSize()), actionCollection()); KStandardAction::zoomIn(this, SLOT(zoomIn()), actionCollection()); KStandardAction::zoomOut(this, SLOT(zoomOut()), actionCollection()); setXMLFile(QStringLiteral("svgpart.rc")); } +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 // so at least just a 1 pixel square one mScene->setSceneRect(QRectF(0,0,1,1)); 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); } void SvgPart::zoomOut() { setZoom(zoom() / 2); } void SvgPart::zoomActualSize() { setZoom(1.0); } qreal SvgPart::zoom() const { return mView->matrix().m11(); } void SvgPart::setZoom(qreal value) { QMatrix matrix; matrix.scale(value, value); mView->setMatrix(matrix); } +int SvgPart::horizontalScrollPosition() const +{ + return mView->horizontalScrollBar()->value(); +} + + +int SvgPart::verticalScrollPosition() const +{ + return mView->verticalScrollBar()->value(); +} + + #include "svgpart.moc" diff --git a/svgpart.h b/svgpart.h index 192c01f..bc46ecc 100644 --- a/svgpart.h +++ b/svgpart.h @@ -1,58 +1,87 @@ /* Copyright 2007 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, Boston, MA 02110-1301, USA. */ #ifndef SVGPART_H #define SVGPART_H // KF #include +class SvgBrowserExtension; + class QGraphicsScene; class QGraphicsSvgItem; class QGraphicsView; class QSvgRenderer; class SvgPart : public KParts::ReadOnlyPart { Q_OBJECT 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 */