- Updated flash navigator: better objects placement, auto-zoom & pan at startup, improved tooltip with some details on each objects.

SVN:trunk[710]
This commit is contained in:
Denis Flaven
2010-08-29 19:38:36 +00:00
parent bd001f8072
commit e6bdaf87e7
6 changed files with 435 additions and 63 deletions

View File

@@ -5,6 +5,7 @@
import flash.net.*;
import flash.events.*;
import flash.text.*;
import flash.xml.*;
import flash.ui.ContextMenu;
import flash.ui.ContextMenuItem;
import iTop.ToolTip;
@@ -14,27 +15,46 @@
public class GraphNode extends Sprite
{
private var m_oIcon:Loader;
private var m_sClass;String;
private var m_sClass:String;
private var m_sClassName:String;
private var m_iId:Number;
private var m_sParentKey:String;
private var m_oToolTip:ToolTip;
private var m_fZoom:Number;
private var m_oParent:Navigator;
private static const ROUND:Number = 20;
private static const PADDING:Number = 5;
public var m_speed_x:Number = 0;
public var m_speed_y:Number = 0;
public var m_bInDrag:Boolean = false;
public function GraphNode(oParent:Navigator, oPt:Point, sClass:String, iId:Number, sLabel:String, sIconPath:String, sParentKey:String, fZoom:Number)
public function GraphNode(oParent:Navigator, oPt:Point, sClass:String, sClassName:String, iId:Number, sLabel:String, sIconPath:String, sParentKey:String, fZoom:Number, oDetails:Object)
{
x = oPt.x;
y = oPt.y;
m_fZoom = fZoom;
m_sClass = sClass;
m_sClassName = sClassName;
m_iId = iId;
m_sLabel.autoSize = TextFieldAutoSize.LEFT;
m_sLabel.multiline = false;
m_sLabel.text = sLabel;
m_sLabel.autoSize = TextFieldAutoSize.CENTER;
m_sLabel.width = m_sLabel.textWidth;
m_sLabel.x = -m_sLabel.width/2;
m_sLabel.height = m_sLabel.textHeight;
// Draw the background
graphics.beginFill( 0xf1f1f6, 0.8 );
graphics.drawRoundRect( m_sLabel.x -PADDING, m_sLabel.y - PADDING, m_sLabel.width+PADDING*2, m_sLabel.height+PADDING*2, ROUND );
graphics.endFill();
m_sParentKey = sParentKey;
m_oToolTip = new ToolTip( "<p><b>"+m_sClass+"</b></p><p>"+sLabel+"</p>");
var sTooltipText:String = "<p><b>"+m_sClassName+"</b></p><p>"+sLabel+"</p>";
for (var s:String in oDetails)
{
sTooltipText += '<p>'+s+': '+oDetails[s]+'</p>';
}
trace('Tooltip text: '+sTooltipText);
m_oToolTip = new ToolTip(sTooltipText);
m_oToolTip.scaleX = 1 / m_fZoom;
m_oToolTip.scaleY = 1 / m_fZoom;
m_oParent = oParent;
@@ -94,11 +114,14 @@
{
trace("Click in Node");
m_oParent.m_bChildDragging = true;
m_bInDrag = true;
m_oToolTip.timer.stop(); // Don't show the tooltip while dragging
startDrag();
}
function mouseReleased(event:MouseEvent):void
{
m_bInDrag = false;
stopDrag();
m_oParent.m_bChildDragging = false;
}
@@ -130,5 +153,10 @@
return sDefaultValue;
}
}
public function GetLabelWidth():Number
{
return m_sLabel.width;
}
}
}

View File

