refactored copy/paste code for new serializers

Vastly improves code quality and reusability
This commit is contained in:
itsmattkc
2022-01-12 16:34:40 -08:00
parent 4ecce20c35
commit 576ea8e81f
18 changed files with 346 additions and 269 deletions
+79 -116
View File
@@ -205,12 +205,30 @@ void NodeView::CopySelected(bool cut)
void NodeView::Paste()
{
PasteNodesInternal();
if (!contexts_.isEmpty()) {
PasteNodesFromClipboard();
}
}
void NodeView::Duplicate()
{
PasteNodesInternal(selected_nodes_);
if (!selected_nodes_.isEmpty()) {
Node::PositionMap map;
QVector<Node*> new_nodes;
new_nodes.resize(selected_nodes_.size());
for (int i=0; i<new_nodes.size(); i++) {
Node *og = selected_nodes_.at(i);
Node *copy = og->copy();
Node::CopyInputs(og, copy, false);
map.insert(copy, GetAssumedPositionForSelectedNode(og));
new_nodes[i] = copy;
}
Node::CopyDependencyGraph(selected_nodes_, new_nodes, nullptr);
PostPaste(new_nodes, map);
}
}
void NodeView::SetColorLabel(int index)
@@ -903,9 +921,9 @@ bool NodeView::eventFilter(QObject *object, QEvent *event)
return super::eventFilter(object, event);
}
void NodeView::CopyNodesToClipboardInternal(QXmlStreamWriter *writer, const QVector<Node *> &nodes, void *userdata)
void NodeView::CopyNodesToClipboardCallback(const QVector<Node*> &nodes, ProjectSerializer::SaveData *sdata, void *userdata)
{
writer->writeStartElement(QStringLiteral("pos"));
ProjectSerializer::SerializedProperties properties;
for (Node *n : nodes) {
NodeViewItem *item = GetAssumedItemForSelectedNode(n);
@@ -913,59 +931,31 @@ void NodeView::CopyNodesToClipboardInternal(QXmlStreamWriter *writer, const QVec
if (item) {
Node::Position pos = item->GetNodePositionData();
writer->writeStartElement(QStringLiteral("node"));
writer->writeAttribute(QStringLiteral("ptr"), QString::number(reinterpret_cast<quintptr>(n)));
writer->writeTextElement(QStringLiteral("x"), QString::number(pos.position.x()));
writer->writeTextElement(QStringLiteral("y"), QString::number(pos.position.y()));
writer->writeTextElement(QStringLiteral("expanded"), QString::number(pos.expanded));
writer->writeEndElement(); // node
properties[n][QStringLiteral("x")] = QString::number(pos.position.x());
properties[n][QStringLiteral("y")] = QString::number(pos.position.y());
properties[n][QStringLiteral("expanded")] = QString::number(pos.expanded);
}
}
writer->writeEndElement(); // pos
sdata->SetProperties(properties);
}
void NodeView::PasteNodesFromClipboardInternal(QXmlStreamReader *reader, XMLNodeData &xml_node_data, void *userdata)
void NodeView::PasteNodesToClipboardCallback(const QVector<Node *> &nodes, const ProjectSerializer::LoadData &ldata, void *userdata)
{
Node::PositionMap *map = static_cast<Node::PositionMap *>(userdata);
Node::PositionMap map;
while (XMLReadNextStartElement(reader)) {
if (reader->name() == QStringLiteral("pos")) {
while (XMLReadNextStartElement(reader)) {
if (reader->name() == QStringLiteral("node")) {
Node *n = nullptr;
Node::Position pos;
for (auto it=ldata.properties.cbegin(); it!=ldata.properties.cend(); it++) {
Node::Position pos;
XMLAttributeLoop(reader, attr) {
if (attr.name() == QStringLiteral("ptr")) {
n = xml_node_data.node_ptrs.value(attr.value().toULongLong());
break;
}
}
const QMap<QString, QString> &node_props = it.value();
pos.position.setX(node_props.value(QStringLiteral("x")).toDouble());
pos.position.setY(node_props.value(QStringLiteral("y")).toDouble());
pos.expanded = node_props.value(QStringLiteral("expanded")).toDouble();
while (XMLReadNextStartElement(reader)) {
if (reader->name() == QStringLiteral("x")) {
pos.position.setX(reader->readElementText().toDouble());
} else if (reader->name() == QStringLiteral("y")) {
pos.position.setY(reader->readElementText().toDouble());
} else if (reader->name() == QStringLiteral("expanded")) {
pos.expanded = reader->readElementText().toInt();
} else {
reader->skipCurrentElement();
}
}
if (n) {
map->insert(n, pos);
}
} else {
reader->skipCurrentElement();
}
}
} else {
reader->skipCurrentElement();
}
map.insert(it.key(), pos);
}
PostPaste(nodes, map);
}
void NodeView::changeEvent(QEvent *e)
@@ -1265,75 +1255,6 @@ void NodeView::ItemAboutToBeDeleted(NodeViewItem *item)
}
}
void NodeView::PasteNodesInternal(const QVector<Node *> &duplicate_nodes)
{
// If no graph, do nothing
if (contexts_.isEmpty()) {
return;
}
// If duplicating nodes, duplicate, otherwise paste
QVector<Node*> new_nodes;
Node::PositionMap map;
if (duplicate_nodes.isEmpty()) {
new_nodes = PasteNodesFromClipboard(nullptr, nullptr, &map);
} else {
new_nodes.resize(selected_nodes_.size());
for (int i=0; i<new_nodes.size(); i++) {
Node *og = selected_nodes_.at(i);
Node *copy = og->copy();
Node::CopyInputs(og, copy, false);
map.insert(copy, GetAssumedPositionForSelectedNode(og));
new_nodes[i] = copy;
}
Node::CopyDependencyGraph(selected_nodes_, new_nodes, nullptr);
}
// If no nodes were retrieved, do nothing
if (!new_nodes.isEmpty()) {
QVector<AttachedItem> new_attached;
NodeViewItem *first_item = nullptr;
for (int i=0; i<new_nodes.size(); i++) {
Node *node = new_nodes.at(i);
// Determine if item had a position, if not don't create an item for it
NodeViewItem *new_item;
if (map.contains(node)) {
new_item = new NodeViewItem(node, nullptr);
new_item->SetFlowDirection(scene_.GetFlowDirection());
new_item->SetNodePosition(map.value(node));
scene_.addItem(new_item);
if (!first_item) {
first_item = new_item;
}
} else {
new_item = nullptr;
}
new_attached.append({new_item, node, QPointF(0, 0)});
}
// Correct positions
if (first_item) {
for (int i=0; i<new_attached.size(); i++) {
AttachedItem &ai = new_attached[i];
if (ai.item) {
ai.original_pos = first_item->pos() - ai.item->pos();
}
}
}
SetAttachedItems(new_attached);
}
}
void NodeView::AddContext(Node *n)
{
NodeViewContext *ctx = scene_.AddContext(n);
@@ -1449,6 +1370,48 @@ void NodeView::EndEdgeDrag(bool cancel)
Core::instance()->undo_stack()->pushIfHasChildren(command);
}
void NodeView::PostPaste(const QVector<Node *> &new_nodes, const Node::PositionMap &map)
{
QVector<AttachedItem> new_attached;
NodeViewItem *first_item = nullptr;
for (int i=0; i<new_nodes.size(); i++) {
Node *node = new_nodes.at(i);
// Determine if item had a position, if not don't create an item for it
NodeViewItem *new_item;
if (map.contains(node)) {
new_item = new NodeViewItem(node, nullptr);
new_item->SetFlowDirection(scene_.GetFlowDirection());
new_item->SetNodePosition(map.value(node));
scene_.addItem(new_item);
if (!first_item) {
first_item = new_item;
}
} else {
new_item = nullptr;
}
new_attached.append({new_item, node, QPointF(0, 0)});
}
// Correct positions
if (first_item) {
for (int i=0; i<new_attached.size(); i++) {
AttachedItem &ai = new_attached[i];
if (ai.item) {
ai.original_pos = first_item->pos() - ai.item->pos();
}
}
}
SetAttachedItems(new_attached);
}
void NodeView::SetAttachedItems(const QVector<AttachedItem> &items)
{
// Detach anything currently attached
+4 -4
View File
@@ -127,8 +127,8 @@ protected:
virtual bool eventFilter(QObject *object, QEvent *event) override;
virtual void CopyNodesToClipboardInternal(QXmlStreamWriter *writer, const QVector<Node*> &nodes, void* userdata) override;
virtual void PasteNodesFromClipboardInternal(QXmlStreamReader *reader, XMLNodeData &xml_node_data, void* userdata) override;
virtual void CopyNodesToClipboardCallback(const QVector<Node *> &nodes, ProjectSerializer::SaveData *data, void* userdata) override;
virtual void PasteNodesToClipboardCallback(const QVector<Node*> &nodes, const ProjectSerializer::LoadData &ldata, void *userdata) override;
virtual void changeEvent(QEvent *e) override;
@@ -155,8 +155,6 @@ private:
void PositionNewEdge(const QPoint &pos);
void PasteNodesInternal(const QVector<Node*> &duplicate_nodes = QVector<Node *>());
void AddContext(Node *n);
void RemoveContext(Node *n);
@@ -169,6 +167,8 @@ private:
void EndEdgeDrag(bool cancel = false);
void PostPaste(const QVector<Node*> &new_nodes, const Node::PositionMap &map);
NodeViewMiniMap *minimap_;
struct AttachedItem {
+39 -57
View File
@@ -300,55 +300,64 @@ void TimelineWidget::DisconnectNodeEvent(ViewerOutput *n)
}
}
void TimelineWidget::CopyNodesToClipboardInternal(QXmlStreamWriter *writer, const QVector<Node *> &nodes, void* userdata)
void TimelineWidget::CopyNodesToClipboardCallback(const QVector<Node *> &nodes, ProjectSerializer::SaveData *sdata, void *userdata)
{
// Cache the earliest in point so all copied clips have a "relative" in point that can be pasted anywhere
QVector<Block*>& selected = *static_cast<QVector<Block*>*>(userdata);
rational earliest_in = RATIONAL_MAX;
ProjectSerializer::SerializedProperties properties;
foreach (Block* block, selected) {
earliest_in = qMin(earliest_in, block->in());
}
foreach (Block* block, selected) {
writer->writeStartElement(QStringLiteral("block"));
writer->writeAttribute(QStringLiteral("ptr"), QString::number(reinterpret_cast<quintptr>(block)));
writer->writeAttribute(QStringLiteral("in"), (block->in() - earliest_in).toString());
Track* track = block->track();
writer->writeAttribute(QStringLiteral("tracktype"), QString::number(track->type()));
writer->writeAttribute(QStringLiteral("trackindex"), QString::number(track->Index()));
writer->writeEndElement();
properties[block][QStringLiteral("in")] = (block->in() - earliest_in).toString();
properties[block][QStringLiteral("track")] = block->track()->ToReference().ToString();
}
sdata->SetProperties(properties);
}
void TimelineWidget::PasteNodesFromClipboardInternal(QXmlStreamReader *reader, XMLNodeData& xml_node_data, void *userdata)
void TimelineWidget::PasteNodesToClipboardCallback(const QVector<Node *> &nodes, const ProjectSerializer::LoadData &load_data, void *userdata)
{
QVector<BlockPasteData>& paste_data = *static_cast<QVector<BlockPasteData>*>(userdata);
bool insert = *(bool*)userdata;
while (XMLReadNextStartElement(reader)) {
if (reader->name() == QStringLiteral("block")) {
BlockPasteData bpd;
MultiUndoCommand *command = new MultiUndoCommand();
foreach (QXmlStreamAttribute attr, reader->attributes()) {
if (attr.name() == QStringLiteral("ptr")) {
bpd.block = static_cast<Block*>(xml_node_data.node_ptrs.value(attr.value().toULongLong()));
} else if (attr.name() == QStringLiteral("in")) {
bpd.in = rational::fromString(attr.value().toString());
} else if (attr.name() == QStringLiteral("tracktype")) {
bpd.track_type = static_cast<Track::Type>(attr.value().toInt());
} else if (attr.name() == QStringLiteral("trackindex")) {
bpd.track_index = attr.value().toInt();
}
}
foreach (Node *n, nodes) {
command->add_child(new NodeAddCommand(GetConnectedNode()->project(), n));
}
paste_data.append(bpd);
rational paste_start = GetTime();
reader->skipCurrentElement();
if (insert) {
rational paste_end = GetTime();
for (auto it=load_data.properties.cbegin(); it!=load_data.properties.cend(); it++) {
rational length = static_cast<Block*>(it.key())->length();
rational in = rational::fromString(it.value()[QStringLiteral("in")]);
paste_end = qMax(paste_end, paste_start + in + length);
}
if (paste_end != paste_start) {
InsertGapsAt(paste_start, paste_end - paste_start, command);
}
}
for (auto it=load_data.properties.cbegin(); it!=load_data.properties.cend(); it++) {
Block *block = static_cast<Block*>(it.key());
rational in = rational::fromString(it.value()[QStringLiteral("in")]);
Track::Reference track = Track::Reference::FromString(it.value()[QStringLiteral("track")]);
command->add_child(new TrackPlaceBlockCommand(sequence()->track_list(track.type()),
track.index(),
block,
paste_start + in));
}
Core::instance()->undo_stack()->pushIfHasChildren(command);
}
void TimelineWidget::SelectAll()
@@ -626,34 +635,7 @@ void TimelineWidget::Paste(bool insert)
return;
}
MultiUndoCommand* command = new MultiUndoCommand();
QVector<BlockPasteData> paste_data;
QVector<Node*> pasted = PasteNodesFromClipboard(GetConnectedNode()->project(), command, &paste_data);
rational paste_start = GetTime();
if (insert) {
rational paste_end = GetTime();
foreach (const BlockPasteData& bpd, paste_data) {
paste_end = qMax(paste_end, paste_start + bpd.in + bpd.block->length());
}
if (paste_end != paste_start) {
InsertGapsAt(paste_start, paste_end - paste_start, command);
}
}
foreach (const BlockPasteData& bpd, paste_data) {
qDebug() << "Placing" << bpd.block;
command->add_child(new TrackPlaceBlockCommand(sequence()->track_list(bpd.track_type),
bpd.track_index,
bpd.block,
paste_start + bpd.in));
}
Core::instance()->undo_stack()->pushIfHasChildren(command);
PasteNodesFromClipboard(&insert);
}
void TimelineWidget::DeleteInToOut(bool ripple)
+2 -9
View File
@@ -272,15 +272,8 @@ protected:
virtual void ConnectNodeEvent(ViewerOutput* n) override;
virtual void DisconnectNodeEvent(ViewerOutput* n) override;
virtual void CopyNodesToClipboardInternal(QXmlStreamWriter *writer, const QVector<Node*> &nodes, void* userdata) override;
virtual void PasteNodesFromClipboardInternal(QXmlStreamReader *reader, XMLNodeData &xml_node_data, void* userdata) override;
struct BlockPasteData {
Block* block;
rational in;
Track::Type track_type;
int track_index;
};
virtual void CopyNodesToClipboardCallback(const QVector<Node*> &nodes, ProjectSerializer::SaveData *data, void *userdata) override;
virtual void PasteNodesToClipboardCallback(const QVector<Node*> &nodes, const ProjectSerializer::LoadData &load_data, void *userdata) override;
private:
QVector<Timeline::EditToInfo> GetEditToInfo(const rational &playhead_time, Timeline::MovementMode mode);