src/it/ht/rcs/console/entities/view/graph/LinkGraph.mxml
<?xml version="1.0" encoding="utf-8"?>
<s:Group xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
xmlns:diagrammer="fr.kapit.diagrammer.*"
width="100%"
height="100%"
xmlns:visualizer="fr.kapit.visualizer.*"
resize="onResize(event)">
<fx:Metadata>
[Event(name="selectionChange", type="flash.events.Event")]
[Event(name="elementDoubleClick", type="flash.events.Event")]
</fx:Metadata>
<fx:Script>
<![CDATA[
import fr.kapit.diagrammer.base.uicomponent.DiagramSprite;
import fr.kapit.layouts.algorithms.balloon.BalloonLayout;
import fr.kapit.layouts.algorithms.balloon.BalloonLayoutParams;
import fr.kapit.layouts.algorithms.basic.params.LayoutParams;
import fr.kapit.layouts.constants.RootSelectionType;
import fr.kapit.layouts.model.Edge;
import fr.kapit.visualizer.base.ISprite;
import fr.kapit.visualizer.base.uicomponent.GenericGroup;
import fr.kapit.visualizer.base.uicomponent.GenericSprite;
import fr.kapit.visualizer.events.VisualizerEvent;
import fr.kapit.visualizer.styles.LinkStyle;
import it.ht.rcs.console.entities.controller.EntityManager;
import it.ht.rcs.console.entities.model.Entity;
import it.ht.rcs.console.entities.model.Link;
import it.ht.rcs.console.entities.view.graph.utils.GraphUtils;
import it.ht.rcs.console.entities.view.renderers.CustomGroupRenderer;
import it.ht.rcs.console.entities.view.renderers.CustomLink;
import it.ht.rcs.console.entities.view.renderers.CustomNodeRenderer;
import it.ht.rcs.console.search.controller.SearchManager;
import mx.collections.ArrayCollection;
import mx.events.ResizeEvent;
import spark.collections.Sort;
import spark.collections.SortField;
private var sort:Sort;
private var entities:ArrayCollection;
private var groups:ArrayCollection;
private var filterCriteria:Object;
private var positions_so:SharedObject;
public var selection:Array;
protected function onElementDoubleClick(event:VisualizerEvent):void
{
dispatchEvent(new Event("elementDoubleClick"))
}
protected function onSelectionChange(event:VisualizerEvent):void
{
selection=vis.selection
dispatchEvent(new Event("selectionChange"))
}
private function initGraph():void
{
trace("GRAPH INIT")
sort=new Sort();
sort.fields=[new SortField("type", true), new SortField("name", false)]
var layout:BalloonLayout=vis.layout as BalloonLayout;
var params:BalloonLayoutParams=new BalloonLayoutParams(layout);
//highlightLinksOnSelection="false"
params.nodesSpacing=50;
params.loopMargin=50;
params.loopSpacing=50;
params.childAngularSector=360;
params.useEvenAngles=false;
params.rootAngularSector=180;
params.rootSelectionPolicy=RootSelectionType.MOST_CLOSED_ROOT_SELECTION; //avoid overlap in a pile
//params.overlappingAvoidMethod=0;
// params.packingPolicy=50;
//params.packingSpacing=50;
vis.balloonLayout.params=params;
//vis.reLayout()
vis.itemsFactory.addOrReplaceClassReference('link', CustomLink);
}
private function linkStyleFunction(data:Object):LinkStyle
{
var linkStyle:LinkStyle=new LinkStyle();
linkStyle.arrowWidth=10
linkStyle.arrowHeight=20
linkStyle.arrowRadius=10
linkStyle.thickness=2
//relevance
if (data['rel'] == '0')
linkStyle.lineColor=0x333333
else if (data['rel'] == '1')
linkStyle.lineColor=0x999999
else if (data['rel'] == '2')
linkStyle.lineColor=0x5DE35F
else if (data['rel'] == '3')
linkStyle.lineColor=0xFFDC42
else if (data['rel'] == '4')
linkStyle.lineColor=0xFF4034
//direction
if (data['versus'] == 'in')
{
linkStyle.arrowSourceType=LinkStyle.LINK_ARROW_ARROW_TYPE
linkStyle.arrowTargetType=LinkStyle.LINK_ARROW_NONE_TYPE
}
else if (data['versus'] == 'out')
{
linkStyle.arrowSourceType=LinkStyle.LINK_ARROW_NONE_TYPE
linkStyle.arrowTargetType=LinkStyle.LINK_ARROW_ARROW_TYPE
}
else if (data['versus'] == 'both')
{
linkStyle.arrowSourceType=LinkStyle.LINK_ARROW_ARROW_TYPE;
linkStyle.arrowTargetType=LinkStyle.LINK_ARROW_ARROW_TYPE;
}
//type
if (data['type'] == 'peer')
linkStyle.renderingPolicy=LinkStyle.LINK_RENDERING_SOLID;
else if (data['type'] == 'know')
linkStyle.renderingPolicy=LinkStyle.LINK_RENDERING_DASH;
else if (data['type'] == 'identity')
{
linkStyle.patterns=[2,2]
linkStyle.renderingPolicy=LinkStyle.LINK_RENDERING_DASH;
}
return linkStyle;
}
private function nodeRendererFunction(data:Object):CustomNodeRenderer
{
var renderer:CustomNodeRenderer=new CustomNodeRenderer();
if (data['type'] == "target")
renderer.icon.source=renderer.TargetIcon;
else if (data['type'] == "person")
renderer.icon.source=renderer.PersonIcon;
else if (data['type'] == "position")
renderer.icon.source=renderer.PositionIcon;
else if (data['type'] == "virtual")
renderer.icon.source=renderer.VirtualIcon;
renderer.label.text=data['name'];
return renderer
}
public function draw(entities:ArrayCollection, filterCriteria:Object=null):void
{
positions_so=SharedObject.getLocal("entityPositions");
positions_so.clear() // uncomment to clean SO for dev/debug purposes
if (positions_so.data.positions == null)
{
positions_so.data.positions=new Dictionary();
}
if (this.entities == entities) //data not changed no need to redraw()
{
//return;
}
vis.removeAll()
vis.clearAll()
//initGraph()
this.entities=entities;
this.filterCriteria=filterCriteria
if (entities.length == 0 || !entities)
{
vis.visible=false;
return;
}
trace("Drawing Graph > Entities to show: " + entities.length)
addEntities()
addGroups()
addLinks()
vis.reLayout()
dispatchEvent(new Event("selectionChange"))
//vis.setFocus()
//vis.invalidateDisplayList()
setTimeout(collapseAll, 10)
}
private function addEntities():void
{
var node:GenericSprite;
var entity:Entity;
var hasPermission:Boolean;
var _id:String;
var child:Entity
var point:Point
var includeInGraphLayout:Boolean;
groups=new ArrayCollection()
for (var i:int=0; i < entities.length; i++)
{
entity=entities.getItemAt(i) as Entity;
if (entity.type != "group")
{
includeInGraphLayout=true
/* if (positions_so.data.positions[entity._id].x && positions_so.data.positions[entity._id].y)
{
point=new Point()
point.x=positions_so.data.positions[entity._id].x
point.y=positions_so.data.positions[entity._id].y
//includeInGraphLayout=false;
includeInGraphLayout=true;
}
else
{
point=null
includeInGraphLayout=true;
} */
node=vis.addNodeElement(entity, null, null, point, entity._id) as GenericSprite
node.includeInGraphLayout=includeInGraphLayout
}
else // is a group
{
groups.addItem(entity)
}
if (entity.type == "group" && entity.stand_for) //draw entities from other operations/groups
{
hasPermission=SearchManager.instance.getItem(entity.stand_for)
for each (_id in entity.children)
{
if (hasPermission)
child=EntityManager.instance.getItem(_id)
else
child=new Entity({_id: _id, type: "person"}); //draw just a fake entity in a closed group
if (child)
{
/* if (positions_so.data.positions[_id].x && positions_so.data.positions[_id].y)
{
point=new Point()
point.x=positions_so.data.positions[_id].x
point.y=positions_so.data.positions[_id].y
includeInGraphLayout=false;
includeInGraphLayout=true;
}
else
{
point=null
includeInGraphLayout=true;
} */
includeInGraphLayout=true
node=vis.addNodeElement(child, null, null, null, child._id) as GenericSprite
node.includeInGraphLayout=includeInGraphLayout;
entities.addItem(child)
}
}
}
}
}
private function addGroups():void
{
var elements:Array;
var c:int;
var entity:Entity
var group:GenericGroup
var hasPermission:Boolean;
var point:Point
var includeInGraphLayout:Boolean
for (var i:int=0; i < groups.length; i++)
{
entity=groups.getItemAt(i) as Entity;
if (entity.type != "group")
continue;
trace("GROUP: " + entity.stand_for)
elements=new Array();
for (c=0; c < entity.children.length; c++)
{
elements.push(vis.nodesMap[entity.children[c]]);
}
if (positions_so.data.positions[entity._id])
{
point=new Point()
point.x=positions_so.data.positions[entity._id].x
point.y=positions_so.data.positions[entity._id].y
//includeInGraphLayout=false;
includeInGraphLayout=true;
}
else
{
point=null
includeInGraphLayout=true;
}
group=vis.groupElements(entity, elements, null, entity._id) as GenericGroup
/* if(point)
group.setPosition(point.x,point.y) */
if (group)
{
if(point)
{
group.x=point.x;
group.y=point.y;
}
group.includeInGraphLayout=true;
//group.includeInGraphLayout=includeInGraphLayout;
hasPermission=SearchManager.instance.getItem(entity.stand_for);
if (!hasPermission && entity.stand_for)
{
group.isGroupExpanded=false;
group.forceNoExpandGroupButton=true;
}
}
}
}
private function collapseAll():void
{
for each (var node:Object in vis.nodesMap)
{
if (node is GenericGroup && GenericGroup(node).isGroupExpanded && !GenericGroup(node).forceNoExpandGroupButton)
GenericGroup(node).isGroupExpanded=false
}
//vis.reLayout();
}
private function addLinks():void
{
var i:int;
var link:Link;
var entity:Entity;
var source:ISprite;
var target:ISprite;
var links:Array=new Array();
for (i=0; i < entities.length; i++)
{
entity=entities.getItemAt(i) as Entity;
if (!entity)
continue;
if (entity.links)
{
for (var j:int=0; j < entity.links.length; j++)
{
link=entity.links.getItemAt(j) as Link;
if (GraphUtils.alreadyLinked(links, entity._id, link.le))
continue;
if (filterCriteria.relevance && filterCriteria.relevance.length > 0 && filterCriteria.relevance.indexOf(link.rel) == -1)
continue;
if (filterCriteria.programs && filterCriteria.programs.length > 0 && !matchPrograms(link)) //check programs
continue;
source=vis.nodesMap[entity._id];
target=vis.nodesMap[link.le];
vis.addLinkElement({type: link.type, rel: link.rel, level: link.level, versus: link.versus, info: link.info, source: entity._id, target: link.le}, source, target);
links.push({source: entity._id, target: link.le})
}
}
}
//vis.reLayout();
}
private function matchPrograms(link:Link):Boolean
{
for(var i:int=0;i<filterCriteria.programs.length;i++)
{
var program:String=filterCriteria.programs[i];
if(link.info[program]!=null)
return true
}
return false;
}
public function highlightLink(from:String, to:String):void
{
for (var k:int=0; k < vis.graph.edges.length; k++)
{
var currentEdge:Edge=vis.graph.edges[k] as Edge;
var currentRenderer:CustomLink=currentEdge.visualEdge as CustomLink
currentRenderer.unHighlight();
if (currentRenderer.data.source == from && currentRenderer.data.target == to || currentRenderer.data.source == to && currentRenderer.data.target == from)
{
currentRenderer.highlight()
selection=[currentRenderer]
dispatchEvent(new Event("selectionChange"))
}
}
}
public function highlightNode(id:String):void
{
for (var k:int=0; k < vis.graph.nodes.length; k++)
{
var vn:DiagramSprite=vis.graph.nodes[k].visualNode as DiagramSprite;
if (!vn)
continue;
vn.isSelected=false;
if (vis.graph.nodes[k].uid == id)
{
vn.isSelected=true;
selection=[vn]
dispatchEvent(new Event("selectionChange"))
}
}
}
protected function onResize(event:ResizeEvent):void
{
// TODO Auto-generated method stub
//vis.autoFit();
}
private function onDrag(e:VisualizerEvent):void
{
}
public function zoom(value:Number):void
{
vis.zoomContent(value,null,false,false);
}
protected function onDragEnd(event:VisualizerEvent):void
{
savePositions()
}
private function savePositions():void
{
for each (var node:Object in vis.nodesMap)
{
if (node is GenericGroup || node is GenericSprite)
{
trace("Entity name: "+node.data.name+"(x: "+node.x+", y: "+node.y+")");
//positions_so.data.positions[node.data._id]=new Point(node.x, node.y);
}
}
}
]]>
</fx:Script>
<diagrammer:Diagrammer width="100%"
height="100%"
id="vis"
layout="balloon"
creationComplete="initGraph()"
groupLabelField="name"
linkStyleFunction="{linkStyleFunction}"
nodeRendererFunction="{nodeRendererFunction}"
enableAnimation="false"
enableAutofitOnLayout="true"
enableZoomOnMouseWheel="false"
groupRendererClass="{CustomGroupRenderer}"
elementsDragFinished="onDragEnd(event)"
showNodeExpandCollapseButton="false"
elementsSelectionChanged="onSelectionChange(event)"
elementDoubleClicked="onElementDoubleClick(event)"/>
</s:Group>