Skip to content

Commit 7bb81ce

Browse files
authored
Merge pull request #477 from brmodeloweb/feature/copy-and-past
Feature batch actions: copy, past and delete
2 parents eb8fa7f + 5cbf2f6 commit 7bb81ce

File tree

9 files changed

+132
-21
lines changed

9 files changed

+132
-21
lines changed

app/angular/conceptual/conceptual.js

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,7 @@ const controller = function (ModelAPI, $stateParams, $rootScope, $timeout, $uibM
157157

158158
ctrl.onSelectElement = (cellView) => {
159159
if (cellView != null) {
160+
configs.elementSelector.cancel();
160161
$timeout(() => {
161162
const elementType = cellView.model.isLink() ? "Link" : cellView.model.attributes.supertype;
162163
ctrl.selectedElement = {
@@ -352,7 +353,7 @@ const controller = function (ModelAPI, $stateParams, $rootScope, $timeout, $uibM
352353
} else {
353354
configs.editorScroller.startPanning(evt);
354355
}
355-
configs.editorActions.setCopyContext(evt);
356+
configs.elementSelector.setCopyContext(evt);
356357
});
357358

358359
paper.on('link:options', (cellView) => {
@@ -402,9 +403,9 @@ const controller = function (ModelAPI, $stateParams, $rootScope, $timeout, $uibM
402403
configs.keyboardController.registerHandler(types.ZOOM_OUT, () => ctrl.zoomOut());
403404
configs.keyboardController.registerHandler(types.ZOOM_NONE, () => ctrl.zoomNone());
404405
configs.keyboardController.registerHandler(types.ESC, () => ctrl.unselectAll());
405-
configs.keyboardController.registerHandler(types.COPY, () => configs.editorActions.copyElement(ctrl.selectedElement.element));
406-
configs.keyboardController.registerHandler(types.PASTE, () => configs.editorActions.pasteElement());
407-
configs.keyboardController.registerHandler(types.DELETE, () => configs.selectedElementActions?.removeElement() );
406+
configs.keyboardController.registerHandler(types.COPY, () => configs.elementSelector.copyAll());
407+
configs.keyboardController.registerHandler(types.PASTE, () => configs.elementSelector.pasteAll());
408+
configs.keyboardController.registerHandler(types.DELETE, () => configs.elementSelector.deleteAll() );
408409
}
409410

410411
const registerGraphEvents = (graph) => {

app/angular/conceptual/sidebarControl.html

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
</div>
1414
</div><!-- End .form-group -->
1515

16-
<div class="form-group" ng-if="($ctrl.configuration.entity)">
16+
<div class="form-group" ng-if="($ctrl.configuration.entity && $ctrl.visible)">
1717
<div class="form-group">
1818
<label for="entry-name">{{ 'Name' | translate }}</label>
1919
<input id="entry-name" type="text" class="form-control" data-ng-model="$ctrl.selectedElement.value"
@@ -35,7 +35,7 @@
3535
</div>
3636
</div><!-- End .form-group -->
3737

38-
<div class="form-group" ng-if="($ctrl.configuration.link)">
38+
<div class="form-group" ng-if="($ctrl.configuration.link && $ctrl.visible)">
3939
<div class="form-group clearfix">
4040
<label for="">{{ 'Cardinality' | translate }}</label>
4141
<dropdown on-select="$ctrl.updateCardinality(selected)"
@@ -60,7 +60,7 @@
6060
</div><!-- End .form-group -->
6161
</div><!-- End .form-group -->
6262

63-
<div class="form-group" ng-if="($ctrl.configuration.extension)">
63+
<div class="form-group" ng-if="($ctrl.configuration.extension && $ctrl.visible)">
6464
<div class="form-group clearfix">
6565
<label for="">{{ 'Edit' | translate }}</label>
6666
<dropdown on-select="$ctrl.updateName(selected.type)"
@@ -70,7 +70,7 @@
7070
</div>
7171
</div><!-- End .form-group -->
7272

73-
<div class="form-group" ng-if="($ctrl.configuration.attribute)">
73+
<div class="form-group" ng-if="($ctrl.configuration.attribute && $ctrl.visible)">
7474
<div class="form-group">
7575
<label for="entry-name">{{ 'Name' | translate }}</label>
7676
<input id="entry-name" type="text" class="form-control" data-ng-model="$ctrl.selectedElement.value.name"
@@ -95,13 +95,13 @@
9595
</div>
9696
</div><!-- End .form-group -->
9797

98-
<div class="form-group" ng-if="($ctrl.configuration.key)">
98+
<div class="form-group" ng-if="($ctrl.configuration.key && $ctrl.visible)">
9999
<label for="entry-name">{{ 'Name' | translate }}</label>
100100
<input id="entry-name" type="text" class="form-control" data-ng-model="$ctrl.selectedElement.value"
101101
ng-change="$ctrl.updateName($ctrl.selectedElement.value)" autofocus/>
102102
</div><!-- End .form-group -->
103103

104-
<div class="form-group" ng-if="($ctrl.configuration.relationship)">
104+
<div class="form-group" ng-if="($ctrl.configuration.relationship && $ctrl.visible)">
105105
<div class="form-group">
106106
<label for="entry-name">{{ 'Name' | translate }}</label>
107107
<input id="entry-name" type="text" class="form-control" data-ng-model="$ctrl.selectedElement.value"

app/angular/editor/elementSelector.js

Lines changed: 91 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,21 @@ joint.ui.ElementSelector = Backbone.View.extend({
2121
...a.paper.model,
2222
...this.options,
2323
graph: a.paper.model,
24+
copyContext: {
25+
clones: [],
26+
event: null
27+
}
2428
};
2529

2630
this.start = this.start.bind(this);
2731
this.stop = this.stop.bind(this);
2832
this.adjust = this.adjust.bind(this);
2933
this.pointerup = this.pointerup.bind(this);
34+
this.deleteAll = this.deleteAll.bind(this);
35+
this.copyAll = this.copyAll.bind(this);
36+
this.pasteAll = this.pasteAll.bind(this);
37+
this.cancel = this.cancel.bind(this);
38+
this.getSelectedElements = this.getSelectedElements.bind(this);
3039

3140
$(document.body).on("mousemove.selectionView touchmove.selectionView", this.adjust);
3241
$(document).on("mouseup.selectionView touchend.selectionView", this.pointerup);
@@ -53,6 +62,84 @@ joint.ui.ElementSelector = Backbone.View.extend({
5362

5463
this.trigger("selection-box:pointerdown", normalizedEvent);
5564
},
65+
deleteAll: function() {
66+
if(this.model.length > 0) {
67+
this.options.graph.trigger("batch:start");
68+
this.model.forEach(model => model.remove());
69+
this.options.graph.trigger("batch:stop");
70+
this.cancel();
71+
}
72+
},
73+
copyAll: function() {
74+
if(this.model.length > 0) {
75+
this.options.copyContext.clones = this.options.graph.cloneSubgraph(this.model.models, {
76+
deep: true,
77+
});
78+
}
79+
},
80+
setCopyContext: function (event) {
81+
if(event.type === "mousedown") {
82+
const normalizedEvent = joint.util.normalizeEvent(event);
83+
let localPoint = this.options.paper.clientToLocalPoint({ x: normalizedEvent.clientX, y: normalizedEvent.clientY });
84+
this.options.copyContext.event = {
85+
"x": localPoint.x,
86+
"y": localPoint.y
87+
}
88+
}
89+
},
90+
pasteAll: function() {
91+
const options = Object.assign({}, this.defaults, options);
92+
const graph = this.options.graph;
93+
const pastePoint = this.options.copyContext.event;
94+
const copiedCells = this.options.copyContext.clones;
95+
const origin = this.findbBox(Object.values(copiedCells));
96+
if (origin) {
97+
const originX = origin.x == 0 ? pastePoint.x : (pastePoint.x - origin.x);
98+
const originY = origin.y == 0 ? pastePoint.y : (pastePoint.y - origin.y);
99+
const translation = {
100+
dx: originX,
101+
dy: originY
102+
};
103+
options.translate = translation;
104+
let zIndex = graph.maxZIndex();
105+
const context = this;
106+
const modifiedCells = Object.values(copiedCells).map(cell => {
107+
return context.moveCell(cell, options, zIndex += 1);
108+
});
109+
graph.startBatch("paste");
110+
graph.addCells(modifiedCells);
111+
graph.stopBatch("paste");
112+
this.options.copyContext = {
113+
clones: [],
114+
event: null
115+
}
116+
}
117+
},
118+
findbBox: function(cells, opt){
119+
return joint.util.toArray(cells).reduce(function(memo, cell) {
120+
if (cell.isLink()) return memo;
121+
let rect = cell.getBBox(opt);
122+
const angle = cell.angle();
123+
if (angle) rect = rect.bbox(angle);
124+
if (memo) {
125+
return memo.union(rect);
126+
} else {
127+
return rect;
128+
}
129+
}, null);
130+
},
131+
moveCell: function(cell, options, zIndex) {
132+
cell.set("z", zIndex);
133+
if (cell.isLink() && options.link) {
134+
cell.set(options.link);
135+
}
136+
if (options.translate) {
137+
const { dx, dy } = options.translate;
138+
cell.translate(Number.isFinite(dx) ? dx : 0, Number.isFinite(dy) ? dy : 0);
139+
}
140+
cell.collection = null;
141+
return cell;
142+
},
56143
start: function(event) {
57144
const normalizedEvent = joint.util.normalizeEvent(event);
58145
this.cancel();
@@ -275,5 +362,8 @@ joint.ui.ElementSelector = Backbone.View.extend({
275362
var d = Array.prototype.slice.call(arguments, 2);
276363
d.unshift("action:" + a + ":" + b);
277364
this.trigger.apply(this, d);
278-
}
365+
},
366+
getSelectedElements: function() {
367+
return this.model.models;
368+
}
279369
});

app/angular/logic/logic.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ <h2 class="h4 pull-left">{{ 'Logical model of:' | translate }} {{$ctrl.model.nam
4747
<li class="divider"><a data-ng-click="$ctrl.print()" title="{{ 'Print (CTRL P)' | translate }}"><i class="fa fa-print"></i></a></li>
4848
</ul>
4949
<aside class="feedback">
50-
<div class="alert" role="alert" data-ng-class="{'hide': !$ctrl.feedback.showing, 'alert-danger': $ctrl.feedback.type=='error', 'alert-success': $ctrl.feedback.type=='success'}">
50+
<div class="alert" role="alert" data-ng-class="{'hide': !$ctrl.feedback.showing, 'alert-danger': $ctrl.feedback.type=='error', 'alert-success': $ctrl.feedback.type=='success', 'alert-warning': $ctrl.feedback.type=='warning'}">
5151
<p>{{ $ctrl.feedback.message }}</p>
5252
</div>
5353
</aside>

app/angular/logic/logic.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,12 @@ const controller = function (
154154
}
155155
});
156156

157+
$rootScope.$on("model:warning-copy", function () {
158+
$timeout(() => {
159+
ctrl.showFeedback("Copy is not allowed on this module when element has references.", true, "warning");
160+
});
161+
});
162+
157163
ctrl.updateCardA = function (card) {
158164
LogicService.editCardinalityA(card);
159165
}

app/angular/logic/sidebarControl.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
</div><!-- End .form-group -->
1414
</div>
1515

16-
<div class="properties-content" ng-if="$ctrl.selectedElement != null && $ctrl.selectedType === 'uml.Class'">
16+
<div class="properties-content" ng-if="$ctrl.selectedElement != null && $ctrl.selectedType === 'uml.Class' && $ctrl.visible">
1717
<section class="sidebar-panel">
1818
<header class="panel-header" ng-click="$ctrl.toggleSection('tableProperties')">
1919
<h3>{{ 'Table properties' | translate }}</h3>

app/angular/service/logicService.js

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ import uml from "../../joint/table";
99
joint.shapes.erd = erd;
1010
joint.shapes.uml = uml;
1111
import "jointjs/dist/joint.min.css";
12-
1312
import "../editor/editorManager"
1413
import "../editor/editorScroller"
1514
import "../editor/editorActions"
@@ -185,7 +184,7 @@ const logicService = ($rootScope, ModelAPI, LogicFactory, LogicConversorService)
185184
ls.editorScroller.startPanning(evt);
186185
}
187186

188-
ls.editorActions.setCopyContext(evt);
187+
ls.elementSelector.setCopyContext(evt);
189188
});
190189

191190
ls.getConnectionType = link => {
@@ -352,6 +351,7 @@ const logicService = ($rootScope, ModelAPI, LogicFactory, LogicConversorService)
352351
var name = "";
353352
if (ls.selectedElement.model != null) ls.selectedElement.unhighlight();
354353
if (cellView.model.attributes.name != null) {
354+
ls.elementSelector.cancel();
355355
ls.selectedElement = cellView;
356356
name = ls.selectedElement.model.attributes.name;
357357
ls.selectedElement.highlight();
@@ -473,6 +473,18 @@ const logicService = ($rootScope, ModelAPI, LogicFactory, LogicConversorService)
473473
ls.editorScroller.zoom();
474474
}
475475

476+
ls.copySelectedElements = function () {
477+
const elements = ls.elementSelector.getSelectedElements();
478+
const hasConnections = elements.some(element => {
479+
return element.attributes.objects.some(attr => attr.FK);
480+
});
481+
if(hasConnections) {
482+
$rootScope.$broadcast('model:warning-copy');
483+
} else {
484+
ls.elementSelector.copyAll();
485+
}
486+
}
487+
476488
ls.registerShortcuts = () => {
477489
ls.keyboardController.registerHandler(types.UNDO, () => ls.undo());
478490
ls.keyboardController.registerHandler(types.REDO, () => ls.redo());
@@ -482,11 +494,11 @@ const logicService = ($rootScope, ModelAPI, LogicFactory, LogicConversorService)
482494
ls.keyboardController.registerHandler(types.ESC, () => ls.clearSelectedElement());
483495
ls.keyboardController.registerHandler(types.SAVE, () => {
484496
ls.updateModel();
485-
$rootScope.$broadcast('model:saved')
497+
$rootScope.$broadcast('model:saved');
486498
});
487-
ls.keyboardController.registerHandler(types.COPY, () => ls.editorActions.copyElement(ls.selectedElement));
488-
ls.keyboardController.registerHandler(types.PASTE, () => ls.editorActions.pasteElement());
489-
ls.keyboardController.registerHandler(types.DELETE, () => ls.selectedActions?.removeElement() );
499+
ls.keyboardController.registerHandler(types.COPY, () => ls.copySelectedElements());
500+
ls.keyboardController.registerHandler(types.PASTE, () => ls.elementSelector.pasteAll());
501+
ls.keyboardController.registerHandler(types.DELETE, () => ls.elementSelector.deleteAll());
490502
}
491503

492504
ls.getTablesMap = function () {

app/i18n/languages/en.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -157,5 +157,6 @@ export default {
157157
'Delete account model error': 'It was not possible to delete your account because you have models. Please remove all your models before deleting your account.',
158158
'Forbidden access': 'Forbidden access',
159159
'Looks like this model does not exist or you don\'t have access to it.': 'Looks like this model does not exist or you don\'t have access to it.',
160-
'Back to models list': 'Back to models list'
160+
'Back to models list': 'Back to models list',
161+
'Copy is not allowed on this module when element has references.': 'Copy is not allowed on this module when element has references.'
161162
};

app/i18n/languages/pt_BR.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -157,5 +157,6 @@ export default {
157157
'Delete account model error': 'Não foi possível deletar sua conta pois você possui modelos. Remova-os antes de prosseguir.',
158158
'Forbidden access': 'Acesso proibido',
159159
'Looks like this model does not exist or you don\'t have access to it.': 'Parece que este modelo não existe ou você não tem acesso a ele.',
160-
'Back to models list': 'Voltar à lista de modelos'
160+
'Back to models list': 'Voltar à lista de modelos',
161+
'Copy is not allowed on this module when element has references.': 'Copiar não é permitido nesse módulo quando elementos já estiverem conectados.'
161162
};

0 commit comments

Comments
 (0)