[krita/rempt/T6282-replace-add-shape-docker] /: FEATURE: Add a docker that gives access to svg symbol libraries

Boudewijn Rempt null at kde.org
Thu Jun 8 10:03:53 UTC 2017


Git commit 3e2039077228535c7661468da0da3c5221cc63c9 by Boudewijn Rempt.
Committed on 08/06/2017 at 10:01.
Pushed by rempt into branch 'rempt/T6282-replace-add-shape-docker'.

FEATURE: Add a docker that gives access to svg symbol libraries

You can drag & drop svg items from the docker to the canvas. This
uses the new KoDrag and KoSvgPaste support: the KoSvgPaste methods
are made static for easier access.

The icons are still a bit iffy, and there is no support for editing
collections, searching collections or tagging collections yet, one
needs inkscape for that.

The objects are dropped as groups of shapes, instead of symbols,
and that is intentional: this is meant for things like speech bubbles
which more often need editing than being exactly the same in the whole
document. Krita's still not map-making software...

CCMAIL:kimageshop at kde.org

M  +80   -51   libs/flake/KoCanvasControllerWidgetViewport_p.cpp
M  +1    -0    libs/flake/KoDrag.cpp
M  +1    -1    libs/flake/KoShapeController.cpp
M  +18   -8    libs/flake/KoSvgPaste.cpp
M  +4    -3    libs/flake/KoSvgPaste.h
M  +6    -8    libs/flake/resources/KoSvgSymbolCollectionResource.cpp
M  +104  -11   plugins/dockers/shapedockers/SvgSymbolCollectionDocker.cpp
M  +20   -0    plugins/dockers/shapedockers/SvgSymbolCollectionDocker.h
M  +1    -1    plugins/dockers/shapedockers/WdgSvgCollection.ui

https://commits.kde.org/krita/3e2039077228535c7661468da0da3c5221cc63c9

diff --git a/libs/flake/KoCanvasControllerWidgetViewport_p.cpp b/libs/flake/KoCanvasControllerWidgetViewport_p.cpp
index 7cc545349e8..ea0302ae59a 100644
--- a/libs/flake/KoCanvasControllerWidgetViewport_p.cpp
+++ b/libs/flake/KoCanvasControllerWidgetViewport_p.cpp
@@ -46,16 +46,16 @@
 #include "KoToolProxy.h"
 #include "KoCanvasControllerWidget.h"
 #include "KoViewConverter.h"
-
+#include "KoSvgPaste.h"
 
 // ********** Viewport **********
 Viewport::Viewport(KoCanvasControllerWidget *parent)
-        : QWidget(parent)
-        , m_draggedShape(0)
-        , m_drawShadow(false)
-        , m_canvas(0)
-        , m_documentOffset(QPoint(0, 0))
-        , m_margin(0)
+    : QWidget(parent)
+    , m_draggedShape(0)
+    , m_drawShadow(false)
+    , m_canvas(0)
+    , m_documentOffset(QPoint(0, 0))
+    , m_margin(0)
 {
     setAutoFillBackground(true);
     setAcceptDrops(true);
@@ -105,6 +105,9 @@ void Viewport::handleDragEnterEvent(QDragEnterEvent *event)
         return;
     }
 
+    delete m_draggedShape;
+    m_draggedShape = 0;
+
     // only allow dropping when active layer is editable
     KoSelection *selection = m_parent->canvas()->shapeManager()->selection();
     KoShapeLayer *activeLayer = selection->activeLayer();