@@ -1,4 +1,4 @@
package iTop
package iTop
{
import flash.display.*;
import flash.geom.*;
@@ -8,7 +8,7 @@
import fl.controls.Slider;
import fl.events.SliderEvent;
import fl.controls.Label;
// The main canvas
public class Navigator extends MovieClip
{
@@ -28,8 +28,14 @@
protected var m_sObjId:String;
// Constants
protected var m_RADIUS = 120;
protected var m_ITEMS_PER_ROW = 8;
protected var m_RADIUS = 150;
protected var m_Q = 0.9; // Electrostatic forces coeff
protected var m_K = 1.0; // Elastic forces coeff
protected var m_Kf = 0.7; // Fluid friction coeff
protected var m_Ks = 30; // Solid friction coeff
protected var m_deltaT = 0.1; // Interval of time between updates
protected var m_MAX_ITEMS_PER_ROW = 8;
protected var m_FOCUS_DELAY_COUNTDOWN = 50; // 50 images to zoom & pan correctly
protected var m_fZoom:Number;
// Constructor
@@ -41,6 +47,8 @@
initParameters();
doLoadData();
addEventListener(Event.ENTER_FRAME, initGraphics);
//Stop scaling the flash content
stage.scaleMode = StageScaleMode.NO_SCALE;
}
protected function initParameters():void
@@ -62,11 +70,6 @@
m_oCanvas.scaleY = m_fZoom;
// Handle listeners...
removeEventListener(Event.ENTER_FRAME,initGraphics);
addEventListener(Event.ENTER_FRAME, drawLines);
m_oZoomSlider.value = 100;
m_oZoomSlider.addEventListener(SliderEvent.CHANGE, onZoomChange);
stage.addEventListener(MouseEvent.MOUSE_DOWN, mouseDown)
stage.addEventListener(MouseEvent.MOUSE_UP, mouseReleased);
}
function mouseDown(event:MouseEvent):void
{
@@ -87,14 +90,24 @@
function onZoomChange(event:SliderEvent):void
{
m_fZoom = event.value/100;
SetZoomLevel(event.value/100);
}
function SetZoomLevel(fZoomLevel:Number):void
{
m_fZoom = fZoomLevel;
m_oCanvas.scaleX = m_fZoom;
m_oCanvas.scaleY = m_fZoom;
}
function doLoadData()
{
var myString:String = m_sDataUrl+'?relation='+m_sRelation+'&class='+m_sObjClass+'&id='+m_sObjId;
var sSeparator:String = '?';
if (m_sDataUrl.indexOf(sSeparator) != -1)
{
sSeparator = '&';
}
var myString:String = m_sDataUrl+sSeparator+'relation='+m_sRelation+'&class='+m_sObjClass+'&id='+m_sObjId;
trace("Requesting:"+myString);
var myXMLURL:URLRequest = new URLRequest(myString);
m_oLoader = new URLLoader();
@@ -109,10 +122,17 @@
var myXML:XML = XML(m_oLoader.data);
//trace("Data loaded." + myXML);
//trace("===========================");
parseXMLData(null, myXML, 0);
parseXMLData(null, myXML, 0, 0);
m_sTitle.text = myXML.attribute("title");
m_oZoomSlider.enabled = true;
removeChild(m_oPreloader);
addEventListener(Event.ENTER_FRAME, drawLines);
m_oZoomSlider.value = 100;
m_oZoomSlider.addEventListener(SliderEvent.CHANGE, onZoomChange);
stage.addEventListener(MouseEvent.MOUSE_DOWN, mouseDown)
stage.addEventListener(MouseEvent.MOUSE_UP, mouseReleased);
//trace('======= Initial Posistions =========');
//DumpPositions();
}
function onXMLLoadError(event:IOErrorEvent):void
@@ -120,11 +140,11 @@
trace("An error occured:" + Event);
}
function parseXMLData(oParentNode:GraphNode, oXMLData:XML, iChildIndex:Number)
function parseXMLData(oParentNode:GraphNode, oXMLData:XML, iChildIndex:Number, iChildCount:Number)
{
//trace(oXMLData.child("node").length());
var oNode:GraphNode;
oNode = addNode(oParentNode, oXMLData.child("node")[0], iChildIndex);
oNode = addNode(oParentNode, oXMLData.child("node")[0], iChildIndex, iChildCount);
if (oParentNode != null)
{
AddLink(oParentNode.GetKey(), oNode.GetKey());
@@ -138,30 +158,43 @@
var oLink = oLinks.link;
for each(var oChild:XML in oLink)
{
parseXMLData(oNode, oChild, iChildIndex);
parseXMLData(oNode, oChild, iChildIndex, oLinks.link.length());
iChildIndex++;
}
}
}
function addNode(oParent:GraphNode, oXMLData:XML, iChildIndex)
function addNode(oParent:GraphNode, oXMLData:XML, iChildIndex:Number, iChildCount:Number)
{
var sClass = oXMLData.@obj_class;
var sClass:String = oXMLData.@obj_class;
var sClassName:String = oXMLData.@obj_class_name;
var iId = oXMLData.@id;
var sLabel = oXMLData.@name;
var sIcon = oXMLData.@icon;
var sLabel:String = oXMLData.@name;
var sIcon:String = oXMLData.@icon;
var oDetails:Object = new Object;
var sZlist:String = oXMLData.@zlist;
var oNode:GraphNode = GetNode(sClass+'/'+iId);
if (oNode == null)
{
// If the node does not already exist, let's create it
var oPt:Point = GetNextFreePosition(oParent, iChildIndex);
var oPt:Point = GetNextFreePosition(oParent, iChildIndex, iChildCount);
var sParentKey = null;
if (oParent != null)
{
sParentKey = oParent.GetKey();
}
oNode = new GraphNode(this, oPt, sClass, iId, sLabel, sIcon, sParentKey, m_fZoom);
// Read the details
var aDetails:Array;
aDetails = sZlist.split(',');
for(var i:String in aDetails)
{
//if (oXMLData.hasOwnProperty('att_'+i))
//{
oDetails[aDetails[i]] = oXMLData.attribute('att_'+i).toString();
//}
}
oNode = new GraphNode(this, oPt, sClass, sClassName, iId, sLabel, sIcon, sParentKey, m_fZoom, oDetails);
this.m_aNodes[oNode.GetKey()] = oNode; //Keep it referenced
if (oParent == null)
{
@@ -185,7 +218,7 @@
}
}
function GetNextFreePosition(oParent:GraphNode, iChildIndex:Number):Point
function GetNextFreePosition(oParent:GraphNode, iChildIndex:Number, iChildCount:Number):Point
{
var oPt:Point = GetInitialPosition();
var angle:Number = GetInitialAngle();
@@ -208,13 +241,43 @@
angle = Math.atan2(dy, dx);
}
}
var radius = m_RADIUS * Math.floor(iChildIndex / m_ITEMS_PER_ROW);
angle += iChildIndex*(2*Math.PI) / m_ITEMS_PER_ROW;
var nbItemsOnRow:Number = 0;
var nbRows:Number = 0;
// Determines the position of this element
// The elements are placed on circles of maximum m_MAX_ITEMS_PER_ROW elements per row
// The last row containing potentially less items
// nbRows indicates on which row (first row = 0) the item is to be placed
if (iChildCount > m_MAX_ITEMS_PER_ROW)
{
nbRows = Math.floor(iChildIndex / m_MAX_ITEMS_PER_ROW);
if ( iChildIndex > (Math.floor(iChildCount / m_MAX_ITEMS_PER_ROW)*m_MAX_ITEMS_PER_ROW))
{
// node is on the last (incomplete) row
nbItemsOnRow = (iChildCount % m_MAX_ITEMS_PER_ROW);
}
else
{
nbItemsOnRow = m_MAX_ITEMS_PER_ROW;
}
}
else
{
if (iChildCount == 2)
{
nbItemsOnRow = 4; // Nicer display than everything aligned at 180 deg.
}
else
{
nbItemsOnRow = iChildCount;
}
}
var radius = this.m_RADIUS * (1 + nbRows);
angle += (1 - 2*((1+iChildIndex) % 2))*(Math.floor((1+iChildIndex) / 2))*(2*Math.PI) / nbItemsOnRow;
oPt.x += m_RADIUS * Math.cos(angle);
oPt.y += m_RADIUS * Math.sin(angle);
oPt.x += radius * Math.cos(angle);
oPt.y += radius * 0.7 * Math.sin(angle); // Ellipse because the labels are written horizontally !
trace("iChildIndex: "+iChildIndex+" x: "+oPt.x+" y: "+oPt.y+" sGdParentKey: "+sGrandParentKey);
//trace("iChildIndex: "+iChildIndex+" (iChildCount: "+iChildCount+") x: "+oPt.x+" y: "+oPt.y+" sGdParentKey: "+sGrandParentKey);
}
return oPt;
}
@@ -280,8 +343,10 @@
function drawLines(event:Event):void
{
var color:uint = 0x666666;
m_oCanvas.graphics.clear();
m_oCanvas.graphics.lineStyle(2,0x666666,100);
UpdatePositions();
for (var index:String in m_aLinks)
{
var oStartNode:GraphNode = GetNode(m_aLinks[index].GetStart());
@@ -289,18 +354,28 @@
m_oCanvas.graphics.moveTo(oStartNode.x, oStartNode.y);
m_oCanvas.graphics.lineTo(oEndNode.x, oEndNode.y);
var oMiddlePoint:Point = new Point((oEndNode.x+oStartNode.x)/2, (oEndNode.y+oStartNode.y)/2);
drawArrow(oMiddlePoint, oEndNode.x - oStartNode.x, oEndNode.y - oStartNode.y);
drawArrow(oMiddlePoint, oEndNode.x - oStartNode.x, oEndNode.y - oStartNode.y, color);
}
if (this.m_FOCUS_DELAY_COUNTDOWN > 0)
{
this.m_FOCUS_DELAY_COUNTDOWN--;
trace('FOCUS_DELAY:'+this.m_FOCUS_DELAY_COUNTDOWN);
UpdatePanAndZoom(m_FOCUS_DELAY_COUNTDOWN / 30);
}
}
function drawArrow(oPt:Point, dx:Number, dy:Number):void
function drawArrow(oPt:Point, dx:Number, dy:Number, color:uint):void
{
var l:Number = Math.sqrt(dx*dx+dy*dy);
var arrowSize:Number = 5;
m_oCanvas.graphics.lineStyle(2,0x666666,100,false,"normal",CapsStyle.ROUND);
m_oCanvas.graphics.moveTo(oPt.x, oPt.y);
m_oCanvas.graphics.lineTo(oPt.x + arrowSize*(dy-dx)/l, oPt.y - arrowSize*(dx+dy)/l);
m_oCanvas.graphics.moveTo(oPt.x, oPt.y);
m_oCanvas.graphics.lineTo(oPt.x - arrowSize*(dx+dy)/l, oPt.y - arrowSize*(dy-dx)/l);
if (l > 0)
{
m_oCanvas.graphics.lineStyle(2,color,100,false,"normal",CapsStyle.ROUND);
m_oCanvas.graphics.moveTo(oPt.x, oPt.y);
m_oCanvas.graphics.lineTo(oPt.x + arrowSize*(dy-dx)/l, oPt.y - arrowSize*(dx+dy)/l);
m_oCanvas.graphics.moveTo(oPt.x, oPt.y);
m_oCanvas.graphics.lineTo(oPt.x - arrowSize*(dx+dy)/l, oPt.y - arrowSize*(dy-dx)/l);
}
}
public function ReadParam(sName:String, sDefaultValue:String)
@@ -316,6 +391,226 @@
return sDefaultValue;
}
}
public function ComputeElectrostaticForces():Array
{
var aForces:Array = new Array;
//trace('====== BEGIN ComputeElectrostaticForces() =======');
for (var i:String in this.m_aNodes)
{
aForces[i] = new Object;
aForces[i].FxTotal = 0;
aForces[i].FyTotal = 0;
var oCurrentNode:GraphNode = GetNode(i);
for (var j:String in this.m_aNodes)
{
if (i != j)
{
var oRemoteNode:GraphNode = GetNode(j);
var dx:Number = oRemoteNode.x - oCurrentNode.x;
var dy:Number = oRemoteNode.y - oCurrentNode.y;
var d2:Number = (dx*dx + dy*dy) / (this.m_RADIUS * this.m_RADIUS);
var d:Number = Math.sqrt(d2);
var Fx:Number = 0;
var Fy:Number = 0;
if (d2 < 0.05)
{
d2 = 0.05;
}
if (d2 < 2 ) // Full influence under 2 * m_RADIUS px
{
Fx = -this.m_Q * dx / d2;
Fy = -this.m_Q * dy / d2;
aForces[i].FxTotal += Fx;
aForces[i].FyTotal += Fy;
}
else if (d2 < 4 ) // Decrease the influence to between 4 and 2 * m_RADIUS px
{
Fx = -this.m_Q * (4 - d2) * dx / d2;
Fy = -this.m_Q * (4 - d2) * dy / d2;
aForces[i].FxTotal += Fx;
aForces[i].FyTotal += Fy;
}
}
}
}
//for (i in this.m_aNodes)
//{
// trace('ELECTROSTATIC forces on '+i+': Fx='+aForces[i].FxTotal+', Fy='+aForces[i].FyTotal);
// if (Math.abs(aForces[i].FyTotal) > 1)
// {
// for (i in this.m_aNodes)
// {
// var oNode:GraphNode = GetNode(i);
// trace('node: '+i+' (x='+oNode.x+', y='+oNode.y+')');
// }
// }
//}
//trace('====== END ComputeElectrostaticForces() =======');
return aForces;
}
function ComputeElasticForces()
{
//trace('====== BEGIN ComputeElasticForces() =======');
var aForces:Array = new Array;
for (var i:String in this.m_aNodes)
{
aForces[i] = new Object;
aForces[i].FxTotal = 0;
aForces[i].FyTotal = 0;
}
// Elastic forces: each link applies a force proportional to its length (F = - K * x)
for(i in this.m_aLinks)
{
var oStartNode:GraphNode = GetNode(m_aLinks[i].GetStart());
var oEndNode = GetNode(m_aLinks[i].GetEnd());
var dx = oStartNode.x - oEndNode.x;
var dy = oStartNode.y - oEndNode.y;
//d = Math.sqrt(dx*dx + dy*dy);
//Fx = -K * d * dx / d;
//Fy = -K * d * dy / d;
// Links with more weight attached are more rigid !
//weightCoeff = (aWeights[aLinks[l].start] + aWeights[aLinks[l].end])/2;
var Fx = -this.m_K * dx;
var Fy = -this.m_K * dy;
aForces[oStartNode.GetKey()].FxTotal += Fx;
aForces[oStartNode.GetKey()].FyTotal += Fy;
aForces[oEndNode.GetKey()].FxTotal -= Fx;
aForces[oEndNode.GetKey()].FyTotal -= Fy;
}
//for (i in this.m_aNodes)
//{
// trace('Elastic forces on '+i+': Fx='+aForces[i].FxTotal+', Fy='+aForces[i].FyTotal);
// if (Math.abs(aForces[i].FyTotal) > 1)
// {
// for (i in this.m_aNodes)
// {
// var oNode:GraphNode = GetNode(i);
// trace('node: '+i+' (x='+oNode.x+', y='+oNode.y+')');
// }
// }
//}
//trace('====== END ComputeElasticForces() =======');
return aForces;
}
/**
* Update the nodes' position based on their current movement and the forces applied
*/
function UpdatePositions()
{
//trace('====== BEGIN UpdatePositions() =======');
var aElasticForces:Array = ComputeElasticForces();
var aElectrostaticForces:Array = ComputeElectrostaticForces();
//DrawForces(aElectrostaticForces, 0xcc0000);
//DrawForces(aElectrostaticForces, 0x0000cc);
for (var i:String in this.m_aNodes)
{
var oNode:GraphNode = GetNode(i);
if (!oNode.m_bInDrag)
{
var Fx:Number = aElasticForces[i].FxTotal + aElectrostaticForces[i].FxTotal;
var Fy:Number = aElasticForces[i].FyTotal + aElectrostaticForces[i].FyTotal;
if ( (Fx*Fx + Fy*Fy) < (this.m_Ks*this.m_Ks))
{
// Movement is less than minimum level (solid friction) => object is stopped
// otherwise let's keep it moving
oNode.m_speed_x = 0;
oNode.m_speed_y = 0;
//trace('object '+i+' stopped ! (x='+oNode.x+', y='+oNode.y+')');
}
else
{
oNode.m_speed_x = oNode.m_speed_x*this.m_Kf + this.m_deltaT*Fx;
oNode.m_speed_y = oNode.m_speed_y*this.m_Kf + this.m_deltaT*Fy;
var dx:Number = oNode.m_speed_x * this.m_deltaT;
var dy:Number = oNode.m_speed_y * this.m_deltaT;
oNode.x = Math.round(oNode.x + dx);
oNode.y = Math.round(oNode.y + dy);
//trace('object '+i+' moves (Force: Fx='+Fx+', Fy='+Fy+')! ');
}
}
}
//trace('======= Updated Positions =========');
//DumpPositions();
//trace('====== END UpdatePositions() =======');
}
public function DrawForces(aForces:Array, color:uint)
{
for (var i:String in aForces)
{
var oNode:GraphNode = GetNode(i);
var oForce:Object = aForces[i];
m_oCanvas.graphics.lineStyle(2,color,100,false,"normal",CapsStyle.ROUND);
m_oCanvas.graphics.moveTo(oNode.x, oNode.y);
var oEndPoint:Point = new Point;
oEndPoint.x = Math.round(oNode.x + oForce.FxTotal);
oEndPoint.y = Math.round(oNode.y + oForce.FyTotal);
m_oCanvas.graphics.lineTo(oEndPoint.x, oEndPoint.y);
drawArrow(oEndPoint, oForce.FxTotal, oForce.FyTotal, color);
//trace('Drawinf vector '+i+': (x='+oNode.x+', y='+oNode.y+') to (x='+oEndPoint.x+', y='+oEndPoint.y+')');
}
}
public function UpdatePanAndZoom(countDownRatio:Number)
{
var sceneRect:Rectangle = null;
for(var i:String in this.m_aNodes)
{
if (sceneRect == null)
{
sceneRect = GetNode(i).getBounds(m_oCanvas);
}
else
{
sceneRect = sceneRect.union(GetNode(i).getBounds(m_oCanvas));
}
}
if (sceneRect != null)
{
var idealZoomLevel:Number = 1;
trace('Stage dimensions: width:'+stage.stageWidth+' height:'+stage.stageHeight);
if ((sceneRect.width > stage.stageWidth) || (sceneRect.height > (stage.stageHeight - 50)))
{
var wRatio:Number = stage.stageWidth / sceneRect.width;
var hRatio:Number = (stage.stageHeight - 50) / sceneRect.height;
idealZoomLevel = Math.min(wRatio, hRatio);
SetZoomLevel(idealZoomLevel);
m_oZoomSlider.value = Math.round(100*idealZoomLevel);
}
var xOffset:Number = 0;
var yOffset:Number = 50;
if (stage.stageWidth > sceneRect.width)
{
xOffset = (stage.stageWidth-sceneRect.width)/2
}
if (stage.stageHeight > sceneRect.height)
{
yOffset = 50 + (stage.stageHeight-50-sceneRect.height)/2
}
m_oCanvas.x = xOffset-sceneRect.x;
m_oCanvas.y = yOffset-sceneRect.y;
trace('Scene bounding rect: x:'+sceneRect.x+' y:'+sceneRect.y+' width:'+sceneRect.width+' height:'+sceneRect.height+' zoomLevel:'+idealZoomLevel);
}
}
public function DumpPositions()
{
for (var i:String in this.m_aNodes)
{
var oNode:GraphNode = GetNode(i);
trace(i+' Position: (x='+oNode.x+', y='+oNode.y+')');
}
}
}
}

View File

@@ -1,6 +1,7 @@
package iTop
package iTop
{
import flash.display.Sprite;
import flash.display.*;
import flash.geom.*;
import flash.text.TextField;
import flash.text.TextFormat;
import flash.text.TextFieldAutoSize;
@@ -15,17 +16,18 @@
// You'll need this for proper text formatting
private var _tf:TextField = new TextField();
private var _format:TextFormat = new TextFormat();
private static const ROUND:Number = 2;
private static const ROUND:Number = 10;
private static const HEIGHT:Number = 25;
private static const FONT_SIZE:uint = 12;
private static const FONT:String = 'Arial';
private static const PADDING:Number = 5;
private static const MIN_ALPHA:Number = 0;
private static const ALPHA_INC:Number = .1;
private static const MIN_ALPHA:Number = 0.0;
private static const ALPHA_INC:Number = 0.1;
private static const MAX_ALPHA:Number = 1;
private static const REFRESH:Number = MAX_ALPHA / ALPHA_INC;
// For fading in and out
private var _timer:Timer;
private static const REFRESH:Number = (MAX_ALPHA - MIN_ALPHA) / ALPHA_INC;
private static const APPEAR_TIMEOUT = 1000; // ms
// For appearence, fading in and out
public var timer:Timer;
public function ToolTip( tip:String )
{
// Hold onto the tip for posterity
@@ -51,7 +53,7 @@
_tf.y += PADDING;
addChild( _tf );
// Draw the background
graphics.beginFill( 0xF6F6F1, 1 );
graphics.beginFill( 0xEEEE99, 0.95 );
graphics.drawRoundRect( 0, 0, _tf.textWidth+PADDING*4, _tf.textHeight+PADDING*4, ROUND );
graphics.endFill();
this.alpha = MIN_ALPHA;
@@ -65,28 +67,74 @@
}
public function mouse_over( e:MouseEvent ):void
{
this.parent.setChildIndex( this, this.parent.numChildren-1 )
// Move the tool tip to the top!
var fadeSpeed:Number = 500 / REFRESH;
_timer = new Timer( fadeSpeed, REFRESH );
_timer.addEventListener( "timer", fadeIn );
_timer.start();
// Make the tooltip appear smoothly after a delay
if (this.timer != null)
{
this.timer.stop();
}
this.timer = new Timer( APPEAR_TIMEOUT, 1 );
this.timer.addEventListener( "timer", appear );
this.timer.start();
this.parent.addEventListener( MouseEvent.MOUSE_OUT, mouse_out );
}
public function mouse_out( e:MouseEvent ):void
{
var fadeSpeed:Number = 500 / REFRESH;
_timer = new Timer( fadeSpeed, REFRESH );
_timer.addEventListener( "timer", fadeOut );
_timer.start();
if (this.timer != null)
{
this.timer.stop();
}
this.timer = new Timer( fadeSpeed, REFRESH );
this.timer.addEventListener( "timer", fadeOut );
this.timer.start();
this.parent.removeEventListener( MouseEvent.MOUSE_OUT, mouse_out );
}
private function appear(i:uint):void
{
// The delay has elapsed, show (smoothly) the tooltip
if (this.timer != null)
{
this.timer.stop();
}
// Reuse the time for the fadeIn
this.parent.setChildIndex( this, this.parent.numChildren-1 )
// Move the tool tip to the top!
var fadeSpeed:Number = 500 / REFRESH;
this.alpha = MIN_ALPHA;
if (this.timer != null)
{
this.timer.stop();
}
this.timer = new Timer( fadeSpeed, REFRESH );
this.timer.addEventListener( "timer", fadeIn );
this.timer.start();
this.parent.addEventListener( MouseEvent.MOUSE_OUT, mouse_out );
}
private function fadeIn( i:uint ):void
{
this.alpha += ALPHA_INC;
if (this.alpha < (1.0 - ALPHA_INC))
{
this.alpha += ALPHA_INC;
}
else
{
this.alpha = 1.0;
}
//trace("++ Tooltip alpha: "+this.alpha+" ALPHA_INC:"+ALPHA_INC);
}
private function fadeOut( i:uint ):void
{
this.alpha -= ALPHA_INC;
if (this.alpha > ALPHA_INC)
{
this.alpha -= ALPHA_INC;
}
else
{
this.alpha = 0.0;
}
//trace("-- Tooltip alpha: "+this.alpha+" ALPHA_INC:"+ALPHA_INC);
}
}
}

Binary file not shown.

Binary file not shown.

View File

@@ -37,7 +37,7 @@ define('MAX_RECURSION_DEPTH', 20);
*/
function AddNodeDetails(&$oNode, $oObj)
{
$aZlist = MetaModel::GetZListItems(get_class($oObj), 'details');
$aZlist = MetaModel::GetZListItems(get_class($oObj), 'list');
$aLabels = array();
$index = 0;
foreach($aZlist as $sAttCode)
@@ -46,7 +46,7 @@ function AddNodeDetails(&$oNode, $oObj)
$aLabels[] = $oAttDef->GetLabel();
if (!$oAttDef->IsLinkSet())
{
$oNode->SetAttribute('att_'.$index, $oObj->Get($sAttCode));
$oNode->SetAttribute('att_'.$index, $oObj->GetAsHTML($sAttCode));
}
$index++;
}
@@ -78,6 +78,7 @@ function GetRelatedObjects(DBObject $oObj, $sRelationName, &$oLinks, &$oXmlDoc,
$oLinkedNode = $oXmlDoc->CreateElement('node');
$oLinkedNode->SetAttribute('id', $oTargetObj->GetKey());
$oLinkedNode->SetAttribute('obj_class', get_class($oTargetObj));
$oLinkedNode->SetAttribute('obj_class_name', MetaModel::GetName(get_class($oTargetObj)));
$oLinkedNode->SetAttribute('name', $oTargetObj->GetName());
$oLinkedNode->SetAttribute('icon', BuildIconPath($oTargetObj->GetIcon(false /* No IMG tag */)));
AddNodeDetails($oLinkedNode, $oTargetObj);
@@ -104,11 +105,10 @@ function BuildIconPath($sIconPath)
require_once('../application/startup.inc.php');
require_once('../application/loginwebpage.class.inc.php');
//// For developping the Navigator
// For developping the Navigator from within Flash
//session_start();
//$_SESSION['auth_user'] = 'admin';
//$_SESSION['auth_pwd'] = 'admin2';
//UserRights::Login($_SESSION['auth_user'], $_SESSION['auth_pwd']); // Set the user's language
//UserRights::Login($_SESSION['auth_user']); // Set the user's language
LoginWebPage::DoLogin(); // Check user rights and prompt if needed
$oPage = new ajax_page("");
@@ -134,6 +134,7 @@ if ($id != 0)
$oXmlNode = $oXmlDoc->CreateElement('node');
$oXmlNode->SetAttribute('id', $oObj->GetKey());
$oXmlNode->SetAttribute('obj_class', get_class($oObj));
$oXmlNode->SetAttribute('obj_class_name', MetaModel::GetName(get_class($oObj)));
$oXmlNode->SetAttribute('name', $oObj->GetName());
$oXmlNode->SetAttribute('icon', BuildIconPath($oObj->GetIcon(false /* No IMG tag */))); // Hard coded for the moment
AddNodeDetails($oXmlNode, $oObj);