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); } } } } } } } }

By default flex builder launches its applications through the system default browser. This can easily be changed to launch projects in the stand alone player. This of course assumes that you have the stand alone player installed.

To change these settings go to the ‘Run’ menu and select ‘Run…’. This will bring up a dialog box as shown in the attached screen shot. To start make sure there is a configuration under ‘Flex Application’ in the left panel. It should be named after the current project. If not, right click on ‘Flex Application’ and create a new configuration.

run settings

Once the configuration is complete you can edit its properties. Under the main tab in the right panel, make sure the project you want is selected and that the appropriate application file is specified. Now uncheck ‘Use defaults’. Click ‘Browse…’ next to the Debug text area. This should open up the current project’s bin folder. From there you can select the .swf version of you application file. Do the same for Run and that is it.

Now when ever you run this project it will launch in the stand alone flash player.