This is an extension of Tree that doesn’t allow items to be dropped at a different depth then they were originally. E.g. if an item is at a depth of 4 it can be moved behind or in front of other items at depth 4. It can even be moved from its parent item into another item(A) as long as the depth of A’s children is 4. The item can never be moved up or down to a different depth.

The dragDropHandler function in Tree has code that prevents dropping a node into one of its descendants. A few lines of code added in the same part would create the additional restrictions. Unfortunately these lines of code need to go into the middle of dragDropHandler’s code. This prevents an override that first makes a call to the superclass’s function, then adds additional functionality.

…in dragDropHandler… var offset:int = getItemIndex(iterator.current); var futureDepth:int = getItemDepth(_dropData.parent, Math.abs(offset - getItemIndex(_dropData.parent))); var currentDepth:int = getItemDepth(parent, Math.abs(offset - getItemIndex(parent))); if(_dropData.parent == null) futureDepth = 0; if(parent == null) currentDepth = 0; if (futureDepth != currentDepth) // the parent should only be 1 greater than item depth return; …

To add those lines of code the whole dragDropHandler function need to be copied and placed into the overriding function. This would be fairly easy if ActionScript could inherit private variables/functions or if Tree had more protected variables/functions.

Instead any private function/variable that dragDropHandler uses must also have a local copy in myTree. If that function sets or interacts with other private functions/variables those were also added. Note that, except for one exception, none of these functions actually override functions in the superclass. Rather they duplicate the same functionality for when myTree needs it.

Unlike the private functions that were duplicated, calculateDropIndex was public. But for some reason an error is thrown if it is not overridden. This is likely because dragDropHandler originally calls super.calculateDropIndex and not calculateDropIndex.

Here is the code. Note that only the function declarations have been included for most functions. The full code from Tree.as can be substituted at those declarations. The lines added to dragDropHandler are set out with comments.

package edu.byu.mtc.education.assessmenttools.view{ import com.adobe.cairngorm.control.*; import edu.byu.mtc.framework.view.*; import edu.byu.mtc.education.assessmenttools.control.*; import mx.events.*; import mx.controls.*; import mx.managers.*; import flash.geom.Point; import mx.collections.*; public class myTree extends Tree{ protected var _dropData:*; public function myTree(){ super(); } override public function calculateDropIndex(event:DragEvent = null):int private function updateDropData(event:DragEvent):void private function getChildIndexInParent(parent:Object, child:Object):int private function getChildren(item:Object, view:Object):ICollectionView private function getParentStack(item:Object):Array private function getItemIndex(item:Object):int override protected function dragDropHandler(event:DragEvent):void{ if (event.isDefaultPrevented()) return; hideDropFeedback(event); if (event.dragSource.hasFormat(”treeItems”)){ //we only support MOVE by default if (event.action == DragManager.MOVE && dragMoveEnabled){ var items:Array = event.dragSource.dataForFormat(”treeItems”) as Array; //Are we dropping on ourselves? if (event.dragInitiator == this){ // If we’re dropping onto ourselves or a child of a descendant then dont actually drop var dropIndex:* = super.calculateDropIndex(event); var index:int; var parent:*; var parentItem:*; var dropParentStack:Array = getParentStack(_dropData.parent); dropParentStack.unshift(_dropData.parent); //optimize stack method for (var i:int = 0; i < items.length; i++) { parent = getParentItem(items[i]); index = getChildIndexInParent(parent, items[i]); //****begin added************************************* var offset:int = getItemIndex(iterator.current); var futureDepth:int = getItemDepth(_dropData.parent, Math.abs(offset - getItemIndex(_dropData.parent))); var currentDepth:int = getItemDepth(parent, Math.abs(offset - getItemIndex(parent))); if(_dropData.parent == null) futureDepth = 0; if(parent == null) currentDepth = 0; // the parent should only be 1 greater than item depth if (futureDepth != currentDepth) return; //****end added************************************** for each (parentItem in dropParentStack) { //we dont want to drop into one of our own sets of children if (items[i] == parentItem) return; } //we remove before we add due to the behavior //of structures with parent pointers like e4x removeChildItem(parent, items[i], index); //is the removed item before the drop location? if (parent == _dropData.parent && index < _dropData.index) { addChildItem(_dropData.parent, items[i], _dropData.index - 1); } else { addChildItem(_dropData.parent, items[i], _dropData.index); } } } } } } } }