@@ -115,50 +118,74 @@ void Viewport::handleDragEnterEvent(QDragEnterEvent *event)
 
     const QMimeData *data = event->mimeData();
     if (data->hasFormat(SHAPETEMPLATE_MIMETYPE) ||
-            data->hasFormat(SHAPEID_MIMETYPE)) {
-        QByteArray itemData;
-        bool isTemplate = true;
-        if (data->hasFormat(SHAPETEMPLATE_MIMETYPE))
-            itemData = data->data(SHAPETEMPLATE_MIMETYPE);
-        else {
-            isTemplate = false;
-            itemData = data->data(SHAPEID_MIMETYPE);
+            data->hasFormat(SHAPEID_MIMETYPE) ||
+            data->hasFormat("image/svg+xml"))
+    {
+        if (data->hasFormat("image/svg+xml")) {
+            KoCanvasBase *canvas = m_parent->canvas();
+            QSizeF fragmentSize;
+
+            QList<KoShape*> shapes = KoSvgPaste::fetchShapesFromData(data->data("image/svg+xml"),
+                                                                     canvas->shapeController()->documentRectInPixels(),
+                                                                     canvas->shapeController()->pixelsPerInch(),
+                                                                     &fragmentSize);
+            if (!shapes.isEmpty()) {
+                m_draggedShape = shapes[0];
+            }
         }
-        QDataStream dataStream(&itemData, QIODevice::ReadOnly);
-        QString id;
-        dataStream >> id;
-        QString properties;
-        if (isTemplate)
-            dataStream >> properties;
-
-        // and finally, there is a point.
-        QPointF offset;
-        dataStream >> offset;
-
-        // The rest of this method is mostly a copy paste from the KoCreateShapeStrategy
-        // So, lets remove this again when Zagge adds his new class that does this kind of thing. (KoLoadSave)
-        KoShapeFactoryBase *factory = KoShapeRegistry::instance()->value(id);
-        if (! factory) {
-            warnFlake << "Application requested a shape that is not registered '" <<
-            id << "', Ignoring";
-            event->ignore();
-            return;
+        else {
+            QByteArray itemData;
+            bool isTemplate = true;
+
+            if (data->hasFormat(SHAPETEMPLATE_MIMETYPE)) {
+                itemData = data->data(SHAPETEMPLATE_MIMETYPE);
+            }
+            else if (data->hasFormat(SHAPEID_MIMETYPE)) {
+                isTemplate = false;
+                itemData = data->data(SHAPEID_MIMETYPE);
+            }
+
+
+            QDataStream dataStream(&itemData, QIODevice::ReadOnly);
+            QString id;
+            dataStream >> id;
+            QString properties;
+            if (isTemplate)
+                dataStream >> properties;
+
+            // and finally, there is a point.
+            QPointF offset;
+            dataStream >> offset;
+
+            // The rest of this method is mostly a copy paste from the KoCreateShapeStrategy
+            // So, lets remove this again when Zagge adds his new class that does this kind of thing. (KoLoadSave)
+            KoShapeFactoryBase *factory = KoShapeRegistry::instance()->value(id);
+            if (! factory) {
+                warnFlake << "Application requested a shape that is not registered '" <<
+                             id << "', Ignoring";
+                event->ignore();
+                return;
+            }
+            if (isTemplate) {
+                KoProperties props;
+                props.load(properties);
+                m_draggedShape = factory->createShape(&props, m_parent->canvas()->shapeController()->resourceManager());
+            }
+            else {
+                m_draggedShape = factory->createDefaultShape(m_parent->canvas()->shapeController()->resourceManager());
+            }
+
+            if (m_draggedShape->shapeId().isEmpty()) {
+                m_draggedShape->setShapeId(factory->id());
+            }
         }
+
         event->setDropAction(Qt::CopyAction);
         event->accept();
 
-        if (isTemplate) {
-            KoProperties props;
-            props.load(properties);
-            m_draggedShape = factory->createShape(&props, m_parent->canvas()->shapeController()->resourceManager());
-        } else
-            m_draggedShape = factory->createDefaultShape(m_parent->canvas()->shapeController()->resourceManager());
-
         Q_ASSERT(m_draggedShape);
         if (!m_draggedShape) return;
 
-        if (m_draggedShape->shapeId().isEmpty())
-            m_draggedShape->setShapeId(factory->id());
         m_draggedShape->setZIndex(KoShapePrivate::MaxZIndex);
         m_draggedShape->setAbsolutePosition(correctPosition(event->pos()));
 
@@ -182,6 +209,7 @@ void Viewport::handleDropEvent(QDropEvent *event)
     QPointF newPos = correctPosition(event->pos());
     m_parent->canvas()->clipToDocument(m_draggedShape, newPos); // ensure the shape is dropped inside the document.
     m_draggedShape->setAbsolutePosition(newPos);
+
     KUndo2Command * cmd = m_parent->canvas()->shapeController()->addShape(m_draggedShape);
     if (cmd) {
         m_parent->canvas()->addCommand(cmd);
@@ -193,8 +221,9 @@ void Viewport::handleDropEvent(QDropEvent *event)
 
         selection->deselectAll();
         selection->select(m_draggedShape);
-    } else
+    } else {
         delete m_draggedShape;
+    }
 
     m_draggedShape = 0;
 }
@@ -265,7 +294,7 @@ void Viewport::handlePaintEvent(QPainter &painter, QPaintEvent *event)
         QWidget *canvasWidget = m_parent->canvas()->canvasWidget();
         Q_ASSERT(canvasWidget); // since we should not allow drag if there is not.
         painter.translate(canvasWidget->x() - m_documentOffset.x(),
-                canvasWidget->y() - m_documentOffset.y());
+                          canvasWidget->y() - m_documentOffset.y());
         QPointF offset = vc->documentToView(m_draggedShape->position());
         painter.setOpacity(0.6);
         painter.translate(offset.x(), offset.y());
