[krita/kazakov/svg-loading] /: Implement copy-pasting of shapes!
Dmitry Kazakov
null at kde.org
Tue Mar 14 09:00:20 UTC 2017
Git commit 1189dccb135a5f35923b8fe9f7adc271eb2887b7 by Dmitry Kazakov.
Committed on 14/03/2017 at 08:59.
Pushed by dkazakov into branch 'kazakov/svg-loading'.
Implement copy-pasting of shapes!
This patch implements the following:
1) The shapes can be copy/pasted inside Krita
2) The shapes can be copy/pasted Krita->Inkscape
(reverse does not yet work)
3) There are two shortcuts (reverse to Inkscape :( )
Ctrl+V paste at original position
Ctrl+Alt+V paste at cursor position
CC:kimageshop at kde.org
M +0 -12 krita/krita.action
M +2 -1 krita/krita.xmlgui
M +12 -0 krita/kritamenu.action
M +1 -0 libs/flake/CMakeLists.txt
M +7 -0 libs/flake/KoCanvasController.h
M +7 -0 libs/flake/KoCanvasControllerWidget.cpp
M +2 -0 libs/flake/KoCanvasControllerWidget.h
M +25 -57 libs/flake/KoDrag.cpp
M +6 -1 libs/flake/KoDrag.h
M +9 -0 libs/flake/KoShape.cpp
M +6 -0 libs/flake/KoShape.h
M +18 -11 libs/flake/KoShapeBasedDocumentBase.cpp
M +17 -0 libs/flake/KoShapeBasedDocumentBase.h
M +15 -0 libs/flake/KoShapeController.cpp
M +16 -0 libs/flake/KoShapeController.h
A +72 -0 libs/flake/KoSvgPaste.cpp [License: GPL (v2+)]
A +38 -0 libs/flake/KoSvgPaste.h [License: GPL (v2+)]
M +9 -3 libs/flake/KoToolManager.cpp
M +117 -11 libs/flake/KoToolProxy.cpp
M +1 -1 libs/flake/KoToolProxy.h
M +15 -2 libs/flake/commands/KoShapeMoveCommand.cpp
M +3 -0 libs/flake/commands/KoShapeMoveCommand.h
M +0 -1 libs/flake/svg/SvgParser.cpp
M +6 -0 libs/flake/tests/CMakeLists.txt
M +8 -0 libs/flake/tests/MockShapes.h
A +87 -0 libs/flake/tests/TestKoDrag.cpp [License: GPL (v2+)]
A +32 -0 libs/flake/tests/TestKoDrag.h [License: GPL (v2+)]
A +76 -0 libs/flake/tests/data/test_svg_file.svg
M +16 -0 libs/global/kis_algebra_2d.h
M +15 -4 libs/ui/actions/kis_selection_action_factories.cpp
M +8 -3 libs/ui/actions/kis_selection_action_factories.h
M +9 -0 libs/ui/canvas/kis_canvas_controller.cpp
M +2 -0 libs/ui/canvas/kis_canvas_controller.h
M +10 -0 libs/ui/flake/kis_shape_controller.cpp
M +5 -2 libs/ui/flake/kis_shape_controller.h
M +3 -2 libs/ui/kis_selection_manager.cc
M +4 -4 plugins/tools/defaulttool/defaulttool/DefaultTool.cpp
https://commits.kde.org/krita/1189dccb135a5f35923b8fe9f7adc271eb2887b7
diff --git a/krita/krita.action b/krita/krita.action
index fe025f82241..e062893251d 100644
--- a/krita/krita.action
+++ b/krita/krita.action
@@ -388,18 +388,6 @@
<isCheckable>true</isCheckable>
<statusTip></statusTip>
</Action>
- <Action name="paste_at">
- <icon></icon>
- <text>Paste at cursor</text>
- <whatsThis></whatsThis>
- <toolTip>Paste at cursor</toolTip>
- <iconText>Paste at cursor</iconText>
- <activationFlags>0</activationFlags>
- <activationConditions>0</activationConditions>
- <shortcut></shortcut>
- <isCheckable>false</isCheckable>
- <statusTip></statusTip>
- </Action>
<Action name="invert">
<icon></icon>
<text>&Invert Selection</text>
diff --git a/krita/krita.xmlgui b/krita/krita.xmlgui
index 13afe46a443..18f97d110c7 100644
--- a/krita/krita.xmlgui
+++ b/krita/krita.xmlgui
@@ -2,7 +2,7 @@
<kpartgui xmlns="http://www.kde.org/standards/kxmlgui/1.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
name="Krita"
-version="103"
+version="104"
xsi:schemaLocation="http://www.kde.org/standards/kxmlgui/1.0 http://www.kde.org/standards/kxmlgui/1.0/kxmlgui.xsd">
<MenuBar>
<Menu name="file">
@@ -49,6 +49,7 @@ xsi:schemaLocation="http://www.kde.org/standards/kxmlgui/1.0 http://www.kde.org
<Action name="copy_sharp"/>
<Action name="copy_merged"/>
<Action name="edit_paste"/>
+ <Action name="paste_at"/>
<Action name="paste_new"/>
<Action name="clear"/>
<Action name="fill_selection_foreground_color"/>
diff --git a/krita/kritamenu.action b/krita/kritamenu.action
index 3ca097eb1f6..85266fe675a 100644
--- a/krita/kritamenu.action
+++ b/krita/kritamenu.action
@@ -368,6 +368,18 @@
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
+ <Action name="paste_at">
+ <icon></icon>
+ <text>Paste at Cursor</text>
+ <whatsThis></whatsThis>
+ <toolTip>Paste at cursor</toolTip>
+ <iconText>Paste at cursor</iconText>
+ <activationFlags>0</activationFlags>
+ <activationConditions>0</activationConditions>
+ <shortcut>Ctrl+Alt+V</shortcut>
+ <isCheckable>false</isCheckable>
+ <statusTip></statusTip>
+ </Action>
<Action name="paste_new">
<icon></icon>
<text>Paste into &New Image</text>
diff --git a/libs/flake/CMakeLists.txt b/libs/flake/CMakeLists.txt
index 8cf05f904ff..5b771412353 100644
--- a/libs/flake/CMakeLists.txt
+++ b/libs/flake/CMakeLists.txt
@@ -80,6 +80,7 @@ set(kritaflake_SRCS
KoVectorPatternBackground.cpp
KoShapeConfigWidgetBase.cpp
KoDrag.cpp
+ KoSvgPaste.cpp
KoDragOdfSaveHelper.cpp
KoShapeOdfSaveHelper.cpp
KoShapePaste.cpp
diff --git a/libs/flake/KoCanvasController.h b/libs/flake/KoCanvasController.h
index e1ab05fb1ac..297650c3001 100644
--- a/libs/flake/KoCanvasController.h
+++ b/libs/flake/KoCanvasController.h
@@ -299,6 +299,12 @@ public:
QPoint documentOffset() const;
+ /**
+ * @return the current position of the cursor fetched from QCursor::pos() and
+ * converted into document coordinates
+ */
+ virtual QPointF currentCursorPosition() const = 0;
+
protected:
void setDocumentSize(const QSize &sz);
QSize documentSize() const;
@@ -465,6 +471,7 @@ public:
virtual void updateDocumentSize(const QSize &/*sz*/, bool /*recalculateCenter*/) {}
virtual void setZoomWithWheel(bool /*zoom*/) {}
virtual void setVastScrolling(qreal /*factor*/) {}
+ QPointF currentCursorPosition() const override { return QPointF(); }
};
diff --git a/libs/flake/KoCanvasControllerWidget.cpp b/libs/flake/KoCanvasControllerWidget.cpp
index 91695c757c3..820559bd722 100644
--- a/libs/flake/KoCanvasControllerWidget.cpp
+++ b/libs/flake/KoCanvasControllerWidget.cpp
@@ -480,6 +480,13 @@ void KoCanvasControllerWidget::setVastScrolling(qreal factor)
d->vastScrollingFactor = factor;
}
+QPointF KoCanvasControllerWidget::currentCursorPosition() const
+{
+ QWidget *canvasWidget = d->canvas->canvasWidget();
+ const KoViewConverter *converter = d->canvas->viewConverter();
+ return converter->viewToDocument(canvasWidget->mapFromGlobal(QCursor::pos()) + d->canvas->canvasController()->documentOffset() - canvasWidget->pos());
+}
+
void KoCanvasControllerWidget::pan(const QPoint &distance)
{
QPoint sourcePoint = scrollBarValue();
diff --git a/libs/flake/KoCanvasControllerWidget.h b/libs/flake/KoCanvasControllerWidget.h
index f8f3bf1da7c..68d9d80f2fa 100644
--- a/libs/flake/KoCanvasControllerWidget.h
+++ b/libs/flake/KoCanvasControllerWidget.h
@@ -139,6 +139,8 @@ public:
virtual void setVastScrolling(qreal factor);
+ QPointF currentCursorPosition() const override;
+
/**
* \internal
*/
diff --git a/libs/flake/KoDrag.cpp b/libs/flake/KoDrag.cpp
index 2f82798c272..51b9f1bd075 100644
--- a/libs/flake/KoDrag.cpp
+++ b/libs/flake/KoDrag.cpp
@@ -38,6 +38,11 @@
#include <KoEmbeddedDocumentSaver.h>
#include "KoShapeSavingContext.h"
+#include <KoShape.h>
+#include <QRect>
+#include <SvgWriter.h>
+
+
class KoDragPrivate {
public:
KoDragPrivate() : mimeData(0) { }
@@ -55,73 +60,36 @@ KoDrag::~KoDrag()
delete d;
}
-bool KoDrag::setOdf(const char *mimeType, KoDragOdfSaveHelper &helper)
+bool KoDrag::setOdf(const char *, KoDragOdfSaveHelper &)
{
- struct Finally {
- Finally(KoStore *s) : store(s) { }
- ~Finally() {
- delete store;
- }
- KoStore *store;
- };
-
- QBuffer buffer;
- KoStore *store = KoStore::createStore(&buffer, KoStore::Write, mimeType);
- Finally finally(store); // delete store when we exit this scope
- Q_ASSERT(store);
- Q_ASSERT(!store->bad());
-
- KoOdfWriteStore odfStore(store);
- KoEmbeddedDocumentSaver embeddedSaver;
-
- KoXmlWriter *manifestWriter = odfStore.manifestWriter(mimeType);
- KoXmlWriter *contentWriter = odfStore.contentWriter();
-
- if (!contentWriter) {
- return false;
- }
+ return false;
+}
- KoGenStyles mainStyles;
- KoXmlWriter *bodyWriter = odfStore.bodyWriter();
- KoShapeSavingContext *context = helper.context(bodyWriter, mainStyles, embeddedSaver);
+bool KoDrag::setSvg(const QList<KoShape *> originalShapes)
+{
+ QRectF boundingRect;
+ QList<KoShape*> shapes;
- if (!helper.writeBody()) {
- return false;
+ Q_FOREACH (KoShape *shape, originalShapes) {
+ boundingRect |= shape->boundingRect();
+ shapes.append(shape->cloneShape());
}
- mainStyles.saveOdfStyles(KoGenStyles::DocumentAutomaticStyles, contentWriter);
+ qSort(shapes.begin(), shapes.end(), KoShape::compareShapeZIndex);
- odfStore.closeContentWriter();
-
- //add manifest line for content.xml
- manifestWriter->addManifestEntry("content.xml", "text/xml");
-
-
- if (!mainStyles.saveOdfStylesDotXml(store, manifestWriter)) {
- return false;
- }
-
- if (!context->saveDataCenter(store, manifestWriter)) {
- debugFlake << "save data centers failed";
- return false;
- }
+ QBuffer buffer;
+ QLatin1String mimeType("image/svg+xml");
- // Save embedded objects
- KoDocumentBase::SavingContext documentContext(odfStore, embeddedSaver);
- if (!embeddedSaver.saveEmbeddedDocuments(documentContext)) {
- debugFlake << "save embedded documents failed";
- return false;
- }
+ buffer.open(QIODevice::WriteOnly);
- // Write out manifest file
- if (!odfStore.closeManifestWriter()) {
- return false;
- }
+ const QSizeF pageSize(boundingRect.right(), boundingRect.bottom());
+ SvgWriter writer(shapes, pageSize);
+ writer.save(buffer);
- delete store; // make sure the buffer if fully flushed.
- finally.store = 0;
- setData(mimeType, buffer.buffer());
+ buffer.close();
+ qDeleteAll(shapes);
+ setData(mimeType, buffer.data());
return true;
}
diff --git a/libs/flake/KoDrag.h b/libs/flake/KoDrag.h
index 8ef1c26b0fc..db1938fe0f3 100644
--- a/libs/flake/KoDrag.h
+++ b/libs/flake/KoDrag.h
@@ -22,11 +22,14 @@
#include "kritaflake_export.h"
+#include <QList>
+
class QMimeData;
class QString;
class QByteArray;
class KoDragOdfSaveHelper;
class KoDragPrivate;
+class KoShape;
/**
* Class for simplifying adding a odf to the clip board
@@ -50,7 +53,9 @@ public:
* @param mimeType used for creating the odf document
* @param helper helper for saving the body of the odf document
*/
- bool setOdf(const char *mimeType, KoDragOdfSaveHelper &helper);
+ bool setOdf(const char *, KoDragOdfSaveHelper &);
+
+ bool setSvg(const QList<KoShape*> shapes);
/**
* Add additional mimeTypes
diff --git a/libs/flake/KoShape.cpp b/libs/flake/KoShape.cpp
index d1bb8c42267..bdaacdeec67 100644
--- a/libs/flake/KoShape.cpp
+++ b/libs/flake/KoShape.cpp
@@ -454,6 +454,15 @@ QRectF KoShape::boundingRect() const
return bb;
}
+QRectF KoShape::boundingRect(const QList<KoShape *> &shapes)
+{
+ QRectF boundingRect;
+ Q_FOREACH (KoShape *shape, shapes) {
+ boundingRect |= shape->boundingRect();
+ }
+ return boundingRect;
+}
+
QTransform KoShape::absoluteTransformation(const KoViewConverter *converter) const
{
Q_D(const KoShape);
diff --git a/libs/flake/KoShape.h b/libs/flake/KoShape.h
index 4e2699fbcbd..bee698062cb 100644
--- a/libs/flake/KoShape.h
+++ b/libs/flake/KoShape.h
@@ -332,6 +332,12 @@ public:
virtual QRectF boundingRect() const;
/**
+ * Get the united bounding box of a group of shapes. This is a utility
+ * function used in many places in Krita.
+ */
+ static QRectF boundingRect(const QList<KoShape*> &shapes);
+
+ /**
* @brief Add a connector point to the shape
*
* A connector is a place on the shape that allows a graphical connection to be made
diff --git a/libs/flake/KoShapeBasedDocumentBase.cpp b/libs/flake/KoShapeBasedDocumentBase.cpp
index fc13c9e9c02..b8dfc0725e4 100644
--- a/libs/flake/KoShapeBasedDocumentBase.cpp
+++ b/libs/flake/KoShapeBasedDocumentBase.cpp
@@ -19,6 +19,7 @@
* Boston, MA 02110-1301, USA.
*/
+#include <QTransform>
#include "KoShapeBasedDocumentBase.h"
#include "KoDocumentResourceManager.h"
#include "KoShapeRegistry.h"
@@ -41,17 +42,15 @@ public:
}
// read persistent application wide resources
KSharedConfigPtr config = KSharedConfig::openConfig();
- if (config->hasGroup("Misc")) {
- KConfigGroup miscGroup = config->group("Misc");
- const qreal pasteOffset = miscGroup.readEntry("CopyOffset", 10.0);
- resourceManager->setPasteOffset(pasteOffset);
- const bool pasteAtCursor = miscGroup.readEntry("PasteAtCursor", true);
- resourceManager->enablePasteAtCursor(pasteAtCursor);
- const uint grabSensitivity = miscGroup.readEntry("GrabSensitivity", 3);
- resourceManager->setGrabSensitivity(grabSensitivity);
- const uint handleRadius = miscGroup.readEntry("HandleRadius", 3);
- resourceManager->setHandleRadius(handleRadius);
- }
+ KConfigGroup miscGroup = config->group("Misc");
+ const qreal pasteOffset = miscGroup.readEntry("CopyOffset", 10.0);
+ resourceManager->setPasteOffset(pasteOffset);
+ const bool pasteAtCursor = miscGroup.readEntry("PasteAtCursor", true);
+ resourceManager->enablePasteAtCursor(pasteAtCursor);
+ const uint grabSensitivity = miscGroup.readEntry("GrabSensitivity", 3);
+ resourceManager->setGrabSensitivity(grabSensitivity);
+ const uint handleRadius = miscGroup.readEntry("HandleRadius", 3);
+ resourceManager->setHandleRadius(handleRadius);
}
~KoShapeBasedDocumentBasePrivate()
@@ -80,3 +79,11 @@ KoDocumentResourceManager *KoShapeBasedDocumentBase::resourceManager() const
{
return d->resourceManager;
}
+
+QRectF KoShapeBasedDocumentBase::documentRect() const
+{
+ const qreal pxToPt = 72.0 / pixelsPerInch();
+
+ QTransform t = QTransform::fromScale(pxToPt, pxToPt);
+ return t.mapRect(documentRectInPixels());
+}
diff --git a/libs/flake/KoShapeBasedDocumentBase.h b/libs/flake/KoShapeBasedDocumentBase.h
index b2f3565b034..95e6327d6d5 100644
--- a/libs/flake/KoShapeBasedDocumentBase.h
+++ b/libs/flake/KoShapeBasedDocumentBase.h
@@ -27,6 +27,7 @@
#include <QList>
+class QRectF;
class KoShape;
class KoShapeBasedDocumentBasePrivate;
class KoDocumentResourceManager;
@@ -78,6 +79,22 @@ public:
*/
virtual KoDocumentResourceManager *resourceManager() const;
+ /**
+ * The size of the document measured in rasterized pixels. This information is needed for loading
+ * SVG documents that use 'px' as the default unit.
+ */
+ virtual QRectF documentRectInPixels() const = 0;
+
+ /**
+ * The size of the document measured in 'pt'
+ */
+ QRectF documentRect() const;
+
+ /**
+ * Resolution of the rasterized representaiton of the document. Used to load SVG documents correctly.
+ */
+ virtual qreal pixelsPerInch() const = 0;
+
private:
KoShapeBasedDocumentBasePrivate * const d;
};
diff --git a/libs/flake/KoShapeController.cpp b/libs/flake/KoShapeController.cpp
index 7d4559247ad..d2f692fd348 100644
--- a/libs/flake/KoShapeController.cpp
+++ b/libs/flake/KoShapeController.cpp
@@ -172,6 +172,21 @@ void KoShapeController::setShapeControllerBase(KoShapeBasedDocumentBase *shapeBa
d->shapeBasedDocument = shapeBasedDocument;
}
+QRectF KoShapeController::documentRectInPixels() const
+{
+ return d->shapeBasedDocument ? d->shapeBasedDocument->documentRectInPixels() : QRectF(0,0,1920,1080);
+}
+
+qreal KoShapeController::pixelsPerInch() const
+{
+ return d->shapeBasedDocument ? d->shapeBasedDocument->pixelsPerInch() : 72.0;
+}
+
+QRectF KoShapeController::documentRect() const
+{
+ return d->shapeBasedDocument ? d->shapeBasedDocument->documentRect() : documentRectInPixels();
+}
+
KoDocumentResourceManager *KoShapeController::resourceManager() const
{
if (!d->shapeBasedDocument)
diff --git a/libs/flake/KoShapeController.h b/libs/flake/KoShapeController.h
index 42f7d450689..377cae0dbec 100644
--- a/libs/flake/KoShapeController.h
+++ b/libs/flake/KoShapeController.h
@@ -111,6 +111,22 @@ public:
void setShapeControllerBase(KoShapeBasedDocumentBase *shapeBasedDocument);
/**
+ * The size of the document measured in rasterized pixels. This information is needed for loading
+ * SVG documents that use 'px' as the default unit.
+ */
+ QRectF documentRectInPixels() const;
+
+ /**
+ * Resolution of the rasterized representaiton of the document. Used to load SVG documents correctly.
+ */
+ qreal pixelsPerInch() const;
+
+ /**
+ * Document rect measured in 'pt'
+ */
+ QRectF documentRect() const;
+
+ /**
* Return a pointer to the resource manager associated with the
* shape-set (typically a document). The resource manager contains
* document wide resources * such as variable managers, the image
diff --git a/libs/flake/KoSvgPaste.cpp b/libs/flake/KoSvgPaste.cpp
new file mode 100644
index 00000000000..205cca02851
--- /dev/null
+++ b/libs/flake/KoSvgPaste.cpp
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2017 Dmitry Kazakov <dimula73 at gmail.com>
+ *
+ * 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 "KoSvgPaste.h"
+
+#include <QApplication>
+#include <QClipboard>
+#include <QMimeData>
+
+#include <SvgParser.h>
+#include <KoDocumentResourceManager.h>
+#include <KoXmlReader.h>
+#include <FlakeDebug.h>
+#include <QRectF>
+
+KoSvgPaste::KoSvgPaste()
+{
+}
+
+bool KoSvgPaste::hasShapes() const
+{
+ const QMimeData *mimeData = QApplication::clipboard()->mimeData();
+ return mimeData && mimeData->hasFormat("image/svg+xml");
+}
+
+QList<KoShape*> KoSvgPaste::fetchShapes(const QRectF viewportInPx, qreal resolutionPPI, QSizeF *fragmentSize) const
+{
+ QList<KoShape*> shapes;
+
+ const QMimeData *mimeData = QApplication::clipboard()->mimeData();
+ if (!mimeData) return shapes;
+
+ QByteArray data = mimeData->data("image/svg+xml");
+ if (data.isEmpty()) return shapes;
+
+ KoXmlDocument doc;
+
+ QString errorMsg;
+ int errorLine = 0;
+ int errorColumn = 0;
+
+ const bool documentValid = doc.setContent(data, false, &errorMsg, &errorLine, &errorColumn);
+
+ if (!documentValid) {
+ errorFlake << "Failed to process an SVG file at"
+ << errorLine << ":" << errorColumn << "->" << errorMsg;
+ return shapes;
+ }
+
+ KoDocumentResourceManager resourceManager;
+ SvgParser parser(&resourceManager);
+ parser.setResolution(viewportInPx, resolutionPPI);
+
+ shapes = parser.parseSvg(doc.documentElement(), fragmentSize);
+
+ return shapes;
+}
diff --git a/libs/flake/KoSvgPaste.h b/libs/flake/KoSvgPaste.h
new file mode 100644
index 00000000000..98400cf6e14
--- /dev/null
+++ b/libs/flake/KoSvgPaste.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2017 Dmitry Kazakov <dimula73 at gmail.com>
+ *
+ * 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 KOSVGPASTE_H
+#define KOSVGPASTE_H
+
+#include "kritaflake_export.h"
+#include <QList>
+
+class KoShape;
+class QRectF;
+class QSizeF;
+
+class KRITAFLAKE_EXPORT KoSvgPaste
+{
+public:
+ KoSvgPaste();
+
+ bool hasShapes() const;
+ QList<KoShape*> fetchShapes(const QRectF viewportInPx, qreal resolutionPPI, QSizeF *fragmentSize = 0) const;
+};
+
+#endif // KOSVGPASTE_H
diff --git a/libs/flake/KoToolManager.cpp b/libs/flake/KoToolManager.cpp
index 7fc1ca04a23..b2571512a84 100644
--- a/libs/flake/KoToolManager.cpp
+++ b/libs/flake/KoToolManager.cpp
@@ -44,6 +44,8 @@
#include "kis_action_registry.h"
#include "KoToolFactoryBase.h"
+#include <krita_container_utils.h>
+
// Qt + kde
#include <QWidget>
#include <QEvent>
@@ -374,13 +376,17 @@ KoCanvasController *KoToolManager::activeCanvasController() const
QString KoToolManager::preferredToolForSelection(const QList<KoShape*> &shapes)
{
QList<QString> types;
- Q_FOREACH (KoShape *shape, shapes)
- if (! types.contains(shape->shapeId()))
- types.append(shape->shapeId());
+ Q_FOREACH (KoShape *shape, shapes) {
+ types << shape->shapeId();
+ }
+
+ KritaUtils::makeContainerUnique(types);
QString toolType = KoInteractionTool_ID;
int prio = INT_MAX;
Q_FOREACH (ToolHelper *helper, d->tools) {
+ if (helper->id() == KoCreateShapesTool_ID) continue;
+
if (helper->priority() >= prio)
continue;
diff --git a/libs/flake/KoToolProxy.cpp b/libs/flake/KoToolProxy.cpp
index 0b71e3580a2..e69760ff322 100644
--- a/libs/flake/KoToolProxy.cpp
+++ b/libs/flake/KoToolProxy.cpp
@@ -50,6 +50,12 @@
#include "KoViewConverter.h"
#include "KoShapeFactoryBase.h"
+#include <KoSvgPaste.h>
+#include <KoSelectedShapesProxy.h>
+#include "kis_algebra_2d.h"
+#include <KoShapeMoveCommand.h>
+#include <KoViewConverter.h>
+
KoToolProxyPrivate::KoToolProxyPrivate(KoToolProxy *p)
: activeTool(0),
@@ -456,27 +462,127 @@ void KoToolProxy::copy() const
d->activeTool->copy();
}
-bool KoToolProxy::paste()
+
+namespace {
+QPointF getFittingOffset(QList<KoShape*> shapes,
+ const QPointF &shapesOffset,
+ const QRectF &documentRect,
+ const qreal fitRatio)
+{
+ QPointF accumulatedFitOffset;
+
+ Q_FOREACH (KoShape *shape, shapes) {
+ const QRectF bounds = shape->boundingRect();
+
+ const QPointF center = bounds.center() + shapesOffset;
+
+ const qreal wMargin = (0.5 - fitRatio) * bounds.width();
+ const qreal hMargin = (0.5 - fitRatio) * bounds.height();
+ const QRectF allowedRect = documentRect.adjusted(-wMargin, -hMargin, wMargin, hMargin);
+
+ const QPointF fittedCenter = KisAlgebra2D::clampPoint(center, allowedRect);
+
+ accumulatedFitOffset += fittedCenter - center;
+ }
+
+ return accumulatedFitOffset;
+}
+}
+
+bool KoToolProxy::paste(bool pasteAtCursorPosition)
{
bool success = false;
KoCanvasBase *canvas = d->controller->canvas();
- if (d->activeTool && d->isActiveLayerEditable())
+ if (d->activeTool && d->isActiveLayerEditable()) {
success = d->activeTool->paste();
+ }
- if (!success) {
- const QMimeData *data = QApplication::clipboard()->mimeData();
+ KoSvgPaste paste;
+ if (!success && paste.hasShapes()) {
+ QSizeF fragmentSize;
+
+ QList<KoShape*> shapes =
+ paste.fetchShapes(canvas->shapeController()->documentRectInPixels(),
+ canvas->shapeController()->pixelsPerInch(), &fragmentSize);
- if (data->hasFormat(KoOdf::mimeType(KoOdf::Text))) {
+ if (!shapes.isEmpty()) {
KoShapeManager *shapeManager = canvas->shapeManager();
- KoShapePaste paste(canvas, shapeManager->selection()->activeLayer());
- success = paste.paste(KoOdf::Text, data);
- if (success) {
- shapeManager->selection()->deselectAll();
- Q_FOREACH (KoShape *shape, paste.pastedShapes()) {
- shapeManager->selection()->select(shape);
+ shapeManager->selection()->deselectAll();
+
+ KUndo2Command *parentCommand = new KUndo2Command(kundo2_i18n("Paste shapes"));
+
+ Q_FOREACH (KoShape *shape, shapes) {
+ canvas->shapeController()->addShapeDirect(shape, parentCommand);
+ }
+
+ QPointF finalShapesOffset;
+
+
+ if (pasteAtCursorPosition) {
+ QRectF boundingRect = KoShape::boundingRect(shapes);
+ const QPointF cursorPos = canvas->canvasController()->currentCursorPosition();
+ finalShapesOffset = cursorPos - boundingRect.center();
+
+ } else {
+ bool foundOverlapping = false;
+
+ QRectF boundingRect = KoShape::boundingRect(shapes);
+ const QPointF offsetStep = 0.1 * QPointF(boundingRect.width(), boundingRect.height());
+
+ QPointF offset;
+
+ Q_FOREACH (KoShape *shape, shapes) {
+ QRectF br1 = shape->boundingRect();
+
+ bool hasOverlappingShape = false;
+
+ do {
+ hasOverlappingShape = false;
+
+ // we cannot use shapesAt() here, because the groups are not
+ // handled in the shape manager's tree
+ QList<KoShape*> conflicts = shapeManager->shapes();
+
+ Q_FOREACH (KoShape *intersectedShape, conflicts) {
+ if (intersectedShape == shape) continue;
+
+ QRectF br2 = intersectedShape->boundingRect();
+
+ const qreal tolerance = 2.0; /* pt */
+ if (KisAlgebra2D::fuzzyCompareRects(br1, br2, tolerance)) {
+ br1.translate(offsetStep.x(), offsetStep.y());
+ offset += offsetStep;
+
+ hasOverlappingShape = true;
+ foundOverlapping = true;
+ break;
+ }
+ }
+ } while (hasOverlappingShape);
+
+ if (foundOverlapping) break;
+ }
+
+ if (foundOverlapping) {
+ finalShapesOffset = offset;
}
}
+
+ const QRectF documentRect = canvas->shapeController()->documentRect();
+ finalShapesOffset += getFittingOffset(shapes, finalShapesOffset, documentRect, 0.1);
+
+ if (!finalShapesOffset.isNull()) {
+ new KoShapeMoveCommand(shapes, finalShapesOffset, parentCommand);
+ }
+
+ canvas->addCommand(parentCommand);
+
+ Q_FOREACH (KoShape *shape, shapes) {
+ canvas->selectedShapesProxy()->selection()->select(shape);
+ }
+
+ success = true;
}
}
diff --git a/libs/flake/KoToolProxy.h b/libs/flake/KoToolProxy.h
index 234fbdfe168..8adbad8e442 100644
--- a/libs/flake/KoToolProxy.h
+++ b/libs/flake/KoToolProxy.h
@@ -142,7 +142,7 @@ public:
void copy() const;
/// Forwarded to the current KoToolBase
- bool paste();
+ bool paste(bool pasteAtCursorPosition = false);
/// Forwarded to the current KoToolBase
QStringList supportedPasteMimeTypes() const;
diff --git a/libs/flake/commands/KoShapeMoveCommand.cpp b/libs/flake/commands/KoShapeMoveCommand.cpp
index 86c2867ac88..b9a88e67f73 100644
--- a/libs/flake/commands/KoShapeMoveCommand.cpp
+++ b/libs/flake/commands/KoShapeMoveCommand.cpp
@@ -33,7 +33,7 @@ public:
};
KoShapeMoveCommand::KoShapeMoveCommand(const QList<KoShape*> &shapes, QList<QPointF> &previousPositions, QList<QPointF> &newPositions, KoFlake::AnchorPosition anchor, KUndo2Command *parent)
- : KUndo2Command(parent),
+ : KUndo2Command(kundo2_i18n("Move shapes"), parent),
d(new Private())
{
d->shapes = shapes;
@@ -42,8 +42,21 @@ KoShapeMoveCommand::KoShapeMoveCommand(const QList<KoShape*> &shapes, QList<QPoi
d->anchor = anchor;
Q_ASSERT(d->shapes.count() == d->previousPositions.count());
Q_ASSERT(d->shapes.count() == d->newPositions.count());
+}
+
+KoShapeMoveCommand::KoShapeMoveCommand(const QList<KoShape *> &shapes, const QPointF &offset, KUndo2Command *parent)
+ : KUndo2Command(kundo2_i18n("Move shapes"), parent),
+ d(new Private())
+{
+ d->shapes = shapes;
+ d->anchor = KoFlake::Center;
+
+ Q_FOREACH (KoShape *shape, d->shapes) {
+ const QPointF pos = shape->absolutePosition();
- setText(kundo2_i18n("Move shapes"));
+ d->previousPositions << pos;
+ d->newPositions << pos + offset;
+ }
}
KoShapeMoveCommand::~KoShapeMoveCommand()
diff --git a/libs/flake/commands/KoShapeMoveCommand.h b/libs/flake/commands/KoShapeMoveCommand.h
index c619edc7dcc..711222440ad 100644
--- a/libs/flake/commands/KoShapeMoveCommand.h
+++ b/libs/flake/commands/KoShapeMoveCommand.h
@@ -45,6 +45,9 @@ public:
*/
KoShapeMoveCommand(const QList<KoShape*> &shapes, QList<QPointF> &previousPositions, QList<QPointF> &newPositions,
KoFlake::AnchorPosition anchor = KoFlake::Center, KUndo2Command *parent = 0);
+
+ KoShapeMoveCommand(const QList<KoShape*> &shapes, const QPointF &offset, KUndo2Command *parent = 0);
+
~KoShapeMoveCommand();
/// redo the command
void redo() override;
diff --git a/libs/flake/svg/SvgParser.cpp b/libs/flake/svg/SvgParser.cpp
index e96d967720b..970730f12f1 100644
--- a/libs/flake/svg/SvgParser.cpp
+++ b/libs/flake/svg/SvgParser.cpp
@@ -1300,7 +1300,6 @@ QList<KoShape*> SvgParser::parseSingleElement(const KoXmlElement &b)
*/
KoShape *defsShape = parseGroup(b);
defsShape->setVisible(false);
- shapes += defsShape;
}
} else if (b.tagName() == "linearGradient" || b.tagName() == "radialGradient") {
} else if (b.tagName() == "pattern") {
diff --git a/libs/flake/tests/CMakeLists.txt b/libs/flake/tests/CMakeLists.txt
index 0a4abe45131..edb3516a4f7 100644
--- a/libs/flake/tests/CMakeLists.txt
+++ b/libs/flake/tests/CMakeLists.txt
@@ -60,6 +60,12 @@ krita_add_broken_unit_test(TestPointMergeCommand.cpp
LINK_LIBRARIES kritaflake Qt5::Test)
ecm_add_test(
+ TestKoDrag.cpp
+ TEST_NAME libs-kritaflake-TestKoDrag
+ LINK_LIBRARIES kritaflake Qt5::Test
+)
+
+ecm_add_test(
TestKoMarkerCollection.cpp
TEST_NAME libs-kritaflake-TestKoMarkerCollection
LINK_LIBRARIES kritaflake Qt5::Test
diff --git a/libs/flake/tests/MockShapes.h b/libs/flake/tests/MockShapes.h
index 829791931a9..07d9cec6370 100644
--- a/libs/flake/tests/MockShapes.h
+++ b/libs/flake/tests/MockShapes.h
@@ -99,6 +99,14 @@ public:
m_shapeManager = shapeManager;
}
+ QRectF documentRectInPixels() const {
+ return QRectF(0,0,100,100);
+ }
+
+ qreal pixelsPerInch() const {
+ return 72.0;
+ }
+
private:
QSet<KoShape * > m_shapes;
KoShapeManager *m_shapeManager = 0;
diff --git a/libs/flake/tests/TestKoDrag.cpp b/libs/flake/tests/TestKoDrag.cpp
new file mode 100644
index 00000000000..543f40bf833
--- /dev/null
+++ b/libs/flake/tests/TestKoDrag.cpp
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) 2017 Dmitry Kazakov <dimula73 at gmail.com>
+ *
+ * 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 "TestKoDrag.h"
+
+#include <KoDrag.h>
+#include <KoSvgPaste.h>
+
+#include <kis_debug.h>
+#include <kis_global.h>
+#include <svg/SvgParser.h>
+#include <KoDocumentResourceManager.h>
+#include <KoShapeGroup.h>
+
+#include "../../sdk/tests/qimage_test_util.h"
+
+void TestKoDrag::test()
+{
+ const QString fileName = TestUtil::fetchDataFileLazy("test_svg_file.svg");
+ QVERIFY(!fileName.isEmpty());
+
+ QFile testShapes(fileName);
+ testShapes.open(QIODevice::ReadOnly);
+
+ KoXmlDocument doc;
+ doc.setContent(testShapes.readAll());
+
+ KoDocumentResourceManager resourceManager;
+ SvgParser parser(&resourceManager);
+ parser.setResolution(QRectF(0, 0, 30, 30) /* px */, 72 /* ppi */);
+
+ QSizeF fragmentSize;
+ QList<KoShape*> shapes = parser.parseSvg(doc.documentElement(), &fragmentSize);
+ QCOMPARE(fragmentSize, QSizeF(30,30));
+
+ {
+ QCOMPARE(shapes.size(), 1);
+
+ KoShapeGroup *layer = dynamic_cast<KoShapeGroup*>(shapes.first());
+ QVERIFY(layer);
+ QCOMPARE(layer->shapeCount(), 2);
+
+ QCOMPARE(KoShape::boundingRect(shapes).toAlignedRect(), QRect(4,3,24,24));
+ }
+
+ KoDrag drag;
+ drag.setSvg(shapes);
+ drag.addToClipboard();
+
+ KoSvgPaste paste;
+ QVERIFY(paste.hasShapes());
+
+ QList<KoShape*> newShapes = paste.fetchShapes(QRectF(0,0,15,15) /* px */, 144 /* ppi */, &fragmentSize);
+
+ {
+ QCOMPARE(newShapes.size(), 1);
+
+ KoShapeGroup *layer = dynamic_cast<KoShapeGroup*>(newShapes.first());
+ QVERIFY(layer);
+ QCOMPARE(layer->shapeCount(), 2);
+
+ QCOMPARE(fragmentSize.toSize(), QSize(54, 53));
+ QCOMPARE(KoShape::boundingRect(newShapes).toAlignedRect(), QRect(4,3,24,24));
+ }
+
+
+ qDeleteAll(shapes);
+ qDeleteAll(newShapes);
+}
+
+
+QTEST_MAIN(TestKoDrag)
diff --git a/libs/flake/tests/TestKoDrag.h b/libs/flake/tests/TestKoDrag.h
new file mode 100644
index 00000000000..edb6a58083d
--- /dev/null
+++ b/libs/flake/tests/TestKoDrag.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2017 Dmitry Kazakov <dimula73 at gmail.com>
+ *
+ * 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 TESTKODRAG_H
+#define TESTKODRAG_H
+
+#include <QObject>
+#include <QTest>
+
+class TestKoDrag : public QObject
+{
+ Q_OBJECT
+private Q_SLOTS:
+ void test();
+};
+
+#endif // TESTKODRAG_H
diff --git a/libs/flake/tests/data/test_svg_file.svg b/libs/flake/tests/data/test_svg_file.svg
new file mode 100644
index 00000000000..025850d432b
--- /dev/null
+++ b/libs/flake/tests/data/test_svg_file.svg
@@ -0,0 +1,76 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="30pt"
+ height="30pt"
+ viewBox="0 0 37.500001 37.5"
+ id="svg2"
+ version="1.1"
+ inkscape:version="0.91 r13725"
+ sodipodi:docname="test_svg_file.svg">
+ <defs
+ id="defs4" />
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="11.2"
+ inkscape:cx="21.813814"
+ inkscape:cy="20.149396"
+ inkscape:document-units="pt"
+ inkscape:current-layer="layer1"
+ showgrid="false"
+ units="pt"
+ inkscape:window-width="1861"
+ inkscape:window-height="1056"
+ inkscape:window-x="59"
+ inkscape:window-y="24"
+ inkscape:window-maximized="1" />
+ <metadata
+ id="metadata7">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer"
+ id="layer1"
+ transform="translate(0,-1014.8622)">
+ <rect
+ style="color:#000000;solid-opacity:1;fill:#fa8072;fill-opacity:0.46551729;fill-rule:evenodd;stroke:#d541a6;stroke-width:2.06454587;stroke-dashoffset:71.24002075;stroke-opacity:0.53448277"
+ id="rect3593"
+ width="16.14974"
+ height="15.703311"
+ x="8.085845"
+ y="1022.6802" />
+ <path
+ style="color:#000000;solid-opacity:1;fill:#1a8072;fill-opacity:0.85714285;fill-rule:evenodd;stroke:#1241a6;stroke-width:2.04706669;stroke-dashoffset:71.24002075;stroke-opacity:0.53448277"
+ id="path4397"
+ sodipodi:type="arc"
+ sodipodi:cx="22.411266"
+ sodipodi:cy="1036.6479"
+ sodipodi:rx="8.440196"
+ sodipodi:ry="8.0838118"
+ sodipodi:start="2.0413895e-11"
+ sodipodi:end="4.9529128"
+ d="m 30.851462,1036.6479 a 8.440196,8.0838118 0 0 1 -5.684712,7.6409 8.440196,8.0838118 0 0 1 -9.396506,-2.6518 8.440196,8.0838118 0 0 1 -0.45067,-9.3724 8.440196,8.0838118 0 0 1 9.102243,-3.4678 l -2.010551,7.8511 z" />
+ </g>
+</svg>
diff --git a/libs/global/kis_algebra_2d.h b/libs/global/kis_algebra_2d.h
index a64f7874c4f..d0015860268 100644
--- a/libs/global/kis_algebra_2d.h
+++ b/libs/global/kis_algebra_2d.h
@@ -479,6 +479,22 @@ inline QPointF absoluteToRelative(const QPointF &pt, const QRectF &rc) {
*/
bool KRITAGLOBAL_EXPORT fuzzyMatrixCompare(const QTransform &t1, const QTransform &t2, qreal delta);
+/**
+ * Compare two rectangles with tolerance \p tolerance. The tolerance means that the
+ * coordinates of top left and bottom right corners should not differ more than \p tolerance
+ * pixels.
+ */
+template<class Rect, typename Difference = decltype(Rect::width())>
+bool fuzzyCompareRects(const Rect &r1, const Rect &r2, Difference tolerance) {
+ typedef decltype(r1.topLeft()) Point;
+
+ const Point d1 = abs(r1.topLeft() - r2.topLeft());
+ const Point d2 = abs(r1.bottomRight() - r2.bottomRight());
+
+ const Difference maxError = std::max({d1.x(), d1.y(), d2.x(), d2.y()});
+ return maxError < tolerance;
+}
+
struct KRITAGLOBAL_EXPORT DecomposedMatix {
DecomposedMatix();
diff --git a/libs/ui/actions/kis_selection_action_factories.cpp b/libs/ui/actions/kis_selection_action_factories.cpp
index 6cbf6e881a1..7c0d377b417 100644
--- a/libs/ui/actions/kis_selection_action_factories.cpp
+++ b/libs/ui/actions/kis_selection_action_factories.cpp
@@ -381,14 +381,25 @@ void KisCopyMergedActionFactory::run(KisViewManager *view)
endAction(ap, KisOperationConfiguration(id()).toXML());
}
-void KisPasteActionFactory::run(KisViewManager *view)
+void KisPasteActionFactory::run(bool pasteAtCursorPosition, KisViewManager *view)
{
- KisImageWSP image = view->image();
+ KisImageSP image = view->image();
if (!image) return;
- KisPaintDeviceSP clip = KisClipboard::instance()->clip(image->bounds(), true);
+ const QRect fittingBounds = pasteAtCursorPosition ? QRect() : image->bounds();
+ KisPaintDeviceSP clip = KisClipboard::instance()->clip(fittingBounds, true);
if (clip) {
+ if (pasteAtCursorPosition) {
+ const QPointF docPos = view->canvasBase()->canvasController()->currentCursorPosition();
+ const QPointF imagePos = view->canvasBase()->coordinatesConverter()->documentToImage(docPos);
+
+ const QPointF offset = (imagePos - QRectF(clip->exactBounds()).center()).toPoint();
+
+ clip->setX(clip->x() + offset.x());
+ clip->setY(clip->y() + offset.y());
+ }
+
KisImportCatcher::adaptClipToImageColorSpace(clip, image);
KisPaintLayer *newLayer = new KisPaintLayer(image.data(), image->nextLayerName() + i18n("(pasted)"), OPACITY_OPAQUE_U8, clip);
KisNodeSP aboveNode = view->activeLayer();
@@ -400,7 +411,7 @@ void KisPasteActionFactory::run(KisViewManager *view)
endAction(ap, KisOperationConfiguration(id()).toXML());
} else {
// XXX: "Add saving of XML data for Paste of shapes"
- view->canvasBase()->toolProxy()->paste();
+ view->canvasBase()->toolProxy()->paste(pasteAtCursorPosition);
}
}
diff --git a/libs/ui/actions/kis_selection_action_factories.h b/libs/ui/actions/kis_selection_action_factories.h
index d5a285805d2..a9885c35d72 100644
--- a/libs/ui/actions/kis_selection_action_factories.h
+++ b/libs/ui/actions/kis_selection_action_factories.h
@@ -89,9 +89,14 @@ struct KRITAUI_EXPORT KisCopyMergedActionFactory : public KisNoParameterActionFa
void run(KisViewManager *view);
};
-struct KRITAUI_EXPORT KisPasteActionFactory : public KisNoParameterActionFactory {
- KisPasteActionFactory() : KisNoParameterActionFactory("paste-ui-action") {}
- void run(KisViewManager *view);
+struct KRITAUI_EXPORT KisPasteActionFactory : public KisOperation {
+ KisPasteActionFactory() : KisOperation("paste-ui-action") {}
+
+ void runFromXML(KisViewManager *view, const KisOperationConfiguration &config) {
+ run(config.getBool("paste-at-cursor-position", false), view);
+ }
+
+ void run(bool pasteAtCursorPosition, KisViewManager *view);
};
struct KRITAUI_EXPORT KisPasteNewActionFactory : public KisNoParameterActionFactory {
diff --git a/libs/ui/canvas/kis_canvas_controller.cpp b/libs/ui/canvas/kis_canvas_controller.cpp
index 2ff3fc13407..a10b8c0208d 100644
--- a/libs/ui/canvas/kis_canvas_controller.cpp
+++ b/libs/ui/canvas/kis_canvas_controller.cpp
@@ -122,6 +122,15 @@ void KisCanvasController::activate()
KoCanvasControllerWidget::activate();
}
+QPointF KisCanvasController::currentCursorPosition() const
+{
+ KoCanvasBase *canvas = m_d->view->canvasBase();
+ QWidget *canvasWidget = canvas->canvasWidget();
+ const QPointF cursorPosWidget = canvasWidget->mapFromGlobal(QCursor::pos());
+
+ return m_d->coordinatesConverter->widgetToDocument(cursorPosWidget);
+}
+
void KisCanvasController::keyPressEvent(QKeyEvent *event)
{
/**
diff --git a/libs/ui/canvas/kis_canvas_controller.h b/libs/ui/canvas/kis_canvas_controller.h
index c371e2616f4..f0fa9b8c732 100644
--- a/libs/ui/canvas/kis_canvas_controller.h
+++ b/libs/ui/canvas/kis_canvas_controller.h
@@ -42,6 +42,8 @@ public:
virtual void updateDocumentSize(const QSize &sz, bool recalculateCenter);
virtual void activate();
+ QPointF currentCursorPosition() const override;
+
public:
using KoCanvasController::documentSize;
bool wrapAroundMode() const;
diff --git a/libs/ui/flake/kis_shape_controller.cpp b/libs/ui/flake/kis_shape_controller.cpp
index d108bf59371..cfd3d883d01 100644
--- a/libs/ui/flake/kis_shape_controller.cpp
+++ b/libs/ui/flake/kis_shape_controller.cpp
@@ -198,6 +198,16 @@ void KisShapeController::removeShape(KoShape* shape)
m_d->doc->setModified(true);
}
+QRectF KisShapeController::documentRectInPixels() const
+{
+ return m_d->doc->image()->bounds();
+}
+
+qreal KisShapeController::pixelsPerInch() const
+{
+ return m_d->doc->image()->xRes() * 72.0;
+}
+
void KisShapeController::setInitialShapeForCanvas(KisCanvas2 *canvas)
{
if (!image()) return;
diff --git a/libs/ui/flake/kis_shape_controller.h b/libs/ui/flake/kis_shape_controller.h
index b25e38d1cbe..01e6dbb0864 100644
--- a/libs/ui/flake/kis_shape_controller.h
+++ b/libs/ui/flake/kis_shape_controller.h
@@ -73,8 +73,11 @@ Q_SIGNALS:
void currentLayerChanged(const KoShapeLayer*);
public:
- void addShape(KoShape* shape);
- void removeShape(KoShape* shape);
+ void addShape(KoShape* shape) override;
+ void removeShape(KoShape* shape) override;
+
+ QRectF documentRectInPixels() const override;
+ qreal pixelsPerInch() const override;
private:
struct Private;
diff --git a/libs/ui/kis_selection_manager.cc b/libs/ui/kis_selection_manager.cc
index 8912eb5001f..4883f647776 100644
--- a/libs/ui/kis_selection_manager.cc
+++ b/libs/ui/kis_selection_manager.cc
@@ -383,12 +383,13 @@ void KisSelectionManager::copyMerged()
void KisSelectionManager::paste()
{
KisPasteActionFactory factory;
- factory.run(m_view);
+ factory.run(false, m_view);
}
void KisSelectionManager::pasteAt()
{
- //XXX
+ KisPasteActionFactory factory;
+ factory.run(true, m_view);
}
void KisSelectionManager::pasteNew()
diff --git a/plugins/tools/defaulttool/defaulttool/DefaultTool.cpp b/plugins/tools/defaulttool/defaulttool/DefaultTool.cpp
index faa81d799f0..fe7101368ac 100644
--- a/plugins/tools/defaulttool/defaulttool/DefaultTool.cpp
+++ b/plugins/tools/defaulttool/defaulttool/DefaultTool.cpp
@@ -59,7 +59,6 @@
#include <QAction>
#include <QKeyEvent>
-#include <QClipboard>
#include <KoResourcePaths.h>
#include <KoCanvasController.h>
@@ -815,11 +814,12 @@ void DefaultTool::repaintDecorations()
void DefaultTool::copy() const
{
+ // all the selected shapes, not only editable!
QList<KoShape *> shapes = canvas()->selectedShapesProxy()->selection()->selectedShapes();
- if (!shapes.empty()) {
- KoShapeOdfSaveHelper saveHelper(shapes);
+
+ if (!shapes.isEmpty()) {
KoDrag drag;
- drag.setOdf(KoOdf::mimeType(KoOdf::Text), saveHelper);
+ drag.setSvg(shapes);
drag.addToClipboard();
}
}
More information about the kimageshop
mailing list