Commit 3e203907 authored by Boudewijn Rempt's avatar Boudewijn Rempt

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@kde.org
parent de2d7bc3
......@@ -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
}
......@@ -82,6 +82,7 @@ bool KoDrag::setSvg(const QList<KoShape *> originalShapes)
writer.save(buffer);
buffer.close();
qDeleteAll(shapes);
setData(mimeType, buffer.data());
......
......@@ -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;
......
......@@ -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;
}
......
......@@ -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
......@@ -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);
......
......@@ -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]);
}
}
......@@ -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
......@@ -18,7 +18,7 @@
<widget class="QComboBox" name="cmbCollections"/>
</item>
<item>
<widget class="QListWidget" name="listCollection"/>
<widget class="QListView" name="listCollection"/>
</item>
</layout>
</widget>
......
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