Add cut/copy/paste
parent
de0075bab4
commit
f49e88484f
|
@ -2,8 +2,12 @@
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
#include <QThread>
|
#include <QThread>
|
||||||
#include <QGuiApplication>
|
#include <QGuiApplication>
|
||||||
|
#include <QClipboard>
|
||||||
|
#include <QMimeData>
|
||||||
|
#include <QApplication>
|
||||||
#include "skeletondocument.h"
|
#include "skeletondocument.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
#include "skeletonxml.h"
|
||||||
|
|
||||||
unsigned long SkeletonDocument::m_maxSnapshot = 1000;
|
unsigned long SkeletonDocument::m_maxSnapshot = 1000;
|
||||||
|
|
||||||
|
@ -497,9 +501,18 @@ void SkeletonDocument::setNodeRootMarkMode(QUuid nodeId, SkeletonNodeRootMarkMod
|
||||||
nodeIt->second.rootMarkMode = mode;
|
nodeIt->second.rootMarkMode = mode;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SkeletonDocument::toSnapshot(SkeletonSnapshot *snapshot)
|
void SkeletonDocument::toSnapshot(SkeletonSnapshot *snapshot, const std::set<QUuid> &limitNodeIds) const
|
||||||
{
|
{
|
||||||
|
std::set<QUuid> limitPartIds;
|
||||||
|
for (const auto &nodeId: limitNodeIds) {
|
||||||
|
const SkeletonNode *node = findNode(nodeId);
|
||||||
|
if (!node)
|
||||||
|
continue;
|
||||||
|
limitPartIds.insert(node->partId);
|
||||||
|
}
|
||||||
for (const auto &partIt : partMap) {
|
for (const auto &partIt : partMap) {
|
||||||
|
if (!limitPartIds.empty() && limitPartIds.find(partIt.first) == limitPartIds.end())
|
||||||
|
continue;
|
||||||
std::map<QString, QString> part;
|
std::map<QString, QString> part;
|
||||||
part["id"] = partIt.second.id.toString();
|
part["id"] = partIt.second.id.toString();
|
||||||
part["visible"] = partIt.second.visible ? "true" : "false";
|
part["visible"] = partIt.second.visible ? "true" : "false";
|
||||||
|
@ -510,6 +523,8 @@ void SkeletonDocument::toSnapshot(SkeletonSnapshot *snapshot)
|
||||||
snapshot->parts[part["id"]] = part;
|
snapshot->parts[part["id"]] = part;
|
||||||
}
|
}
|
||||||
for (const auto &nodeIt: nodeMap) {
|
for (const auto &nodeIt: nodeMap) {
|
||||||
|
if (!limitNodeIds.empty() && limitNodeIds.find(nodeIt.first) == limitNodeIds.end())
|
||||||
|
continue;
|
||||||
std::map<QString, QString> node;
|
std::map<QString, QString> node;
|
||||||
node["id"] = nodeIt.second.id.toString();
|
node["id"] = nodeIt.second.id.toString();
|
||||||
node["radius"] = QString::number(nodeIt.second.radius);
|
node["radius"] = QString::number(nodeIt.second.radius);
|
||||||
|
@ -526,6 +541,10 @@ void SkeletonDocument::toSnapshot(SkeletonSnapshot *snapshot)
|
||||||
for (const auto &edgeIt: edgeMap) {
|
for (const auto &edgeIt: edgeMap) {
|
||||||
if (edgeIt.second.nodeIds.size() != 2)
|
if (edgeIt.second.nodeIds.size() != 2)
|
||||||
continue;
|
continue;
|
||||||
|
if (!limitNodeIds.empty() &&
|
||||||
|
(limitNodeIds.find(edgeIt.second.nodeIds[0]) == limitNodeIds.end() ||
|
||||||
|
limitNodeIds.find(edgeIt.second.nodeIds[1]) == limitNodeIds.end()))
|
||||||
|
continue;
|
||||||
std::map<QString, QString> edge;
|
std::map<QString, QString> edge;
|
||||||
edge["id"] = edgeIt.second.id.toString();
|
edge["id"] = edgeIt.second.id.toString();
|
||||||
edge["from"] = edgeIt.second.nodeIds[0].toString();
|
edge["from"] = edgeIt.second.nodeIds[0].toString();
|
||||||
|
@ -538,10 +557,87 @@ void SkeletonDocument::toSnapshot(SkeletonSnapshot *snapshot)
|
||||||
qDebug() << "Export edge to snapshot " << edge["from"] << "<=>" << edge["to"];
|
qDebug() << "Export edge to snapshot " << edge["from"] << "<=>" << edge["to"];
|
||||||
}
|
}
|
||||||
for (const auto &partIdIt: partIds) {
|
for (const auto &partIdIt: partIds) {
|
||||||
|
if (!limitPartIds.empty() && limitPartIds.find(partIdIt) == limitPartIds.end())
|
||||||
|
continue;
|
||||||
snapshot->partIdList.push_back(partIdIt.toString());
|
snapshot->partIdList.push_back(partIdIt.toString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SkeletonDocument::addFromSnapshot(const SkeletonSnapshot &snapshot)
|
||||||
|
{
|
||||||
|
std::map<QUuid, QUuid> oldNewIdMap;
|
||||||
|
for (const auto &partKv : snapshot.parts) {
|
||||||
|
SkeletonPart part;
|
||||||
|
oldNewIdMap[QUuid(partKv.first)] = part.id;
|
||||||
|
part.name = valueOfKeyInMapOrEmpty(partKv.second, "name");
|
||||||
|
part.visible = isTrueValueString(valueOfKeyInMapOrEmpty(partKv.second, "visible"));
|
||||||
|
part.locked = isTrueValueString(valueOfKeyInMapOrEmpty(partKv.second, "locked"));
|
||||||
|
part.subdived = isTrueValueString(valueOfKeyInMapOrEmpty(partKv.second, "subdived"));
|
||||||
|
partMap[part.id] = part;
|
||||||
|
}
|
||||||
|
for (const auto &nodeKv : snapshot.nodes) {
|
||||||
|
if (nodeKv.second.find("radius") == nodeKv.second.end() ||
|
||||||
|
nodeKv.second.find("x") == nodeKv.second.end() ||
|
||||||
|
nodeKv.second.find("y") == nodeKv.second.end() ||
|
||||||
|
nodeKv.second.find("z") == nodeKv.second.end() ||
|
||||||
|
nodeKv.second.find("partId") == nodeKv.second.end())
|
||||||
|
continue;
|
||||||
|
SkeletonNode node;
|
||||||
|
oldNewIdMap[QUuid(nodeKv.first)] = node.id;
|
||||||
|
node.name = valueOfKeyInMapOrEmpty(nodeKv.second, "name");
|
||||||
|
node.radius = valueOfKeyInMapOrEmpty(nodeKv.second, "radius").toFloat();
|
||||||
|
node.x = valueOfKeyInMapOrEmpty(nodeKv.second, "x").toFloat();
|
||||||
|
node.y = valueOfKeyInMapOrEmpty(nodeKv.second, "y").toFloat();
|
||||||
|
node.z = valueOfKeyInMapOrEmpty(nodeKv.second, "z").toFloat();
|
||||||
|
node.partId = oldNewIdMap[QUuid(valueOfKeyInMapOrEmpty(nodeKv.second, "partId"))];
|
||||||
|
node.rootMarkMode = SkeletonNodeRootMarkModeFromString(valueOfKeyInMapOrEmpty(nodeKv.second, "rootMarkMode"));
|
||||||
|
nodeMap[node.id] = node;
|
||||||
|
}
|
||||||
|
for (const auto &edgeKv : snapshot.edges) {
|
||||||
|
if (edgeKv.second.find("from") == edgeKv.second.end() ||
|
||||||
|
edgeKv.second.find("to") == edgeKv.second.end() ||
|
||||||
|
edgeKv.second.find("partId") == edgeKv.second.end())
|
||||||
|
continue;
|
||||||
|
SkeletonEdge edge;
|
||||||
|
oldNewIdMap[QUuid(edgeKv.first)] = edge.id;
|
||||||
|
edge.name = valueOfKeyInMapOrEmpty(edgeKv.second, "name");
|
||||||
|
edge.partId = oldNewIdMap[QUuid(valueOfKeyInMapOrEmpty(edgeKv.second, "partId"))];
|
||||||
|
edge.branchMode = SkeletonEdgeBranchModeFromString(valueOfKeyInMapOrEmpty(edgeKv.second, "branchMode"));
|
||||||
|
QString fromNodeId = valueOfKeyInMapOrEmpty(edgeKv.second, "from");
|
||||||
|
if (!fromNodeId.isEmpty()) {
|
||||||
|
QUuid fromId = oldNewIdMap[QUuid(fromNodeId)];
|
||||||
|
edge.nodeIds.push_back(fromId);
|
||||||
|
nodeMap[fromId].edgeIds.push_back(edge.id);
|
||||||
|
}
|
||||||
|
QString toNodeId = valueOfKeyInMapOrEmpty(edgeKv.second, "to");
|
||||||
|
if (!toNodeId.isEmpty()) {
|
||||||
|
QUuid toId = oldNewIdMap[QUuid(toNodeId)];
|
||||||
|
edge.nodeIds.push_back(toId);
|
||||||
|
nodeMap[toId].edgeIds.push_back(edge.id);
|
||||||
|
}
|
||||||
|
edgeMap[edge.id] = edge;
|
||||||
|
}
|
||||||
|
for (const auto &nodeIt: nodeMap) {
|
||||||
|
partMap[nodeIt.second.partId].nodeIds.push_back(nodeIt.first);
|
||||||
|
}
|
||||||
|
for (const auto &partIdIt: snapshot.partIdList) {
|
||||||
|
partIds.push_back(oldNewIdMap[QUuid(partIdIt)]);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const auto &nodeIt: nodeMap) {
|
||||||
|
emit nodeAdded(nodeIt.first);
|
||||||
|
}
|
||||||
|
for (const auto &edgeIt: edgeMap) {
|
||||||
|
emit edgeAdded(edgeIt.first);
|
||||||
|
}
|
||||||
|
for (const auto &partIt : partMap) {
|
||||||
|
emit partAdded(partIt.first);
|
||||||
|
}
|
||||||
|
|
||||||
|
emit partListChanged();
|
||||||
|
emit skeletonChanged();
|
||||||
|
}
|
||||||
|
|
||||||
void SkeletonDocument::fromSnapshot(const SkeletonSnapshot &snapshot)
|
void SkeletonDocument::fromSnapshot(const SkeletonSnapshot &snapshot)
|
||||||
{
|
{
|
||||||
for (const auto &nodeIt: nodeMap) {
|
for (const auto &nodeIt: nodeMap) {
|
||||||
|
@ -560,61 +656,7 @@ void SkeletonDocument::fromSnapshot(const SkeletonSnapshot &snapshot)
|
||||||
partIds.clear();
|
partIds.clear();
|
||||||
emit partListChanged();
|
emit partListChanged();
|
||||||
|
|
||||||
for (const auto &nodeKv : snapshot.nodes) {
|
addFromSnapshot(snapshot);
|
||||||
SkeletonNode node(QUuid(nodeKv.first));
|
|
||||||
node.name = valueOfKeyInMapOrEmpty(nodeKv.second, "name");
|
|
||||||
node.radius = valueOfKeyInMapOrEmpty(nodeKv.second, "radius").toFloat();
|
|
||||||
node.x = valueOfKeyInMapOrEmpty(nodeKv.second, "x").toFloat();
|
|
||||||
node.y = valueOfKeyInMapOrEmpty(nodeKv.second, "y").toFloat();
|
|
||||||
node.z = valueOfKeyInMapOrEmpty(nodeKv.second, "z").toFloat();
|
|
||||||
node.partId = QUuid(valueOfKeyInMapOrEmpty(nodeKv.second, "partId"));
|
|
||||||
node.rootMarkMode = SkeletonNodeRootMarkModeFromString(valueOfKeyInMapOrEmpty(nodeKv.second, "rootMarkMode"));
|
|
||||||
nodeMap[node.id] = node;
|
|
||||||
}
|
|
||||||
for (const auto &edgeKv : snapshot.edges) {
|
|
||||||
SkeletonEdge edge(QUuid(edgeKv.first));
|
|
||||||
edge.name = valueOfKeyInMapOrEmpty(edgeKv.second, "name");
|
|
||||||
edge.partId = QUuid(valueOfKeyInMapOrEmpty(edgeKv.second, "partId"));
|
|
||||||
edge.branchMode = SkeletonEdgeBranchModeFromString(valueOfKeyInMapOrEmpty(edgeKv.second, "branchMode"));
|
|
||||||
QString fromNodeId = valueOfKeyInMapOrEmpty(edgeKv.second, "from");
|
|
||||||
if (!fromNodeId.isEmpty()) {
|
|
||||||
edge.nodeIds.push_back(QUuid(fromNodeId));
|
|
||||||
nodeMap[QUuid(fromNodeId)].edgeIds.push_back(edge.id);
|
|
||||||
}
|
|
||||||
QString toNodeId = valueOfKeyInMapOrEmpty(edgeKv.second, "to");
|
|
||||||
if (!toNodeId.isEmpty()) {
|
|
||||||
edge.nodeIds.push_back(QUuid(toNodeId));
|
|
||||||
nodeMap[QUuid(toNodeId)].edgeIds.push_back(edge.id);
|
|
||||||
}
|
|
||||||
edgeMap[edge.id] = edge;
|
|
||||||
}
|
|
||||||
for (const auto &partKv : snapshot.parts) {
|
|
||||||
SkeletonPart part(QUuid(partKv.first));
|
|
||||||
part.name = valueOfKeyInMapOrEmpty(partKv.second, "name");
|
|
||||||
part.visible = isTrueValueString(valueOfKeyInMapOrEmpty(partKv.second, "visible"));
|
|
||||||
part.locked = isTrueValueString(valueOfKeyInMapOrEmpty(partKv.second, "locked"));
|
|
||||||
part.subdived = isTrueValueString(valueOfKeyInMapOrEmpty(partKv.second, "subdived"));
|
|
||||||
partMap[part.id] = part;
|
|
||||||
}
|
|
||||||
for (const auto &nodeIt: nodeMap) {
|
|
||||||
partMap[nodeIt.second.partId].nodeIds.push_back(nodeIt.first);
|
|
||||||
}
|
|
||||||
for (const auto &partIdIt: snapshot.partIdList) {
|
|
||||||
partIds.push_back(QUuid(partIdIt));
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const auto &nodeIt: nodeMap) {
|
|
||||||
emit nodeAdded(nodeIt.first);
|
|
||||||
}
|
|
||||||
for (const auto &edgeIt: edgeMap) {
|
|
||||||
emit edgeAdded(edgeIt.first);
|
|
||||||
}
|
|
||||||
for (const auto &partIt : partMap) {
|
|
||||||
emit partAdded(partIt.first);
|
|
||||||
}
|
|
||||||
|
|
||||||
emit partListChanged();
|
|
||||||
emit skeletonChanged();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const char *SkeletonNodeRootMarkModeToString(SkeletonNodeRootMarkMode mode)
|
const char *SkeletonNodeRootMarkModeToString(SkeletonNodeRootMarkMode mode)
|
||||||
|
@ -814,3 +856,14 @@ void SkeletonDocument::redo()
|
||||||
qDebug() << "Undo/Redo items:" << m_undoItems.size() << m_redoItems.size();
|
qDebug() << "Undo/Redo items:" << m_undoItems.size() << m_redoItems.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SkeletonDocument::paste()
|
||||||
|
{
|
||||||
|
const QClipboard *clipboard = QApplication::clipboard();
|
||||||
|
const QMimeData *mimeData = clipboard->mimeData();
|
||||||
|
if (mimeData->hasText()) {
|
||||||
|
QXmlStreamReader xmlStreamReader(mimeData->text());
|
||||||
|
SkeletonSnapshot snapshot;
|
||||||
|
loadSkeletonFromXmlStream(&snapshot, xmlStreamReader);
|
||||||
|
addFromSnapshot(snapshot);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -160,8 +160,9 @@ public:
|
||||||
std::vector<QUuid> partIds;
|
std::vector<QUuid> partIds;
|
||||||
QImage turnaround;
|
QImage turnaround;
|
||||||
SkeletonDocumentEditMode editMode;
|
SkeletonDocumentEditMode editMode;
|
||||||
void toSnapshot(SkeletonSnapshot *snapshot);
|
void toSnapshot(SkeletonSnapshot *snapshot, const std::set<QUuid> &limitNodeIds=std::set<QUuid>()) const;
|
||||||
void fromSnapshot(const SkeletonSnapshot &snapshot);
|
void fromSnapshot(const SkeletonSnapshot &snapshot);
|
||||||
|
void addFromSnapshot(const SkeletonSnapshot &snapshot);
|
||||||
const SkeletonNode *findNode(QUuid nodeId) const;
|
const SkeletonNode *findNode(QUuid nodeId) const;
|
||||||
const SkeletonEdge *findEdge(QUuid edgeId) const;
|
const SkeletonEdge *findEdge(QUuid edgeId) const;
|
||||||
const SkeletonPart *findPart(QUuid partId) const;
|
const SkeletonPart *findPart(QUuid partId) const;
|
||||||
|
@ -191,6 +192,7 @@ public slots:
|
||||||
void saveSnapshot();
|
void saveSnapshot();
|
||||||
void undo();
|
void undo();
|
||||||
void redo();
|
void redo();
|
||||||
|
void paste();
|
||||||
private:
|
private:
|
||||||
void splitPartByNode(std::vector<std::vector<QUuid>> *groups, QUuid nodeId);
|
void splitPartByNode(std::vector<std::vector<QUuid>> *groups, QUuid nodeId);
|
||||||
void joinNodeAndNeiborsToGroup(std::vector<QUuid> *group, QUuid nodeId, std::set<QUuid> *visitMap, QUuid noUseEdgeId=QUuid());
|
void joinNodeAndNeiborsToGroup(std::vector<QUuid> *group, QUuid nodeId, std::set<QUuid> *visitMap, QUuid noUseEdgeId=QUuid());
|
||||||
|
|
|
@ -193,6 +193,7 @@ SkeletonDocumentWindow::SkeletonDocumentWindow() :
|
||||||
connect(graphicsWidget, &SkeletonGraphicsWidget::groupOperationAdded, m_document, &SkeletonDocument::saveSnapshot);
|
connect(graphicsWidget, &SkeletonGraphicsWidget::groupOperationAdded, m_document, &SkeletonDocument::saveSnapshot);
|
||||||
connect(graphicsWidget, &SkeletonGraphicsWidget::undo, m_document, &SkeletonDocument::undo);
|
connect(graphicsWidget, &SkeletonGraphicsWidget::undo, m_document, &SkeletonDocument::undo);
|
||||||
connect(graphicsWidget, &SkeletonGraphicsWidget::redo, m_document, &SkeletonDocument::redo);
|
connect(graphicsWidget, &SkeletonGraphicsWidget::redo, m_document, &SkeletonDocument::redo);
|
||||||
|
connect(graphicsWidget, &SkeletonGraphicsWidget::paste, m_document, &SkeletonDocument::paste);
|
||||||
|
|
||||||
connect(m_document, &SkeletonDocument::nodeAdded, graphicsWidget, &SkeletonGraphicsWidget::nodeAdded);
|
connect(m_document, &SkeletonDocument::nodeAdded, graphicsWidget, &SkeletonGraphicsWidget::nodeAdded);
|
||||||
connect(m_document, &SkeletonDocument::nodeRemoved, graphicsWidget, &SkeletonGraphicsWidget::nodeRemoved);
|
connect(m_document, &SkeletonDocument::nodeRemoved, graphicsWidget, &SkeletonGraphicsWidget::nodeRemoved);
|
||||||
|
|
|
@ -6,9 +6,12 @@
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <QVector2D>
|
#include <QVector2D>
|
||||||
#include <QMenu>
|
#include <QMenu>
|
||||||
|
#include <QApplication>
|
||||||
|
#include <QClipboard>
|
||||||
#include "skeletongraphicswidget.h"
|
#include "skeletongraphicswidget.h"
|
||||||
#include "theme.h"
|
#include "theme.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
#include "skeletonxml.h"
|
||||||
|
|
||||||
SkeletonGraphicsWidget::SkeletonGraphicsWidget(const SkeletonDocument *document) :
|
SkeletonGraphicsWidget::SkeletonGraphicsWidget(const SkeletonDocument *document) :
|
||||||
m_document(document),
|
m_document(document),
|
||||||
|
@ -79,12 +82,28 @@ void SkeletonGraphicsWidget::showContextMenu(const QPoint &pos)
|
||||||
emit setEditMode(SkeletonDocumentEditMode::Add);
|
emit setEditMode(SkeletonDocumentEditMode::Add);
|
||||||
});
|
});
|
||||||
contextMenu.addAction(&addAction);
|
contextMenu.addAction(&addAction);
|
||||||
|
|
||||||
contextMenu.addSeparator();
|
contextMenu.addSeparator();
|
||||||
|
|
||||||
QAction deleteAction("Delete", this);
|
QAction deleteAction("Delete", this);
|
||||||
connect(&deleteAction, &QAction::triggered, this, &SkeletonGraphicsWidget::deleteSelected);
|
connect(&deleteAction, &QAction::triggered, this, &SkeletonGraphicsWidget::deleteSelected);
|
||||||
deleteAction.setEnabled(!m_rangeSelectionSet.empty());
|
deleteAction.setEnabled(!m_rangeSelectionSet.empty());
|
||||||
contextMenu.addAction(&deleteAction);
|
contextMenu.addAction(&deleteAction);
|
||||||
|
|
||||||
|
QAction cutAction("Cut", this);
|
||||||
|
connect(&cutAction, &QAction::triggered, this, &SkeletonGraphicsWidget::cut);
|
||||||
|
cutAction.setEnabled(!nodeItemMap.empty());
|
||||||
|
contextMenu.addAction(&cutAction);
|
||||||
|
|
||||||
|
QAction copyAction("Copy", this);
|
||||||
|
connect(©Action, &QAction::triggered, this, &SkeletonGraphicsWidget::copy);
|
||||||
|
copyAction.setEnabled(!nodeItemMap.empty());
|
||||||
|
contextMenu.addAction(©Action);
|
||||||
|
|
||||||
|
QAction pasteAction("Paste", this);
|
||||||
|
connect(&pasteAction, &QAction::triggered, m_document, &SkeletonDocument::paste);
|
||||||
|
contextMenu.addAction(&pasteAction);
|
||||||
|
|
||||||
contextMenu.addSeparator();
|
contextMenu.addSeparator();
|
||||||
|
|
||||||
QAction selectAllAction("Select All", this);
|
QAction selectAllAction("Select All", this);
|
||||||
|
@ -621,6 +640,18 @@ bool SkeletonGraphicsWidget::keyPress(QKeyEvent *event)
|
||||||
emit redo();
|
emit redo();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else if (event->key() == Qt::Key_X) {
|
||||||
|
if (QGuiApplication::queryKeyboardModifiers().testFlag(Qt::ControlModifier)) {
|
||||||
|
cut();
|
||||||
|
}
|
||||||
|
} else if (event->key() == Qt::Key_C) {
|
||||||
|
if (QGuiApplication::queryKeyboardModifiers().testFlag(Qt::ControlModifier)) {
|
||||||
|
copy();
|
||||||
|
}
|
||||||
|
} else if (event->key() == Qt::Key_V) {
|
||||||
|
if (QGuiApplication::queryKeyboardModifiers().testFlag(Qt::ControlModifier)) {
|
||||||
|
emit paste();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -632,6 +663,10 @@ void SkeletonGraphicsWidget::nodeAdded(QUuid nodeId)
|
||||||
qDebug() << "New node added but node id not exist:" << nodeId;
|
qDebug() << "New node added but node id not exist:" << nodeId;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (nodeItemMap.find(nodeId) != nodeItemMap.end()) {
|
||||||
|
qDebug() << "New node added but node item already exist:" << nodeId;
|
||||||
|
return;
|
||||||
|
}
|
||||||
SkeletonGraphicsNodeItem *mainProfileItem = new SkeletonGraphicsNodeItem(SkeletonProfile::Main);
|
SkeletonGraphicsNodeItem *mainProfileItem = new SkeletonGraphicsNodeItem(SkeletonProfile::Main);
|
||||||
SkeletonGraphicsNodeItem *sideProfileItem = new SkeletonGraphicsNodeItem(SkeletonProfile::Side);
|
SkeletonGraphicsNodeItem *sideProfileItem = new SkeletonGraphicsNodeItem(SkeletonProfile::Side);
|
||||||
mainProfileItem->setOrigin(scenePosFromUnified(QPointF(node->x, node->y)));
|
mainProfileItem->setOrigin(scenePosFromUnified(QPointF(node->x, node->y)));
|
||||||
|
@ -679,6 +714,10 @@ void SkeletonGraphicsWidget::edgeAdded(QUuid edgeId)
|
||||||
qDebug() << "Node not found:" << toNodeId;
|
qDebug() << "Node not found:" << toNodeId;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (edgeItemMap.find(edgeId) != edgeItemMap.end()) {
|
||||||
|
qDebug() << "New edge added but edge item already exist:" << edgeId;
|
||||||
|
return;
|
||||||
|
}
|
||||||
SkeletonGraphicsEdgeItem *mainProfileEdgeItem = new SkeletonGraphicsEdgeItem();
|
SkeletonGraphicsEdgeItem *mainProfileEdgeItem = new SkeletonGraphicsEdgeItem();
|
||||||
SkeletonGraphicsEdgeItem *sideProfileEdgeItem = new SkeletonGraphicsEdgeItem();
|
SkeletonGraphicsEdgeItem *sideProfileEdgeItem = new SkeletonGraphicsEdgeItem();
|
||||||
mainProfileEdgeItem->setId(edgeId);
|
mainProfileEdgeItem->setId(edgeId);
|
||||||
|
@ -980,3 +1019,26 @@ void SkeletonGraphicsWidget::unselectAll()
|
||||||
}
|
}
|
||||||
m_rangeSelectionSet.clear();
|
m_rangeSelectionSet.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SkeletonGraphicsWidget::cut()
|
||||||
|
{
|
||||||
|
copy();
|
||||||
|
deleteSelected();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SkeletonGraphicsWidget::copy()
|
||||||
|
{
|
||||||
|
std::set<QUuid> nodeIdSet;
|
||||||
|
std::set<QUuid> edgeIdSet;
|
||||||
|
readSkeletonNodeAndEdgeIdSetFromRangeSelection(&nodeIdSet, &edgeIdSet);
|
||||||
|
if (nodeIdSet.empty())
|
||||||
|
return;
|
||||||
|
SkeletonSnapshot snapshot;
|
||||||
|
m_document->toSnapshot(&snapshot, nodeIdSet);
|
||||||
|
QString snapshotXml;
|
||||||
|
QXmlStreamWriter xmlStreamWriter(&snapshotXml);
|
||||||
|
saveSkeletonToXmlStream(&snapshot, &xmlStreamWriter);
|
||||||
|
QClipboard *clipboard = QApplication::clipboard();
|
||||||
|
clipboard->setText(snapshotXml);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -262,6 +262,7 @@ signals:
|
||||||
void groupOperationAdded();
|
void groupOperationAdded();
|
||||||
void undo();
|
void undo();
|
||||||
void redo();
|
void redo();
|
||||||
|
void paste();
|
||||||
public:
|
public:
|
||||||
SkeletonGraphicsWidget(const SkeletonDocument *document);
|
SkeletonGraphicsWidget(const SkeletonDocument *document);
|
||||||
std::map<QUuid, std::pair<SkeletonGraphicsNodeItem *, SkeletonGraphicsNodeItem *>> nodeItemMap;
|
std::map<QUuid, std::pair<SkeletonGraphicsNodeItem *, SkeletonGraphicsNodeItem *>> nodeItemMap;
|
||||||
|
@ -301,6 +302,8 @@ public slots:
|
||||||
void selectAll();
|
void selectAll();
|
||||||
void unselectAll();
|
void unselectAll();
|
||||||
void selectPartAll();
|
void selectPartAll();
|
||||||
|
void cut();
|
||||||
|
void copy();
|
||||||
private slots:
|
private slots:
|
||||||
void turnaroundImageReady();
|
void turnaroundImageReady();
|
||||||
private:
|
private:
|
||||||
|
|
|
@ -11,6 +11,15 @@ void saveSkeletonToXmlStream(SkeletonSnapshot *snapshot, QXmlStreamWriter *write
|
||||||
writer->writeAttribute(canvasIterator->first, canvasIterator->second);
|
writer->writeAttribute(canvasIterator->first, canvasIterator->second);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
writer->writeStartElement("partIdList");
|
||||||
|
std::vector<QString>::iterator partIdIterator;
|
||||||
|
for (partIdIterator = snapshot->partIdList.begin(); partIdIterator != snapshot->partIdList.end(); partIdIterator++) {
|
||||||
|
writer->writeStartElement("partId");
|
||||||
|
writer->writeAttribute("id", *partIdIterator);
|
||||||
|
writer->writeEndElement();
|
||||||
|
}
|
||||||
|
writer->writeEndElement();
|
||||||
|
|
||||||
writer->writeStartElement("nodes");
|
writer->writeStartElement("nodes");
|
||||||
std::map<QString, std::map<QString, QString>>::iterator nodeIterator;
|
std::map<QString, std::map<QString, QString>>::iterator nodeIterator;
|
||||||
for (nodeIterator = snapshot->nodes.begin(); nodeIterator != snapshot->nodes.end(); nodeIterator++) {
|
for (nodeIterator = snapshot->nodes.begin(); nodeIterator != snapshot->nodes.end(); nodeIterator++) {
|
||||||
|
@ -34,6 +43,18 @@ void saveSkeletonToXmlStream(SkeletonSnapshot *snapshot, QXmlStreamWriter *write
|
||||||
writer->writeEndElement();
|
writer->writeEndElement();
|
||||||
}
|
}
|
||||||
writer->writeEndElement();
|
writer->writeEndElement();
|
||||||
|
|
||||||
|
writer->writeStartElement("parts");
|
||||||
|
std::map<QString, std::map<QString, QString>>::iterator partIterator;
|
||||||
|
for (partIterator = snapshot->parts.begin(); partIterator != snapshot->parts.end(); partIterator++) {
|
||||||
|
std::map<QString, QString>::iterator partAttributeIterator;
|
||||||
|
writer->writeStartElement("part");
|
||||||
|
for (partAttributeIterator = partIterator->second.begin(); partAttributeIterator != partIterator->second.end(); partAttributeIterator++) {
|
||||||
|
writer->writeAttribute(partAttributeIterator->first, partAttributeIterator->second);
|
||||||
|
}
|
||||||
|
writer->writeEndElement();
|
||||||
|
}
|
||||||
|
writer->writeEndElement();
|
||||||
writer->writeEndElement();
|
writer->writeEndElement();
|
||||||
|
|
||||||
writer->writeEndDocument();
|
writer->writeEndDocument();
|
||||||
|
@ -50,16 +71,33 @@ void loadSkeletonFromXmlStream(SkeletonSnapshot *snapshot, QXmlStreamReader &rea
|
||||||
}
|
}
|
||||||
} else if (reader.name() == "node") {
|
} else if (reader.name() == "node") {
|
||||||
QString nodeId = reader.attributes().value("id").toString();
|
QString nodeId = reader.attributes().value("id").toString();
|
||||||
|
if (nodeId.isEmpty())
|
||||||
|
continue;
|
||||||
std::map<QString, QString> *nodeMap = &snapshot->nodes[nodeId];
|
std::map<QString, QString> *nodeMap = &snapshot->nodes[nodeId];
|
||||||
foreach(const QXmlStreamAttribute &attr, reader.attributes()) {
|
foreach(const QXmlStreamAttribute &attr, reader.attributes()) {
|
||||||
(*nodeMap)[attr.name().toString()] = attr.value().toString();
|
(*nodeMap)[attr.name().toString()] = attr.value().toString();
|
||||||
}
|
}
|
||||||
} else if (reader.name() == "edge") {
|
} else if (reader.name() == "edge") {
|
||||||
QString nodeId = reader.attributes().value("id").toString();
|
QString edgeId = reader.attributes().value("id").toString();
|
||||||
std::map<QString, QString> *edgeMap = &snapshot->edges[nodeId];
|
if (edgeId.isEmpty())
|
||||||
|
continue;
|
||||||
|
std::map<QString, QString> *edgeMap = &snapshot->edges[edgeId];
|
||||||
foreach(const QXmlStreamAttribute &attr, reader.attributes()) {
|
foreach(const QXmlStreamAttribute &attr, reader.attributes()) {
|
||||||
(*edgeMap)[attr.name().toString()] = attr.value().toString();
|
(*edgeMap)[attr.name().toString()] = attr.value().toString();
|
||||||
}
|
}
|
||||||
|
} else if (reader.name() == "part") {
|
||||||
|
QString partId = reader.attributes().value("id").toString();
|
||||||
|
if (partId.isEmpty())
|
||||||
|
continue;
|
||||||
|
std::map<QString, QString> *partMap = &snapshot->parts[partId];
|
||||||
|
foreach(const QXmlStreamAttribute &attr, reader.attributes()) {
|
||||||
|
(*partMap)[attr.name().toString()] = attr.value().toString();
|
||||||
|
}
|
||||||
|
} else if (reader.name() == "partId") {
|
||||||
|
QString partId = reader.attributes().value("id").toString();
|
||||||
|
if (partId.isEmpty())
|
||||||
|
continue;
|
||||||
|
snapshot->partIdList.push_back(partId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue