diff --git a/Kieker.WebGUI/src/main/java/kieker/webgui/web/beans/view/CurrentAnalysisEditorGraphBean.java b/Kieker.WebGUI/src/main/java/kieker/webgui/web/beans/view/CurrentAnalysisEditorGraphBean.java index 7d616f5b9dfc62d3a94af8a22a272cc8ca04d5aa..67603b6c5af2ea7d65d912987d77f13aed856d4a 100644 --- a/Kieker.WebGUI/src/main/java/kieker/webgui/web/beans/view/CurrentAnalysisEditorGraphBean.java +++ b/Kieker.WebGUI/src/main/java/kieker/webgui/web/beans/view/CurrentAnalysisEditorGraphBean.java @@ -85,9 +85,9 @@ public class CurrentAnalysisEditorGraphBean { private static final String JS_CMD_PORT_TYPE_INPUT = "inputPort"; private static final String JS_CMD_ADD_EDGE = "graph.addEdge('%s', '%s', '%s', false, false)"; - private static final String JS_CMD_ADD_FILTER = "graph.addNode(%d, %d, %s,[%s],[%s],[%s],'Filter', false)"; - private static final String JS_CMD_ADD_READER = "graph.addNode(%d, %d, %s,[%s],null,[%s], 'Reader', false)"; - private static final String JS_CMD_ADD_REPOSITORY = "graph.addNode(%d, %d, %s, null, [%s], null, 'Repository',false)"; + private static final String JS_CMD_ADD_FILTER = "var center = graph.getScreenCenter(); graph.addNode(center.x, center.y, %s,[%s],[%s],[%s],'Filter', false)"; + private static final String JS_CMD_ADD_READER = "var center = graph.getScreenCenter(); graph.addNode(center.x, center.y, %s,[%s],null,[%s], 'Reader', false)"; + private static final String JS_CMD_ADD_REPOSITORY = "var center = graph.getScreenCenter(); graph.addNode(center.x, center.y, %s, null, [%s], null, 'Repository',false)"; private static final String JS_CMD_ENABLE_GRID = "graph.setGridVisible(true, false)"; private static final String JS_CMD_DISABLE_GRID = "graph.setGridVisible(false, false)"; @@ -190,10 +190,9 @@ public class CurrentAnalysisEditorGraphBean { * The filter which should be added to the graph. */ public void addFilter(final MIFilter filter) { - RequestContext.getCurrentInstance().execute( - String.format(CurrentAnalysisEditorGraphBean.JS_CMD_ADD_FILTER, 0, 0, this.assembleGraphString(filter), - this.assembleGraphRepositoryPortString(filter.getRepositories()), this.assembleGraphInputPortString(filter), - this.assembleGraphOutputPortString(filter))); + RequestContext.getCurrentInstance().execute(String.format(CurrentAnalysisEditorGraphBean.JS_CMD_ADD_FILTER, this.assembleGraphString(filter), + this.assembleGraphRepositoryPortString(filter.getRepositories()), this.assembleGraphInputPortString(filter), + this.assembleGraphOutputPortString(filter))); } /** @@ -203,7 +202,7 @@ public class CurrentAnalysisEditorGraphBean { * The reader which should be added to the graph. */ public void addReader(final MIReader reader) { - RequestContext.getCurrentInstance().execute(String.format(CurrentAnalysisEditorGraphBean.JS_CMD_ADD_READER, 0, 0, this.assembleGraphString(reader), + RequestContext.getCurrentInstance().execute(String.format(CurrentAnalysisEditorGraphBean.JS_CMD_ADD_READER, this.assembleGraphString(reader), this.assembleGraphRepositoryPortString(reader.getRepositories()), this.assembleGraphOutputPortString(reader))); } @@ -216,8 +215,8 @@ public class CurrentAnalysisEditorGraphBean { public void addRepository(final MIRepository repository) { final String repoPort = String.format(CurrentAnalysisEditorGraphBean.JS_CMD_PORT, CurrentAnalysisEditorGraphBean.JS_CMD_PORT_TYPE_INPUT, CurrentAnalysisEditorGraphBean.REPOSITORY_INPUT_PORT, "N/A"); - RequestContext.getCurrentInstance().execute(String.format(CurrentAnalysisEditorGraphBean.JS_CMD_ADD_REPOSITORY, 0, 0, - this.assembleGraphString(repository), repoPort)); + RequestContext.getCurrentInstance().execute( + String.format(CurrentAnalysisEditorGraphBean.JS_CMD_ADD_REPOSITORY, this.assembleGraphString(repository), repoPort)); } /** diff --git a/Kieker.WebGUI/src/main/webapp/js/flowEditor.js b/Kieker.WebGUI/src/main/webapp/js/flowEditor.js index d54d6adff103cd2e019c6568fc4d6a5798f0ac46..90afa0eec4be6291cd335a4c386c34b035f72c09 100644 --- a/Kieker.WebGUI/src/main/webapp/js/flowEditor.js +++ b/Kieker.WebGUI/src/main/webapp/js/flowEditor.js @@ -144,12 +144,8 @@ function GraphFlow(){ // screen position 'centerX' : 0, 'centerY' : 0, - // these determine when the screen should move with a dragged node - /* 'borderLeft' : 0, - 'borderRight' : 0, - 'borderTop' : 0, - 'borderBottom' : 0, - */ + 'isDragging' : false, + 'isDraggingEdge' : false, // how far a the screen is dragged / where a dragged node is touched 'dragX' : null, 'dragY' : null}; @@ -924,11 +920,6 @@ function GraphFlow(){ // prepare loop variables var node, data, adja, x, y; - var left = Number.POSITIVE_INFINITY, - right = Number.NEGATIVE_INFINITY, - top = Number.POSITIVE_INFINITY, - bottom = Number.NEGATIVE_INFINITY; - var id, width, height; var xOffset = -grid.data.$width/2, yOffset = -grid.data.$height/2; @@ -959,12 +950,6 @@ function GraphFlow(){ width = parseFloat(nodeProps[p++]); height = parseFloat(nodeProps[p]); - // calculate new center of graph - left = Math.min(left, x - width / 2); - right = Math.max(right, x + width / 2); - top = Math.min(top, y - height / 2); - bottom = Math.max(bottom, y + height / 2); - // is custom node if(node.data.$custom){ @@ -1053,27 +1038,11 @@ function GraphFlow(){ } + // scale to fit the new graph + scaleToFit(true); - - // scaling values - var canvas = fd.canvas, - oldScale = canvas.scaleOffsetX, - scaleX = grid.data.$width / (right - left + 4*vertexLabelSize), - scaleY = grid.data.$height / (bottom - top + 4*vertexLabelSize) - scale = Math.min(scaleX, scaleY); - - // translation values - var midX = -(left + right) / 2, - midY = -(top + bottom) / 2; - + // animate if necessary if(isAnimated){ - - // set animation parameters - canvas.translateOffsetXEnd = midX; - canvas.translateOffsetYEnd = midY; - - canvas.scaleOffsetOld = oldScale; - canvas.scaleOffsetEnd = scale; animation.busyCanvas = true; fd.animate({ @@ -1082,17 +1051,6 @@ function GraphFlow(){ duration: animation.duration, onComplete: function(){animation.busyCanvas = false; fd.plot();} }); - - }else{ - // translate to the center of the graph and scale - midX -= fd.canvas.translateOffsetX / oldScale; - midY -= fd.canvas.translateOffsetY / oldScale; - scale /= oldScale; - - canvas.translate(midX, midY); - canvas.scale(scale, scale); - - fd.plot(); } return; @@ -1346,56 +1304,147 @@ function GraphFlow(){ } } + /** - Scales and centers the graph to fit the space provided by the container - in which it is drawn. - */ - this.scaleToFit = function(){ - // do nothing if the graph is empty - if(fd.graph.nodes.length == 2){ - return; - } - - var isAnimated = animation.enabled && !animation.busyCanvas; + * Returns the bounds and the exact center of the graph as an + * {x, y, left, right, top, bottom}-Object + * @param checkEndPos - if true, checks the endPos instead of pos + * of the node. This is used by autoLayout. + */ + this.getGraphBounds = function(checkEndPos){ // init loop variables - var data, + var data, width = 0, height = 0; + // the position of the node which is checked + var pos = 'pos'; + if(checkEndPos){ + pos = 'endPos'; + } + + var x, y; + var b, bendPoints; + // init the outer bounds of the displayed graph var left = Number.POSITIVE_INFINITY, right = Number.NEGATIVE_INFINITY, top = Number.POSITIVE_INFINITY, bottom = Number.NEGATIVE_INFINITY; + // this function checks the position of bendPoints + var checkBendPoints = function(node){ + node.eachAdjacency(function(adja){ + + data = adja.data; + + // is the first node the source node? + if(adja.nodeFrom.id == data.$direction[0]){ + bendPoints = data.$bendPoints; + for(b in bendPoints){ + b = bendPoints[b]; + + x = b.x; + y = b.y; + + left = Math.min(left, x); + right = Math.max(right, x); + top = Math.min(top, y); + bottom = Math.max(bottom, y); + } + + } + }); + }; + // check nodeFamilies for position iterateAllNodes(function(node){ data = node.data; if(data.$custom || data.$type == "nodeFamily"){ + x = node[pos].x; + y = node[pos].y; + width = data.$width/2; height = data.$height/2; - - left = Math.min(left, node.pos.x - width); - right = Math.max(right, node.pos.x + width); - top = Math.min(top, node.pos.y - height); - bottom = Math.max(bottom, node.pos.y + height); + left = Math.min(left, x - width); + right = Math.max(right, x + width); + top = Math.min(top, y - height); + bottom = Math.max(bottom, y + height); + + if(data.$custom){ + checkBendPoints(node); + } + } + // is a port + else{ + checkBendPoints(node); } }); + var midX = (left + right) / 2, + midY = (top + bottom) / 2; + + var bounds = + { + 'x' : midX, + 'y' : midY, + 'left' : left, + 'right' : right, + 'top' : top, + 'bottom' : bottom + }; + + return bounds; + } + + + /** + * Returns the exact center of the canvas where the screen is currently located + * as an {x, y}-Object + */ + this.getScreenCenter = function(){ + var canvas = fd.canvas, + scale = canvas.scaleOffsetX, + centerX = -canvas.translateOffsetX / scale, + centerY = -canvas.translateOffsetX / scale; + + return {'x' : centerX, 'y' : centerY}; + } + + /** + Scales and centers the graph to fit the space provided by the container + in which it is drawn. + @param forLoadPositions - if exists and true, the animation won't be called, + and the endPos of nodes will be checked. These are + the positions where the nodes will be after the + animation. + */ + this.scaleToFit = function(forLoadPositions){ + // do nothing if the graph is empty + if(fd.graph.nodes.length == 2){ + return; + } + + var isAnimated = animation.enabled && !animation.busyCanvas; + + // get graph bounds + var bounds = getGraphBounds(forLoadPositions && isAnimated); + // scaling values var canvas = fd.canvas, oldScale = canvas.scaleOffsetX, - scaleX = grid.data.$width / (right - left + 4*vertexLabelSize), - scaleY = grid.data.$height / (bottom - top + 4*vertexLabelSize) + scaleX = grid.data.$width / (bounds.right - bounds.left + 4*vertexLabelSize), + scaleY = grid.data.$height / (bounds.bottom - bounds.top + 4*vertexLabelSize) scale = Math.min(scaleX, scaleY); // translation values - var midX = -(left + right) / 2, - midY = -(top + bottom) / 2; + var midX = - bounds.x, + midY = - bounds.y; + if(isAnimated){ @@ -1406,13 +1455,15 @@ function GraphFlow(){ canvas.scaleOffsetOld = oldScale; canvas.scaleOffsetEnd = scale; - animation.busyCanvas = true; - fd.animate({ - modes: ['canvas:zoom:translate'], - transition: $jit.Trans.Cubic.easeInOut, - duration: animation.duration, - onComplete: function(){animation.busyCanvas = false; fd.plot();} - }); + if(!forLoadPositions){ + animation.busyCanvas = true; + fd.animate({ + modes: ['canvas:zoom:translate'], + transition: $jit.Trans.Cubic.easeInOut, + duration: animation.duration, + onComplete: function(){animation.busyCanvas = false; fd.plot();} + }); + } }else{ // translate to the center of the graph and scale @@ -2037,15 +2088,15 @@ function GraphFlow(){ */ function moveNode(node, x, y, smoothMove){ - var isAnimated = smoothMove; + var data = node.data; // snap to grid - if(grid.data.$snap){ + if(!smoothMove && grid.data.$snap && data.$movable){ var gridDim = grid.data.$dim, - left = (x - node.data.$width/2), - top = (y - node.data.$height/2), - right = (x + node.data.$width/2), - bottom = (y + node.data.$height/2), + left = (x - data.$width/2), + top = (y - data.$height/2), + right = (x + data.$width/2), + bottom = (y + data.$height/2), offLeft = Math.abs(left % gridDim), offTop = Math.abs(top % gridDim), @@ -2104,7 +2155,7 @@ function GraphFlow(){ deltaY = y - pos.y; // move parent - if(isAnimated){ + if(smoothMove){ node.setPos(new $jit.Complex(x, y), "end"); }else{ node.pos.setc(x, y); @@ -2119,7 +2170,7 @@ function GraphFlow(){ child = children[c]; pos = child.pos.getc(true); - moveNode(child, pos.x + deltaX, pos.y + deltaY, isAnimated); + moveNode(child, pos.x + deltaX, pos.y + deltaY, smoothMove); } } } @@ -2283,12 +2334,14 @@ function GraphFlow(){ } selectedNode = null; + return newEdge; } // first node clicked else{ selectedNode = {"id": nodeID, "isIncoming" : isIncoming, "label" : label}; + navi.isDraggingEdge = true; // determine direction of mouse edge if(isIncoming){ return addEdge(mouseNode.id, nodeID, label, data); @@ -2583,7 +2636,7 @@ function GraphFlow(){ * @returns true if a mouse edge exists */ this.isEdgeDragged = function(){ - return !!selectedNode; + return navi.isDraggingEdge; } /** @@ -2639,15 +2692,6 @@ function GraphFlow(){ '$snap' : false }, 'id': '#DUMMY_GRID_NODE' - }, - - // define canvas node - { - 'data': { - '$dim' : vertexLabelSize*4, - '$type' : 'none' - }, - 'id': '#DUMMY_NAV_NODE' } ); @@ -2797,7 +2841,6 @@ function GraphFlow(){ navi.dragX = e.layerX; //e.pageX; navi.dragY = e.layerY; //e.pageY; - navi.isDragging = true; return; } @@ -2811,6 +2854,9 @@ function GraphFlow(){ navi.dragX = nodePos.x - mousePos.x; navi.dragY = nodePos.y - mousePos.y; + // is dragging a node + navi.isDragging = true; + /* // memorize the node position before node movement navi.dragX = nodePos.x; @@ -2830,7 +2876,6 @@ function GraphFlow(){ */ onDragMove: function(node, eventInfo, e) { if(!node || !node.data.$movable || selectedNode || !readOnly.move){ - // || node.id == mouseNode.id){ return; } @@ -2840,25 +2885,6 @@ function GraphFlow(){ y = pos.y + navi.dragY; moveNode(node, x, y); - // move screen if near edge - /* - var screenX = e.layerX, - screenY = e.layerY; - - if(screenX < navi.borderLeft && x < navi.dragX){ - fd.canvas.translate(20,0); - } - else if(screenX > navi.borderRight && x > navi.dragX){ - fd.canvas.translate(-20,0); - } - - if(screenY < navi.borderTop && y < navi.dragY){ - fd.canvas.translate(0,20); - } - else if(screenY > navi.borderBottom && y > navi.dragY){ - fd.canvas.translate(0,-20); - } - */ callListener("onDragMove", [node, eventInfo, e]); fd.plot(); @@ -2902,6 +2928,7 @@ function GraphFlow(){ for(var c in children){ updateOffset(children[c]); } + } fd.plot(); @@ -2921,6 +2948,7 @@ function GraphFlow(){ onClick: function(node, eventInfo, e){ var isNotDragging = true; + var wasDraggingEdge = navi.isDraggingEdge; // set mouse position (required for dragging edges) var pos = eventInfo.getPos(); @@ -2986,6 +3014,11 @@ function GraphFlow(){ } navi.isDragging = false; + + if(wasDraggingEdge){ + navi.isDraggingEdge = false; + } + }, /**