Skip to content

Commit da07fd0

Browse files
committed
Copy&Paste in Model Builder #48
1 parent bc4dbe4 commit da07fd0

File tree

3 files changed

+79
-22
lines changed

3 files changed

+79
-22
lines changed

CHANGELOG.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
66

77
## [Unreleased]
88

9+
### Added
10+
11+
- `ModelBuilder`:
12+
- Duplicate single blocks via button
13+
- Copy&Paste multiple blocks (without edges)
14+
915
## [2.13.0] - 2022-12-08
1016

1117
### Added
@@ -47,7 +53,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
4753

4854
### Fixed
4955

50-
- Better error messages for copy/paste
56+
- Better error messages for copy&paste
5157
- Close callbacks get fired for tabs
5258

5359
## [2.10.2] - 2022-08-29

components/ModelBuilder.vue

Lines changed: 57 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -314,6 +314,7 @@ export default {
314314
if (!this.supports('editArguments')) {
315315
this.$on('editArguments', (...args) => this.showParameterViewer(...args));
316316
}
317+
this.$on('duplicate', this.duplicate.bind(this));
317318
},
318319
async mounted() {
319320
Utils.loadFontAwesome(this);
@@ -442,6 +443,9 @@ export default {
442443
let refs = PgUtils.getRefs(value, true, true).filter(ref => typeof ref.from_parameter !== 'undefined');
443444
for(let ref of refs) {
444445
try {
446+
if(!process.$el) {
447+
continue;
448+
}
445449
if (process.$el.isParameterScoped(argName, ref.from_parameter)) {
446450
continue; // Skip if the parameter usage is scoped (i.e. defined as process parameetr for the children)
447451
}
@@ -598,7 +602,20 @@ export default {
598602
return;
599603
}
600604
if (this.hasSelection && this.clipboard) {
601-
return; // ToDo: Implement pasting for selected blocks
605+
if (this.clipboard.edges.length > 0) {
606+
this.$emit('error', 'Pasting edges is not supported yet.');
607+
return;
608+
}
609+
if (this.clipboard.blocks.length > 0) {
610+
this.clipboard.blocks.forEach(block => {
611+
if (block.type === 'process' && block.origin !== 'schema') {
612+
this.duplicate(Utils.pickFromObject(block, ["arguments", "description", "namespace", "position", "process_id"]));
613+
}
614+
else {
615+
this.$emit('error', `Pasting block '${block.id}' is not supported.`);
616+
}
617+
});
618+
}
602619
}
603620
else {
604621
try {
@@ -876,6 +893,15 @@ export default {
876893
return [x, y];
877894
},
878895
896+
duplicate(data) {
897+
data = Utils.deepClone(data);
898+
if (data.position) {
899+
data.position[1] += 100;
900+
}
901+
let block = this.addBlock(data);
902+
this.$nextTick(() => this.createEdgesForArguments(block.id, data.arguments));
903+
},
904+
879905
addProcess(process_id, args = {}, position = [], namespace = null) {
880906
return this.addBlock({
881907
process_id,
@@ -1380,44 +1406,54 @@ export default {
13801406
13811407
async importEdges(pg) {
13821408
var nodes = pg.getNodes();
1383-
for(var node of Object.values(nodes)) {
1384-
var args = node.getArgumentNames();
1385-
for(let i in args) {
1386-
var val = node.getRawArgument(args[i]);
1387-
switch(node.getArgumentType(args[i])) {
1388-
case 'result':
1389-
await this.addEdgeByNames('#' + pg.getNode(val.from_node).id, "output", '#' + node.id, args[i], false);
1390-
break;
1391-
case 'parameter':
1392-
await this.addEdgeByNames('$' + val.from_parameter, "output", '#' + node.id, args[i], false);
1393-
break;
1394-
case 'object':
1395-
case 'array':
1396-
await this.importEdgeDeep(val, pg, node, args, i);
1397-
break;
1398-
}
1409+
return Promise.all(Object.values(nodes).map(node => this.importEdgesForNode(node)));
1410+
},
1411+
1412+
async importEdgesForNode(node) {
1413+
var args = node.getArgumentNames();
1414+
for(let i in args) {
1415+
let arg = args[i];
1416+
let val = node.getRawArgument(arg);
1417+
let ref = '#' + node.id;
1418+
switch(node.getArgumentType(arg)) {
1419+
case 'result':
1420+
await this.addEdgeByNames('#' + val.from_node, "output", ref, arg, false);
1421+
break;
1422+
case 'parameter':
1423+
await this.addEdgeByNames('$' + val.from_parameter, "output", ref, arg, false);
1424+
break;
1425+
case 'object':
1426+
case 'array':
1427+
await this.importEdgeDeep(val, ref, arg);
1428+
break;
13991429
}
14001430
}
14011431
},
14021432
1403-
async importEdgeDeep(val, pg, node, args, i) {
1433+
async importEdgeDeep(val, nodeId, arg) {
14041434
for(let k in val) {
14051435
// k !== 'process_graph' prevents importing sub process graphs like in load_collection, see #118
14061436
if(val[k] && typeof val[k] === "object" && k !== 'process_graph') {
1407-
await this.importEdgeDeep(val[k], pg, node, args, i);
1437+
await this.importEdgeDeep(val[k], nodeId, arg);
14081438
}
14091439
else if (!Utils.isRef(val)) {
14101440
continue;
14111441
}
14121442
else if (val.from_node) {
1413-
await this.addEdgeByNames('#' + pg.getNode(val.from_node).id, "output", '#' + node.id, args[i], false);
1443+
await this.addEdgeByNames('#' + val.from_node, "output", nodeId, arg, false);
14141444
}
14151445
else if (val.from_parameter) {
1416-
await this.addEdgeByNames('$' + val.from_parameter, "output", '#' + node.id, args[i], false);
1446+
await this.addEdgeByNames('$' + val.from_parameter, "output", nodeId, arg, false);
14171447
}
14181448
}
14191449
},
14201450
1451+
async createEdgesForArguments(nodeId, args) {
1452+
for(let arg in args) {
1453+
await this.importEdgeDeep(args[arg], nodeId, arg);
1454+
}
1455+
},
1456+
14211457
async importNodes(nodes, x = 0, y = 0, imported = []) {
14221458
let nextNodes = [];
14231459
let maxX = 0;

components/model-builder/Block.vue

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@
1212
<span v-show="allowsDescription && !showDescriptionField" class="addDescription" title="Add description" @click.stop.prevent="addDescription()">
1313
<i class="fas fa-comment-medical"></i>
1414
</span>
15+
<span v-show="allowsDuplicate" class="duplicate" title="Duplicate" @click.stop.prevent="duplicate">
16+
<i class="fas fa-clone"></i>
17+
</span>
1518
<span v-show="allowsDelete" class="delete" title="Remove (DEL)" @click.stop.prevent="remove()">
1619
<i class="fas fa-trash"></i>
1720
</span>
@@ -261,6 +264,9 @@ export default {
261264
return this.parameters.filter(p => p.isEditable()).length > 0;
262265
}
263266
},
267+
allowsDuplicate() {
268+
return (this.state.editable && this.type === 'process' && this.origin !== 'schema');
269+
},
264270
allowsDelete() {
265271
return (this.state.editable && (!this.spec || (Utils.isObject(this.spec) && this.origin !== 'schema')));
266272
},
@@ -365,6 +371,15 @@ export default {
365371
this.showArguments();
366372
}
367373
},
374+
duplicate() {
375+
this.$parent.$emit('duplicate', {
376+
position: this.position,
377+
process_id: this.process_id,
378+
namespace: this.namespace,
379+
arguments: this.args,
380+
description: this.description
381+
});
382+
},
368383
hasParameter(name) {
369384
return this.hasParametersDefined && !!this.spec.parameters.find(p => p.name === name);
370385
},

0 commit comments

Comments
 (0)