@@ -293,10 +322,10 @@ void Viewport::resetLayout()
     int resizeW = viewW;
     int resizeH = viewH;
 
-//     debugFlake <<"viewH:" << viewH << endl
-//              << "docH: " << docH << endl
-//              << "viewW: " << viewW << endl
-//              << "docW: " << docW << endl;
+    //     debugFlake <<"viewH:" << viewH << endl
+    //              << "docH: " << docH << endl
+    //              << "viewW: " << viewW << endl
+    //              << "docW: " << docW << endl;
 
     if (viewH == docH && viewW == docW) {
         // Do nothing
@@ -366,8 +395,8 @@ void Viewport::resetLayout()
 
     emit sizeChanged();
 #if 0
-     debugFlake <<"View port geom:" << geometry();
-     if (m_canvas)
+    debugFlake <<"View port geom:" << geometry();
+    if (m_canvas)
         debugFlake <<"Canvas widget geom:" << m_canvas->geometry();
 #endif
 }
diff --git a/libs/flake/KoDrag.cpp b/libs/flake/KoDrag.cpp
index 08d5ccd328e..c3be65f33f0 100644
--- a/libs/flake/KoDrag.cpp
+++ b/libs/flake/KoDrag.cpp
@@ -82,6 +82,7 @@ bool KoDrag::setSvg(const QList<KoShape *> originalShapes)
     writer.save(buffer);
 
     buffer.close();
+
     qDeleteAll(shapes);
 
     setData(mimeType, buffer.data());
