blob: bf32f073e8649f2844f8ec6e631ef1bde4591b0b [file] [log] [blame]
////////////////////////////////////////////////////////////////////////////////
//
// Licensed to the Apache Software Foundation (ASF) under one or more
// contributor license agreements. See the NOTICE file distributed with
// this work for additional information regarding copyright ownership.
// The ASF licenses this file to You under the Apache License, Version 2.0
// (the "License"); you may not use this file except in compliance with
// the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
////////////////////////////////////////////////////////////////////////////////
package mx.collections
{
import flash.events.EventDispatcher;
import mx.collections.errors.ChildItemPendingError;
import mx.collections.errors.ItemPendingError;
import mx.core.mx_internal;
import mx.events.CollectionEvent;
import mx.events.CollectionEventKind;
import mx.events.FlexEvent;
import mx.utils.ObjectUtil;
import mx.utils.UIDUtil;
use namespace mx_internal;
/**
* The HierarchicalCollectionViewCursor class defines a
* cursor for a hierarchical view of a standard collection.
* The collection that this cursor walks across need not be hierarchical - it may be flat.
* This class delegates to the IHierarchicalData for information regarding the tree
* structure of the data it walks across.
*
* @see HierarchicalCollectionView
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public class HierarchicalCollectionViewCursor extends EventDispatcher
implements IHierarchicalCollectionViewCursor
{
include "../core/Version.as";
//--------------------------------------------------------------------------
//
// Constructor
//
//--------------------------------------------------------------------------
/**
* Constructor.
*
* @param collection The HierarchicalCollectionView instance referenced by this cursor.
*
* @param model The source data collection.
*
* @param hierarchicalData The data used to create the HierarchicalCollectionView instance.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function HierarchicalCollectionViewCursor(
collection:HierarchicalCollectionView,
model:ICollectionView,
hierarchicalData:IHierarchicalData)
{
super();
//fields
this.collection = collection;
this.hierarchicalData = hierarchicalData;
this.model = model;
//events
collection.addEventListener(CollectionEvent.COLLECTION_CHANGE, collectionChangeHandler, false, 0, true);
//init
modelCursor = model.createCursor();
//check to see if the model has more than one top level items
more = model.length > 1;
}
//--------------------------------------------------------------------------
//
// Variables
//
//--------------------------------------------------------------------------
/**
* @private
*/
private var hierarchicalData:IHierarchicalData;
/**
* @private
* The bookmark representing the position of the current node in its siblings collection
*/
private var currentChildBookmark:CursorBookmark = CursorBookmark.FIRST;
/**
* @private
* Its effective offset into the "array".
*/
private var currentIndex:int = 0;
/**
* @private
* The depth of the current node.
*/
private var _currentDepth:int = 1;
/**
* @private
* The current set of childNodes we are walking.
*/
private var childNodes:ICollectionView;
/**
* @private
* The current set of parentNodes that we have walked from
*/
private var parentNodes:Array = [];
/**
* @private
* A stack of the currentChildBookmark in all parents of the currentNode.
*/
private var parentBookmarkStack:Array = [];
/**
* @private
* The collection that stores the user data
*/
private var model:ICollectionView;
/**
* @private
* The collection wrapper of the model
*/
private var collection:HierarchicalCollectionView;
/**
* @private
* Flag indicating model has more data
*/
private var more:Boolean;
/**
* @private
* Cursor from the model
*/
private var modelCursor:IViewCursor;
//--------------------------------------------------------------------------
//
// Properties
//
//--------------------------------------------------------------------------
//----------------------------------
// index
//----------------------------------
/**
* @private
*/
public function get index():int
{
return currentIndex;
}
//----------------------------------
// bookmark
//----------------------------------
/**
* @inheritDoc
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function get bookmark():CursorBookmark
{
return new CursorBookmark(currentIndex.toString());
}
//----------------------------------
// current
//----------------------------------
/**
* @inheritDoc
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function get current():Object
{
if (parentBookmarkStack.length == 0)
{
return modelCursor.current;
}
else
{
var childCursor:IViewCursor = childNodes.createCursor();
try
{
childCursor.seek(currentChildBookmark);
return childCursor.current;
}
catch (e:ItemPendingError)
{
return null;
}
}
return null;
}
//---------------------------------
// currentDepth
//---------------------------------
/**
* @inheritDoc
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function get currentDepth():int
{
return _currentDepth;
}
//----------------------------------
// beforeFirst
//----------------------------------
/**
* @copy mx.collections.IViewCursor#beforeFirst
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function get beforeFirst():Boolean
{
return currentIndex < 0 && current == null;
}
//----------------------------------
// afterLast
//----------------------------------
/**
* @copy mx.collections.IViewCursor#afterLast
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function get afterLast():Boolean
{
return currentIndex >= collection.length && current == null;
}
//----------------------------------
// view
//----------------------------------
/**
* @copy mx.collections.IViewCursor#view
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function get view():ICollectionView
{
return model;
}
//--------------------------------------------------------------------------
//
// Methods
//
//--------------------------------------------------------------------------
/**
* <p>Note that for this class, the view does not need to be sorted in order to
* call this method. Also, if the item cannot be found, the cursor location is
* left on the last queried object.</p>
*
* @inheritDoc
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function findAny(valuesToMatch:Object):Boolean
{
seek(CursorBookmark.FIRST);
do
{
if (ObjectUtil.valuesAreSubsetOfObject(valuesToMatch, hierarchicalData.getData(current)))
return true;
}
while(moveNext());
return false;
}
/**
* <p>Note that for this class, the view does not need to be sorted in order to
* call this method. Also, if the item cannot be found, the cursor location is
* left on the last queried object.</p>
*
* @inheritDoc
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function findFirst(values:Object):Boolean
{
return findAny(values);
}
/**
* <p>Note that for this class, the view does not need to be sorted in order to
* call this method. Also, if the item cannot be found, the cursor location is
* left on the last queried object.</p>
*
* @inheritDoc
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function findLast(valuesToMatch:Object):Boolean
{
seek(CursorBookmark.LAST);
do
{
if (ObjectUtil.valuesAreSubsetOfObject(valuesToMatch, hierarchicalData.getData(current)))
return true;
}
while(movePrevious());
return false;
}
/**
* @inheritDoc
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function moveNext():Boolean
{
var currentNode:Object = current;
//if there is no currentNode then we can't move forward and must be off the ends
if (currentNode == null)
return false;
var childCursor:IViewCursor;
var uid:String = UIDUtil.getUID(currentNode);
// If current node is a branch and is open, the first child is our next item so return it
if (collection.openNodes[uid] &&
hierarchicalData.canHaveChildren(currentNode) &&
hierarchicalData.hasChildren(currentNode))
{
var previousChildNodes:ICollectionView = childNodes;
childNodes = collection.getChildren(currentNode);
if (childNodes.length > 0)
{
parentBookmarkStack.push(currentChildBookmark);
parentNodes.push(currentNode);
currentChildBookmark = CursorBookmark.FIRST;
childCursor = childNodes.createCursor();
try
{
currentNode = childCursor.current;
// update parent map
updateParentMap(currentNode);
}
catch (e:ItemPendingError)
{
currentNode = null;
throw new ChildItemPendingError(e.message);
}
currentIndex++;
_currentDepth++;
return true;
}
else
{
childNodes = previousChildNodes;
}
}
// Otherwise until we find the next child (could be on any level)
while (true)
{
// If we hit the end of this list, pop up a level.
if(childNodes != null)
{
childCursor = childNodes.createCursor();
try
{
childCursor.seek(currentChildBookmark);
childCursor.moveNext();
}
catch(e:ItemPendingError)
{
childCursor.seek(CursorBookmark.FIRST);
}
}
var grandParent:Object;
if (childNodes != null &&
childNodes.length > 0 &&
(childCursor.bookmark == CursorBookmark.LAST || childCursor.afterLast))
{
//check for the end of the tree here.
if (parentBookmarkStack.length < 1 && !more)
{
currentNode = null;
currentIndex++;
_currentDepth = 1;
return false;
}
else
{
//pop up to parent
currentNode = parentNodes.pop();
//get parents siblings
grandParent = parentNodes[parentNodes.length-1];
//we could probably assume that a non-null grandparent has descendants
//but the analogy only goes so far...
if (grandParent != null &&
hierarchicalData.canHaveChildren(grandParent) &&
hierarchicalData.hasChildren(grandParent))
{
childNodes = collection.getChildren(grandParent);
}
else
{
childNodes = null;
}
//get new current nodes index
currentChildBookmark = parentBookmarkStack.pop();
//pop the level up one
_currentDepth--;
}
}
else
{
//if no childnodes then we're probably at the top level
if (parentBookmarkStack.length == 0)
{
//check for more top level siblings
//and if we're here the depth should be 1
_currentDepth = 1;
more = modelCursor.moveNext();
if (more)
{
currentNode = modelCursor.current;
break;
}
else
{
//at the end of the tree
_currentDepth = 1;
currentIndex++; //this should push us to afterLast
currentNode = null;
return false;
}
}
else
{
//get the next child node
try
{
childCursor = childNodes.createCursor();
childCursor.seek(currentChildBookmark);
childCursor.moveNext();
currentChildBookmark = childCursor.bookmark;
currentNode = childCursor.current;
break;
}
catch(e:ItemPendingError)
{
//pop up to parent
currentNode = parentNodes.pop();
//get parents siblings
grandParent = parentNodes[parentNodes.length-1];
//we could probably assume that a non-null grandparent has descendants
//but the analogy only goes so far...
if (grandParent != null &&
hierarchicalData.canHaveChildren(grandParent) &&
hierarchicalData.hasChildren(grandParent))
{
childNodes = collection.getChildren(grandParent);
}
else
{
childNodes = null;
}
//get new current nodes index
currentChildBookmark = parentBookmarkStack.pop();
//pop the level up one
_currentDepth--;
if (parentBookmarkStack.length == 0)
{
//check for more top level siblings
//and if we're here the depth should be 1
_currentDepth = 1;
more = modelCursor.moveNext();
if (more)
{
currentNode = modelCursor.current;
throw new ChildItemPendingError(e.message);
}
else
{
//at the end of the tree
_currentDepth = 1;
currentIndex++; //this should push us to afterLast
currentNode = null;
return false;
}
}
break;
}
}
}
}
updateParentMap(currentNode);
currentIndex++;
return true;
}
/**
* @inheritDoc
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function movePrevious():Boolean
{
var currentNode:Object = current;
// If there are no items, there's no current node, so return false.
if (currentNode == null)
return false;
var childCursor:IViewCursor;
//if not at top level
if (parentNodes && parentNodes.length > 0)
{
if (currentChildBookmark == CursorBookmark.FIRST)
{
//at the first node in this branch so move to parent
currentNode = parentNodes.pop();
currentChildBookmark = parentBookmarkStack.pop();
var grandParent:Object = parentNodes[parentNodes.length-1];
//we could probably assume that a non-null grandparent has descendants
//but the analogy only goes so far...
if (grandParent != null &&
hierarchicalData.canHaveChildren(grandParent) &&
hierarchicalData.hasChildren(grandParent))
{
childNodes = collection.getChildren(grandParent);
}
else
{
childNodes = null;
}
_currentDepth--;
currentIndex--;
return true;
}
else
{
// get previous child sibling
try
{
childCursor = childNodes.createCursor();
childCursor.seek(currentChildBookmark);
childCursor.movePrevious();
currentChildBookmark = childCursor.bookmark;
currentNode = childCursor.current;
try
{
// this is needed because the cursor bookmark for
// the zero position and CursorBookmark.FIRST is not
// same. this condition is encountered while doing
// a movePrevious()
childCursor.movePrevious();
if (childCursor.bookmark == CursorBookmark.FIRST)
currentChildBookmark = CursorBookmark.FIRST;
}
catch (e:ItemPendingError)
{
}
}
catch(e:ItemPendingError)
{
//lets try to recover
return false;
}
}
}
//handle top level siblings
else
{
more = modelCursor.movePrevious();
if (more)
{
//move back one node and then loop through children
currentNode = modelCursor.current;
}
//if past the begining of the tree return false
else
{
//currentIndex--; //should be before first
currentIndex = -1;
currentNode = null;
return false;
}
}
while (true)
{
// If there are children, walk backwards on the children
// and consider youself after your children.
if (collection.openNodes[UIDUtil.getUID(currentNode)] &&
hierarchicalData.canHaveChildren(currentNode) &&
hierarchicalData.hasChildren(currentNode))
{
var previousChildNodes:ICollectionView = childNodes;
childNodes = collection.getChildren(currentNode);
if (childNodes.length > 0)
{
parentBookmarkStack.push(currentChildBookmark);
parentNodes.push(currentNode);
// if the child collection has only one item then set the
// bookmark to first
if (childNodes.length == 1)
currentChildBookmark = CursorBookmark.FIRST;
else
currentChildBookmark = CursorBookmark.LAST;
childCursor = childNodes.createCursor();
try
{
childCursor.seek(currentChildBookmark);
currentNode = childCursor.current;
}
catch (e:ItemPendingError)
{
try
{
childCursor.seek(CursorBookmark.FIRST);
while (!childCursor.afterLast)
{
currentNode = childCursor.current;
childCursor.moveNext();
}
}
catch(e1:ItemPendingError)
{
}
throw new ChildItemPendingError(e.message);
}
_currentDepth++;
}
else
{
childNodes = previousChildNodes;
break;
}
}
else
{
//No more open branches so we'll bail
break;
}
}
updateParentMap(currentNode);
currentIndex--;
return true;
}
/**
* @inheritDoc
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function seek(bookmark:CursorBookmark, offset:int=0,
prefetch:int = 0):void
{
var value:int;
if (bookmark == CursorBookmark.FIRST)
{
value = 0;
}
else if (bookmark == CursorBookmark.CURRENT)
{
value = currentIndex;
}
else if (bookmark == CursorBookmark.LAST)
{
value = collection.length - 1;
}
else
{
value = int(bookmark.value);
}
value = Math.max(Math.min(value + offset, collection.length), 0);
var dc:int = Math.abs(currentIndex - value);
var de:int = Math.abs(collection.length - value);
var movedown:Boolean = true;
// if we're closer to the current than the beginning
if (dc < value)
{
// if we're closer to the end than the current position
if (de < dc)
{
moveToLast();
if (de == 0)
{
// if de = 0; we need to be "off the end"
moveNext();
value = 0;
}
else
{
value = de - 1;
}
movedown = false;
}
else
{
movedown = currentIndex < value;
value = dc;
// if current is off the end, reset
if (currentIndex == collection.length)
{
value--;
moveToLast();
}
}
}
else // we're closer to the beginning than the current
{
// if we're closer to the end than the beginning
if (de < value)
{
moveToLast();
if (de == 0)
{
// if de = 0; we need to be "off the end"
moveNext();
value = 0;
}
else
{
value = de - 1;
}
movedown = false;
}
else
{
moveToFirst();
}
}
if (movedown)
{
while (value && value > 0)
{
moveNext();
value--;
}
}
else
{
while (value && value > 0)
{
movePrevious();
value--;
}
}
}
/**
* @inheritDoc
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function insert(item:Object):void
{
var parent:Object = collection.getParentItem(current);
collection.addChildAt(parent, item, currentIndex);
}
/**
* @inheritDoc
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function remove():Object
{
var obj:Object = current;
var parent:Object = collection.getParentItem(current);
collection.removeChild(parent, current);
return obj;
}
/**
* @private
* Determines if a node is visible on the screen
*/
private function isItemVisible(node:Object):Boolean
{
var parentNode:Object = collection.getParentItem(node);
while (parentNode != null)
{
if (collection.openNodes[UIDUtil.getUID(parentNode)] == null)
return false;
parentNode = collection.getParentItem(parentNode);
}
return true;
}
/**
* @private
* Update the parent map.
*/
private function updateParentMap(currentNode:Object):void
{
if (currentNode != null)
{
var uid:String = UIDUtil.getUID(currentNode);
collection.addParentMapping(uid, parentNodes[parentNodes.length - 1], false);
}
}
/**
* @private
* Creates a stack of parent nodes by walking upwards
*/
private function getParentStack(node:Object):Array
{
var nodeParents:Array = [];
// Make a list of parents of the node.
var obj:Object = collection.getParentItem(node);
while (obj != null)
{
nodeParents.unshift(obj);
obj = collection.getParentItem(obj);
}
return nodeParents;
}
/**
* @private
* When something happens to the tree, find out if it happened
* to children that occur before the current child in the tree walk.
*/
private function isNodeBefore(node:Object, currentNode:Object):Boolean
{
if (currentNode == null)
return false;
var i:int;
var tmpChildNodes:ICollectionView;
var sameParent:Object;
var nodeParents:Array = getParentStack(node);
var curParents:Array = getParentStack(currentNode);
var cursor:IViewCursor;
var child:Object;
// Starting from the root, compare parents
// (assumes the root must be the same).
while (nodeParents.length != 0 && curParents.length != 0)
{
var nodeParent:Object = nodeParents.shift();
var curParent:Object = curParents.shift();
// If not the same parentm then which ever one is first
// in the child list is before the other.
if (nodeParent != curParent)
{
// The last parent must be common.
sameParent = collection.getParentItem(nodeParent);
// Get the child list.
if (sameParent != null &&
hierarchicalData.canHaveChildren(sameParent) &&
hierarchicalData.hasChildren(sameParent))
{
tmpChildNodes = collection.getChildren(sameParent);
}
else
{
tmpChildNodes = model;
}
// Walk it until you hit one or the other.
{
cursor = tmpChildNodes.createCursor();
try
{
cursor.seek(CursorBookmark.FIRST, i);
child = cursor.current;
}
catch (e:ItemPendingError)
{
// item pending - this may never happen
return false;
}
if (child == curParent)
return false;
if (child == nodeParent)
return true;
}
}
}
if (nodeParents.length)
node = nodeParents.shift();
if (curParents.length)
currentNode = curParents.shift();
// If we get here, they have the same parentage or one or both
// had a root parent. Who's first?
tmpChildNodes = model;
cursor = tmpChildNodes.createCursor();
while (!cursor.afterLast)
{
child = cursor.current;
if (child == currentNode)
return false;
if (child == node)
return true;
try
{
cursor.moveNext();
}
catch (e:ItemPendingError)
{
// item pending
return false;
}
}
return false;
}
/**
* @private
*/
private function moveToFirst():void
{
childNodes = null;
//first move to the begining of the top level collection
// let it throw an IPE, the classes using this cursor will handle it
modelCursor.seek(CursorBookmark.FIRST, 0);
more = model.length > 1;
currentChildBookmark = CursorBookmark.FIRST;
parentNodes = [];
parentBookmarkStack = [];
currentIndex = 0;
_currentDepth = 1;
}
/**
* @private
*/
public function moveToLast():void
{
childNodes = null;
parentBookmarkStack = [];
_currentDepth = 1;
parentNodes = [];
//first move to the end of the top level collection
// let it throw an IPE, the classes using this cursor will handle it
modelCursor.seek(CursorBookmark.LAST, 0);
//if its a branch and open then get children for the last item
var currentNode:Object = modelCursor.current;
//if current node is open get its children
while (collection.openNodes[UIDUtil.getUID(currentNode)] &&
hierarchicalData.canHaveChildren(currentNode) &&
hierarchicalData.hasChildren(currentNode))
{
var previousChildNodes:ICollectionView = childNodes;
childNodes = collection.getChildren(currentNode);
if (childNodes != null && childNodes.length > 0)
{
var childCursor:IViewCursor = childNodes.createCursor();
try
{
childCursor.seek(CursorBookmark.LAST);
}
catch (e:ItemPendingError)
{
// just break because if the last child item is pending
// return its parent
break;
}
parentNodes.push(currentNode);
parentBookmarkStack.push(currentChildBookmark);
currentNode = childCursor.current;
currentChildBookmark = CursorBookmark.LAST;
try
{
// this is needed because the cursor bookmark for
// the zero position and CursorBookmark.FIRST is not
// same. this condition is encountered while doing
// a movePrevious()
childCursor.movePrevious();
if (childCursor.bookmark == CursorBookmark.FIRST)
currentChildBookmark = CursorBookmark.FIRST;
}
catch (e:ItemPendingError)
{
}
_currentDepth++;
}
else
{
childNodes = previousChildNodes;
break;
}
}
currentIndex = collection.length - 1;
}
//--------------------------------------------------------------------------
//
// Event handlers
//
//--------------------------------------------------------------------------
/**
* @private
*/
public function collectionChangeHandler(event:CollectionEvent):void
{
var i:int;
var n:int;
var changingNode:Object;
var parentOfChangingNode:Object;
var parentOfCurrentNode:Object;
var parentStack:Array;
var isBefore:Boolean = false;
var parentOfChangingNodeIndex:int;
var isChangingNodeParentAncestorOfSelectedNode:Boolean;
var bookmarkInChangingCollection:CursorBookmark;
var changingNodeCollectionBookmarkIndex:int;
var changingNodeAndSiblings:ICollectionView;
var changingCollectionCursor:IViewCursor;
if (event.kind == CollectionEventKind.ADD)
{
n = event.items.length;
if (event.location <= currentIndex)
{
currentIndex += n;
isBefore = true;
}
parentStack = getParentStack(current);
parentOfCurrentNode = parentStack[parentStack.length - 1];
for (i = 0; i < n; i++)
{
changingNode = event.items[i];
if (isBefore)
{
// if the added node is before the current
// and they share parents then we have to
// adjust the currentChildIndex or
// the stack of child indexes.
parentOfChangingNode = collection.getParentItem(changingNode);
changingNodeAndSiblings = collection.getChildren(parentOfChangingNode);
if (parentOfChangingNode == parentOfCurrentNode)
{
if (changingNodeAndSiblings != null)
{
changingCollectionCursor = changingNodeAndSiblings.createCursor();
try
{
changingCollectionCursor.seek(currentChildBookmark);
changingCollectionCursor.moveNext();
currentChildBookmark = changingCollectionCursor.bookmark;
}
catch (e:ItemPendingError)
{
}
}
}
else {
parentOfChangingNodeIndex = parentStack.indexOf(parentOfChangingNode);
isChangingNodeParentAncestorOfSelectedNode = parentOfChangingNodeIndex != -1;
if (isChangingNodeParentAncestorOfSelectedNode)
{
if (changingNodeAndSiblings != null)
{
changingNodeCollectionBookmarkIndex = parentOfChangingNodeIndex + 1;
changingCollectionCursor = changingNodeAndSiblings.createCursor();
bookmarkInChangingCollection = parentBookmarkStack[changingNodeCollectionBookmarkIndex];
try
{
changingCollectionCursor.seek(bookmarkInChangingCollection);
changingCollectionCursor.moveNext();
}
catch (e:ItemPendingError)
{
}
parentBookmarkStack[changingNodeCollectionBookmarkIndex] = changingCollectionCursor.bookmark;
}
}
}
}
}
}
else if (event.kind == CollectionEventKind.REMOVE)
{
n = event.items.length;
if (event.location <= currentIndex)
{
var lastIndexAffectedByDeletion:int = event.location + n;
var isCurrentIndexAmongRemovedNodes:Boolean = lastIndexAffectedByDeletion >= currentIndex;
var currentItemNotFoundAmongItsSiblings:Boolean = isCurrentIndexAmongRemovedNodes ? false : (!afterLast && !beforeFirst && current == null);
if (isCurrentIndexAmongRemovedNodes || currentItemNotFoundAmongItsSiblings)
{
// the list classes expect that we
// leave the cursor on whatever falls
// into that slot
var indexToReturnTo:int = isCurrentIndexAmongRemovedNodes ? event.location : currentIndex - n;
moveToFirst();
seek(CursorBookmark.FIRST, indexToReturnTo);
return;
}
currentIndex -= n;
isBefore = true;
}
parentStack = getParentStack(current);
parentOfCurrentNode = parentStack[parentStack.length - 1];
for (i = 0; i < n; i++)
{
changingNode = event.items[i];
if (isBefore)
{
// if the removed node is before the current
// and they share parents then we have to
// adjust the currentChildIndex or
// the stack of child indexes.
parentOfChangingNode = collection.getParentItem(changingNode);
changingNodeAndSiblings = collection.getChildren(parentOfChangingNode);
if (parentOfChangingNode == parentOfCurrentNode)
{
if(currentChildBookmark == CursorBookmark.LAST)
break;
if (changingNodeAndSiblings != null)
{
changingCollectionCursor = changingNodeAndSiblings.createCursor();
try
{
changingCollectionCursor.seek(currentChildBookmark);
changingCollectionCursor.movePrevious();
currentChildBookmark = changingCollectionCursor.bookmark;
}
catch (e:ItemPendingError)
{
}
}
}
else {
parentOfChangingNodeIndex = parentStack.indexOf(parentOfChangingNode);
isChangingNodeParentAncestorOfSelectedNode = parentOfChangingNodeIndex != -1;
if (isChangingNodeParentAncestorOfSelectedNode)
{
if (changingNodeAndSiblings != null)
{
changingNodeCollectionBookmarkIndex = parentOfChangingNodeIndex + 1;
changingCollectionCursor = changingNodeAndSiblings.createCursor();
bookmarkInChangingCollection = parentBookmarkStack[changingNodeCollectionBookmarkIndex];
if(bookmarkInChangingCollection == CursorBookmark.LAST)
break;
try
{
changingCollectionCursor.seek(bookmarkInChangingCollection);
changingCollectionCursor.movePrevious();
}
catch (e:ItemPendingError)
{
}
parentBookmarkStack[changingNodeCollectionBookmarkIndex] = changingCollectionCursor.bookmark;
}
}
}
}
}
}
else if (event.kind == CollectionEventKind.RESET)
{
// update the source collection and the cursor
model = collection.treeData;
modelCursor = model.createCursor();
// dispatch CURSOR_UPDATE event
collection.dispatchEvent(new FlexEvent(FlexEvent.CURSOR_UPDATE));
}
else if (event.kind == CollectionEventKind.REFRESH)
{
// find the correct index of the item
if (!(beforeFirst || afterLast))
{
findFirst(current);
}
}
}
}
}