opttree.lzx
<library>
<class name="opttree" focusable="true">
<attribute name="nodepath" value="*
" type="string"/>
<attribute name="nodeclass" type="string" setter="setNodeInst( nodeclass )"/>
<attribute name="openattr" value="__OPTTREE_META_open
" type="string"/>
<attribute name="openattrpath" value="@__OPTTREE_META_open
" type="string"/>
<attribute name="depthattr" value="__OPTTREE_META_depth
" type="string"/>
<attribute name="depthattrpath" value="@__OPTTREE_META_depth
" type="string"/>
<attribute name="nodecollection" value="${ opttreecontroller.nodelist }"/>
<attribute name="nodelist" value="nodecollection
" when="always" setter="this.nodeinst.datapath.setNodes( nodelist )"/>
<include href="opttreecontroller.lzx"/>
<dataselectionmanager name="selman">
</dataselectionmanager>
<method name="setNodeInst" args="nc">
this.nodeclass = lz[ nc ];
new nodeclass( this , { name : "nodeinst" } );
</method>
<method name="setOpen" args="p , val">
p.setAttr( openattr, val ? "true" : "false" );
</method>
<method name="recursiveOpen" args="p, val">
opttreecontroller.lock( p );
this.setOpen( p , val );
var c = opttreecontroller.getChildNodes( p );
if (c != null) {
for ( var k=0, len=c.length; k<len; k++){
this.recursiveOpen( c[ k ] , val );
}
}
opttreecontroller.unlock( p );
</method>
<method name="setAllOpen" args="val">
var ilist = opttreecontroller.getChildNodes( datapath.p );
if (ilist != null) {
opttreecontroller.lock( "ao" );
for ( var k=0, len=ilist.length; k<len; k++ ){
this.recursiveOpen( ilist[ k ], val );
}
opttreecontroller.unlock( "ao" );
}
</method>
<method name="select" args="el">
if (el != null) {
selman.select( el );
} else {
selman.clearSelection();
}
</method>
<attribute name="highlighted" value="null
"/>
<handler name="onkeyup" args="k">
var curr = this.highlighted;
if ( !curr ){
var cls = this.nodeinst.clones;
if ( !cls.length ) return;
this.select( cls[ 0 ] );
return;
}
if ( k == 38 || k == 40 ){
//up down
var dir = k - 39;
var cl = this._findNextClone( curr , dir );
this.select( cl );
} else if ( k == 37 ){
//left
if ( curr.open ) {
this.setOpen( curr.datapath.p , false );
}else {
var pnode = curr.datapath.p.parentNode;
if ( pnode == datapath.p ) return;
var cl = this._findInClones( pnode );
var wentin = false;
while( cl == null ){
wentin = true;
this._adjustMargin( - 1 );
cl = this._findInClones( pnode );
}
if ( wentin ) this._adjustMargin( - 1 );
this.select( cl );
}
} else if ( k == 39 ){
//right
if ( !curr.open ) {
this.setOpen( curr.datapath.p , true );
}else {
var cl = this._findNextClone( curr , 1 );
this.select( cl );
}
} else if ( k == 32 || k == 13 ){
if ( highlighted ){
highlighted.doSelected();
}
}
</handler>
<method name="_findNextClone" args="who, dir">
//returns the next clone in the flattened list given an existing
//clone and a direction to move in. adjusts the position of the
//opttree within its clipping region if necessary.
var cls = this.nodeinst.clones;
if ( !cls.length ) return null;
var clnum = null;
for ( var i = 0; i < cls.length; i++ ){
if ( cls[ i ] == who ) {
clnum = i;
break;
}
}
if ( clnum == null ) return null;
var nextnum = clnum + dir;
var nextone = null;
if ( nextnum >= 0 && nextnum < cls.length ){
var nextone = cls[ nextnum ];
}
if ( nextone ){
var itheight = cls[ 0 ].height;
var adjy = nextone.y + y;
if ( adjy < itheight ){
this._adjustMargin( -1 );
} else if ( adjy > mask.height - 2*itheight ){
this._adjustMargin( 1 );
}
return nextone;
}
var clptr = who.datapath.p;
var clptrnum = null;
var nodes = this.nodeinst.nodes;
var clptrnum = null;
for ( var i = 0; i < nodes.length; i++ ){
if ( nodes[ i ] == clptr ) {
clptrnum = i;
break;
}
}
var nexthnode = nodes[ clptrnum + dir ];
this._adjustMargin( nextnum < 0 ? -1 : 1 );
return this._findInClones( nexthnode );
</method>
<method name="_adjustMargin" args="dir">
//moves the opttree up or down (depending on the dir) in within
//its clipping region.
var cls = this.nodeinst.clones;
var itheight = cls[ 0 ].height;
var adj = itheight * -dir;
var ny = y + adj;
this.setAttribute('y', Math.min( 0 , Math.max( mask.height - height, ny ) ) );
</method>
<method name="_findInClones" args="p">
//finds the clone pointing to the given LzDataElement or returns
//null if that clone is not mapped
var cls = this.nodeinst.clones;
for ( var i = 0; i < cls.length; i++ ){
if ( cls[ i ].datapath.p == p ) {
return cls[ i ];
}
}
return null;
</method>
<attribute name="dragee" value="null
"/>
<attribute name="dragged" value="null
"/>
<view name="_clicker" height="1" bgcolor="black" visible="false" width="100%">
<attribute name="movedel" value="$once{ new lz.Delegate( this, '_updatePos' ) }"/>
<method name="drag" args="on">
if ( on ){
movedel.register( lz.Idle, "onidle" );
} else {
movedel.unregisterAll();
this.setAttribute('visible', false );
}
</method>
<method name="_updatePos" args="ignore=null">
var py = parent.getMouse( 'y' );
var czero = parent.nodeinst.clones[ 0 ];
var cy = czero.y;
var ay = py - cy;
var h = czero.height;
var rem = ( py - czero.y ) % h;
if ( rem < h * .25 ){
//it's over this one
this.dragisover = false;
this.cpos = Math.floor( ( py - czero.y ) / h );
} else if ( rem > h *.75 ){
this.dragisover = false;
this.cpos = Math.ceil( ( py - czero.y ) / h );
} else {
this.dragisover = true;
this.cpos = Math.floor( ( py - czero.y ) / h );
}
if ( this.dragisover ){
parent.select( parent.nodeinst.clones[ this.cpos ] );
} else {
this.setAttribute('y', cy + this.cpos * h );
parent.select( null );
}
if ( visible != !this.dragisover ){
this.setAttribute('visible', !this.dragisover );
}
</method>
</view>
<method name="_setupDrag">
this.dragged = new nodeclass( this );
dragged.setAttribute('visible', false );
this.dragupdel = new lz.Delegate( this, "_updateDragged" );
this.dragindent = dragged.indent;
</method>
<method name="beginDrag" args="who, ix, iy">
this._ydragoff = iy;
if ( !dragged ){
this._setupDrag();
}
dragged.setAttributeRelative( 'x', who );
dragged.setAttribute('visible', true );
this.dragdata = who.datapath.p;
var dragparent = this.dragdata.parentNode
dragparent.removeChild( this.dragdata );
var dpc = this._findInClones( dragparent );
if ( dpc ) dpc.checkChildren();
dragged.datapath.setPointer( this.dragdata );
dragged.bringToFront();
this.dragupdel.register( lz.Idle, "onidle" );
_clicker.drag( true );
</method>
<method name="_updateDragged" args="ignore=null">
dragged.setAttribute('y', this.getMouse( 'y' ) - this._ydragoff );
</method>
<method name="endDrag" args="who">
this.dragupdel.unregisterAll();
dragged.setAttribute('visible', false );
_clicker.drag( false );
if ( _clicker.dragisover ){
var p = selman.getSelection()[ 0 ].p;
if ( p.childNodes && p.childNodes.length ){
p.insertBefore( this.dragdata , p.getFirstChild() );
} else {
p.appendChild( this.dragdata );
}
this._findInClones( p ).checkChildren();
} else {
var cn = _clicker.cpos;
var ac = this.nodeinst.clones[ cn - 1 ];
if ( !ac ){
return;
}
//p is the node we want to be after
var p = ac.datapath.p;
if ( p.attributes[ openattr ] == "true" &&
ac.datapath.xpathQuery( nodepath ) ){
p.insertBefore( this.dragdata , p.getFirstChild() );
} else{
var ns = p.getNextSibling();
if ( ns ){
ns.parentNode.insertBefore( this.dragdata , ns );
} else {
p.parentNode.appendChild( this.dragdata );
}
}
}
this.updateHierarchy( this.dragdata );
</method>
<method name="updateHierarchy" args="p">
//Debug.write( 'update', p );
</method>
</class>
</library>
Cross References
Includes
Classes