Commit d1ee5a0e authored by Friedrich W. H. Kossebau's avatar Friedrich W. H. Kossebau
Browse files

Support loading by stream and restoring state on reload

Summary:
The KTextEditor Preview plugin repeatedly feeds new
versions to the same kpart instance, to allow instant
preview of changes. To avoid stressing of the filesystem
the stream API of the kpart is used if available.

This patch adds support for the stream API.
Additionally it remembers the view state on closing an url,
and if the same url is loaded again, the view state is restored.
The latter allows continuous display of the same, but
updated file as e.g. happening with the preview plugin.

Test Plan:
Editing SVG files in Kate/KDevelop using the KTextEditor Preview plugin will
no longer reset the offset to 0,0 each time the view is updated on
changes, also is the filesystem no longer used on updates.

Reviewers: #frameworks, dfaure

Reviewed By: dfaure

Differential Revision: https://phabricator.kde.org/D7580
parent f8ee697f
......@@ -30,6 +30,7 @@ add_definitions(
add_definitions(-DTRANSLATION_DOMAIN="svgpart")
set(svgpart_SRCS
svgbrowserextension.cpp
svgpart.cpp
)
......
/*
Copyright (C) 2017 by Friedrich W. H. Kossebau <kossebau@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, Boston, MA 02110-1301, USA.
*/
#include "svgbrowserextension.h"
#include "svgpart.h"
// KF
#include <KLocalizedString>
// Qt
#include <QDataStream>
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);
}
/*
Copyright (C) 2017 by Friedrich W. H. Kossebau <kossebau@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, Boston, MA 02110-1301, USA.
*/
#ifndef SVGBROWSEREXTENSION_H
#define SVGBROWSEREXTENSION_H
// KF
#include <KParts/BrowserExtension>
class SvgPart;
class SvgBrowserExtension : public KParts::BrowserExtension
{
Q_OBJECT
public:
explicit SvgBrowserExtension(SvgPart* part);
int xOffset() override;
int yOffset() override;
void saveState(QDataStream& stream) override;
void restoreState(QDataStream& stream) override;
private:
SvgPart* m_part;
};
#endif
......@@ -18,6 +18,8 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#include "svgpart.h"
#include "svgbrowserextension.h"
// KF
#include <KAboutData>
#include <KLocalizedString>
......@@ -30,6 +32,9 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#include <QGraphicsSvgItem>
#include <QGraphicsView>
#include <QSvgRenderer>
#include <QMimeDatabase>
#include <QScrollBar>
#include <QTimer>
static KAboutData createAboutData()
......@@ -49,6 +54,7 @@ K_PLUGIN_FACTORY(SvgPartFactory, registerPlugin<SvgPart>();)
SvgPart::SvgPart(QWidget* parentWidget, QObject* parent, const QVariantList&)
: KParts::ReadOnlyPart(parent)
, mItem(nullptr)
, m_browserExtension(new SvgBrowserExtension(this))
{
setComponentData(createAboutData());
......@@ -66,22 +72,80 @@ SvgPart::SvgPart(QWidget* parentWidget, QObject* parent, const QVariantList&)
}
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 @@ bool SvgPart::closeUrl()
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 @@ void SvgPart::setZoom(qreal value)
mView->setMatrix(matrix);
}
int SvgPart::horizontalScrollPosition() const
{
return mView->horizontalScrollBar()->value();
}
int SvgPart::verticalScrollPosition() const
{
return mView->verticalScrollBar()->value();
}
#include "svgpart.moc"
......@@ -22,6 +22,8 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
// KF
#include <KParts/ReadOnlyPart>
class SvgBrowserExtension;
class QGraphicsScene;
class QGraphicsSvgItem;
class QGraphicsView;
......@@ -34,25 +36,52 @@ class SvgPart : public KParts::ReadOnlyPart
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 */
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment