diff --git a/ui/src/app/locale/locale.constant.js b/ui/src/app/locale/locale.constant.js index bd5858f688..c515e3e630 100644 --- a/ui/src/app/locale/locale.constant.js +++ b/ui/src/app/locale/locale.constant.js @@ -1208,6 +1208,7 @@ export default angular.module('thingsboard.locale', []) "delete-selected-objects": "Delete selected nodes and connections", "delete-selected": "Delete selected", "select-all": "Select all", + "copy-selected": "Copy selected", "deselect-all": "Deselect all", "rulenode-details": "Rule node details", "debug-mode": "Debug mode", diff --git a/ui/src/app/rulechain/rulechain.controller.js b/ui/src/app/rulechain/rulechain.controller.js index f6572163dd..d77d644577 100644 --- a/ui/src/app/rulechain/rulechain.controller.js +++ b/ui/src/app/rulechain/rulechain.controller.js @@ -29,7 +29,7 @@ import addRuleNodeLinkTemplate from './add-link.tpl.html'; /*@ngInject*/ export function RuleChainController($state, $scope, $compile, $q, $mdUtil, $timeout, $mdExpansionPanel, $window, $document, $mdDialog, - $filter, $translate, hotkeys, types, ruleChainService, Modelfactory, flowchartConstants, + $filter, $translate, hotkeys, types, ruleChainService, itembuffer, Modelfactory, flowchartConstants, ruleChain, ruleChainMetaData, ruleNodeComponents) { var vm = this; @@ -140,6 +140,22 @@ export function RuleChainController($state, $scope, $compile, $q, $mdUtil, $time subtitle: $translate.instant('rulechain.rulechain') }; contextInfo.items = []; + contextInfo.items.push( + { + action: function ($event) { + pasteRuleNodes($event); + }, + enabled: itembuffer.hasRuleNodes(), + value: "action.paste", + icon: "content_paste", + shortcut: "M-V" + } + ); + contextInfo.items.push( + { + divider: true + } + ); if (objectsSelected()) { contextInfo.items.push( { @@ -152,6 +168,17 @@ export function RuleChainController($state, $scope, $compile, $q, $mdUtil, $time shortcut: "Esc" } ); + contextInfo.items.push( + { + action: function (event) { + copyRuleNodes(event); + }, + enabled: true, + value: "rulenode.copy-selected", + icon: "content_copy", + shortcut: "M-C" + } + ); contextInfo.items.push( { action: function () { @@ -176,6 +203,11 @@ export function RuleChainController($state, $scope, $compile, $q, $mdUtil, $time } ); } + contextInfo.items.push( + { + divider: true + } + ); contextInfo.items.push( { action: function () { @@ -220,6 +252,16 @@ export function RuleChainController($state, $scope, $compile, $q, $mdUtil, $time icon: "menu" } ); + contextInfo.items.push( + { + action: function (event) { + copyNode(event, node); + }, + enabled: true, + value: "action.copy", + icon: "content_copy" + } + ); contextInfo.items.push( { action: function () { @@ -526,7 +568,7 @@ export function RuleChainController($state, $scope, $compile, $q, $mdUtil, $time if (destNode.component.type == types.ruleNodeType.RULE_CHAIN.value) { deferred.reject(); } else { - var res = $filter('filter')(vm.ruleChainModel.edges, {source: vm.inputConnectorId}); + var res = $filter('filter')(vm.ruleChainModel.edges, {source: vm.inputConnectorId}, true); if (res && res.length) { vm.modelservice.edges.delete(res[0]); } @@ -582,6 +624,112 @@ export function RuleChainController($state, $scope, $compile, $q, $mdUtil, $time } } + function copyNode(event, node) { + var offset = angular.element(vm.canvasControl.modelservice.getCanvasHtmlElement()).offset(); + var x = Math.round(event.clientX - offset.left); + var y = Math.round(event.clientY - offset.top); + itembuffer.copyRuleNodes(x, y, [node], []); + } + + function copyRuleNodes(event) { + var offset = angular.element(vm.canvasControl.modelservice.getCanvasHtmlElement()).offset(); + var x = Math.round(event.clientX - offset.left); + var y = Math.round(event.clientY - offset.top); + var nodes = vm.modelservice.nodes.getSelectedNodes(); + var edges = vm.modelservice.edges.getSelectedEdges(); + var connections = []; + for (var i=0;i -1) && toIndex > -1 ) { + var connection = { + isInputSource: isInputSource, + fromIndex: fromIndex, + toIndex: toIndex, + label: edge.label + }; + connections.push(connection); + } + } + itembuffer.copyRuleNodes(x, y, nodes, connections); + } + + function pasteRuleNodes(event) { + var offset = angular.element(vm.canvasControl.modelservice.getCanvasHtmlElement()).offset(); + var x = Math.round(event.clientX - offset.left); + var y = Math.round(event.clientY - offset.top); + var ruleNodes = itembuffer.pasteRuleNodes(x, y, event); + if (ruleNodes) { + vm.modelservice.deselectAll(); + var nodes = []; + for (var i=0;i
- + + {{item.icon}} {{item.value}} diff --git a/ui/src/app/services/item-buffer.service.js b/ui/src/app/services/item-buffer.service.js index 9fce81171a..d6a9ce7a32 100644 --- a/ui/src/app/services/item-buffer.service.js +++ b/ui/src/app/services/item-buffer.service.js @@ -24,10 +24,11 @@ export default angular.module('thingsboard.itembuffer', [angularStorage]) .name; /*@ngInject*/ -function ItemBuffer($q, bufferStore, types, utils, dashboardUtils) { +function ItemBuffer($q, bufferStore, types, utils, dashboardUtils, ruleChainService) { const WIDGET_ITEM = "widget_item"; const WIDGET_REFERENCE = "widget_reference"; + const RULE_NODES = "rule_nodes"; var service = { prepareWidgetItem: prepareWidgetItem, @@ -37,7 +38,10 @@ function ItemBuffer($q, bufferStore, types, utils, dashboardUtils) { canPasteWidgetReference: canPasteWidgetReference, pasteWidget: pasteWidget, pasteWidgetReference: pasteWidgetReference, - addWidgetToDashboard: addWidgetToDashboard + addWidgetToDashboard: addWidgetToDashboard, + copyRuleNodes: copyRuleNodes, + hasRuleNodes: hasRuleNodes, + pasteRuleNodes: pasteRuleNodes } return service; @@ -151,6 +155,69 @@ function ItemBuffer($q, bufferStore, types, utils, dashboardUtils) { }; } + function copyRuleNodes(x, y, nodes, connections) { + var ruleNodes = { + nodes: [], + connections: [], + originX: x, + originY: y + }; + for (var i=0;i