mirror of
				https://github.com/node-red/node-red.git
				synced 2025-03-01 10:36:34 +00:00 
			
		
		
		
	Allow multiple links to be selected by ctrl-click
This commit is contained in:
		| @@ -63,7 +63,8 @@ RED.view = (function() { | ||||
|     var activeGroups = []; | ||||
|     var dirtyGroups = {}; | ||||
|  | ||||
|     var selected_link = null; | ||||
|     var selectedLinks = []; | ||||
|  | ||||
|     var mousedown_link = null; | ||||
|     var mousedown_node = null; | ||||
|     var mousedown_group = null; | ||||
| @@ -909,7 +910,7 @@ RED.view = (function() { | ||||
|             return; | ||||
|         } | ||||
|         if (!mousedown_node && !mousedown_link && !mousedown_group) { | ||||
|             selected_link = null; | ||||
|             selectedLinks = []; | ||||
|             updateSelection(); | ||||
|         } | ||||
|         if (mouse_mode === 0) { | ||||
| @@ -1348,7 +1349,7 @@ RED.view = (function() { | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         if (mouse_mode != RED.state.QUICK_JOINING && mouse_mode != RED.state.IMPORT_DRAGGING && !mousedown_node && !mousedown_group && selected_link == null) { | ||||
|         if (mouse_mode != RED.state.QUICK_JOINING && mouse_mode != RED.state.IMPORT_DRAGGING && !mousedown_node && !mousedown_group && selectedLinks.length === 0) { | ||||
|             return; | ||||
|         } | ||||
|  | ||||
| @@ -1372,16 +1373,18 @@ RED.view = (function() { | ||||
|                     // Get all the wires we need to detach. | ||||
|                     var links = []; | ||||
|                     var existingLinks = []; | ||||
|                     if (selected_link && | ||||
|                         ((mousedown_port_type === PORT_TYPE_OUTPUT && | ||||
|                             selected_link.source === mousedown_node && | ||||
|                             selected_link.sourcePort === mousedown_port_index | ||||
|                         ) || | ||||
|                         (mousedown_port_type === PORT_TYPE_INPUT && | ||||
|                             selected_link.target === mousedown_node | ||||
|                         )) | ||||
|                     ) { | ||||
|                         existingLinks = [selected_link]; | ||||
|                     if (selectedLinks.length > 0) { | ||||
|                         selectedLinks.forEach(function(link) { | ||||
|                             if (((mousedown_port_type === PORT_TYPE_OUTPUT && | ||||
|                                 link.source === mousedown_node && | ||||
|                                 link.sourcePort === mousedown_port_index | ||||
|                             ) || | ||||
|                             (mousedown_port_type === PORT_TYPE_INPUT && | ||||
|                                 link.target === mousedown_node | ||||
|                             ))) { | ||||
|                                 existingLinks.push(link); | ||||
|                             } | ||||
|                         }) | ||||
|                     } else { | ||||
|                         var filter; | ||||
|                         if (mousedown_port_type === PORT_TYPE_OUTPUT) { | ||||
| @@ -1419,7 +1422,7 @@ RED.view = (function() { | ||||
|                 } else if (mousedown_node && !quickAddLink) { | ||||
|                     showDragLines([{node:mousedown_node,port:mousedown_port_index,portType:mousedown_port_type}]); | ||||
|                 } | ||||
|                 selected_link = null; | ||||
|                 selectedLinks = []; | ||||
|             } | ||||
|             mousePos = mouse_position; | ||||
|             for (i=0;i<drag_lines.length;i++) { | ||||
| @@ -1941,7 +1944,7 @@ RED.view = (function() { | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         selected_link = null; | ||||
|         selectedLinks = []; | ||||
|         if (mouse_mode !== RED.state.SELECTING_NODE) { | ||||
|             updateSelection(); | ||||
|         } | ||||
| @@ -1956,7 +1959,7 @@ RED.view = (function() { | ||||
|             n.n.selected = false; | ||||
|         } | ||||
|         movingSet.clear(); | ||||
|         selected_link = null; | ||||
|         selectedLinks = []; | ||||
|         if (activeGroup) { | ||||
|             activeGroup.active = false | ||||
|             activeGroup.dirty = true; | ||||
| @@ -2050,12 +2053,16 @@ RED.view = (function() { | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|                 if (activeFlowLinks.length === 0 && selected_link !== null && selected_link.link) { | ||||
|                     activeLinks.push(selected_link); | ||||
|                     activeLinkNodes[selected_link.source.id] = selected_link.source; | ||||
|                     selected_link.source.dirty = true; | ||||
|                     activeLinkNodes[selected_link.target.id] = selected_link.target; | ||||
|                     selected_link.target.dirty = true; | ||||
|                 if (activeFlowLinks.length === 0 && selectedLinks.length > 0) { | ||||
|                     selectedLinks.forEach(function(link) { | ||||
|                         if (link.link) { | ||||
|                             activeLinks.push(link); | ||||
|                             activeLinkNodes[link.source.id] = link.source; | ||||
|                             link.source.dirty = true; | ||||
|                             activeLinkNodes[link.target.id] = link.target; | ||||
|                             link.target.dirty = true; | ||||
|                         } | ||||
|                     }) | ||||
|                 } | ||||
|             } else { | ||||
|                 selection.flows = workspaceSelection; | ||||
| @@ -2066,6 +2073,10 @@ RED.view = (function() { | ||||
|                 return value.map(function(n) { return n.id }) | ||||
|             } else if (key === 'link') { | ||||
|                 return value.source.id+":"+value.sourcePort+":"+value.target.id; | ||||
|             } else if (key === 'links') { | ||||
|                 return value.map(function(link) { | ||||
|                     link.source.id+":"+link.sourcePort+":"+link.target.id; | ||||
|                 }); | ||||
|             } | ||||
|             return value; | ||||
|         }); | ||||
| @@ -2135,7 +2146,7 @@ RED.view = (function() { | ||||
|             updateActiveNodes(); | ||||
|             updateSelection(); | ||||
|             redraw(); | ||||
|         } else if (movingSet.length() > 0 || selected_link != null) { | ||||
|         } else if (movingSet.length() > 0 || selectedLinks.length > 0) { | ||||
|             var result; | ||||
|             var node; | ||||
|             var removedNodes = []; | ||||
| @@ -2234,71 +2245,81 @@ RED.view = (function() { | ||||
|                     RED.nodes.dirty(true); | ||||
|                 } | ||||
|             } | ||||
|             var historyEvent; | ||||
|  | ||||
|             if (selected_link && selected_link.link) { | ||||
|                 var sourceId = selected_link.source.id; | ||||
|                 var targetId = selected_link.target.id; | ||||
|                 var sourceIdIndex = selected_link.target.links.indexOf(sourceId); | ||||
|                 var targetIdIndex = selected_link.source.links.indexOf(targetId); | ||||
|             var linkRemoveHistoryEvents = []; | ||||
|             if (selectedLinks.length > 0) { | ||||
|                 selectedLinks.forEach(function(link) { | ||||
|                     if (link.link) { | ||||
|                         var sourceId = link.source.id; | ||||
|                         var targetId = link.target.id; | ||||
|                         var sourceIdIndex = link.target.links.indexOf(sourceId); | ||||
|                         var targetIdIndex = link.source.links.indexOf(targetId); | ||||
|  | ||||
|                 historyEvent = { | ||||
|                     t:"multi", | ||||
|                     events: [ | ||||
|                         { | ||||
|                             t: "edit", | ||||
|                             node: selected_link.source, | ||||
|                             changed: selected_link.source.changed, | ||||
|                             changes: { | ||||
|                                 links: $.extend(true,{},{v:selected_link.source.links}).v | ||||
|                             } | ||||
|                         }, | ||||
|                         { | ||||
|                             t: "edit", | ||||
|                             node: selected_link.target, | ||||
|                             changed: selected_link.target.changed, | ||||
|                             changes: { | ||||
|                                 links: $.extend(true,{},{v:selected_link.target.links}).v | ||||
|                             } | ||||
|                         } | ||||
|                         linkRemoveHistoryEvents.push({ | ||||
|                             t:"multi", | ||||
|                             events: [ | ||||
|                                 { | ||||
|                                     t: "edit", | ||||
|                                     node: link.source, | ||||
|                                     changed: link.source.changed, | ||||
|                                     changes: { | ||||
|                                         links: $.extend(true,{},{v:link.source.links}).v | ||||
|                                     } | ||||
|                                 }, | ||||
|                                 { | ||||
|                                     t: "edit", | ||||
|                                     node: link.target, | ||||
|                                     changed: link.target.changed, | ||||
|                                     changes: { | ||||
|                                         links: $.extend(true,{},{v:link.target.links}).v | ||||
|                                     } | ||||
|                                 } | ||||
|  | ||||
|                     ], | ||||
|                     dirty:RED.nodes.dirty() | ||||
|                 } | ||||
|                 RED.nodes.dirty(true); | ||||
|                 selected_link.source.changed = true; | ||||
|                 selected_link.target.changed = true; | ||||
|                 selected_link.target.links.splice(sourceIdIndex,1); | ||||
|                 selected_link.source.links.splice(targetIdIndex,1); | ||||
|                 selected_link.source.dirty = true; | ||||
|                 selected_link.target.dirty = true; | ||||
|                             ], | ||||
|                             dirty:startDirty | ||||
|                         }) | ||||
|  | ||||
|             } else { | ||||
|                 if (selected_link) { | ||||
|                     RED.nodes.removeLink(selected_link); | ||||
|                     removedLinks.push(selected_link); | ||||
|                 } | ||||
|                 RED.nodes.dirty(true); | ||||
|                 historyEvent = { | ||||
|                     t:"delete", | ||||
|                     nodes:removedNodes, | ||||
|                     links:removedLinks, | ||||
|                     groups: removedGroups, | ||||
|                     subflowOutputs:removedSubflowOutputs, | ||||
|                     subflowInputs:removedSubflowInputs, | ||||
|                     subflow: { | ||||
|                         id: activeSubflow?activeSubflow.id:undefined, | ||||
|                         instances: subflowInstances | ||||
|                     }, | ||||
|                     dirty:startDirty | ||||
|                 }; | ||||
|                 if (removedSubflowStatus) { | ||||
|                     historyEvent.subflow.status = removedSubflowStatus; | ||||
|                 } | ||||
|                         link.source.changed = true; | ||||
|                         link.target.changed = true; | ||||
|                         link.target.links.splice(sourceIdIndex,1); | ||||
|                         link.source.links.splice(targetIdIndex,1); | ||||
|                         link.source.dirty = true; | ||||
|                         link.target.dirty = true; | ||||
|  | ||||
|                     } else { | ||||
|                         RED.nodes.removeLink(link); | ||||
|                         removedLinks.push(link); | ||||
|                     } | ||||
|                 }) | ||||
|             } | ||||
|             RED.nodes.dirty(true); | ||||
|             var historyEvent = { | ||||
|                 t:"delete", | ||||
|                 nodes:removedNodes, | ||||
|                 links:removedLinks, | ||||
|                 groups: removedGroups, | ||||
|                 subflowOutputs:removedSubflowOutputs, | ||||
|                 subflowInputs:removedSubflowInputs, | ||||
|                 subflow: { | ||||
|                     id: activeSubflow?activeSubflow.id:undefined, | ||||
|                     instances: subflowInstances | ||||
|                 }, | ||||
|                 dirty:startDirty | ||||
|             }; | ||||
|             if (removedSubflowStatus) { | ||||
|                 historyEvent.subflow.status = removedSubflowStatus; | ||||
|             } | ||||
|             if (linkRemoveHistoryEvents.length > 0) { | ||||
|                 linkRemoveHistoryEvents.unshift(historyEvent); | ||||
|                 RED.history.push({ | ||||
|                     t:"multi", | ||||
|                     events: linkRemoveHistoryEvents | ||||
|                 }) | ||||
|             } else { | ||||
|                 RED.history.push(historyEvent); | ||||
|             } | ||||
|             RED.history.push(historyEvent); | ||||
|  | ||||
|             selected_link = null; | ||||
|             selectedLinks = []; | ||||
|             updateActiveNodes(); | ||||
|             updateSelection(); | ||||
|             redraw(); | ||||
| @@ -2709,7 +2730,9 @@ RED.view = (function() { | ||||
|                     } else { | ||||
|                         resetMouseVars(); | ||||
|                     } | ||||
|                     selected_link = select_link; | ||||
|                     //TODO: selectedLinks | ||||
|                     console.log("Wanted to set selected_link (1)",select_link) | ||||
|                     // selected_link = select_link; | ||||
|                     mousedown_link = select_link; | ||||
|                     if (select_link) { | ||||
|                         updateSelection(); | ||||
| @@ -2721,7 +2744,9 @@ RED.view = (function() { | ||||
|  | ||||
|             resetMouseVars(); | ||||
|             hideDragLines(); | ||||
|             selected_link = select_link; | ||||
|             if (select_link) { | ||||
|                 selectedLinks = [select_link]; | ||||
|             } | ||||
|             mousedown_link = select_link; | ||||
|             if (select_link) { | ||||
|                 updateSelection(); | ||||
| @@ -3212,7 +3237,7 @@ RED.view = (function() { | ||||
|                 mousedown_node.selected = true; | ||||
|                 movingSet.add(mousedown_node); | ||||
|             } | ||||
|             selected_link = null; | ||||
|             selectedLinks = []; | ||||
|             if (d3.event.button != 2) { | ||||
|                 mouse_mode = RED.state.MOVING; | ||||
|                 var mouse = d3.touches(this)[0]||d3.mouse(this); | ||||
| @@ -3338,19 +3363,33 @@ RED.view = (function() { | ||||
|             d3.event.stopPropagation(); | ||||
|             return; | ||||
|         } | ||||
|          mousedown_link = d; | ||||
|          clearSelection(); | ||||
|          selected_link = mousedown_link; | ||||
|          updateSelection(); | ||||
|          redraw(); | ||||
|          focusView(); | ||||
|          d3.event.stopPropagation(); | ||||
|          if (d3.event.metaKey || d3.event.ctrlKey) { | ||||
|              d3.select(this).classed("red-ui-flow-link-splice",true); | ||||
|              var point = d3.mouse(this); | ||||
|              var clickedGroup = getGroupAt(point[0],point[1]); | ||||
|              showQuickAddDialog({position:point, splice:selected_link, group:clickedGroup}); | ||||
|          } | ||||
|         mousedown_link = d; | ||||
|  | ||||
|         if (selectedLinks.length === 0 || !(d3.event.metaKey || d3.event.ctrlKey)) { | ||||
|             clearSelection(); | ||||
|         } | ||||
|         if (d3.event.metaKey || d3.event.ctrlKey) { | ||||
|             var linkIndex = selectedLinks.indexOf(mousedown_link); | ||||
|             if (linkIndex === -1) { | ||||
|                 selectedLinks.push(mousedown_link); | ||||
|             } else { | ||||
|                 if (selectedLinks.length !== 1) { | ||||
|                     selectedLinks.splice(linkIndex,1); | ||||
|                 } | ||||
|             } | ||||
|         } else { | ||||
|             selectedLinks.push(mousedown_link); | ||||
|         } | ||||
|         updateSelection(); | ||||
|         redraw(); | ||||
|         focusView(); | ||||
|         d3.event.stopPropagation(); | ||||
|         if (selectedLinks.length === 1 && selectedLinks[0] === mousedown_link && (d3.event.metaKey || d3.event.ctrlKey)) { | ||||
|             d3.select(this).classed("red-ui-flow-link-splice",true); | ||||
|             var point = d3.mouse(this); | ||||
|             var clickedGroup = getGroupAt(point[0],point[1]); | ||||
|             showQuickAddDialog({position:point, splice:mousedown_link, group:clickedGroup}); | ||||
|         } | ||||
|     } | ||||
|     function linkTouchStart(d) { | ||||
|         if (mouse_mode === RED.state.SELECTING_NODE) { | ||||
| @@ -3359,7 +3398,7 @@ RED.view = (function() { | ||||
|         } | ||||
|         mousedown_link = d; | ||||
|         clearSelection(); | ||||
|         selected_link = mousedown_link; | ||||
|         selectedLinks = [mousedown_link]; | ||||
|         updateSelection(); | ||||
|         redraw(); | ||||
|         focusView(); | ||||
| @@ -3595,7 +3634,7 @@ RED.view = (function() { | ||||
|     function showTouchMenu(obj,pos) { | ||||
|         var mdn = mousedown_node; | ||||
|         var options = []; | ||||
|         options.push({name:"delete",disabled:(movingSet.length()===0 && selected_link === null),onselect:function() {deleteSelection();}}); | ||||
|         options.push({name:"delete",disabled:(movingSet.length()===0 && selectedLinks.length === 0),onselect:function() {deleteSelection();}}); | ||||
|         options.push({name:"cut",disabled:(movingSet.length()===0),onselect:function() {copySelection();deleteSelection();}}); | ||||
|         options.push({name:"copy",disabled:(movingSet.length()===0),onselect:function() {copySelection();}}); | ||||
|         options.push({name:"paste",disabled:(clipboard.length===0),onselect:function() {importNodes(clipboard, {generateIds: true, touchImport: true});}}); | ||||
| @@ -4352,7 +4391,7 @@ RED.view = (function() { | ||||
|             link.exit().remove(); | ||||
|             link.each(function(d) { | ||||
|                 var link = d3.select(this); | ||||
|                 if (d.added || d===selected_link || d.selected || dirtyNodes[d.source.id] || dirtyNodes[d.target.id]) { | ||||
|                 if (d.added || selectedLinks.includes(d) || d.selected || dirtyNodes[d.source.id] || dirtyNodes[d.target.id]) { | ||||
|                     var numOutputs = d.source.outputs || 1; | ||||
|                     var sourcePort = d.sourcePort || 0; | ||||
|                     var y = -((numOutputs-1)/2)*13 +13*sourcePort; | ||||
| @@ -4375,7 +4414,7 @@ RED.view = (function() { | ||||
|                     this.__pathLine__.classList.toggle("red-ui-flow-node-disabled",!!(d.source.d || d.target.d)); | ||||
|                     this.__pathLine__.classList.toggle("red-ui-flow-subflow-link", !d.link && activeSubflow); | ||||
|                 } | ||||
|                 this.classList.toggle("red-ui-flow-link-selected", !!(d===selected_link||d.selected)); | ||||
|                 this.classList.toggle("red-ui-flow-link-selected", !!(selectedLinks.includes(d)||d.selected)); | ||||
|  | ||||
|                 var connectedToUnknown = !!(d.target.type == "unknown" || d.source.type == "unknown"); | ||||
|                 this.classList.toggle("red-ui-flow-link-unknown",!!(d.target.type == "unknown" || d.source.type == "unknown")) | ||||
| @@ -5083,8 +5122,10 @@ RED.view = (function() { | ||||
|         if (allNodes.size > 0) { | ||||
|             selection.nodes = Array.from(allNodes); | ||||
|         } | ||||
|         if (selected_link != null) { | ||||
|             selection.link = selected_link; | ||||
|         if (selectedLinks.length > 0) { | ||||
|             selection.link = selectedLinks[0]; | ||||
|             // TODO: clone the array | ||||
|             selection.links = selectedLinks; | ||||
|         } | ||||
|         return selection; | ||||
|     } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user