diff --git a/libs/flake/KoShapeController.cpp b/libs/flake/KoShapeController.cpp
index d277c9366b8..0c67c96b159 100644
--- a/libs/flake/KoShapeController.cpp
+++ b/libs/flake/KoShapeController.cpp
@@ -57,7 +57,7 @@ public:
     KUndo2Command* addShape(KoShape *shape, bool showDialog, KUndo2Command *parent) {
 
         if (canvas) {
-            if (showDialog) {
+            if (showDialog && !shape->shapeId().isEmpty()) {
                 KoShapeFactoryBase *factory = KoShapeRegistry::instance()->value(shape->shapeId());
                 Q_ASSERT(factory);
                 int z = 0;
diff --git a/libs/flake/KoSvgPaste.cpp b/libs/flake/KoSvgPaste.cpp
index 205cca02851..6da3f9abb7a 100644
--- a/libs/flake/KoSvgPaste.cpp
+++ b/libs/flake/KoSvgPaste.cpp
@@ -28,17 +28,13 @@
 #include <FlakeDebug.h>
 #include <QRectF>
 
-KoSvgPaste::KoSvgPaste()
-{
-}
-
-bool KoSvgPaste::hasShapes() const
+bool KoSvgPaste::hasShapes()
 {
     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*> KoSvgPaste::fetchShapes(const QRectF viewportInPx, qreal resolutionPPI, QSizeF *fragmentSize)
 {
     QList<KoShape*> shapes;
 
@@ -46,7 +42,21 @@ QList<KoShape*> KoSvgPaste::fetchShapes(const QRectF viewportInPx, qreal resolut
     if (!mimeData) return shapes;
 
     QByteArray data = mimeData->data("image/svg+xml");
-    if (data.isEmpty()) return shapes;
+    if (data.isEmpty()) {
+        return shapes;
+    }
+
+    return fetchShapesFromData(data, viewportInPx, resolutionPPI, fragmentSize);
+
+}
+
+QList<KoShape*> KoSvgPaste::fetchShapesFromData(const QByteArray &data, const QRectF viewportInPx, qreal resolutionPPI, QSizeF *fragmentSize)
+{
+    QList<KoShape*> shapes;
+
+    if (data.isEmpty()) {
+        return shapes;
+    }
 
     KoXmlDocument doc;
 
@@ -57,7 +67,7 @@ QList<KoShape*> KoSvgPaste::fetchShapes(const QRectF viewportInPx, qreal resolut
     const bool documentValid = doc.setContent(data, false, &errorMsg, &errorLine, &errorColumn);
 
     if (!documentValid) {
-        errorFlake << "Failed to process an SVG file at"
+        qWarning() << "Failed to process an SVG file at"
                    << errorLine << ":" << errorColumn << "->" << errorMsg;
         return shapes;
     }
diff --git a/libs/flake/KoSvgPaste.h b/libs/flake/KoSvgPaste.h
index 98400cf6e14..6c44b9e3f87 100644
--- a/libs/flake/KoSvgPaste.h
+++ b/libs/flake/KoSvgPaste.h
@@ -25,14 +25,15 @@
 class KoShape;
 class QRectF;
 class QSizeF;
+class QByteArray;
 
 class KRITAFLAKE_EXPORT KoSvgPaste
 {
 public:
-    KoSvgPaste();
+    static bool hasShapes();
+    static QList<KoShape*> fetchShapes(const QRectF viewportInPx, qreal resolutionPPI, QSizeF *fragmentSize = 0);
+    static QList<KoShape*> fetchShapesFromData(const QByteArray &data, const QRectF viewportInPx, qreal resolutionPPI, QSizeF *fragmentSize = 0);
 
-    bool hasShapes() const;
-    QList<KoShape*> fetchShapes(const QRectF viewportInPx, qreal resolutionPPI, QSizeF *fragmentSize = 0) const;
 };
 
 #endif // KOSVGPASTE_H
diff --git a/libs/flake/resources/KoSvgSymbolCollectionResource.cpp b/libs/flake/resources/KoSvgSymbolCollectionResource.cpp
index d9beb8cb3a8..0951e5d7463 100644
--- a/libs/flake/resources/KoSvgSymbolCollectionResource.cpp
+++ b/libs/flake/resources/KoSvgSymbolCollectionResource.cpp
@@ -76,8 +76,6 @@ KoSvgSymbolCollectionResource::~KoSvgSymbolCollectionResource()
 
 bool KoSvgSymbolCollectionResource::load()
 {
-    qDebug() << "Going to load" << filename();
-
     QFile file(filename());
     if (file.size() == 0) return false;
     if (!file.open(QIODevice::ReadOnly)) {
@@ -136,12 +134,12 @@ bool KoSvgSymbolCollectionResource::loadFromDevice(QIODevice *dev)
     // We're not interested in the shapes themselves
     qDeleteAll(parser.parseSvg(doc.documentElement(), &fragmentSize));
     d->symbols = parser.takeSymbols();
-    qDebug() << "Loaded" << filename() << "\n\t"
-             << "Title" << parser.documentTitle() << "\n\t"
-             << "Description" << parser.documentDescription()
-             << "\n\tgot" << d->symbols.size() << "symbols"
-             << d->symbols[0]->shape->outlineRect()
-             << d->symbols[0]->shape->size();
+//    qDebug() << "Loaded" << filename() << "\n\t"
+//             << "Title" << parser.documentTitle() << "\n\t"
+//             << "Description" << parser.documentDescription()
+//             << "\n\tgot" << d->symbols.size() << "symbols"
+//             << d->symbols[0]->shape->outlineRect()
+//             << d->symbols[0]->shape->size();
 
     d->title = parser.documentTitle();
     setName(d->title);
diff --git a/plugins/dockers/shapedockers/SvgSymbolCollectionDocker.cpp b/plugins/dockers/shapedockers/SvgSymbolCollectionDocker.cpp
index ebaaaf9ebb3..eb23bd549ca 100644
--- a/plugins/dockers/shapedockers/SvgSymbolCollectionDocker.cpp
+++ b/plugins/dockers/shapedockers/SvgSymbolCollectionDocker.cpp
@@ -22,12 +22,110 @@
 #include <klocalizedstring.h>
 
 #include <QDebug>
+#include <QAbstractListModel>
+#include <QMimeData>
+#include <QDomDocument>
+#include <QDomElement>
 
 #include <KoResourceServerProvider.h>
 #include <KoResourceServer.h>
+#include <KoShapeFactoryBase.h>
+#include <KoProperties.h>
+#include <KoDrag.h>
 
 #include "ui_WdgSvgCollection.h"
 
+#include <resources/KoSvgSymbolCollectionResource.h>
+
+//
+// SvgCollectionModel
+//
+SvgCollectionModel::SvgCollectionModel(QObject *parent)
+    : QAbstractListModel(parent)
+{
+    setSupportedDragActions(Qt::CopyAction);
+}
+
+QVariant SvgCollectionModel::data(const QModelIndex &index, int role) const
+{
+    if (!index.isValid() || index.row() > m_symbolCollection->symbols().count()) {
+        return QVariant();
+    }
+
+    switch (role) {
+    case Qt::ToolTipRole:
+        return m_symbolCollection->symbols()[index.row()]->title;
+
+    case Qt::DecorationRole:
+    {
+        QPixmap px = QPixmap::fromImage(m_symbolCollection->symbols()[index.row()]->icon);
+        QIcon icon(px);
+        return icon;
+    }
+    case Qt::UserRole:
+        return m_symbolCollection->symbols()[index.row()]->id;
+
+    case Qt::DisplayRole:
+        return m_symbolCollection->symbols()[index.row()]->title;
+
+    default:
+        return QVariant();
+    }
+
+    return QVariant();
+}
+
+int SvgCollectionModel::rowCount(const QModelIndex &parent) const
+{
+    Q_UNUSED(parent);
+    return m_symbolCollection->symbols().count();
+}
+
+QMimeData *SvgCollectionModel::mimeData(const QModelIndexList &indexes) const
+{
+    if (indexes.isEmpty()) {
+        return 0;
+    }
+
+    QModelIndex index = indexes.first();
+
+    if (!index.isValid()) {
+        return 0;
+    }
+
+    if (m_symbolCollection->symbols().isEmpty()) {
+        return 0;
+    }
+
+    QList<KoShape*> shapes;
+    shapes.append(m_symbolCollection->symbols()[index.row()]->shape);
+    KoDrag drag;
+    drag.setSvg(shapes);
+    QMimeData *mimeData = drag.mimeData();
+
+    return mimeData;
+}
+
+QStringList SvgCollectionModel::mimeTypes() const
+{
+    return QStringList() << SHAPETEMPLATE_MIMETYPE << "image/svg+xml";
+}
+
+Qt::ItemFlags SvgCollectionModel::flags(const QModelIndex &index) const
+{
+    if (index.isValid()) {
+        return QAbstractListModel::flags(index) | Qt::ItemIsDragEnabled;
+    }
+    return QAbstractListModel::flags(index);
+}
+
+void SvgCollectionModel::setSvgSymbolCollectionResource(KoSvgSymbolCollectionResource *resource)
+{
+    m_symbolCollection = resource;
+}
+
+
+
 //
 // SvgSymbolCollectionDockerFactory
 //
@@ -66,8 +164,10 @@ SvgSymbolCollectionDocker::SvgSymbolCollectionDocker(QWidget *parent)
 
     KoResourceServer<KoSvgSymbolCollectionResource>  *svgCollectionProvider = KoResourceServerProvider::instance()->svgSymbolCollectionServer();
     Q_FOREACH(KoSvgSymbolCollectionResource *r, svgCollectionProvider->resources()) {
-        QVariant v = QVariant::fromValue<KoSvgSymbolCollectionResource*>(r);
-        m_wdgSvgCollection->cmbCollections->addItem(r->name(), v);
+        m_wdgSvgCollection->cmbCollections->addItem(r->name());
+        SvgCollectionModel *model = new SvgCollectionModel();
+        model->setSvgSymbolCollectionResource(r);
+        m_models.append(model);
     }
 
     m_wdgSvgCollection->listCollection->setDragEnabled(true);
@@ -90,15 +190,8 @@ void SvgSymbolCollectionDocker::unsetCanvas()
 
 void SvgSymbolCollectionDocker::collectionActivated(int index)
 {
-    QVariant v = m_wdgSvgCollection->cmbCollections->itemData(index);
-    KoSvgSymbolCollectionResource *r = v.value<KoSvgSymbolCollectionResource *>();
-    if (r) {
-        m_wdgSvgCollection->listCollection->clear();
-        Q_FOREACH(KoSvgSymbol *symbol, r->symbols()) {
-            QListWidgetItem *item = new QListWidgetItem(symbol->title);
-            item->setIcon(QIcon(QPixmap::fromImage(symbol->icon)));
-            m_wdgSvgCollection->listCollection->addItem(item);
-        }
+    if (index < m_models.size()) {
+        m_wdgSvgCollection->listCollection->setModel(m_models[index]);
     }
 
 }
diff --git a/plugins/dockers/shapedockers/SvgSymbolCollectionDocker.h b/plugins/dockers/shapedockers/SvgSymbolCollectionDocker.h
index 25e3fe8c27f..bb349bc0ac9 100644
--- a/plugins/dockers/shapedockers/SvgSymbolCollectionDocker.h
+++ b/plugins/dockers/shapedockers/SvgSymbolCollectionDocker.h
@@ -20,6 +20,7 @@
 #define SVGSYMBOLCOLLECTIONDOCKER_H
 
 #include <QDockWidget>
+#include <QAbstractItemModel>
 #include <QModelIndex>
 #include <QMap>
 #include <QIcon>
@@ -29,6 +30,24 @@
 
 #include "ui_WdgSvgCollection.h"
 
+class KoSvgSymbolCollectionResource;
+
+class SvgCollectionModel : public QAbstractListModel
+{
+    Q_OBJECT
+public:
+    explicit SvgCollectionModel(QObject *parent = 0);
+    QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
+    int rowCount(const QModelIndex &parent = QModelIndex()) const override;
+    QMimeData *mimeData(const QModelIndexList &indexes) const override;
+    QStringList mimeTypes() const override;
+    Qt::ItemFlags flags(const QModelIndex &index) const override;
+public:
+    void setSvgSymbolCollectionResource(KoSvgSymbolCollectionResource *resource);
+private:
+    KoSvgSymbolCollectionResource *m_symbolCollection;
+};
+
 
 class SvgSymbolCollectionDockerFactory : public KoDockFactoryBase
 {
@@ -61,6 +80,7 @@ private Q_SLOTS:
 private:
 
     Ui_WdgSvgCollection *m_wdgSvgCollection;
+    QVector<SvgCollectionModel*> m_models;
 };
 
 #endif //KOSHAPECOLLECTIONDOCKER_H
diff --git a/plugins/dockers/shapedockers/WdgSvgCollection.ui b/plugins/dockers/shapedockers/WdgSvgCollection.ui
index e3057847eb3..64b41a2d93e 100644
--- a/plugins/dockers/shapedockers/WdgSvgCollection.ui
+++ b/plugins/dockers/shapedockers/WdgSvgCollection.ui
@@ -18,7 +18,7 @@
     <widget class="QComboBox" name="cmbCollections"/>
    </item>
    <item>
-    <widget class="QListWidget" name="listCollection"/>
+    <widget class="QListView" name="listCollection"/>
    </item>
   </layout>
  </widget>


More information about the kimageshop mailing list