Montag, 7. Januar 2008

FlexTreeGrid withDragDrop for XML


I played a bit with the flexlib TreeGrid from Yaniv De Ridder which is really great! Its has still some bugs in flexlib version 2.2 and some of the features of the extended DataGrid do not work anymore as sorting columns and dragdrop, but I believe they will in a future version.

Until then I wanted some features to work in the TreeGrid which are
  1. Treegrid should update when dataprovider changes (there is a workaround for that : issue 15), and
    1. openItems should stay open when a child is added or removed from the dataProvider
    2. scrollPositions should stay when a child is added or removed from the dataProvider
  2. drag and drop of elements from one tree item to another
    1. it should be possible to ativate drag drop only work if the items will be placed at the same depth
My approaches are just a first try and maybe there are still some bugs. So I would be glad to receive comments and improvements from you.



As I said for point 1 there is a workaround, wich I copied. The only problem was, that the scrollindex was set back to default and all items had been closed when the dataProvider was changed.

The following code ensures, that if you still have the same dataProvider object whith the same uid than openItemsChanged=true will be set.

Then the TreeGrid will keep the last openItems and tries to set them again, which I ensured by adding code at the end of the commitProperties function.

At the end a for loop calls Yanivs openItemAt(rowIdx,item) for every item. In commitProperties also the scrollPositions will be remembered before the dataProvider of the underlying DataGrid will be changed by super.dataProvider = _displayedModel;
and set back to the grid afterwards.
// a new field to remember the set dataProvider with its uid.
private var _orgValue : Object;
...

override public function set dataProvider( value:Object ) : void
{

var uidd1:String = UIDUtil.getUID(value);
var uidd2:String = UIDUtil.getUID(_orgValue);
if (UIDUtil.getUID(value) == UIDUtil.getUID(_orgValue)){
openItemsChanged = true;
}else{
_orgValue = value;
}

...



// in commitproperties:
...
var vScrollPos:int = verticalScrollPosition;
  var hScrollPos:int = horizontalScrollPosition;

super.dataProvider = _displayedModel;
if ( openItemsChanged )
{


verticalScrollPosition = vScrollPos;
   horizontalScrollPosition = hScrollPos;


for each(var item:* in openItems){
     openItemAt(getItemIndex(item),item);
   }


openItemsChanged = false;
//setting open items resets the collection
var event : CollectionEvent =
new CollectionEvent(CollectionEvent.COLLECTION_CHANGE);
event.kind = CollectionEventKind.RESET;
collection.dispatchEvent(event);
}
super.commitProperties();
}

I also changed the function rootModelChangeHandler from te issue 15 workaround

protected function rootModelChangeHandler(event:Event):void
{
dataProviderChanged = true;
// hinzugefügt, um, wenn es der gleiche dataProvider mit veränderten kindern handelt
//   (wird in commitProperties überprüft) die bisher geöffneten element wieder zu öffnen.
openItemsChanged = true;
invalidateProperties();
}
DragDrop was the other point to achieve. I recognized, that TreeGrid has a own dataProvider (_rootModel) and updates the original DataGrid.dataProvider only with the items to display. So you could activate dragDrop in the TreeGrid and after another workaround (see issue 16) it works in the display, but the underlying _rootModel of the TreeGrid is never changed.

To enable dragDrop I copied the dragDropHandler function from the original mx.control.listClasses.ListBase Class and changed it, so it would recognize the parent child structures of the TreeGrid. The problem is that it only works for XML based dataProviders, where every child knows its parent.

Furthermore I added a method, which checks if a node dragged will be dropped at the same tree-depth again because I needed it.

