hackedteam/rcs-console

View on GitHub
src/org/un/cava/birdeye/ravis/enhancedGraphLayout/visual/EnhancedVisualGraph.as

Summary

Maintainability
Test Coverage
package org.un.cava.birdeye.ravis.enhancedGraphLayout.visual
{
    import flash.display.DisplayObject;
    import flash.events.MouseEvent;
    import flash.geom.Point;
    import flash.utils.Dictionary;
    
    import mx.collections.ArrayCollection;
    import mx.containers.Canvas;
    import mx.core.IDataRenderer;
    import mx.core.IFactory;
    import mx.core.ScrollPolicy;
    import mx.core.UIComponent;
    import mx.utils.ObjectUtil;
    
    import org.un.cava.birdeye.ravis.enhancedGraphLayout.event.VGEdgeEvent;
    import org.un.cava.birdeye.ravis.enhancedGraphLayout.event.VGNodeEvent;
    import org.un.cava.birdeye.ravis.enhancedGraphLayout.visual.edgeRenderers.EdgeControlRenderer;
    import org.un.cava.birdeye.ravis.enhancedGraphLayout.visual.edgeRenderers.EdgeRenderer;
    import org.un.cava.birdeye.ravis.graphLayout.data.IEdge;
    import org.un.cava.birdeye.ravis.graphLayout.data.INode;
    import org.un.cava.birdeye.ravis.graphLayout.layout.HierarchicalLayouter;
    import org.un.cava.birdeye.ravis.graphLayout.visual.IVisualEdge;
    import org.un.cava.birdeye.ravis.graphLayout.visual.IVisualNode;
    import org.un.cava.birdeye.ravis.graphLayout.visual.VisualGraph;
    import org.un.cava.birdeye.ravis.graphLayout.visual.events.VisualGraphEvent;
    import org.un.cava.birdeye.ravis.utils.LogUtil;
    
    public class EnhancedVisualGraph extends VisualGraph
    {
        private static var _LOG:String = "graphLayout.visual.EnhancedVisualGraph";
        
        protected var _viewToVEdgeRendererMap:Dictionary;
        
        protected var _enableDefaultDoubleClick:Boolean = true;
        
        protected var _enableDragNodeWithSubTree:Boolean = false;
        
        /**
         * This is the current Edge Control component that is dragged by the mouse.
         * */
        protected var _dragControlComponent:UIComponent;
        
        
        /**
         * Also allow the specification of an IFactory for edge
         * controls.
         * */
        protected var _edgeControlRendererFactory:IFactory = null;
        
        /**
         * Specify whether edge labels should be displayed or not
         * */
        protected var _displayEdgeControls:Boolean = true;
        
        
        /**
         * Also allow the specification of an IFactory for node
         * labels.
         * */
        protected var _nodeLabelRendererFactory:IFactory = null;
        
        /**
         * Specify whether node labels should be displayed or not
         * */
        protected var _displayNodeLabels:Boolean = true;
        
        public function EnhancedVisualGraph()
        {
            super();
            _viewToVEdgeRendererMap = new Dictionary();
        }
        
        /**
         * @inheritDoc
         * */
        public function set edgeControlRenderer(ecr:IFactory):void {
            /* if the factory was changed, then we have to remove all
            * instances of vedgeViews to have them updated */
            if(ecr != _edgeLabelRendererFactory) {
                /* set all edges invisible, this should delete all instances
                * of view components */
                setAllEdgesInVisible();
                
                /* set the new renderer */
                _edgeControlRendererFactory = ecr;    
                
                /* update i.e. recreate the instances */
                updateEdgeVisibility();
            }
        }
        
        /**
         * @private
         * */
        public function get edgeControlRenderer():IFactory {
            return _edgeControlRendererFactory;
        }
        
        /**
         * @inheritDoc
         * */
        public function set nodeLabelRenderer(elr:IFactory):void {
            /* if the factory was changed, then we have to remove all
            * instances of vedgeViews to have them updated */
            if(elr != _nodeLabelRendererFactory) {
                /* set all edges invisible, this should delete all instances
                * of view components */
                setAllInVisible();
                
                /* set the new renderer */
                _nodeLabelRendererFactory = elr;    
                
                /* update i.e. recreate the instances */
                updateVisibility();
            }
        }
        
        /**
         * @private
         * */
        public function get nodeLabelRenderer():IFactory {
            return _nodeLabelRendererFactory;
        }
        
        /**
         * @inheritDoc
         * */
        public function set displayEdgeControls(dec:Boolean):void {
            var e:IEdge;
            
            if(_displayEdgeControls == dec) {
                // no change
            } else {
                _displayEdgeControls = dec;
                setAllEdgesInVisible();
                //_canvas.invalidateDisplayList(); // maybe this is enough
                updateEdgeVisibility();
            }
        }
        
        /**
         * @private
         * */
        public function get displayEdgeControls():Boolean {
            return _displayEdgeControls;
        }
        
        /**
         * @inheritDoc
         * */
        public function set displayNodeLabels(del:Boolean):void {
            var e:INode;
            
            if(_displayNodeLabels == del) {
                // no change
            } else {
                _displayNodeLabels = del;
                setAllInVisible();
                //_canvas.invalidateDisplayList(); // maybe this is enough
                updateVisibility();
            }
        }
        
        /**
         * @private
         * */
        public function get displayNodeLabels():Boolean {
            return _displayNodeLabels;
        }
        
        
        public function set moveGraphInDrag(f:Boolean):void {
            _moveGraphInDrag = f;
        }
        
        public function set moveEdgeInDrag(f:Boolean):void {
            _moveEdgeInDrag = f;
        }
        
        /**
         * Creates VNode and requires a Graph node to associate
         * it with. Originally also created the view, but we no
         * longer do that directly but only on demand.
         * @param n The graph node to be associated with.
         * @return The created VisualNode.
         * */
        protected override function createVNode(n:INode):IVisualNode {
            
            var vnode:IVisualNode;
            
            /* as an id we use the id of the graph node for simplicity
            * for now, it is not really used separately anywhere
            * we also use the graph data object as our data object.
            * the view is set to null and remains so. */
            vnode = new EnhancedVisualNode(this, n, n.id, null, n.data);
            
            /* if the node should be visible by default 
            * we need to make sure that the view is created */
            if(newNodesDefaultVisible) {
                setNodeVisibility(vnode, true);
            }
            
            /* now set the vnode in the node */
            n.vnode = vnode;
            
            /* add the node to the hash to keep track */
            _vnodes[vnode] = vnode;
            
            
            return vnode;
        }
        
        protected override function removeNodeView(component:UIComponent, honorEffect:Boolean = true):void {
            
            var vn:IVisualNode;
            
            /* if there is an effect, start the effect and register a
            * handler that actually calls this method again, but
            * with honorEffect set to false */
            if(honorEffect && (removeItemEffect != null)) {
                super.removeNodeView(component, honorEffect);
            } else {
                vn = _nodeViewToVNodeMap[component];
                super.removeNodeView(component, honorEffect);
                var labelView:UIComponent = IEnhancedVisualNode(vn).labelView;
                if (labelView && labelView.parent){
                    labelView.parent.removeChild(labelView);
                }
            }
        }
        
        /**
         * Removes a VNode, this also removes the node's view
         * if it existed, but does not touch the Graph node.
         * @param vn The VisualNode to be removed.
         * */
        protected override function removeVNode(vn:IVisualNode):void {            
            if (vn is IEnhancedVisualNode)
            {
                var labelView:UIComponent;
                labelView = IEnhancedVisualNode(vn).labelView;
                
                IEnhancedVisualNode(vn).labelView = null;
                
                if (labelView != null && labelView.parent) {
                    labelView.parent.removeChild(labelView);    
                }
            }
            
            super.removeVNode(vn);            
        }
        
        /**
         * Creates a VEdge from a graph Edge.
         * @param e The Graph Edge.
         * @return The created VEdge.
         * */
        protected override function createVEdge(e:IEdge):IVisualEdge {
            
            var vedge:IVisualEdge;
            var n1:INode;
            var n2:INode;
            var lStyle:Object;
            var edgeAttrs:XMLList;
            var attr:XML;
            var attname:String;
            var attrs:Array;
            
            /* create a copy of the default style */
            lStyle = ObjectUtil.copy(_defaultEdgeStyle);
            
            /* extract style data from associated XML data for each parameter */
            attrs = ObjectUtil.getClassInfo(lStyle).properties;
            
            if (e.data is XML)
            {
                for each(attname in attrs) {
                    if(e.data != null && (e.data as XML).attribute(attname).length() > 0) {
                        lStyle[attname] = e.data.@[attname];
                    }
                }
            }
            else
            {
                for each(attname in attrs) {
                    if(e.data != null && e.data.hasOwnProperty(attname)) {
                        lStyle[attname] = e.data[attname];
                    }
                }
            }
            vedge = new EnhancedVisualEdge(this, e, e.id, e.data, null, lStyle);
            
            /* set the VisualEdge reference in the graph edge */
            e.vedge = vedge;
            
            /* check if the edge is supposed to be visible */
            n1 = e.node1;
            n2 = e.node2;
            
            /* if both nodes are visible, the edge should
            * be made visible, which may also create a label
            */
            if(n1.vnode.isVisible && n2.vnode.isVisible) {
                setEdgeVisibility(vedge, true);
            }
            
            /* add to tracking hash */
            _vedges[vedge] = vedge;
            
            return vedge;
        }
        
        
        /**
         * Create a "view" object (UIComponent) for the given node and
         * return it. These methods are only exported to be used by
         * the VisualNode. Alas, AS does not provide the "friend" directive.
         * Not sure how to get around this problem right now.
         * @param vn The node to replace/add a view object.
         * @return The created view object.
         * */
        protected override function createVNodeComponent(vn:IVisualNode):UIComponent {
            
            var mycomponent:UIComponent = null;
            var mylabelcomponent:UIComponent = null;
            /////////////////////////////////////////////////////
            
            if((_nodeLabelRendererFactory != null) && 
                (vn is IEnhancedVisualNode) && 
                (_edgeRendererFactory.newInstance() is IControllableEdgeRenderer)) {
                mylabelcomponent = _nodeLabelRendererFactory.newInstance();
                /* assigns the edge to the IDataRenderer part of the view
                * this is important to access the data object of the VEdge
                * which contains information for rendering. */        
                if(mylabelcomponent is IDataRenderer) {
                    (mylabelcomponent as IDataRenderer).data = vn;
                }
                
                mylabelcomponent.x = this.width / 2.0;
                mylabelcomponent.y = this.height / 2.0;
                
                /* enable bitmap cachine if required */
                mylabelcomponent.cacheAsBitmap = cacheRendererObjects;
                /* add the component to its parent component
                * this can create problems, we have to see where we
                * check for all children
                * Add after the edges layer, but below all other elements such as nodes */
                nodeLayer.addChildAt(mylabelcomponent, 0);
                IEnhancedVisualNode(vn).labelView = mylabelcomponent;
            }    
            
            return super.createVNodeComponent(vn);
        }
        
        /**
         * Create a "view" object (UIComponent) for the given edge and
         * return it.
         * @param ve The edge to replace/add a view object.
         * @return The created view object.
         * */
        protected override function createVEdgeLabelView(ve:IVisualEdge):UIComponent {
            var mycomponent:UIComponent = super.createVEdgeLabelView(ve);
            if(_edgeLabelRendererFactory != null) {
                var fromControl:UIComponent;
                var toControl:UIComponent
                if ((_edgeControlRendererFactory != null) && (ve is IControllableVisualEdge)) {
                    fromControl = _edgeControlRendererFactory.newInstance();
                    fromControl['type'] = EdgeControlRenderer.FROM_CONTROL;
                    toControl = _edgeControlRendererFactory.newInstance();
                    toControl['type'] = EdgeControlRenderer.TO_CONTROL;
                    if (toControl is IDataRenderer)
                    {
                        (fromControl as IDataRenderer).data = ve;
                        (toControl as IDataRenderer).data = ve;
                    }
                    fromControl.cacheAsBitmap = cacheRendererObjects;
                    toControl.cacheAsBitmap = cacheRendererObjects;
                    IControllableVisualEdge(ve).fromControl = fromControl;
                    IControllableVisualEdge(ve).toControl = toControl;
                    Object(mycomponent).fromControl = fromControl;
                    Object(mycomponent).toControl = toControl;
                    fromControl.addEventListener(MouseEvent.MOUSE_DOWN, edgeMouseDown);
                    toControl.addEventListener(MouseEvent.MOUSE_DOWN, edgeMouseDown);
                    _viewToVEdgeRendererMap[fromControl] = mycomponent;
                    _viewToVEdgeRendererMap[toControl] = mycomponent;
                    
                    if(ve.edgeView)
                        ve.edgeView.render();
                }
            }
            
            return mycomponent;
        }        
        
        /**
         * Remove a "view" object (UIComponent) for the given edge.
         * @param component The UIComponent to be removed.
         * */
        protected override function removeVEdgeLabelView(component:UIComponent):void {
            
            /* remove the  control component from it's parent (which should be the canvas) */
            if(component.parent != null) {
                if (component is EdgeRenderer)
                {
                    var fromControl:UIComponent = EdgeRenderer(component).fromControl;
                    if (fromControl)
                    {
                        fromControl.parent.removeChild(fromControl);
                        delete _viewToVEdgeRendererMap[fromControl];
                        
                    }
                    
                    var toControl:UIComponent = EdgeRenderer(component).toControl
                    if (toControl)
                    {
                        toControl.parent.removeChild(toControl);
                        delete _viewToVEdgeRendererMap[toControl];
                    }
                }
            }
            
            super.removeVEdgeLabelView(component);
        }
        
        protected function edgeMouseDown(e:MouseEvent):void{
            dragControlBegin(e);
        }
        
        /**
         * Start a drag operation. This sets the drag node and
         * registeres a 'MouseMove' event handler with the
         * VNode, so it can follow the mouse movement.
         * @param event The MouseEvent that was triggered by clicking on the node.
         * @see handleDrag()
         * */
        protected override function dragBegin(event:MouseEvent):void {
            
            var ecomponent:UIComponent;
            var evnode:IVisualNode;
            var node:INode;
            
            var pt:Point;
            
            if (_moveNodeInDrag == false)
                return;
            
            /* if there is an animation in progress, we ignore
            * the drag attempt */
            if(_layouter && _layouter.animInProgress) {
                LogUtil.info(_LOG,"Animation in progress, drag attempt ignored");
                return;
            }
            
            /* make sure we get the right component */
            if(event.currentTarget is UIComponent) {
                
                ecomponent = (event.currentTarget as UIComponent);
                
                /* get the associated VNode of the view */
                evnode = _nodeViewToVNodeMap[ecomponent];
                
                /* stop propagation to prevent a concurrent backgroundDrag */
                event.stopImmediatePropagation();
                
                if(evnode != null) {
                    node = evnode.node;
                    
                    /* if (ElectricalGraph.isConnected(node) == false)
                    {
                    var dragSource:DragSource = new DragSource();
                    dragSource.addData(node, 'node');
                    DragManager.doDrag(ecomponent, dragSource, event);
                    return;
                    } */
                    
                    if(!dragLockCenter) {
                        // lockCenter is false, use the mouse coordinates at the point
                        pt = ecomponent.localToGlobal(new Point(ecomponent.mouseX, ecomponent.mouseY));
                    } else {
                        // lockCenter is true, ignore the mouse coordinates
                        // and use (0,0) instead as the point
                        //TODO: change to the components`s center instead
                        pt = ecomponent.localToGlobal(new Point(0,0));
                    }
                    
                    /* Save the offset values in the map 
                    * so we can compute x and y correctly in case
                    * we use lockCenter */
                    var nodeComponent:UIComponent;
                    var enode:INode = evnode.node;
                    
                    var arrTreeNodes:ArrayCollection = new ArrayCollection();
                    var arrTreeRoots:Array = [enode];
                    var curTreeRoot:INode = arrTreeRoots.pop();
                    
                    if (_enableDragNodeWithSubTree)
                    {
                        while(curTreeRoot)
                        {
                            if (arrTreeNodes.contains(curTreeRoot) == false)
                            {
                                arrTreeNodes.addItem(curTreeRoot);
                                for each (var nextTreeRoot:INode in curTreeRoot.successors)
                                arrTreeRoots.push(nextTreeRoot);
                            }
                            curTreeRoot = arrTreeRoots.pop();
                        }
                    }
                    else
                    {
                        arrTreeNodes.addItem(enode);
                    }
                    
                    for each (var movedNode:INode in arrTreeNodes)
                    {
                        nodeComponent = movedNode.vnode.view;
                        if (nodeComponent)
                        {
                            _drag_x_offsetMap[nodeComponent] = pt.x / (scaleX*this.scaleX) - nodeComponent.x;
                            _drag_y_offsetMap[nodeComponent] = pt.y / (scaleY*this.scaleY) - nodeComponent.y;
                        }
                    }
                    
                    
                    /* now we would need to set the bounds
                    * rectangle in _drag_boundsMap, but this is
                    * currently not implemented *
                    _drag_boundsMap[ecomponent] = rectangle;
                    */
                    
                    /* Registeran eventListener with the component's stage that
                    * handles any mouse move. This wires the component
                    * to the mouse. On every mouse move, the event handler
                    * is called, which updates its coordinates.
                    * We need to save the drag component, since we have to 
                    * register the event handler with the stage, not the component
                    * itself. But from the stage we have no way to get back to
                    * the component or the VNode in case of the mouse move or 
                    * drop event. 
                    */
                    _dragComponent = ecomponent;
                    ecomponent.stage.addEventListener(MouseEvent.MOUSE_MOVE, handleDrag);
                    this.addEventListener(MouseEvent.MOUSE_UP,dragEnd);
                    /* also register a drop event listener */
                    // ecomponent.stage.addEventListener(MouseEvent.MOUSE_UP, dragEnd);
                    
                    /* and inform the layouter about the dragEvent */
                    _layouter.dragEvent(event, evnode);
                } else {
                    throw Error("Event Component was not in the viewToVNode Map");
                }
            } else {
                throw Error("MouseEvent target was no UIComponent");
            }
        }
        
        protected function dragControlBegin(event:MouseEvent):void {
            
            var ecomponent:UIComponent;
            var evedge:IVisualEdge;
            var pt:Point;
            
            if (_moveEdgeInDrag == false)
                return;
            
            /* if there is an animation in progress, we ignore
            * the drag attempt */
            if(_layouter && _layouter.animInProgress) {
                LogUtil.info(_LOG,"Animation in progress, drag attempt ignored");
                return;
            }
            
            /* make sure we get the right component */
            if(event.currentTarget is UIComponent) {
                
                ecomponent = (event.currentTarget as UIComponent);
                
                /* stop propagation to prevent a concurrent backgroundDrag */
                event.stopImmediatePropagation();
                
                pt = ecomponent.localToGlobal(new Point(0,0));
                
                /* Save the offset values in the map 
                * so we can compute x and y correctly in case
                * we use lockCenter */
                _drag_x_offsetMap[ecomponent] = pt.x / (scaleX*this.scaleX) - ecomponent.x;
                _drag_y_offsetMap[ecomponent] = pt.y / (scaleY*this.scaleY) - ecomponent.y;
                
                /* now we would need to set the bounds
                * rectangle in _drag_boundsMap, but this is
                * currently not implemented *
                _drag_boundsMap[ecomponent] = rectangle;
                */
                
                /* Registeran eventListener with the component's stage that
                * handles any mouse move. This wires the component
                * to the mouse. On every mouse move, the event handler
                * is called, which updates its coordinates.
                * We need to save the drag component, since we have to 
                * register the event handler with the stage, not the component
                * itself. But from the stage we have no way to get back to
                * the component or the VNode in case of the mouse move or 
                * drop event. 
                */
                _dragControlComponent = ecomponent;
                ecomponent.stage.addEventListener(MouseEvent.MOUSE_MOVE, handleDragControl);
                this.addEventListener(MouseEvent.MOUSE_UP,dragControlEnd);
                /* also register a drop event listener */
                // ecomponent.stage.addEventListener(MouseEvent.MOUSE_UP, dragEnd);
            } else {
                throw Error("MouseEvent target was no UIComponent");
            }
        }
        
        /**
         * Called everytime the mouse moves after the dragBegin() method has
         * been called.  Updates the position of the Component based on
         * the location of the mouse cursor.
         * @param event The MouseMove event that has been triggered.
         */
        protected override function handleDrag(event:MouseEvent):void {
            var myvnode:IVisualNode;
            var sp:UIComponent;
            
            if (_moveNodeInDrag == false) {    
                return;
            }
            
            /* we set our Component to be the saved
            * dragComponent, because we cannot access it
            * through the event. */
            sp = _dragComponent;
            
            /* Sometimes we get spurious events */
            if(_dragComponent == null) {
                LogUtil.info(_LOG,"received handleDrag event but _dragComponent is null, ignoring");
                return;
            }
            
            /* update the coordinates with the current
            * event's stage coordinates (i.e. current mouse position),
            * modified by the lock-center offset */
            
            var ptrObj:Object;
            for (ptrObj in _drag_x_offsetMap)
            {
                sp = ptrObj as UIComponent;
                sp.x = event.stageX / (scaleX*this.scaleX) - _drag_x_offsetMap[sp];    
            }
            for (ptrObj in _drag_y_offsetMap)
            {
                sp = ptrObj as UIComponent;
                sp.y = event.stageY / (scaleY*this.scaleY) - _drag_y_offsetMap[sp];
            }
            
            /* and inform the layouter about the dragEvent */
            for (ptrObj in _drag_x_offsetMap)
            {
                sp = ptrObj as UIComponent;
                myvnode = _nodeViewToVNodeMap[sp];
                myvnode.refresh();
                if (myvnode is IEnhancedVisualNode)
                    IEnhancedVisualNode(myvnode).setNodeLabelCoordinates();
                _layouter.dragContinue(event, myvnode);
            }
            
            /* make sure flashplayer does an update after the event */
            refresh();
            event.updateAfterEvent();            
        }
        
        protected function handleDragControl(event:MouseEvent):void {
            var eRendererComp:EdgeRenderer;
            var sp:UIComponent;
            var controlType:int;
            var layoutOrientation:int;
            
            if (_moveEdgeInDrag == false) {    
                return;
            }
            //var bounds:Rectangle;
            
            /* we set our Component to be the saved
            * dragComponent, because we cannot access it
            * through the event. */
            sp = _dragControlComponent;
            eRendererComp = _viewToVEdgeRendererMap[sp];
            
            /* Sometimes we get spurious events */
            if(_dragControlComponent == null) {
                LogUtil.info(_LOG,"received handleDrag event but _dragComponent is null, ignoring");
                return;
            }
            
            sp['isDragging'] = true;
            
            /* update the coordinates with the current
            * event's stage coordinates (i.e. current mouse position),
            * modified by the lock-center offset */
            
            layoutOrientation = eRendererComp['layoutOrientation'];
            controlType = sp['type'];
            var oldPos:Number;
            if (controlType == EdgeControlRenderer.FROM_CONTROL)
            {
                var dFrom:Number;
                if ((layoutOrientation == HierarchicalLayouter.ORIENT_LEFT_RIGHT) ||
                    (layoutOrientation == HierarchicalLayouter.ORIENT_RIGHT_LEFT))
                {
                    oldPos = sp.x;
                    sp.x = event.stageX / (scaleX*this.scaleX) - _drag_x_offsetMap[sp];
                    dFrom = (sp.x - oldPos);
                }
                else if ((layoutOrientation == HierarchicalLayouter.ORIENT_BOTTOM_UP) ||
                    (layoutOrientation == HierarchicalLayouter.ORIENT_TOP_DOWN))
                {
                    oldPos = sp.y;
                    sp.y = event.stageY / (scaleY*this.scaleY) - _drag_y_offsetMap[sp];
                    dFrom = (sp.y - oldPos);
                }
                eRendererComp['fromDistance'] += dFrom;
            }
            else if (controlType == EdgeControlRenderer.TO_CONTROL)
            {
                var dTo:Number;
                if ((layoutOrientation == HierarchicalLayouter.ORIENT_BOTTOM_UP) ||
                    (layoutOrientation == HierarchicalLayouter.ORIENT_TOP_DOWN))
                {
                    oldPos = sp.x
                    sp.x = event.stageX / (scaleX*this.scaleX) - _drag_x_offsetMap[sp];
                    dTo = (sp.x - oldPos);
                }
                else if ((layoutOrientation == HierarchicalLayouter.ORIENT_LEFT_RIGHT) ||
                    (layoutOrientation == HierarchicalLayouter.ORIENT_RIGHT_LEFT))
                {
                    oldPos = sp.y
                    sp.y = event.stageY / (scaleY*this.scaleY) - _drag_y_offsetMap[sp];
                    dTo = (sp.y - oldPos);
                }
                
                eRendererComp['toDistance'] += dTo;
            }
            
            /* make sure flashplayer does an update after the event */
            refresh();            
        }
        
        /**
         * This handles a background drag (i.e. scroll). The
         * event listener is usually registered with the canvas,
         * i.e. this object.
         * @param event The triggered event.
         * */
        protected override function backgroundDragBegin(event:MouseEvent):void {
            
            if (_moveGraphInDrag == false)
                return;
            super.backgroundDragBegin(event);
        }
        
        /**
         * This method handles the drop event (usually MOUSE_UP).
         * It stops any dragging in progress (including background drag)
         * and unregisters the current dragged node.
         * @param event The triggered event.
         * */
        protected override function dragEnd(event:MouseEvent):void {
            
            var mycomp:UIComponent;
            var myback:DisplayObject;
            var myvnode:IVisualNode;
            this.removeEventListener(MouseEvent.MOUSE_UP,dragEnd);
            if(_backgroundDragInProgress) {
                
                /* if it was a background drag we stop it here */
                _backgroundDragInProgress = false;
                
                /* get the background drag object, which is usually
                * the canvasm so we just set it to this */
                //myback = (event.currentTarget as DisplayObject);
                myback = (this as DisplayObject);
                
                /* unregister event handler */                
                myback.removeEventListener(MouseEvent.MOUSE_MOVE,backgroundDragContinue);
                // myback.removeEventListener(MouseEvent.MOUSE_MOVE,dragEnd);
                
                /* and inform the layouter about the dropEvent */
                if(_layouter) {
                    _layouter.bgDropEvent(event);
                }
            } else {
                var ptrObj:Object; 
                if (_dragComponent){
                    _dragComponent = null;
                    
                    for (ptrObj in _drag_x_offsetMap)
                    {
                        mycomp = ptrObj as UIComponent;
                        if (mycomp.stage != null)
                        {
                            mycomp.stage.removeEventListener(MouseEvent.MOUSE_MOVE, handleDrag);
                        }
                        delete _drag_x_offsetMap[ptrObj];
                    }
                    for (ptrObj in _drag_y_offsetMap)
                    {
                        mycomp = ptrObj as UIComponent;
                        myvnode = _nodeViewToVNodeMap[mycomp];
                        if(_layouter) 
                        {
                            _layouter.dropEvent(event, myvnode);
                        }
                        delete _drag_y_offsetMap[ptrObj];
                        //TODO, Fix this
                        
                        this.dispatchEvent(new VGNodeEvent(VGNodeEvent.VG_NODE_END_DRAG, myvnode.node, event));
                    }
                }
                if (_dragControlComponent)
                {
                    
                    mycomp = _dragControlComponent;
                    
                    /* reset the dragComponent */
                    _dragControlComponent = null;
                    
                    for (ptrObj in _drag_x_offsetMap)
                    {
                        mycomp = ptrObj as UIComponent;
                        if (mycomp.stage != null)
                        {
                            mycomp.stage.removeEventListener(MouseEvent.MOUSE_MOVE, handleDrag);
                        }
                        delete _drag_x_offsetMap[ptrObj];
                    }
                    for (ptrObj in _drag_y_offsetMap)
                    {
                        mycomp = ptrObj as UIComponent;
                        if(_layouter) 
                        {
                            _layouter.dropEvent(event, myvnode);
                        }
                        delete _drag_y_offsetMap[ptrObj];
                    }
                    
                    /* remove the event listeners */
                    //mycomp.stage.removeEventListener(MouseEvent.MOUSE_DOWN, dragEnd);
                    // HACK: I have to check the stage because there are eventual components not added to the display list
                    if (mycomp.stage != null)
                    {
                        mycomp.stage.removeEventListener(MouseEvent.MOUSE_MOVE, handleDragControl);
                    }
                }
            }
            
            /* and stop propagation, as otherwise we could get the
            * event multiple times */
            //event.stopImmediatePropagation();            
        }
        
        protected function dragControlEnd(event:MouseEvent):void {
            
            this.removeEventListener(MouseEvent.MOUSE_UP,dragControlEnd);
            if(_backgroundDragInProgress) {
                var myback:DisplayObject;
                /* if it was a background drag we stop it here */
                _backgroundDragInProgress = false;
                
                /* get the background drag object, which is usually
                * the canvasm so we just set it to this */
                //myback = (event.currentTarget as DisplayObject);
                myback = (this as DisplayObject);
                
                /* unregister event handler */                
                myback.removeEventListener(MouseEvent.MOUSE_MOVE,backgroundDragContinue);
                // myback.removeEventListener(MouseEvent.MOUSE_MOVE,dragEnd);
                
                /* and inform the layouter about the dropEvent */
                if(_layouter) {
                    _layouter.bgDropEvent(event);
                }
            } else {
                
                /* if it was no background drag, the component
                * is the saved dragComponent */
                var sp:UIComponent = _dragControlComponent;
                var mycomp:UIComponent;
                var myvedge:IVisualEdge;
                /* But sometimes the dragComponent was already null, 
                * in this case we have to ignore the thing. */
                if(sp == null) {
                    LogUtil.info(_LOG,"dragEnd: received dragEnd but _dragComponent was null, ignoring");
                    return;
                }
                
                var eRendererComp:EdgeRenderer = _viewToVEdgeRendererMap[sp];
                myvedge = _edgeLabelViewToVEdgeMap[eRendererComp];
                /* remove the event listeners */
                //mycomp.stage.removeEventListener(MouseEvent.MOUSE_DOWN, dragEnd);
                // HACK: I have to check the stage because there are eventual components not added to the display list
                
                /* bounds are not implemented:
                bounds = _drag_boundsMap[sp];
                */
                
                /* update the coordinates with the current
                * event's stage coordinates (i.e. current mouse position),
                * modified by the lock-center offset */
                
                var layoutOrientation:int = eRendererComp['layoutOrientation'];
                var controlType:uint = sp['type'];
                var isDragging:Boolean = sp['isDragging'] as Boolean;
                var oldPos:Number;
                if (isDragging == true)
                {
                    if (controlType == EdgeControlRenderer.FROM_CONTROL)
                    {
                        var dFrom:Number;
                        if ((layoutOrientation == HierarchicalLayouter.ORIENT_LEFT_RIGHT) ||
                            (layoutOrientation == HierarchicalLayouter.ORIENT_RIGHT_LEFT))
                        {
                            oldPos = sp.x;
                            sp.x = event.stageX / (scaleX*this.scaleX) - _drag_x_offsetMap[sp];
                            dFrom = (sp.x - oldPos);
                        }
                        else if ((layoutOrientation == HierarchicalLayouter.ORIENT_BOTTOM_UP) ||
                            (layoutOrientation == HierarchicalLayouter.ORIENT_TOP_DOWN))
                        {
                            oldPos = sp.y;
                            sp.y = event.stageY / (scaleY*this.scaleY) - _drag_y_offsetMap[sp];
                            dFrom = (sp.y - oldPos);
                        }
                        eRendererComp['fromDistance'] += dFrom;
                    }
                    else if (controlType == EdgeControlRenderer.TO_CONTROL)
                    {
                        var dTo:Number;
                        if ((layoutOrientation == HierarchicalLayouter.ORIENT_BOTTOM_UP) ||
                            (layoutOrientation == HierarchicalLayouter.ORIENT_TOP_DOWN))
                        {
                            oldPos = sp.x
                            sp.x = event.stageX / (scaleX*this.scaleX) - _drag_x_offsetMap[sp];
                            dTo = (sp.x - oldPos);
                        }
                        else if ((layoutOrientation == HierarchicalLayouter.ORIENT_LEFT_RIGHT) ||
                            (layoutOrientation == HierarchicalLayouter.ORIENT_RIGHT_LEFT))
                        {
                            oldPos = sp.y
                            sp.y = event.stageY / (scaleY*this.scaleY) - _drag_y_offsetMap[sp];
                            dTo = (sp.y - oldPos);
                        }
                        
                        eRendererComp['toDistance'] += dTo;
                    }
                }
                /* reset the dragComponent */
                var ptrObj:Object;
                for (ptrObj in _drag_x_offsetMap)
                {
                    mycomp = ptrObj as UIComponent;
                    if (mycomp.stage != null)
                    {
                        mycomp.stage.removeEventListener(MouseEvent.MOUSE_MOVE, handleDragControl);
                    }
                    delete _drag_x_offsetMap[ptrObj];
                }
                for (ptrObj in _drag_y_offsetMap)
                {
                    mycomp = ptrObj as UIComponent;
                    delete _drag_y_offsetMap[ptrObj];
                }
                _dragControlComponent = null;
                sp['isDragging'] = false;
                this.removeEventListener(MouseEvent.MOUSE_UP,dragControlEnd);
                if (isDragging)
                    this.dispatchEvent(new VGEdgeEvent(VGEdgeEvent.VG_EDGE_CONTROL_END_DRAG, myvedge.edge, event));
                
            }
            
            /* and stop propagation, as otherwise we could get the
            * event multiple times */
            //event.stopImmediatePropagation();            
        }
        
        public override function draw(flags:uint=0):void
        {
            super.draw(flags);
            //_canvas.dispatchEvent(new VGraphEvent(VGraphEvent.VGRAPH_CHANGED)); 
        } 
        
        public function get enableDefaultDoubleClick():Boolean
        {
            return _enableDefaultDoubleClick;
        }
        
        public function set enableDefaultDoubleClick(value:Boolean):void
        {
            _enableDefaultDoubleClick = value;
        }
        
        protected override function nodeDoubleClick(e:MouseEvent):void 
        {
            if (_enableDefaultDoubleClick)
                super.nodeDoubleClick(e);
        }
        
        public function get enableDragNodeWithSubTree():Boolean
        {
            return _enableDragNodeWithSubTree;
        }
        
        public function set enableDragNodeWithSubTree(value:Boolean):void
        {
            _enableDragNodeWithSubTree = value;
        }
        
        public function createVNodeFromVO(sid:String, data:Object):IVisualNode
        {
            var tmpVEdge:IVisualEdge;
            var tmpEdge:IEdge;
            var edgeData:Object;
            var node:INode;
            var vnode:IVisualNode;
            
            if (_graph == null)
                return null;
            
            node = _graph.createNode(sid, data);
            
            vnode = createVNode(node);
            setNodeVisibility(vnode, true);
            
            return vnode;
        }
        
        public function addVNodeAsChild(sid:String, data:Object, parentNode:INode):Boolean
        {
            var tmpVEdge:IVisualEdge;
            var tmpEdge:IEdge;
            var edgeData:Object;
            var node:INode;
            var vnode:IVisualNode;
            
            if (_graph == null)
                return false;
            
            node = _graph.createNode(sid, data);
            vnode = createVNode(node);
            setNodeVisibility(vnode, true);
            
            var nodeID:String = node.stringid;
            edgeData = new Object()
            edgeData.fromID = parentNode.stringid;
            edgeData.toID = node.stringid;
            
            tmpEdge = _graph.link(parentNode,node,edgeData);
            
            if(tmpEdge == null) {
                throw Error("Could not create or find Graph edge!!");
            } else {
                if(tmpEdge.vedge == null) {
                    /* we have a new edge, so we create a new VEdge */
                    tmpVEdge = createVEdge(tmpEdge);
                } else {
                    /* existing one, so we use the existing vedge */
                    tmpVEdge = tmpEdge.vedge;
                }
            }
            draw();
            return true;
        }
        
        public function addVNodeToEdge(sid:String, data:Object, edge:IEdge, removeOldEdge:Boolean = false):Boolean
        {
            var fromNode:INode = edge.fromNode;
            var toNode:INode = edge.toNode;    
            var tmpVEdge:IVisualEdge;
            var tmpEdge:IEdge;
            var edgeData:Object;
            var node:INode;
            var vnode:IVisualNode;
            
            node = _graph.createNode(sid, data);
            node.data['id'] = node.stringid;
            
            vnode = createVNode(node);
            setNodeVisibility(vnode, true);
            var nodeID:String = node.stringid;
            
            edgeData = new Object();
            edgeData.fromID = fromNode.stringid;
            edgeData.toID = node.stringid;
            
            //tmpVEdge = linkNodes(fromNode.vnode, node.vnode, edgeData);
            
            tmpEdge = _graph.link(fromNode,node,edgeData);
            
            if(tmpEdge == null) {
                throw Error("Could not create or find Graph edge!!");
            } else {
                if(tmpEdge.vedge == null) {
                    /* we have a new edge, so we create a new VEdge */
                    tmpVEdge = createVEdge(tmpEdge);
                } else {
                    /* existing one, so we use the existing vedge */
                    tmpVEdge = tmpEdge.vedge;
                }
            }
            
            edgeData = new Object();
            edgeData.fromID = node.stringid;
            edgeData.toID = toNode.stringid;
            //tmpVEdge = linkNodes(node.vnode, toNode.vnode, edgeData);
            
            tmpEdge = _graph.link(node,toNode,edgeData);
            
            if(tmpEdge == null) {
                throw Error("Could not create or find Graph edge!!");
            } else {
                if(tmpEdge.vedge == null) {
                    /* we have a new edge, so we create a new VEdge */
                    tmpVEdge = createVEdge(tmpEdge);
                } else {
                    /* existing one, so we use the existing vedge */
                    tmpVEdge = tmpEdge.vedge;
                }
            }
            
            if (removeOldEdge)
            {
                //unlinkNodes(fromNode.vnode, toNode.vnode);
                tmpEdge = _graph.getEdge(fromNode,toNode);
                if (tmpEdge)
                {
                    tmpVEdge = tmpEdge.vedge;            
                    removeVEdge(tmpVEdge);
                    _graph.removeEdge(tmpEdge);
                }
            }
            
            draw();
            return true;
        }
        
        public function removeNodeWithOption(node:INode, rebindEdge:Boolean = true):Boolean
        {
            var vnode:IVisualNode = node.vnode;
            var successors:Array = node.successors;
            var precessors:Array = node.predecessors;
            
            var tmpEdge:IEdge;
            var tmpVEdge:IVisualEdge;
            
            if (rebindEdge && (precessors.length == 1))
            {
                var precessor:INode = precessors[0] as INode;
                var oldEdge:IEdge = _graph.getEdge(precessor, node);
                if (oldEdge != null)
                {
                    for (var i:int = 0; i < successors.length; i++)
                    {
                        var fromNode:INode = precessor;
                        var toNode:INode = successors[i] as INode;
                        var edgeData:Object = ObjectUtil.copy(oldEdge.data);
                        edgeData.toID = toNode.stringid;
                        tmpEdge = _graph.link(fromNode,toNode,edgeData);
                        if(tmpEdge == null) {
                            throw Error("Could not create or find Graph edge!!");
                        } else {
                            if(tmpEdge.vedge == null) {
                                /* we have a new edge, so we create a new VEdge */
                                tmpVEdge = createVEdge(tmpEdge);
                            } else {
                                /* existing one, so we use the existing vedge */
                                tmpVEdge = tmpEdge.vedge;
                            }
                        }
                    }
                }
            }
            
            removeNode(vnode);
            draw();
            return true;
        }
        
        /**
         * Removes a subtree from the main tree for which the root node is @node.
         */ 
        public function removeSubTree(node:INode, isRootNodeRemovable:Boolean = true):Boolean
        {
            if ((isRootNodeRemovable == false) && (node.vnode == currentRootVNode))
                return false;
            
            var arrTreeRoots:Array = [node];
            var curTreeRoot:INode = arrTreeRoots.pop();
            
            while(curTreeRoot)
            {
                for each (var nextTreeRoot:INode in curTreeRoot.successors)
                arrTreeRoots.push(nextTreeRoot)
                removeNode(curTreeRoot.vnode);
                
                curTreeRoot = arrTreeRoots.pop();
            }
            
            draw();
            return true;
        }
        
        /**
         * Removes all hierarchical children of the node referred to by parameter
         * @node.
         */ 
        public function removeNodeChildren(node:INode):Boolean
        {
            var arrTreeRoots:Array = new Array();
            for each (var nextTreeRoot:INode in node.successors)
            arrTreeRoots.push(nextTreeRoot);
            
            var curTreeRoot:INode = arrTreeRoots.pop();
            
            while(curTreeRoot)
            {
                for each (var nextTreeRoot2:INode in curTreeRoot.successors)
                arrTreeRoots.push(nextTreeRoot)
                removeNode(curTreeRoot.vnode);
                
                curTreeRoot = arrTreeRoots.pop();
            }
            
            draw();
            return true;
        }
        
        public function setVNodeData(vn:IVisualNode, data:Object):void
        {
            vn.data = data;
            vn.node.data = data;
            
            if (vn.view)
            {
                IDataRenderer(vn.view).data = vn;
            }
            
            this.refresh();
        }
        
        public function setVEdgeData(ve:IVisualEdge, data:Object):void
        {
            ve.data = data;
            ve.edge.data = data;
            if (ve.labelView)
            {
                IDataRenderer(ve.labelView).data = ve;
            }
            this.refresh();
        }
        
        public function setVisibleNodeWithRelated(vn:IVisualNode):void
        {
            _layouter.resetAll();
            
            for each(var tmpNode:IVisualNode in _visibleVNodes) {
                setNodeVisibility(tmpNode, false);
            }
            
            for each(var e:IEdge in _graph.edges) {
                setEdgeVisibility(e.vedge, true);
            }
            
            var enode:INode = vn.node;
            
            var arrTreeNodes:ArrayCollection = new ArrayCollection();
            var arrTreeRoots:Array = [enode];
            var curTreeRoot:INode = arrTreeRoots.pop();
            
            while(curTreeRoot)
            {
                if (arrTreeNodes.contains(curTreeRoot) == false)
                {
                    arrTreeNodes.addItem(curTreeRoot);
                }
                
                if     (curTreeRoot.vnode != currentRootVNode)
                {
                    for each (var prevTreeRoot:INode in curTreeRoot.predecessors)
                    {
                        if (arrTreeRoots.indexOf(prevTreeRoot) < 0)
                            arrTreeRoots.push(prevTreeRoot);
                    }
                }
                curTreeRoot = arrTreeRoots.pop();
            }
            
            arrTreeRoots = [enode];
            curTreeRoot = arrTreeRoots.pop();
            
            while(curTreeRoot)
            {
                if (arrTreeNodes.contains(curTreeRoot) == false)
                {
                    arrTreeNodes.addItem(curTreeRoot);
                }
                
                for each (var nextTreeRoot:INode in curTreeRoot.successors)
                {
                    if (arrTreeRoots.indexOf(nextTreeRoot) < 0)
                        arrTreeRoots.push(nextTreeRoot);
                }
                curTreeRoot = arrTreeRoots.pop();
            }
            
            
            for each (var movedNode:INode in arrTreeNodes)
            {
                setNodeVisibility(movedNode.vnode, true);
            }
            
            for each(var tmpEdge:IVisualEdge in _visibleVEdges) {
                if (tmpEdge.edge.fromNode.vnode.isVisible == false ||
                    tmpEdge.edge.toNode.vnode.isVisible == false)
                {
                    setEdgeVisibility(tmpEdge, false);
                }
            }
            
            this.draw();
        }
    }
}