From 1c24f2b6f35da8c6a0562d682d709c396fc08e0a Mon Sep 17 00:00:00 2001 From: Nils Christian Ehmke <nie@informatik.uni-kiel.de> Date: Thu, 3 Jan 2013 17:29:12 +0100 Subject: [PATCH] #621; Updated the FlowEditor; Resizing the layout units will resize the graph canvas as well --- .../beans/view/CurrentAnalysisEditorBean.java | 41 ++- .../src/main/webapp/js/flowEditor.js | 309 ++++++++++++++---- Kieker.WebGUI/src/main/webapp/js/jit.js | 302 +++++++---------- .../webapp/pages/AnalysisEditorPage.xhtml | 4 +- .../main/webapp/pages/CockpitEditorPage.xhtml | 2 +- .../src/main/webapp/pages/CockpitPage.xhtml | 2 +- .../main/webapp/pages/ControllerPage.xhtml | 2 +- .../webapp/pages/ProjectOverviewPage.xhtml | 2 +- .../main/webapp/templates/PagesTemplate.xhtml | 2 +- 9 files changed, 400 insertions(+), 266 deletions(-) diff --git a/Kieker.WebGUI/src/main/java/kieker/webgui/web/beans/view/CurrentAnalysisEditorBean.java b/Kieker.WebGUI/src/main/java/kieker/webgui/web/beans/view/CurrentAnalysisEditorBean.java index af025164..35b266bb 100644 --- a/Kieker.WebGUI/src/main/java/kieker/webgui/web/beans/view/CurrentAnalysisEditorBean.java +++ b/Kieker.WebGUI/src/main/java/kieker/webgui/web/beans/view/CurrentAnalysisEditorBean.java @@ -78,6 +78,7 @@ public final class CurrentAnalysisEditorBean { private ComponentListContainer availableComponents; private MIAnalysisComponent selectedComponent; + private boolean unsavedModifications; private MIProject project; private String projectName; private long timeStamp; @@ -97,8 +98,8 @@ public final class CurrentAnalysisEditorBean { * Creates a new instance of this class. <b>Do not call this constructor manually. It will only be accessed by Spring.</b> */ public CurrentAnalysisEditorBean() { - this.availableComponents = new ComponentListContainer(Collections.<PluginContainer>emptyList(), Collections.<PluginContainer>emptyList(), - Collections.<RepositoryContainer>emptyList()); + this.availableComponents = new ComponentListContainer(Collections.<PluginContainer> emptyList(), Collections.<PluginContainer> emptyList(), + Collections.<RepositoryContainer> emptyList()); } /** @@ -117,6 +118,8 @@ public final class CurrentAnalysisEditorBean { this.initializeModelLibraries(); // Load the available readers, filters and repositories this.reloadAvailableComponents(); + + this.unsavedModifications = false; } } catch (final ProjectLoadException ex) { CurrentAnalysisEditorBean.LOG.error("An error occured while loading the project.", ex); @@ -173,6 +176,8 @@ public final class CurrentAnalysisEditorBean { // We have to reinitialize the tool palette completely! This is necessary as some of the already existing classes could need the newly loaded classes. this.reloadAvailableComponents(); + + this.setModificationsFlag(); } catch (final IOException ex) { CurrentAnalysisEditorBean.LOG.error("An error occured while uploading the library.", ex); GlobalPropertiesBean.showMessage(FacesMessage.SEVERITY_ERROR, this.globalPropertiesBean.getMsgLibraryUploadingException()); @@ -200,6 +205,8 @@ public final class CurrentAnalysisEditorBean { // We have to reinitialize the tool palette completely! This is necessary as some of the already existing classes could need the newly loaded // classes. this.reloadAvailableComponents(); + + this.setModificationsFlag(); } } catch (final IOException ex) { CurrentAnalysisEditorBean.LOG.error("An error occured while removing the library.", ex); @@ -310,6 +317,8 @@ public final class CurrentAnalysisEditorBean { GlobalPropertiesBean.showMessage(FacesMessage.SEVERITY_INFO, this.globalPropertiesBean.getMsgProjectSaved()); // Update the time stamp! this.resetTimeStamp(); + + this.clearModificationsFlag(); } catch (final IOException ex) { CurrentAnalysisEditorBean.LOG.error("An error occured while saving the project.", ex); GlobalPropertiesBean.showMessage(FacesMessage.SEVERITY_ERROR, this.globalPropertiesBean.getMsgProjectSavingException()); @@ -338,6 +347,8 @@ public final class CurrentAnalysisEditorBean { this.project.getRepositories().add(repository); this.currentAnalysisEditorGraphBean.addRepository(repository); this.currentAnalysisEditorGraphBean.refreshGraph(); + + this.setModificationsFlag(); } /** @@ -358,6 +369,22 @@ public final class CurrentAnalysisEditorBean { this.currentAnalysisEditorGraphBean.addFilter((MIFilter) plugin); } this.currentAnalysisEditorGraphBean.refreshGraph(); + + this.setModificationsFlag(); + } + + private synchronized void clearModificationsFlag() { + this.unsavedModifications = false; + RequestContext.getCurrentInstance().update("menuForm"); + } + + private synchronized void setModificationsFlag() { + this.unsavedModifications = true; + RequestContext.getCurrentInstance().update("menuForm"); + } + + public synchronized boolean isUnsavedModification() { + return this.unsavedModifications; } /** @@ -563,6 +590,8 @@ public final class CurrentAnalysisEditorBean { if (this.selectedComponent == node) { this.selectedComponent = null; // NOPMD } + + this.setModificationsFlag(); } /** @@ -575,6 +604,8 @@ public final class CurrentAnalysisEditorBean { */ public synchronized void edgeCreated(final MIOutputPort sourcePort, final MIInputPort targetPort) { sourcePort.getSubscribers().add(targetPort); + + this.setModificationsFlag(); } /** @@ -587,6 +618,8 @@ public final class CurrentAnalysisEditorBean { */ public synchronized void edgeRemoved(final MIOutputPort sourcePort, final MIInputPort targetPort) { sourcePort.getSubscribers().remove(targetPort); + + this.setModificationsFlag(); } /** @@ -599,6 +632,8 @@ public final class CurrentAnalysisEditorBean { */ public synchronized void edgeCreated(final MIRepositoryConnector sourcePort, final MIRepository target) { sourcePort.setRepository(target); + + this.setModificationsFlag(); } /** @@ -611,6 +646,8 @@ public final class CurrentAnalysisEditorBean { */ public synchronized void edgeRemoved(final MIRepositoryConnector sourcePort, final MIRepository target) { sourcePort.setRepository(null); + + this.setModificationsFlag(); } } diff --git a/Kieker.WebGUI/src/main/webapp/js/flowEditor.js b/Kieker.WebGUI/src/main/webapp/js/flowEditor.js index 1bfdf40e..5d5515f8 100644 --- a/Kieker.WebGUI/src/main/webapp/js/flowEditor.js +++ b/Kieker.WebGUI/src/main/webapp/js/flowEditor.js @@ -49,7 +49,6 @@ var Log = { function GraphFlow(){ - ///////////////////////////////////////////// // VARIABLES // ///////////////////////////////////////////// @@ -140,16 +139,87 @@ function GraphFlow(){ This information is needed to correctly draw the Grid and may come in handy in future functions. */ - var navi = { 'centerX' : 0, + var navi = { // 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, + // memorize where we touch a node upon dragging + 'nodeOffsetX' : 0, + 'nodeOffsetY' : 0, + // how far a node will be dragged 'dragX' : null, 'dragY' : null, + // dragged nodefamily and ports 'draggedNodes' : null}; ///////////////////////////////////////////// // FUNCTIONS // ///////////////////////////////////////////// + /** + * Zooms in or out, depending on the factor + * + * @param factor - the graph zooms out if it is smaller than 1 + */ + this.zoom = function(factor){ + + // check argument + if(!validArg("zoom()", factor, "number")){ + return; + }else if(factor < 0){ + callListener("onError", ["Error in function zoom(): Argument factor must be a positive number!"]); + } + + // zoom + var canvas = fd.canvas; + canvas.scale(factor, factor); + + // show text only if it is big enough + canvas.showLabels = (canvas.scaleOffsetX > canvas.labelThreshold); + } + + /** + Checks if the GraphFlow container has changed in size and + updates the canvas area accordingly. + */ + this.updateCanvasSize = function(){ + var container = document.getElementById(visContainer); + var widthOld = grid.data.$width, + heightOld = grid.data.$height, + widthNew = container.clientWidth, + heightNew = container.clientHeight; + + // check if the dimensions changed + if( widthOld != widthNew || heightOld != heightNew){ + + // calculate translation parameters + var scale = fd.canvas.scaleOffsetX, + transX = fd.canvas.translateOffsetX + (widthOld - widthNew)/2, + transY = fd.canvas.translateOffsetY + (heightOld - heightNew)/2; + + // resize canvas, scale and translate content to maintain its position + fd.canvas.resize(container.clientWidth, container.clientHeight, true); + fd.canvas.scale(scale, scale); + fd.canvas.translate(transX / scale, transY / scale); + + // update grid dimensions + grid.data.$width = container.clientWidth; + grid.data.$height = container.clientHeight; + + // apply changes to the grid if visible + if(grid.visible){ + var domGrid = fd.graph.getNode(grid.id); + domGrid.data.$width = widthNew; + domGrid.data.$height = heightNew; + } + } + fd.plot(); + } + /** Concatenates nodefamily 'id's and their x- and y-position to a string, separating each element with a space. @@ -199,17 +269,17 @@ function GraphFlow(){ if(!value){ value = "Lucida Console"; } - value = 0.83 * node.data.$dim +"px " + value; + value = "px " + value; } // apply new value to data node.data[field] = value; - domNode.data[field] = value; + if(!noPlot){ domNode.data[field] = value;} } // make changes to name or id else{ node[field] = value; - domNode[field] = value; + if(!noPlot){ domNode[field] = value;} } } @@ -575,12 +645,11 @@ function GraphFlow(){ grid.data.$color = newColor; // apply changes immediately - if(grid.visible){ + if(!noPlot && grid.visible){ var domGrid = fd.graph.getNode(grid.id); domGrid.data.$color = newColor; - // apply visual changes - if(!noPlot){ fd.plot();} + fd.plot(); } } } @@ -595,11 +664,11 @@ function GraphFlow(){ grid.data.$dim = newSize; // apply changes immediately - if(grid.visible){ + if(!noPlot &&grid.visible){ var domGrid = fd.graph.getNode(grid.id); domGrid.data.$dim = newSize; - // apply visual changes - if(!noPlot){ fd.plot();} + + fd.plot(); } } } @@ -621,14 +690,16 @@ function GraphFlow(){ grid.visible = visibility; // apply changes immediately - if(visibility){ - fd.graph.addNode(grid); - } - else{ - fd.graph.removeNode(grid.id); + if(!noPlot){ + if(visibility){ + fd.graph.addNode(grid); + } + else{ + fd.graph.removeNode(grid.id); + } + // apply visual changes + fd.plot(); } - // apply visual changes - if(!noPlot){ fd.plot();} } /** @@ -685,19 +756,26 @@ function GraphFlow(){ } // scaling values - var oldScale = fd.canvas.scaleOffsetX, + var canvas = fd.canvas, + oldScale = canvas.scaleOffsetX, scaleX = grid.data.$width / (rightMost - leftMost + 4*vertexLabelSize), - scaleY = grid.data.$height / (bottomMost - topMost +4*vertexLabelSize) + scaleY = grid.data.$height / (bottomMost - topMost + 4*vertexLabelSize) scale = Math.min(scaleX, scaleY) / oldScale; // translation values var midX = (leftMost + rightMost) / 2, midY = (topMost + bottomMost) / 2 -vertexLabelSize; + // translate to origin, scale, and translate to the center of the graph - fd.canvas.translate( -fd.canvas.translateOffsetX/oldScale, -fd.canvas.translateOffsetY/oldScale); - fd.canvas.scale(scale, scale); - fd.canvas.translate( -midX, -midY); + canvas.translate( -canvas.translateOffsetX/oldScale, -canvas.translateOffsetY/oldScale); + canvas.scale(scale, scale); + canvas.translate( -midX, -midY); + + // show text only if it is big enough + canvas.showLabels = (canvas.scaleOffsetX > canvas.labelThreshold); + + fd.plot(); } @@ -724,6 +802,7 @@ function GraphFlow(){ var icon; icon = new Image(); icon.src = path; + icon.onload = function(){}; // this may happen on IE, when linking to images via web if(!icon.complete){ @@ -1162,7 +1241,7 @@ function GraphFlow(){ "$nodeType": nodeType, "$xPos": xPosition, "$yPos": yPosition, - "$font": 0.83 * size +"px "+font, + "$font": "px "+font, "$tooltip": nodeFamily.tooltip }, "id": nodeFamily.id, @@ -1252,7 +1331,8 @@ function GraphFlow(){ "$yPos": y, "$color": nodeStrokeColor[loopType], "$fillColor": nodeColor, - "$tooltip": port.tooltip}, + "$tooltip": port.tooltip, + "$symbol": port.symbol}, "id": nodeFamily.id+"."+port.id, "name": port.name}; @@ -1476,6 +1556,7 @@ function GraphFlow(){ nodes = navi.draggedNodes, pos = node.pos.getc(true); + // set pos of family box var data= json[familyIndex].data; data.$xPos = pos.x; @@ -1641,10 +1722,13 @@ function GraphFlow(){ adja.push(edge); // apply changes immediately - var domSource = fd.graph.getNode(sourceID), + + if(!noPlot){ + var domSource = fd.graph.getNode(sourceID), domTarget = fd.graph.getNode(targetID); - fd.graph.addAdjacence(domSource, domTarget, edge.data); - if(!noPlot){ fd.plot();} + fd.graph.addAdjacence(domSource, domTarget, edge.data); + fd.plot(); + } return true; } @@ -1673,7 +1757,9 @@ function GraphFlow(){ adja[a].data.$bendPoints = bendPoints; // update displayed graph (no need for refresh then) - fd.graph.getAdjacence(sourceID, targetID).data.$bendPoints = bendPoints; + if(!noPlot){ + fd.graph.getAdjacence(sourceID, targetID).data.$bendPoints = bendPoints; + } break; } } @@ -1756,8 +1842,11 @@ function GraphFlow(){ } // apply changes immediately - fd.graph.removeAdjacence(sourceID, targetID); - if(!noPlot){ fd.plot();} + + if(!noPlot){ + fd.graph.removeAdjacence(sourceID, targetID); + fd.plot(); + } } /** @@ -1805,6 +1894,10 @@ function GraphFlow(){ else{ hover.setData('color', nodeStrokeColor[type], 'end'); } + // decrease size of ports and cross button + if(type != 'nodeFamily'){ + hover.setData('dim', vertexLabelSize*2, 'end'); + } } hover = null; @@ -1817,6 +1910,11 @@ function GraphFlow(){ node.setData('lineWidth', 2, 'end'); }else{ node.setData('color', nodeColorFocus, 'end'); + + // increase size of ports and cross button + if(node.data.$type != 'nodeFamily'){ + node.setData('dim', vertexLabelSize*3, 'end'); + } } hover = node; }else{ @@ -1830,7 +1928,7 @@ function GraphFlow(){ // of the edge animation will not be called fd.animate({ modes: ['edge-property:lineWidth:color', - 'node-property:color'], + 'node-property:color:dim'], transition: trans, duration: dur }); @@ -1909,22 +2007,28 @@ function GraphFlow(){ what happens on various mouse events is determined. */ this.initGraph = function(){ - json = [ null, null, null, null, null, null, null, null, null, null, - { - "adjacencies": [], - "data": { - "$dim": 0, - "$type": "none", - "&xPos": 0, - "&typeSelected" : null, - "&yPos": 0 - }, - "id": "#DUMMY_MOUSE_NODE", - "name": "", - "alpha": 0 - }]; + + if(!json){ + json = [ null, null, null, null, null, null, null, null, null, null, + { + "adjacencies": [], + "data": { + "$dim": 0, + "$type": "none", + "&xPos": 0, + "&typeSelected" : null, + "&yPos": 0 + }, + "id": "#DUMMY_MOUSE_NODE", + "name": "", + "alpha": 0 + }]; + } fd = new $jit.FlowGraph({ + width : grid.data.$width, + height : grid.data.$height, + //id of the visualization container injectInto: visContainer, //Enable zooming and panning @@ -2063,29 +2167,44 @@ function GraphFlow(){ /** * EVENT: OnDragStart + * Actually this Event fires whenever the mouse button is down */ onDragStart: function(node, eventInfo, e) { var mousePos = eventInfo.getPos(); + // dragging the canvas if(node == false || node.id == mouseNode.id || node.data.$type != "nodeFamily"){ - navi.dragX = e.pageX; - navi.dragY = e.pageY; + navi.dragX = e.layerX//e.pageX; + navi.dragY = e.layerX//e.pageY; navi.isDragging = true; return; } - // memorize where we drag the node instead on centering - // it to the mouse pointer - var nodePos = node.pos.getc(true); + //dragging a node + if(!selectedNode){ + // memorize where we drag the node instead on centering + // it to the mouse pointer + var nodePos = node.pos.getc(true); - navi.dragX = nodePos.x - mousePos.x; - navi.dragY = nodePos.y - mousePos.y; - - // we memorize all subnodes of the dragged nodefamily, to speed up - // dragging - prepareNodeMove(node); - + // memorize the node position before node movement + navi.dragX = nodePos.x; + navi.dragY = nodePos.y; + // the offset between the center of the node and the actual position + // where we grabbed the node + navi.nodeOffsetX = nodePos.x - mousePos.x; + navi.nodeOffsetY = nodePos.y - mousePos.y; + + // prepare borders. These determine when to move the canvas with the node + navi.borderLeft = (node.data.$width / 2 - navi.nodeOffsetX) * fd.canvas.scaleOffsetX; + navi.borderTop = (node.data.$height / 2 - navi.nodeOffsetY) * fd.canvas.scaleOffsetX; + navi.borderRight = grid.data.$width - (node.data.$width / 2 + navi.nodeOffsetX) * fd.canvas.scaleOffsetX; + navi.borderBottom = grid.data.$height - (node.data.$height / 2 + navi.nodeOffsetY) * fd.canvas.scaleOffsetX; + + // we memorize all subnodes of the dragged nodefamily, to speed up + // dragging + prepareNodeMove(node); + } callListener("onDragStart", [node, eventInfo, e]); }, @@ -2097,9 +2216,30 @@ function GraphFlow(){ node.id == mouseNode.id || node.data.$type != "nodeFamily"){ return; } - var pos = eventInfo.getPos(); - moveNode(pos.x+navi.dragX, - pos.y+navi.dragY); + + // move node + var pos = eventInfo.getPos(), + x = pos.x + navi.nodeOffsetX, + y = pos.y + navi.nodeOffsetY; + moveNode(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(); @@ -2112,6 +2252,7 @@ function GraphFlow(){ if(node.data.$type == "nodeFamily"){ saveNodeMove(); } + fd.plot(); callListener("onDragEnd", [node, eventInfo, e]); }, @@ -2129,11 +2270,31 @@ function GraphFlow(){ */ onClick: function(node, eventInfo, e){ - if(node == false){ - var deltaX = e.pageX - navi.dragX, - deltaY = e.pageY - navi.dragY; + // finished dragging the screen + if(!node){ + var deltaX = e.layerX - navi.dragX, + deltaY = e.layerY - navi.dragY; navi.centerX -= deltaX; navi.centerY -= deltaY; + + /* + // deblur edges by moving the canvas to a half pixel + var scale = fd.canvas.scaleOffsetX; + halfX = fd.canvas.translateOffsetX, + halfY = fd.canvas.translateOffsetY; + if(halfX < 0){ + halfX = -(halfX - Math.ceil(halfX) + 0.0)/scale; + } else{ + halfX = -(halfX - Math.floor(halfX) + 0.0)/scale; + } + if(halfY < 0){ + halfY = -(halfY - Math.ceil(halfY) + 0.0)/scale; + } else{ + halfY = -(halfY - Math.floor(halfY) + 0.0)/scale; + } + fd.canvas.translate(halfX, halfY); + */ + //fd.canvas.translate(deltaX, deltaY); } else if(mouseOverNode && readOnly.deleteCreate){ @@ -2156,10 +2317,10 @@ function GraphFlow(){ // add edge to mouseNode if(selectedNode.from == "inputPort"){ - addEdge(mouseNode.id, node.id, null, false, true); + addEdge(mouseNode.id, node.id); } else{ - addEdge(node.id, mouseNode.id, null, false, true); + addEdge(node.id, mouseNode.id); } var pos = node.pos.getc(true); @@ -2191,10 +2352,10 @@ function GraphFlow(){ // remove selectedEdge var label = node.data.$label; - removeEdge(selectedNode.id, node.data.$direction[1],false, true); + removeEdge(selectedNode.id, node.data.$direction[1]); // add edge to mouseNode - addEdge(selectedNode.id, mouseNode.id, label, false, true); + addEdge(selectedNode.id, mouseNode.id, label); var pos = eventInfo.getPos(); // set mouse position @@ -2217,6 +2378,14 @@ function GraphFlow(){ } callListener("onClick", [node, eventInfo, e]); navi.isDragging = false; + }, + + /** + * EVENT: OnMouseWheel + */ + onMouseWheel: function(e, delta) { + // update whether labels should be displayed + fd.canvas.showLabels = (fd.canvas.scaleOffsetX > fd.canvas.labelThreshold); } }, @@ -2229,6 +2398,10 @@ function GraphFlow(){ // load JSON data. refresh(); + + // set zoom threshold for displaying text + fd.canvas.labelThreshold = 1/vertexLabelSize; + fd.canvas.showLabels = true; } // initialize graph diff --git a/Kieker.WebGUI/src/main/webapp/js/jit.js b/Kieker.WebGUI/src/main/webapp/js/jit.js index 6029803a..98284eb0 100644 --- a/Kieker.WebGUI/src/main/webapp/js/jit.js +++ b/Kieker.WebGUI/src/main/webapp/js/jit.js @@ -7254,7 +7254,7 @@ Graph.Plot = { min = Math.min, opt = opt || this.viz.controller; //opt.clearCanvas && canvas.clear(); - + var root = aGraph.getNode(id); if(!root) return; @@ -17209,107 +17209,6 @@ Layouts.FlowGraph = new Class({ nodef: function(x) { return k2 / (x || 1); }, edgef: function(x) { return /* x * x / k; */ k * (x - l); } }; - }, - - compute: function(property, incremental) { - var prop = $.splat(property || ['current', 'start', 'end']); - var opt = this.getOptions(); - NodeDim.compute(this.graph, prop, this.config); - this.graph.computeLevels(this.root, 0, "ignore"); - this.graph.eachNode(function(n) { - $.each(prop, function(p) { - var pos = n.getPos(p); - if(pos.equals(Complex.KER)) { - pos.x = opt.width/5 * (Math.random() - 0.5); - pos.y = opt.height/5 * (Math.random() - 0.5); - } - //initialize disp vector - n.disp = {}; - $.each(prop, function(p) { - n.disp[p] = $C(0, 0); - }); - }); - }); - this.computePositions(prop, opt, incremental); - }, - - computePositions: function(property, opt, incremental) { - var times = this.config.iterations, i = 0, that = this; - if(incremental) { - (function iter() { - for(var total=incremental.iter, j=0; j<total; j++) { - opt.t = opt.tstart * (1 - i++/(times -1)); - that.computePositionStep(property, opt); - if(i >= times) { - incremental.onComplete(); - return; - } - } - incremental.onStep(Math.round(i / (times -1) * 100)); - setTimeout(iter, 1); - })(); - } else { - for(; i < times; i++) { - opt.t = opt.tstart * (1 - i/(times -1)); - this.computePositionStep(property, opt); - } - } - }, - - computePositionStep: function(property, opt) { - var graph = this.graph; - var min = Math.min, max = Math.max; - var dpos = $C(0, 0); - //calculate repulsive forces - graph.eachNode(function(v) { - //initialize disp - $.each(property, function(p) { - v.disp[p].x = 0; v.disp[p].y = 0; - }); - graph.eachNode(function(u) { - if(u.id != v.id) { - $.each(property, function(p) { - var vp = v.getPos(p), up = u.getPos(p); - dpos.x = vp.x - up.x; - dpos.y = vp.y - up.y; - var norm = dpos.norm() || 1; - v.disp[p].$add(dpos - .$scale(opt.nodef(norm) / norm)); - }); - } - }); - }); - //calculate attractive forces - var T = !!graph.getNode(this.root).visited; - graph.eachNode(function(node) { - node.eachAdjacency(function(adj) { - var nodeTo = adj.nodeTo; - if(!!nodeTo.visited === T) { - $.each(property, function(p) { - var vp = node.getPos(p), up = nodeTo.getPos(p); - dpos.x = vp.x - up.x; - dpos.y = vp.y - up.y; - var norm = dpos.norm() || 1; - node.disp[p].$add(dpos.$scale(-opt.edgef(norm) / norm)); - nodeTo.disp[p].$add(dpos.$scale(-1)); - }); - } - }); - node.visited = !T; - }); - //arrange positions to fit the canvas - var t = opt.t, w2 = opt.width / 2, h2 = opt.height / 2; - graph.eachNode(function(u) { - $.each(property, function(p) { - var disp = u.disp[p]; - var norm = disp.norm() || 1; - var p = u.getPos(p); - p.$add($C(disp.x * min(Math.abs(disp.x), t) / norm, - disp.y * min(Math.abs(disp.y), t) / norm)); - p.x = min(w2, max(-w2, p.x)); - p.y = min(h2, max(-h2, p.y)); - }); - }); } }); @@ -17367,8 +17266,8 @@ $jit.FlowGraph = new Class( { var $FlowGraph = $jit.FlowGraph; var config = { - iterations: 50, - levelDistance: 50 + iterations: 1, + levelDistance: 0 }; this.controller = this.config = $.merge(Options("Canvas", "Node", "Edge", @@ -17414,12 +17313,12 @@ $jit.FlowGraph = new Class( { Computes positions and plots the tree. */ refresh: function() { - this.compute(); + //this.compute(); this.plot(); }, reposition: function() { - this.compute('end'); + //this.compute('end'); }, /* @@ -17488,7 +17387,7 @@ $jit.FlowGraph = new Class( { */ computeIncremental: function(opt) { opt = $.merge( { - iter: 20, + iter: 1, // 20 property: 'end', onStep: $.empty, onComplete: $.empty @@ -17883,33 +17782,35 @@ $jit.FlowGraph.$extend = true; var name = node.name; var midX, midY, textWidth; + var textSize = 0.83 * dim; - if(name != undefined){ - ctx.fillStyle = node.getData('color'); - ctx.font = node.data.$font; - - textWidth = ctx.measureText(name).width/2; - midX = tX - textWidth; - midY = posY; - - ctx.fillText(name, midX, midY); - } - - // paint class - - var nodeClass = node.data.$nodeClass; - if(nodeClass != undefined){ - nodeClass = "<"+nodeClass+">"; - ctx.font = 0.83 * (dim-4) + "px Lucida Console"; - - var classWidth = ctx.measureText(nodeClass).width/2; - midX = tX - classWidth; - midY += dim * 0.83; + if(canvas.showLabels){ + if(name != undefined){ + ctx.fillStyle = node.getData('color'); + ctx.font = textSize + node.data.$font; + textWidth = ctx.measureText(name).width/2; + midX = tX - textWidth; + midY = posY; + + ctx.fillText(name, midX, midY); + } - ctx.fillText(nodeClass, midX, midY); + // paint class - if(textWidth < classWidth){ - textWidth = classWidth; + var nodeClass = node.data.$nodeClass; + if(nodeClass != undefined){ + nodeClass = "<"+nodeClass+">"; + ctx.font = (textSize - 3.22) + node.data.$font; + + var classWidth = ctx.measureText(nodeClass).width/2; + midX = tX - classWidth; + midY += textSize; + + ctx.fillText(nodeClass, midX, midY); + + if(textWidth < classWidth){ + textWidth = classWidth; + } } } @@ -17945,15 +17846,18 @@ $jit.FlowGraph.$extend = true; return; } - var pos = node.pos.getc(true), - size = node.getData('dim'), - hSize = size/2, - bx = pos.x - hSize, - by = pos.y - hSize/2, - ctx = canvas.getCtx(); - - ctx.font = 0.83 * size +"px Verdana"; - ctx.fillText("x", bx, by); + var size = node.getData('dim'); + + if(canvas.showLabels){ + var pos = node.pos.getc(true), + hSize = size/2, + bx = pos.x - hSize, + by = pos.y - hSize/2, + ctx = canvas.getCtx(); + + ctx.font = 0.83 * size +"px Verdana"; + ctx.fillText("x", bx, by); + } }, 'contains': function(node, pos){ @@ -17976,7 +17880,7 @@ $jit.FlowGraph.$extend = true; 'render': function(node, canvas){ var pos = node.pos.getc(true), - size = node.getData('dim'), + size = node.data.$dim, bx = pos.x, by = pos.y, ctx = canvas.getCtx(); @@ -17984,19 +17888,24 @@ $jit.FlowGraph.$extend = true; // draw close-button area this.nodeHelper.rectangle.render('fill', {x: bx, y: by+size/4}, size, size/2, canvas); - var bSize = size * 0.4; - bx -= size/6; - by += bSize; - - ctx.fillStyle = node.getData('fillColor'); - ctx.font = bSize +"px Arial Black"; - ctx.fillText("R", bx, by); + if(canvas.showLabels){ + var bSize = size * 0.4; + bx -= size/6; + by += bSize; + + var symbol = node.data.$symbol; + if(!symbol){ symbol = "R";} + + ctx.fillStyle = node.data.$fillColor; + ctx.font = bSize +"px Arial Black"; + ctx.fillText(symbol, bx, by); + } }, 'contains': function(node, pos){ var npos = node.pos.getc(true), - size = node.getData('dim')/2; + size = node.data.$dim / 2; return Math.abs(pos.x - npos.x) <= size && Math.abs(pos.y - npos.y-size) <= size; } }, @@ -18006,10 +17915,9 @@ $jit.FlowGraph.$extend = true; */ 'inputPort': { 'render': function(node, canvas){ - //var vertexFillColor = '#F62929'; var pos = node.pos.getc(true), - size = node.getData('dim'), + size = node.data.$dim, bx = pos.x, by = pos.y, ctx = canvas.getCtx(); @@ -18018,30 +17926,37 @@ $jit.FlowGraph.$extend = true; this.nodeHelper.rectangle.render('fill', {x: bx, y: by+size/4}, size, size/2, canvas); - // draw arrow - /*var bSize = size * 0.4; - bx -= size/2; - by += bSize; - ctx.fillStyle = node.getData('fillColor'); - ctx.font = bSize +"px Arial Black"; - ctx.fillText(">", bx, by);*/ - by += size/4; - var octo = size/8, - xp = bx - size/2 + octo; - ctx.fillStyle = node.getData('fillColor'); - ctx.beginPath(); - ctx.moveTo(xp, by - octo); - ctx.lineTo(bx , by); - ctx.lineTo(xp, by + octo); - ctx.closePath(); - ctx.fill(); + ctx.fillStyle = node.data.$fillColor; + var symbol = node.data.$symbol; + if(symbol && canvas.showLabels){ + + // draw symbol + var bSize = size * 0.4; + bx -= size/6; + by += bSize; + ctx.font = bSize +"px Arial Black"; + ctx.fillText(symbol, bx, by); + } + else{ + + // draw arrow + by += size/4; + var octo = size/8, + xp = bx - size/2 + octo; + ctx.beginPath(); + ctx.moveTo(xp, by - octo); + ctx.lineTo(bx , by); + ctx.lineTo(xp, by + octo); + ctx.closePath(); + ctx.fill(); + } }, 'contains': function(node, pos){ var npos = node.pos.getc(true), - size = node.getData('dim')/2; + size = node.data.$dim / 2; return Math.abs(pos.x - npos.x) <= size && Math.abs(pos.y - npos.y-size) <= size; } }, @@ -18051,7 +17966,7 @@ $jit.FlowGraph.$extend = true; 'outputPort': { 'render': function(node, canvas){ var pos = node.pos.getc(true), - size = node.getData('dim'), + size = node.data.$dim, bx = pos.x, by = pos.y, ctx = canvas.getCtx(); @@ -18059,29 +17974,37 @@ $jit.FlowGraph.$extend = true; // draw rectangle this.nodeHelper.rectangle.render('fill', {x: bx, y: by+size/4}, size, size/2, canvas); + ctx.fillStyle = node.data.$fillColor; + var symbol = node.data.$symbol; - // draw arrow - /*var bSize = size * 0.4; - bx += size/4; - by += bSize; - ctx.fillStyle = node.getData('fillColor'); - ctx.font = bSize +"px Arial Black"; - ctx.fillText(">", bx, by);*/ - by += size/4; - var octo = size/8; - ctx.fillStyle = node.getData('fillColor'); - ctx.beginPath(); - ctx.moveTo(bx, by - octo); - ctx.lineTo(bx + size/2 - octo , by); - ctx.lineTo(bx, by + octo); - ctx.closePath(); - ctx.fill(); + if(symbol && canvas.showLabels){ + // draw symbol + + var bSize = size * 0.4; + bx -= size/6; + by += bSize; + + + ctx.font = bSize +"px Arial Black"; + ctx.fillText(symbol, bx, by); + } + else{ + // draw arrow + by += size/4; + var octo = size/8; + ctx.beginPath(); + ctx.moveTo(bx, by - octo); + ctx.lineTo(bx + size/2 - octo , by); + ctx.lineTo(bx, by + octo); + ctx.closePath(); + ctx.fill(); + } }, 'contains': function(node, pos){ var npos = node.pos.getc(true), - size = node.getData('dim')/2; + size = node.data.$dim / 2; return Math.abs(pos.x - npos.x) <= size && Math.abs(pos.y - npos.y-size) <= size; } }, @@ -18163,7 +18086,6 @@ $jit.FlowGraph.$extend = true; var from = {"x": posFrom.x, "y" : posFrom.y+dim/2}; var to = {"x": posTo.x, "y" : posTo.y+dim/2}; - // swap points if the edge direction is "wrong" if(inverse){ var temp = to; @@ -18189,7 +18111,7 @@ $jit.FlowGraph.$extend = true; // add the label var label = adj.data.$label; - if(label != undefined){ + if(label != undefined && canvas.showLabels){ var ctx = canvas.getCtx(); ctx.font = (1.23 * dim)+"px Arial"; @@ -18256,7 +18178,7 @@ $jit.FlowGraph.$extend = true; // add the label var label = adj.data.$label; - if(label != undefined){ + if(label != undefined && canvas.showLabels){ var ctx = canvas.getCtx(); ctx.font = (1.23 * dim)+"px Arial"; diff --git a/Kieker.WebGUI/src/main/webapp/pages/AnalysisEditorPage.xhtml b/Kieker.WebGUI/src/main/webapp/pages/AnalysisEditorPage.xhtml index 38800bc6..b16d5161 100644 --- a/Kieker.WebGUI/src/main/webapp/pages/AnalysisEditorPage.xhtml +++ b/Kieker.WebGUI/src/main/webapp/pages/AnalysisEditorPage.xhtml @@ -12,7 +12,8 @@ <h:body> <ui:composition template="/templates/PagesTemplate.xhtml"> - + + <ui:param name="unsavedModifications" value="#{currentAnalysisEditorBean.unsavedModification}"/> <ui:param name="projectName" value="#{currentAnalysisEditorBean.projectName}"/> <ui:param name="pagename" value="analysisEditor"/> <ui:param name="showProjectName" value="true"/> @@ -127,6 +128,7 @@ </ui:define> <ui:define name="furtherLayoutUnits"> + <p:ajax event="resize" onstart="graph.updateCanvasSize();"/> <!-- This is the component presenting the available properties. --> <p:layoutUnit style="font-size: 12px" position="south" size="150" header="#{localizedMessages.properties}" resizable="true" collapsible="true"> <h:form id="propertiesForm" > diff --git a/Kieker.WebGUI/src/main/webapp/pages/CockpitEditorPage.xhtml b/Kieker.WebGUI/src/main/webapp/pages/CockpitEditorPage.xhtml index 2e5a6b1f..72dfeee7 100644 --- a/Kieker.WebGUI/src/main/webapp/pages/CockpitEditorPage.xhtml +++ b/Kieker.WebGUI/src/main/webapp/pages/CockpitEditorPage.xhtml @@ -11,7 +11,7 @@ <h:body> <ui:composition template="/templates/PagesTemplate.xhtml"> - + <ui:param name="unsavedModifications" value="false"/> <ui:param name="projectName" value="#{currentCockpitEditorBean.projectName}"/> <ui:param name="pagename" value="cockpitEditor"/> <ui:param name="showProjectName" value="true"/> diff --git a/Kieker.WebGUI/src/main/webapp/pages/CockpitPage.xhtml b/Kieker.WebGUI/src/main/webapp/pages/CockpitPage.xhtml index 95c89407..5fa439fc 100644 --- a/Kieker.WebGUI/src/main/webapp/pages/CockpitPage.xhtml +++ b/Kieker.WebGUI/src/main/webapp/pages/CockpitPage.xhtml @@ -11,7 +11,7 @@ <h:body> <ui:composition template="#{root}/templates/PagesTemplate.xhtml"> - + <ui:param name="unsavedModifications" value="false"/> <ui:param name="projectName" value="#{currentCockpitBean.projectName}"/> <ui:param name="pagename" value="cockpit"/> <ui:param name="showProjectName" value="true"/> diff --git a/Kieker.WebGUI/src/main/webapp/pages/ControllerPage.xhtml b/Kieker.WebGUI/src/main/webapp/pages/ControllerPage.xhtml index 89d6bede..b4adb2c5 100644 --- a/Kieker.WebGUI/src/main/webapp/pages/ControllerPage.xhtml +++ b/Kieker.WebGUI/src/main/webapp/pages/ControllerPage.xhtml @@ -11,7 +11,7 @@ <h:body> <ui:composition template="/templates/PagesTemplate.xhtml"> - + <ui:param name="unsavedModifications" value="false"/> <ui:param name="projectName" value="#{currentControllerBean.projectName}"/> <ui:param name="pagename" value="controller"/> <ui:param name="showProjectName" value="true"/> diff --git a/Kieker.WebGUI/src/main/webapp/pages/ProjectOverviewPage.xhtml b/Kieker.WebGUI/src/main/webapp/pages/ProjectOverviewPage.xhtml index 95d28cff..cf321bc2 100644 --- a/Kieker.WebGUI/src/main/webapp/pages/ProjectOverviewPage.xhtml +++ b/Kieker.WebGUI/src/main/webapp/pages/ProjectOverviewPage.xhtml @@ -12,7 +12,7 @@ <h:body> <ui:composition template="/templates/PagesTemplate.xhtml"> - + <ui:param name="unsavedModifications" value="false"/> <ui:param name="projectName" value="#{currentProjectOverviewBean.projectName}"/> <ui:param name="pagename" value="projectOverview"/> <ui:param name="showProjectName" value="false"/> diff --git a/Kieker.WebGUI/src/main/webapp/templates/PagesTemplate.xhtml b/Kieker.WebGUI/src/main/webapp/templates/PagesTemplate.xhtml index da013907..70d76aff 100644 --- a/Kieker.WebGUI/src/main/webapp/templates/PagesTemplate.xhtml +++ b/Kieker.WebGUI/src/main/webapp/templates/PagesTemplate.xhtml @@ -22,7 +22,7 @@ <h:form id="menuForm"> <p:toolbar> <p:toolbarGroup align="left"> - <h:outputText styleClass="kieker-title" value="Kieker #{showProjectName ? '»' : ''} #{showProjectName ? stringBean.shortenLongName(projectName, 30) : ''}"/> + <h:outputText styleClass="kieker-title" value="Kieker #{showProjectName ? '»' : ''} #{showProjectName ? stringBean.shortenLongName(projectName, 30) : ''}#{unsavedModifications ? '*' : ''}"/> </p:toolbarGroup> <p:toolbarGroup align="right"> <p:button styleClass="perspective-button" icon="ui-icon-home" outcome="projectOverview" disabled="#{pagename == 'projectOverview'}" /> -- GitLab