override protected function dragDropHandler(event:DragEvent):void
{
if (event.isDefaultPrevented())
return;

hideDropFeedback(event);

if ( enabled && event.dragSource.hasFormat("items"))
{
if (!dataProvider)
// Create an empty collection to drop items into.
dataProvider = [];

var items:Array = event.dragSource.dataForFormat("items") as Array;
var dropIndex:int = calculateDropIndex(event);

var dragDropAtThisDepthAllowed:Boolean = getDragDropAtDepthAllowed(dropIndex);

if (dragDropAtThisDepthAllowed){
if (event.action == DragManager.MOVE && dragMoveEnabled)
{
if (event.dragInitiator == this)
{

var indices:Array = selectedIndices;
indices.sort(Array.NUMERIC);

for (var i:int = indices.length - 1; i >= 0; i--)
{

var collectionIterator:IViewCursor = collection.createCursor();
collectionIterator.seek(CursorBookmark.FIRST, indices[i]);

var dragItem:XML = XML(_displayedModel.getItemAt(indices[i]));

//  funkioniert nur mit einem XML dataProvider im moment

if (dragItem is XML){
var dragItemParent:XML = XML(XML(dragItem).parent());
if (dragItemParent != null){
//var cIdx:int = XML(dragItem).childIndex();
var idx:int = dragItem.childIndex();

delete dragItemParent.children()[idx];
}
}                 
/* if (indices[i] < int =" items.length">= 0; i--)
{
if (event.action == DragManager.COPY)
{
//collectionIterator.insert(copyItemWithUID(items[i]));
var copyItem:Object = copyItemWithUID(items[i]);
try{
dropOverItem = _displayedModel.getItemAt(dropIndex);
}catch(e:RangeError){
// wenn das item hinter das letzte item gesetzt wird,
//  gibt es kein dropOverItem
}
if (dropOverItem != null){
if (items[i] is XML){
var dropItemParent:XML = XML(dropOverItem).parent() as XML;
if (dropItemParent != null){
dropItemParent.insertChildBefore(dropOverItem,copyItem);
}
}
}else{
// wenn null, dann nach ganz hinten!
XML(_orgValue).appendChild(items[i]);
}

}
else if (event.action == DragManager.MOVE)
{
//collectionIterator.insert(items[i]);
if (items[i] is XML){
var dropOverItem:Object = null;
try{
dropOverItem = _displayedModel.getItemAt(dropIndex);
}catch(e:RangeError){
// wenn das item hinter das letzte item gesetzt wird,
//  gibt es kein dropOverItem
}
if (dropOverItem != null){
var dropItemParent:XML = XML(dropOverItem).parent() as XML;
if (dropItemParent != null){
dropItemParent.insertChildBefore(dropOverItem,items[i]);
}
}else{
// wenn null, dann nach ganz hinten!
XML(_orgValue).appendChild(items[i]);
}
}
}
}
}
}
}





private var _dragDropDepthEqual:Boolean = false;



/**
* neu:
* wenn dragDropDepthEqual=true muss die Tiefe im Baum des gedraggden und des dropOverItem die gleiche sein.
*/
private function getDragDropAtDepthAllowed(dropIndex:int):Boolean{

var dragDropAtThisDepthAllowed:Boolean = false;

// die tiefe des drag und des dropOver elements. wird benötigt,
//   um zu sehen, ob drag und drop tiefe die gleiche sind. wsa relevant ist bei dragDropDepthEqual=true
var dropOverItemDepth:int = showRoot ? 1 : 2; // default ist 2 (root hat 1 und kann nicht wirklich verschoben werden, erst dessen kinder)
var dragItemDepth:int = showRoot ? 1 : 2;     // default ist 2 (root hat 1 und kann nicht wirklich verschoben werden, erst dessen kinder)
var dropOverItem:Object = null;
try{
dropOverItem = _displayedModel.getItemAt(dropIndex);
dropOverItemDepth = getItemDepth(dropOverItem, -1);
}catch(e:RangeError){
// wenn das item hinter das letzte item gesetzt wird,
//  gibt es kein dropOverItem
}
var selectedItem:Object = _displayedModel.getItemAt(selectedIndices[0]);
dragItemDepth = getItemDepth(selectedItem,-1);

if (dragDropDepthEqual &&  (dropOverItemDepth == dragItemDepth)){
dragDropAtThisDepthAllowed = true;
}else if (!dragDropDepthEqual){
dragDropAtThisDepthAllowed = true;
}
return dragDropAtThisDepthAllowed;

}




the whole code of the changed TreeGrid:

package {

import flash.events.Event;
import flash.xml.XMLNode;

import flexlib.controls.treeGridClasses.TreeGridListData;

import mx.collections.ArrayCollection;
import mx.collections.CursorBookmark;
import mx.collections.ICollectionView;
import mx.collections.IList;
import mx.collections.IViewCursor;
import mx.collections.ListCollectionView;
import mx.collections.XMLListCollection;
import mx.controls.DataGrid;
import mx.controls.dataGridClasses.DataGridColumn;
import mx.controls.listClasses.BaseListData;
import mx.controls.listClasses.IListItemRenderer;
import mx.controls.treeClasses.DefaultDataDescriptor;
import mx.controls.treeClasses.ITreeDataDescriptor;
import mx.core.EventPriority;
import mx.events.CollectionEvent;
import mx.events.CollectionEventKind;
import mx.events.DragEvent;
import mx.managers.DragManager;
import mx.utils.UIDUtil;


[Style(name="disclosureOpenIcon", type="Class", format="EmbeddedFile", inherit="no")]

[Style(name="disclosureClosedIcon", type="Class", format="EmbeddedFile", inherit="no")]

[Style(name="folderOpenIcon", type="Class", format="EmbeddedFile", inherit="no")]

[Style(name="folderClosedIcon", type="Class", format="EmbeddedFile", inherit="no")]

[Style(name="defaultLeafIcon", type="Class", format="EmbeddedFile", inherit="no")]

[Style(name="indentation", type="Number", inherit="no")]

[Style(name="verticalTrunks", type="String", enumeration="none,normal,dotted", inherit="no")]

/**
*
* Der Code wurde größtenteils übernommen von der flexlib Version 2.2 des TreeGrid.
* Es wurden der Workaround für issue 15
*         (http://code.google.com/p/flexlib/issues/detail?id=15 - Treegrid not updated when dataprovider changes)
* hinzugefügt.
*
*/
public class XMLTreeGrid extends DataGrid
{

/**
*  @private
*  Used to hold a list of items that are opened or set opened.
*/
private var _openItems : Object = {};


/**
*  An object that specifies the icons for the items.
*  Each entry in the object has a field name that is the item UID
*  and a value that is an an object with the following format:
*  


*  {iconID: Class, iconID2: Class}
*  
* The iconID field value is the class of the icon for * a closed or leaf item and the iconID2 is the class * of the icon for an open item. * * This property is intended to allow initialization of item icons. * Changes to this array after initialization are not detected * automatically. * Use the setItemIcon() method to change icons dynamically. * * @see #setItemIcon() * @default undefined */ public var itemIcons : Object; /** * @private * Storage variable for showRoot flag. */ private var _showRoot : Boolean = true; /** * @private * Storage variable for changes to showRoot. */ private var showRootChanged : Boolean = false; /** * @private * Flag to indicate if the model has a root */ private var _hasRoot : Boolean = false; /** * @private * Storage variable for the original dataProvider */ private var _rootModel : ICollectionView; /** * neu: * das original dataprovider object, der bei set dataProvider gesetzt wird. */ private var _orgValue : Object; /** * neu: * wenn true, ist dragdrop von elementen nur in der gleichen baum-tiefe möglich */ private var _dragDropDepthEqual:Boolean = false; /** * @private * Storage variable for the displayed dataProvider */ private var _displayedModel : ArrayCollection = new ArrayCollection(); public function XMLTreeGrid() { super(); setStyle("indentation", 18); addEventListener(DragEvent.DRAG_DROP, dragDropHandler, false, EventPriority.DEFAULT_HANDLER); } public function set dragDropDepthEqual(value:Boolean):void{ _dragDropDepthEqual =value; } public function get dragDropDepthEqual():Boolean{ return _dragDropDepthEqual ; } public function dispatchTreeEvent(type:String, listData:TreeGridListData, renderer:IListItemRenderer, trigger:Event = null, opening:Boolean = true, dispatch:Boolean = true) : void { if( opening ) { openItemAt( getItemIndex( listData.item ), listData.item );//listData.rowIndex - 1 } else { closeItemAt( getItemIndex( listData.item ), listData.item ); //listData.rowIndex - 1 } } //-------------------------------------------------------------------------- // // DataProvider // //-------------------------------------------------------------------------- /** * @private */ private var dataProviderChanged : Boolean = false; [Inspectable(category="Data", defaultValue="undefined")] override public function set dataProvider( value:Object ) : void { /** * um zu erkennen, ob ein komplett neuer dataProvider zugewiesen wurde oder ein bekanntes objekt. * ist das objekt bekannt, wird davon ausgegangen, dass die bisher geöffneten items geöffnet bleiben sollen und * dementsprechend wird openItemsChanged = true; gesetzt, wodurch die liste der openItems erneut gesetzt wird **/ var uidd1:String = UIDUtil.getUID(value); var uidd2:String = UIDUtil.getUID(_orgValue); if (UIDUtil.getUID(value) == UIDUtil.getUID(_orgValue)){ openItemsChanged = true; }else{ _orgValue = value; } if (_rootModel) _rootModel.removeEventListener( CollectionEvent.COLLECTION_CHANGE, collectionChangeHandler); // handle strings and xml if (typeof(value)=="string") value = new XML(value); else if (value is XMLNode) value = new XML(XMLNode(value).toString()); else if (value is XMLList) value = new XMLListCollection(value as XMLList); if (value is XML) { _hasRoot = true; var xl:XMLList = new XMLList(); xl += value; _rootModel = new XMLListCollection(xl); } //if already a collection dont make new one else if (value is ICollectionView) { _rootModel = ICollectionView(value); } else if (value is Array) { _rootModel = new ArrayCollection(value as Array); } //all other types get wrapped in an ArrayCollection else if (value is Object) { _hasRoot = true; // convert to an array containing this one item var tmp:Array = []; tmp.push(value); _rootModel = new ArrayCollection(tmp); } else { _rootModel = new ArrayCollection(); } /** * neuer code : hinzugefügt wegen bug issue 15 http://code.google.com/p/flexlib/issues/detail?id=15 * Treegrid not updated when dataprovider changes * */ _rootModel.addEventListener(CollectionEvent.COLLECTION_CHANGE, rootModelChangeHandler); /** * ende neuer code : hinzugefügt wegen bug issue 15 */ //flag for processing in commitProps dataProviderChanged = true; invalidateProperties(); } /** * neu : function hinzugefügt wegen bug issue 15 http://code.google.com/p/flexlib/issues/detail?id=15 * Treegrid not updated when dataprovider changes * */ protected function rootModelChangeHandler(event:Event):void { dataProviderChanged = true; // hinzugefügt, um, wenn es der gleiche dataProvider mit veränderten kindern handelt // (wird in commitProperties überprüft) die bisher geöffneten element wieder zu öffnen. openItemsChanged = true; invalidateProperties(); } override public function get dataProvider() : Object { return _rootModel; } //-------------------------------------------------------------------------- // dataDescriptor //-------------------------------------------------------------------------- /** * @private */ private var _dataDescriptor : ITreeDataDescriptor = new DefaultDataDescriptor(); [Inspectable(category="Data")] public function set dataDescriptor( value : ITreeDataDescriptor ) : void { _dataDescriptor = value; } public function get dataDescriptor() : ITreeDataDescriptor { return ITreeDataDescriptor( _dataDescriptor ); } /** * Sets the visibility of the root item. * * If the dataProvider data has a root node, and this is set to * false, the Tree control does not display the root item. * Only the decendants of the root item are displayed. * * This flag has no effect on non-rooted dataProviders, such as List and Array. * * @default true * @see #hasRoot */ public function get showRoot() : Boolean { return _showRoot; } /** * @private */ public function set showRoot( value : Boolean ) : void { if ( _showRoot != value ) { _showRoot = value; showRootChanged = true; invalidateProperties(); } } /** * Indicates that the current dataProvider has a root item; for example, * a single top node in a hierarchical structure. XML and Object * are examples of types that have a root. Lists and arrays do not. * * @see #showRoot */ public function get hasRoot() : Boolean { return _hasRoot; } override protected function commitProperties() : void { if ( showRootChanged ) { if ( !_hasRoot ) showRootChanged = false; } if ( dataProviderChanged || showRootChanged ) { var tmpCollection : ICollectionView; var row : Object; //we always reset the displayed rows on a dataprovider assignment or when the showRoot property change _displayedModel = new ArrayCollection(); //reset flags dataProviderChanged = false; showRootChanged = false; //we always reset the open and selected items on a dataprovider assignment if ( !openItemsChanged ) _openItems = {}; // are we swallowing the root? if ( _rootModel && !_showRoot && _hasRoot ) { var rootItem : * = _rootModel.createCursor().current; if ( rootItem != null && _dataDescriptor.isBranch( rootItem, _rootModel ) && _dataDescriptor.hasChildren( rootItem, _rootModel )) { // then get rootItem children tmpCollection = getChildren( rootItem, _rootModel ); for each( row in tmpCollection ) { _displayedModel.addItem( row ); } } } else { if( _hasRoot ) { if( _rootModel != null && _rootModel.length > 0 ) { _displayedModel.addItem( _rootModel[0] ); } } else { for each( row in _rootModel ) { _displayedModel.addItem( row ); } } } var vScrollPos:int = verticalScrollPosition; var hScrollPos:int = horizontalScrollPosition; super.dataProvider = _displayedModel; //TODO: maybe we can use HierarchicalCollectionView? /*// at this point _rootModel may be null so we dont need to continue if ( _rootModel ) { //wrap userdata in a TreeCollection and pass that collection to the List super.dataProvider = wrappedCollection = new HierarchicalCollectionView( tmpCollection != null ? tmpCollection : _rootModel, _dataDescriptor, _openItems); // not really a default handler, but we need to be later than the wrapper wrappedCollection.addEventListener(CollectionEvent.COLLECTION_CHANGE, collectionChangeHandler, false, EventPriority.DEFAULT_HANDLER, true); } else { super.dataProvider = null; }*/ } if ( openItemsChanged ) { /* start: um die scrollposition nach setzen des dataProviders wiederherzustellen. */ verticalScrollPosition = vScrollPos; horizontalScrollPosition = hScrollPos; /* start: da sonst set openItems keine wirkung hat! */ for each(var item:* in openItems){ openItemAt(getItemIndex(item),item); } /* ende: */ openItemsChanged = false; //setting open items resets the collection var event : CollectionEvent = new CollectionEvent(CollectionEvent.COLLECTION_CHANGE); event.kind = CollectionEventKind.RESET; collection.dispatchEvent(event); } super.commitProperties(); } override protected function makeListData( data : Object, uid : String, rowNum : int, columnNum : int, column : DataGridColumn ) : BaseListData { var treeGridListData : TreeGridListData; if ( data is DataGridColumn ) { treeGridListData = new TreeGridListData( (column.headerText != null) ? column.headerText : column.dataField, column.dataField, columnNum, uid, this, rowNum); } else { treeGridListData = new TreeGridListData( column.itemToLabel(data), column.dataField, columnNum, uid, this, rowNum); initListData( data, treeGridListData ); } return treeGridListData; } protected function initListData( item : Object, treeListData : TreeGridListData ) : void { if (item == null) return; var open:Boolean = isItemOpen(item); var branch:Boolean = isBranch(item); var uid:String = itemToUID(item); // this is hidden by non-branches but kept so we know how wide it is so things align treeListData.disclosureIcon = getStyle(open ? "disclosureOpenIcon" : "disclosureClosedIcon"); treeListData.open = open; treeListData.hasChildren = branch; treeListData.depth = getItemDepth( item, treeListData.rowIndex ); treeListData.indent = (treeListData.depth - ( _showRoot ? 1 : 2 ) ) * getStyle("indentation"); treeListData.indentationGap = getStyle("indentation"); treeListData.item = item; treeListData.icon = itemToIcon( item ); treeListData.trunk = getStyle("verticalTrunks"); if( treeListData.trunk ) { treeListData.trunkOffsetTop = getStyle("paddingTop"); treeListData.trunkOffsetBottom = getStyle("paddingBottom"); treeListData.hasSibling =false;// !isLastItem( treeListData ); } } private function getChildren( item : Object, view : Object) : ICollectionView { //get the collection of children var children : ICollectionView = _dataDescriptor.getChildren( item, view ); return children; } /** * This method find if the current node is the last displayed sibling * * Used to draw the vertical trunk lines, * if it is the last child then the vertical trunk line should stop in the middle of the row **/ protected function isLastItem( listData : TreeGridListData ) : Boolean { //TODO: find a way to optimize this method, it's SLOOOWWWW maybe by using HierarchicalCollectionView? ... // einkommentiert - lieferte immer false! var rowIndex : int = getItemIndex( listData.item ); var data : Object = IList( dataProvider ).getItemAt( rowIndex ); if( IList( dataProvider ).length > rowIndex + 1 ) { for(var i:int = rowIndex + 1; i < object =" IList(" int =" getItemDepth(" nextdatadepth ="=" depth =" searchForCurrentDepth(" int =" 0;" depth =" searchForCurrentDepth(" depth ="=" depth ="=" int =" 1" value ="=" value ="=" int =" 0;" int =" searchForCurrentDepth(" iviewcursor =" collection.createCursor();" int =" 0;" current ="=" boolean =" false;" array =" [];" openitemschanged =" true;" string =" itemToUID(" item ="=" boolean =" isItemOpen(" boolean =" isBranch(" string =" itemToUID(" class =" itemIcons" icon =" String(" icon =" item[" icon =" item.icon;" icon ="=" icon =" getStyle(" iconclass =" Class(">iconField and iconFunction properties for * this item if it is a leaf item. Branch items don't use the * iconField and iconFunction properties. * They use the folderOpenIcon and folderClosedIcon properties. * * @param item Item to affect. * @param iconID Linkage ID for the closed (or leaf) icon. * @param iconID2 Linkage ID for the open icon. * * @tiptext Sets the icons for the specified item * @helpid 3201 */ public function setItemIcon(item:Object, iconID:Class, iconID2:Class):void { if ( !itemIcons ) itemIcons = {}; if ( !iconID2 ) iconID2 = iconID; itemIcons[ itemToUID(item) ] = { iconID: iconID, iconID2: iconID2 }; itemsSizeChanged = true; invalidateDisplayList(); } /** * */ public function closeAllItems() : void { for( var i : int = 0; i < object =" null," boolean =" true" item ="=" item =" ListCollectionView(" string =" itemToUID(" int =" 0;" object =" null" object =" null" string =" itemToUID(" selectedindex =" -1;" item ="=" item =" ListCollectionView(" int =" 0;" number =" 0;" i =" 0;" object =" _dataDescriptor.getChildren(" dataprovider =" [];" array =" event.dragSource.dataForFormat(" int =" calculateDropIndex(event);" boolean =" getDragDropAtDepthAllowed(dropIndex);" action ="=" draginitiator ="=" array =" selectedIndices;" int =" indices.length">= 0; i--) { var collectionIterator:IViewCursor = collection.createCursor(); collectionIterator.seek(CursorBookmark.FIRST, indices[i]); var dragItem:XML = XML(_displayedModel.getItemAt(indices[i])); // funkioniert nur mit einem XML dataProvider im moment if (dragItem is XML){ var dragItemParent:XML = XML(XML(dragItem).parent()); if (dragItemParent != null){ //var cIdx:int = XML(dragItem).childIndex(); var idx:int = dragItem.childIndex(); delete dragItemParent.children()[idx]; } } /* if (indices[i] < int =" items.length">= 0; i--) { if (event.action == DragManager.COPY) { //collectionIterator.insert(copyItemWithUID(items[i])); var copyItem:Object = copyItemWithUID(items[i]); try{ dropOverItem = _displayedModel.getItemAt(dropIndex); }catch(e:RangeError){ // wenn das item hinter das letzte item gesetzt wird, // gibt es kein dropOverItem } if (dropOverItem != null){ if (items[i] is XML){ var dropItemParent:XML = XML(dropOverItem).parent() as XML; if (dropItemParent != null){ dropItemParent.insertChildBefore(dropOverItem,copyItem); } } }else{ // wenn null, dann nach ganz hinten! XML(_orgValue).appendChild(items[i]); } } else if (event.action == DragManager.MOVE) { //collectionIterator.insert(items[i]); if (items[i] is XML){ var dropOverItem:Object = null; try{ dropOverItem = _displayedModel.getItemAt(dropIndex); }catch(e:RangeError){ // wenn das item hinter das letzte item gesetzt wird, // gibt es kein dropOverItem } if (dropOverItem != null){ var dropItemParent:XML = XML(dropOverItem).parent() as XML; if (dropItemParent != null){ dropItemParent.insertChildBefore(dropOverItem,items[i]); } }else{ // wenn null, dann nach ganz hinten! XML(_orgValue).appendChild(items[i]); } } } } } } } /** * neu:hinzugefügt * wenn dragDropDepthEqual=true muss die Tiefe im Baum des gedraggden und des dropOverItem die gleiche sein. */ private function getDragDropAtDepthAllowed(dropIndex:int):Boolean{ var dragDropAtThisDepthAllowed:Boolean = false; // die tiefe des drag und des dropOver elements. wird benötigt, // um zu sehen, ob drag und drop tiefe die gleiche sind. wsa relevant ist bei dragDropDepthEqual=true var dropOverItemDepth:int = showRoot ? 1 : 2; // default ist 2 (root hat 1 und kann nicht wirklich verschoben werden, erst dessen kinder) var dragItemDepth:int = showRoot ? 1 : 2; // default ist 2 (root hat 1 und kann nicht wirklich verschoben werden, erst dessen kinder) var dropOverItem:Object = null; try{ dropOverItem = _displayedModel.getItemAt(dropIndex); dropOverItemDepth = getItemDepth(dropOverItem, -1); }catch(e:RangeError){ // wenn das item hinter das letzte item gesetzt wird, // gibt es kein dropOverItem } var selectedItem:Object = _displayedModel.getItemAt(selectedIndices[0]); dragItemDepth = getItemDepth(selectedItem,-1); if (dragDropDepthEqual && (dropOverItemDepth == dragItemDepth)){ dragDropAtThisDepthAllowed = true; }else if (!dragDropDepthEqual){ dragDropAtThisDepthAllowed = true; } return dragDropAtThisDepthAllowed; } } // end class } // end package

Keine Kommentare: