| //////////////////////////////////////////////////////////////////////////////// |
| // |
| // 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.Event; |
| import flash.events.EventDispatcher; |
| import flash.utils.Proxy; |
| import flash.utils.flash_proxy; |
| import flash.utils.getQualifiedClassName; |
| |
| import mx.binding.utils.BindingUtils; |
| import mx.binding.utils.ChangeWatcher; |
| import mx.collections.errors.CollectionViewError; |
| import mx.collections.errors.ItemPendingError; |
| import mx.collections.errors.SortError; |
| import mx.core.IMXMLObject; |
| import mx.core.mx_internal; |
| import mx.events.CollectionEvent; |
| import mx.events.CollectionEventKind; |
| import mx.events.PropertyChangeEvent; |
| import mx.resources.IResourceManager; |
| import mx.resources.ResourceManager; |
| import mx.utils.ObjectUtil; |
| |
| use namespace mx_internal; |
| |
| /** |
| * Dispatched when the ICollectionView has been updated in some way. |
| * |
| * @eventType mx.events.CollectionEvent.COLLECTION_CHANGE |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| [Event(name="collectionChange", type="mx.events.CollectionEvent")] |
| |
| [ResourceBundle("collections")] |
| |
| /** |
| * The ListCollectionView class adds the properties and methods of the |
| * <code>ICollectionView</code> interface to an object that conforms to the |
| * <code>IList</code> interface. As a result, you can pass an object of this class |
| * to anything that requires an <code>IList</code> or <code>ICollectionView</code>. |
| * |
| * <p>This class also lets you use [ ] array notation |
| * to access the <code>getItemAt()</code> and <code>setItemAt()</code> methods. |
| * If you use code such as <code>myListCollectionView[index]</code> |
| * Flex calls the <code>myListCollectionView</code> object's |
| * <code>getItemAt()</code> or <code>setItemAt()</code> method.</p> |
| * |
| * @mxml |
| * |
| * <p>The <code><mx:ListCollectionView></code> has the following attributes, |
| * which all of its subclasses inherit:</p> |
| * |
| * <pre> |
| * <mx:ListCollectionView |
| * <b>Properties</b> |
| * filterFunction="null" |
| * list="null" |
| * sort="null" |
| * <b>Events</b> |
| * collectionChange="<i>No default</i>" |
| * /> |
| * </pre> |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public class ListCollectionView extends Proxy |
| implements ICollectionView, IList, IMXMLObject |
| { |
| include "../core/Version.as"; |
| |
| //-------------------------------------------------------------------------- |
| // |
| // Private variables |
| // |
| //-------------------------------------------------------------------------- |
| |
| /** |
| * @private |
| * Change watcher for complex sort fields. |
| */ |
| private var _complexFieldWatcher:ComplexFieldChangeWatcher; |
| |
| /** |
| * @private |
| * Internal event dispatcher. |
| */ |
| private var eventDispatcher:EventDispatcher; |
| |
| /** |
| * @private |
| * Revisions are used for bookmark maintenace, |
| * see getBookmark() and getBookmarkIndex() along with reset(). |
| */ |
| private var revision:int; |
| |
| /** |
| * @private |
| * Used internally for managing disableAutoUpdate and enableAutoUpdate |
| * calls. disableAutoUpdate increments the counter, enable decrements. |
| * When the counter reaches 0 handlePendingUpdates is called. |
| */ |
| private var autoUpdateCounter:int; |
| |
| /** |
| * @private |
| * Any update events that occured while autoUpdateCounter > 0 |
| * are stored here. |
| * This may be null when there are no updates. |
| */ |
| private var pendingUpdates:Array; |
| |
| /** |
| * @private |
| * Flag that indicates whether a RESET type of collectionChange |
| * event should be emitted when reset() is called. |
| */ |
| mx_internal var dispatchResetEvent:Boolean = true; |
| |
| /** |
| * @private |
| * Used for accessing localized Error messages. |
| */ |
| private var resourceManager:IResourceManager = |
| ResourceManager.getInstance(); |
| |
| //-------------------------------------------------------------------------- |
| // |
| // Protected variables |
| // |
| //-------------------------------------------------------------------------- |
| |
| /** |
| * When the view is sorted or filtered the <code>localIndex</code> property |
| * contains an array of items in the sorted or filtered (ordered, reduced) |
| * view, in the sorted order. |
| * The ListCollectionView class uses this property to access the items in |
| * the view. |
| * The <code>localIndex</code> property should never contain anything |
| * that is not in the source, but may not have everything in the source. |
| * This property is <code>null</code> when there is no sort. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| protected var localIndex:Array; |
| |
| //-------------------------------------------------------------------------- |
| // |
| // Constructor |
| // |
| //-------------------------------------------------------------------------- |
| |
| /** |
| * The ListCollectionView constructor. |
| * |
| * @param list the IList this ListCollectionView is meant to wrap. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function ListCollectionView(list:IList = null) |
| { |
| super(); |
| |
| eventDispatcher = new EventDispatcher(this); |
| this.list = list; |
| } |
| |
| /** |
| * Called automatically by the MXML compiler when the ListCollectionView |
| * is created using an MXML tag. |
| * If you create the ListCollectionView through ActionScript, you |
| * must call this method passing in the MXML document and |
| * <code>null</code> for the <code>id</code>. |
| * |
| * @param document The MXML document containing this ListCollectionView. |
| * |
| * @param id Ignored. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function initialized(document:Object, id:String):void |
| { |
| refresh(); |
| } |
| |
| //-------------------------------------------------------------------------- |
| // |
| // Properties |
| // |
| //-------------------------------------------------------------------------- |
| |
| //---------------------------------- |
| // length |
| //---------------------------------- |
| |
| [Bindable("collectionChange")] |
| |
| /** |
| * @inheritDoc |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function get length():int |
| { |
| if (localIndex) |
| { |
| return localIndex.length; |
| } |
| else if (list) |
| { |
| return list.length; |
| } |
| else |
| { |
| return 0; |
| } |
| } |
| |
| //---------------------------------- |
| // list |
| //---------------------------------- |
| |
| /** |
| * @private |
| * Storage for the list property. |
| */ |
| private var _list:IList; |
| |
| [Inspectable(category="General")] |
| [Bindable("listChanged")] |
| |
| /** |
| * The IList that this collection view wraps. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function get list():IList |
| { |
| return _list; |
| } |
| |
| /** |
| * @private |
| */ |
| public function set list(value:IList):void |
| { |
| if (_list != value) |
| { |
| var oldHasItems:Boolean; |
| var newHasItems:Boolean; |
| if (_list) |
| { |
| _list.removeEventListener(CollectionEvent.COLLECTION_CHANGE, |
| listChangeHandler); |
| oldHasItems = _list.length > 0; |
| } |
| |
| _list = value; |
| |
| if (_list) |
| { |
| // weak listeners to collections and dataproviders |
| _list.addEventListener(CollectionEvent.COLLECTION_CHANGE, |
| listChangeHandler, false, 0, true); |
| newHasItems = _list.length > 0; |
| } |
| |
| if (oldHasItems || newHasItems) |
| reset(); |
| dispatchEvent(new Event("listChanged")); |
| } |
| } |
| |
| //---------------------------------- |
| // filterFunction |
| //---------------------------------- |
| |
| /** |
| * @private |
| * Storage for the filterFunction property. |
| */ |
| private var _filterFunction:Function; |
| |
| [Bindable("filterFunctionChanged")] |
| [Inspectable(category="General")] |
| |
| /** |
| * @inheritDoc |
| * |
| * @see #refresh() |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function get filterFunction():Function |
| { |
| return _filterFunction; |
| } |
| |
| /** |
| * @private |
| */ |
| public function set filterFunction(f:Function):void |
| { |
| _filterFunction = f; |
| dispatchEvent(new Event("filterFunctionChanged")); |
| } |
| |
| //---------------------------------- |
| // sort |
| //---------------------------------- |
| |
| /** |
| * @private |
| * Storage for the sort property. |
| */ |
| private var _sort:ISort; |
| |
| [Bindable("sortChanged")] |
| [Inspectable(category="General")] |
| |
| /** |
| * @inheritDoc |
| * |
| * @see #refresh() |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function get sort():ISort |
| { |
| return _sort; |
| } |
| |
| /** |
| * @private |
| */ |
| public function set sort(value:ISort):void |
| { |
| if(_sort != value) |
| { |
| stopWatchingForComplexFieldsChanges(); |
| |
| _sort = value; |
| |
| startWatchingForComplexFieldsChanges(); |
| |
| dispatchEvent(new Event("sortChanged")); |
| } |
| } |
| |
| |
| |
| //-------------------------------------------------------------------------- |
| // |
| // ICollectionView Methods |
| // |
| //-------------------------------------------------------------------------- |
| |
| /** |
| * @inheritDoc |
| * |
| * @see #enableAutoUpdate() |
| * @see mx.events.CollectionEvent |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function contains(item:Object):Boolean |
| { |
| return getItemIndex(item) != -1; |
| } |
| |
| /** |
| * @inheritDoc |
| * |
| * @see mx.collections.ICollectionView#enableAutoUpdate() |
| * @see mx.events.CollectionEvent |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function disableAutoUpdate():void |
| { |
| autoUpdateCounter++; |
| } |
| |
| /** |
| * @inheritDoc |
| * |
| * @see mx.collections.ICollectionView#disableAutoUpdate() |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function enableAutoUpdate():void |
| { |
| if (autoUpdateCounter > 0) |
| { |
| autoUpdateCounter--; |
| if (autoUpdateCounter == 0) |
| { |
| handlePendingUpdates(); |
| } |
| } |
| } |
| |
| /** |
| * @inheritDoc |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function createCursor():IViewCursor |
| { |
| return new ListCollectionViewCursor(this); |
| } |
| |
| /** |
| * @inheritDoc |
| * |
| * @see mx.events.CollectionEvent |
| * @see mx.core.IPropertyChangeNotifier |
| * @see mx.events.PropertyChangeEvent |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function itemUpdated(item:Object, property:Object = null, |
| oldValue:Object = null, |
| newValue:Object = null):void |
| { |
| list.itemUpdated(item, property, oldValue, newValue); |
| } |
| |
| /** |
| * @inheritDoc |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function refresh():Boolean |
| { |
| return internalRefresh(true); |
| } |
| |
| //-------------------------------------------------------------------------- |
| // |
| // IList Methods |
| // |
| //-------------------------------------------------------------------------- |
| |
| [Bindable("collectionChange")] |
| |
| /** |
| * @inheritDoc |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function getItemAt(index:int, prefetch:int = 0):Object |
| { |
| if (index < 0 || index >= length) |
| { |
| var message:String = resourceManager.getString( |
| "collections", "outOfBounds", [ index ]); |
| throw new RangeError(message); |
| } |
| |
| if (localIndex) |
| { |
| return localIndex[index]; |
| } |
| else if (list) |
| { |
| return list.getItemAt(index, prefetch); |
| } |
| |
| return null; |
| } |
| |
| /** |
| * @inheritDoc |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function setItemAt(item:Object, index:int):Object |
| { |
| if (index < 0 || !list || index >= length) |
| { |
| var message:String = resourceManager.getString( |
| "collections", "outOfBounds", [ index ]); |
| throw new RangeError(message); |
| } |
| |
| var listIndex:int = index; |
| if (localIndex) |
| { |
| if (index > localIndex.length) |
| { |
| listIndex = list.length; |
| } |
| else |
| { |
| var oldItem:Object = localIndex[index]; |
| // FIXME fails on duplicates |
| listIndex = list.getItemIndex(oldItem); |
| } |
| } |
| return list.setItemAt(item, listIndex); |
| } |
| |
| /** |
| * @inheritDoc |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function addItem(item:Object):void |
| { |
| if (localIndex) |
| addItemAt(item, localIndex.length); |
| else |
| addItemAt(item, length); |
| } |
| |
| /** |
| * @inheritDoc |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function addItemAt(item:Object, index:int):void |
| { |
| if (index < 0 || !list || index > length) |
| { |
| var message:String = resourceManager.getString( |
| "collections", "outOfBounds", [ index ]); |
| throw new RangeError(message); |
| } |
| |
| var listIndex:int = index; |
| |
| // if we're sorted addItemAt is meaningless, just add to the end |
| if (localIndex && sort) |
| { |
| listIndex = list.length; |
| } |
| else if (localIndex && filterFunction != null) |
| { |
| // if end of filtered list, put at end of source list |
| if (listIndex == localIndex.length) |
| listIndex = list.length; |
| // if somewhere in filtered list, find it and insert before it |
| // or at beginning |
| else |
| listIndex = list.getItemIndex(localIndex[index]); |
| } |
| // List is sorted or filtered but refresh has not been called |
| // Just add to end of list |
| else if (localIndex) |
| { |
| listIndex = list.length; |
| } |
| |
| list.addItemAt(item, listIndex); |
| } |
| |
| /** |
| * Adds a list of items to the current list, placing them at the end of |
| * the list in the order they are passed. |
| * |
| * @param addList IList The list of items to add to the current list |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function addAll(addList:IList):void |
| { |
| if (localIndex) |
| addAllAt(addList, localIndex.length); |
| else |
| addAllAt(addList, length); |
| } |
| |
| /** |
| * Adds a list of items to the current list, placing them at the position |
| * index passed in to the function. The items are placed at the index location |
| * and placed in the order they are recieved. |
| * |
| * @param addList IList The list of items to add to the current list |
| * @param index The location of the current list to place the new items. |
| * @throws RangeError if index is less than 0 or greater than the length of the list. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function addAllAt(addList:IList, index:int):void |
| { |
| if (index < 0 || index > this.length) |
| { |
| var message:String = resourceManager.getString( |
| "collections", "outOfBounds", [ index ]); |
| throw new RangeError(message); |
| } |
| |
| var length:int = addList.length; |
| |
| for (var i:int=0; i < length; i++) |
| { |
| var insertIndex:int = i + index; |
| |
| // incremental index may be out of bounds because of filtering, |
| // so add this item to the end. |
| if (insertIndex > this.length) |
| insertIndex = this.length; |
| |
| this.addItemAt(addList.getItemAt(i), insertIndex); |
| } |
| } |
| |
| /** |
| * @inheritDoc |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function getItemIndex(item:Object):int |
| { |
| var i:int; |
| |
| if (localIndex && filterFunction != null) |
| { |
| var len:int = localIndex.length; |
| for (i = 0; i < len; i++) |
| { |
| if (localIndex[i] == item) |
| return i; |
| } |
| |
| return -1; |
| } |
| else if (localIndex && sort) |
| { |
| var startIndex:int = findItem(item, Sort.FIRST_INDEX_MODE); |
| if (startIndex == -1) |
| return -1; |
| |
| var endIndex:int = findItem(item, Sort.LAST_INDEX_MODE); |
| for (i = startIndex; i <= endIndex; i++) |
| { |
| if (localIndex[i] == item) |
| return i; |
| } |
| |
| return -1; |
| } |
| // List is sorted or filtered but refresh has not been called |
| else if (localIndex) |
| { |
| len = localIndex.length; |
| for (i = 0; i < len; i++) |
| { |
| if (localIndex[i] == item) |
| return i; |
| } |
| |
| return -1; |
| } |
| |
| // fallback |
| return list.getItemIndex(item); |
| } |
| |
| /** |
| * @inheritDoc |
| */ |
| mx_internal function getLocalItemIndex(item:Object):int |
| { |
| var i:int; |
| |
| var len:int = localIndex.length; |
| for (i = 0; i < len; i++) |
| { |
| if (localIndex[i] == item) |
| return i; |
| } |
| |
| return -1; |
| } |
| |
| /** |
| * @private |
| */ |
| private function getFilteredItemIndex(item:Object):int |
| { |
| // loc is wrong |
| // the intent of this function is to find where this new item |
| // should be in the filtered list, by looking at the main list |
| // for it's neighbor that is also in this filtered list |
| // and trying to insert item after that neighbor in the insert locao filtered list |
| |
| // 1st get the position in the original list |
| var loc:int = list.getItemIndex(item); |
| |
| // something gone wrong and list is not filtered so just return loc to stop RTE |
| if (filterFunction == null) |
| return loc; |
| |
| // if it's 0 then item must be also the first in the filtered list |
| if (loc == 0) |
| return 0; |
| |
| // scan backwards for an item that also in the filtered list |
| for (var i:int = loc - 1; i >= 0; i--) |
| { |
| var prevItem:Object = list.getItemAt(i); |
| if (filterFunction(prevItem)) |
| { |
| var len:int = localIndex.length; |
| // get the index of the item in the filtered set |
| // for (var j:int = 0; j < len; j++) |
| for (var j:int = 0; j < len; j++) |
| { |
| if (localIndex[j] == prevItem) |
| return j + 1; |
| } |
| } |
| } |
| |
| // turns out that there are no neighbors of item in the filtered |
| // list, so item is the 1st item |
| return 0; |
| } |
| |
| /** |
| * Removes the specified item from this list, should it exist. |
| * Relies on ArrayList implementation |
| * |
| * @param item Object reference to the item that should be removed. |
| * @return Boolean indicating if the item was removed. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Apache Flex 4.10 |
| */ |
| public function removeItem(item:Object):Boolean |
| { |
| if ("removeItem" in list) |
| return list["removeItem"](item); |
| return false; |
| } |
| |
| /** |
| * @inheritDoc |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function removeItemAt(index:int):Object |
| { |
| if (index < 0 || index >= length) |
| { |
| var message:String = resourceManager.getString( |
| "collections", "outOfBounds", [ index ]); |
| throw new RangeError(message); |
| } |
| |
| var listIndex:int = index; |
| if (localIndex) |
| { |
| var oldItem:Object = localIndex[index]; |
| listIndex = list.getItemIndex(oldItem); |
| } |
| return list.removeItemAt(listIndex); |
| } |
| |
| /** |
| * Remove all items from the list. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function removeAll():void |
| { |
| var len:int = length; |
| if (len > 0) |
| { |
| if (localIndex && filterFunction != null) |
| { |
| len = localIndex.length; |
| for (var i:int = len - 1; i >= 0; i--) |
| { |
| removeItemAt(i); |
| } |
| } |
| else |
| { |
| localIndex = null; |
| list.removeAll(); |
| } |
| } |
| } |
| |
| /** |
| * @inheritDoc |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function toArray():Array |
| { |
| var ret:Array; |
| if (localIndex) |
| ret = localIndex.concat(); |
| else |
| ret = list.toArray(); |
| return ret; |
| } |
| |
| /** |
| * Prints the contents of this view to a string and returns it. |
| * |
| * @return The contents of this view, in string form. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function toString():String |
| { |
| if (localIndex) |
| { |
| return ObjectUtil.toString(localIndex); |
| } |
| else |
| { |
| if (list && Object(list).toString) |
| return Object(list).toString(); |
| else |
| return getQualifiedClassName(this); |
| } |
| } |
| |
| //-------------------------------------------------------------------------- |
| // |
| // Proxy methods |
| // |
| //-------------------------------------------------------------------------- |
| |
| /** |
| * @private |
| * Attempts to call getItemAt(), converting the property name into an int. |
| */ |
| override flash_proxy function getProperty(name:*):* |
| { |
| if (name is QName) |
| name = name.localName; |
| |
| try |
| { |
| var n:Number = parseInt(String(name)); |
| } |
| catch(e:Error) // localName was not a number |
| { |
| } |
| |
| if (isNaN(n)) |
| { |
| var message:String = resourceManager.getString( |
| "collections", "unknownProperty", [ name ]); |
| throw new Error(message); |
| } |
| else |
| { |
| // If caller passed in a number such as 5.5, it will be floored. |
| return getItemAt(int(n)); |
| } |
| } |
| |
| /** |
| * @private |
| * Attempts to call setItemAt(), converting the property name into an int. |
| */ |
| override flash_proxy function setProperty(name:*, value:*):void |
| { |
| if (name is QName) |
| name = name.localName; |
| |
| try |
| { |
| var n:Number = parseInt(String(name)); |
| } |
| catch(e:Error) // localName was not a number |
| { |
| } |
| |
| if (isNaN(n)) |
| { |
| var message:String = resourceManager.getString( |
| "collections", "unknownProperty", [ name ]); |
| throw new Error(message); |
| } |
| else |
| { |
| // If caller passed in a number such as 5.5, it will be floored. |
| setItemAt(value, int(n)); |
| } |
| } |
| |
| /** |
| * @private |
| * This is an internal function. |
| * The VM will call this method for code like <code>"foo" in bar</code> |
| * |
| * @param name The property name that should be tested for existence. |
| */ |
| override flash_proxy function hasProperty(name:*):Boolean |
| { |
| if (name is QName) |
| name = name.localName; |
| |
| var index:int = -1; |
| try |
| { |
| // If caller passed in a number such as 5.5, it will be floored. |
| var n:Number = parseInt(String(name)); |
| if (!isNaN(n)) |
| index = int(n); |
| } |
| catch(e:Error) // localName was not a number |
| { |
| } |
| |
| if (index == -1) |
| return false; |
| |
| return index >= 0 && index < length; |
| } |
| |
| /** |
| * @private |
| */ |
| override flash_proxy function nextNameIndex(index:int):int |
| { |
| return index < length ? index + 1 : 0; |
| } |
| |
| /** |
| * @private |
| */ |
| override flash_proxy function nextName(index:int):String |
| { |
| return (index - 1).toString(); |
| } |
| |
| /** |
| * @private |
| */ |
| override flash_proxy function nextValue(index:int):* |
| { |
| return getItemAt(index - 1); |
| } |
| |
| /** |
| * @private |
| * Any methods that can't be found on this class shouldn't be called, |
| * so return null |
| */ |
| override flash_proxy function callProperty(name:*, ... rest):* |
| { |
| return null; |
| } |
| |
| //-------------------------------------------------------------------------- |
| // |
| // EventDispatcher methods |
| // |
| //-------------------------------------------------------------------------- |
| |
| /** |
| * @inheritDoc |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function addEventListener(eventType:String, |
| listener:Function, |
| useCapture:Boolean = false, |
| priority:int = 0, |
| useWeakReference:Boolean = false):void |
| { |
| eventDispatcher.addEventListener(eventType, listener, useCapture, |
| priority, useWeakReference); |
| } |
| |
| /** |
| * @inheritDoc |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function removeEventListener(eventType:String, |
| listener:Function, |
| useCapture:Boolean = false):void |
| { |
| eventDispatcher.removeEventListener(eventType, listener, useCapture); |
| } |
| |
| /** |
| * @inheritDoc |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function dispatchEvent(event:Event):Boolean |
| { |
| return eventDispatcher.dispatchEvent(event); |
| } |
| |
| /** |
| * @inheritDoc |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function hasEventListener(eventType:String):Boolean |
| { |
| return eventDispatcher.hasEventListener(eventType); |
| } |
| |
| /** |
| * @inheritDoc |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function willTrigger(eventType:String):Boolean |
| { |
| return eventDispatcher.willTrigger(eventType); |
| } |
| |
| //-------------------------------------------------------------------------- |
| // |
| // Internal methods |
| // |
| //-------------------------------------------------------------------------- |
| |
| /** |
| * Take the item and insert it into the view. If we don't have a sort |
| * use the sourceLocation. Dispatch the CollectionEvent with kind ADD |
| * if dispatch is true. |
| * |
| * @param items the items to add into the view |
| * @param sourceLocation the location within the list where the items were added |
| * @param dispatch true if the view should dispatch a corresponding |
| * CollectionEvent with kind ADD (default is true) |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| private function addItemsToView(items:Array, sourceLocation:int, |
| dispatch:Boolean = true):int |
| { |
| var addedItems:Array = localIndex ? [] : items; |
| var addLocation:int = sourceLocation; |
| var firstOne:Boolean = true; |
| |
| if (localIndex) |
| { |
| var loc:int = sourceLocation; |
| var length:int = items.length; |
| |
| for (var i:int = 0; i < length; i++) |
| { |
| var item:Object = items[i]; |
| |
| if (filterFunction != null) |
| { |
| if (filterFunction(item)) |
| { |
| if (sort) |
| loc = findItem(item, Sort.ANY_INDEX_MODE, true); |
| else |
| loc = getFilteredItemIndex(item); |
| |
| if (firstOne) |
| { |
| addLocation = loc; |
| firstOne = false; |
| } |
| } |
| else |
| { |
| loc = -1; |
| addLocation = -1; |
| } |
| } |
| else if (sort) |
| { |
| loc = findItem(item, Sort.ANY_INDEX_MODE, true); |
| if (firstOne) |
| { |
| addLocation = loc; |
| firstOne = false; |
| } |
| } |
| else |
| // List is sorted or filtered but refresh has not been called |
| // Just add to end of list |
| { |
| loc = localIndex.length; |
| addLocation = loc; |
| } |
| |
| if (loc != -1) |
| { |
| localIndex.splice(loc++, 0, item); |
| addedItems.push(item); |
| } |
| } |
| } |
| |
| if (sort && sort.unique && sort.compareFunction(item, localIndex[loc]) == 0) |
| { |
| // We cause all adds to fail here, not just the one. |
| var message:String = resourceManager.getString( |
| "collections", "incorrectAddition"); |
| throw new CollectionViewError(message); |
| } |
| |
| if (localIndex && addedItems.length > 1) |
| { |
| addLocation = -1; |
| } |
| |
| if (dispatch && addedItems.length > 0) |
| { |
| var event:CollectionEvent = |
| new CollectionEvent(CollectionEvent.COLLECTION_CHANGE); |
| event.kind = CollectionEventKind.ADD; |
| event.location = addLocation; |
| event.items = addedItems; |
| dispatchEvent(event); |
| } |
| |
| return addLocation; |
| } |
| |
| /** |
| * Find the item specified using the Sort find mode constants. |
| * If there is no sort assigned throw an error. |
| * |
| * @param values the values object that can be passed into Sort.findItem |
| * @param mode the mode to pass to Sort.findItem (see Sort) |
| * @param insertIndex true if it should find the insertion point |
| * @return the index where the item is located, -1 if not found or SortError |
| * caught |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| mx_internal function findItem(values:Object, mode:String, insertIndex:Boolean = false):int |
| { |
| if (!sort || !localIndex) |
| { |
| var message:String = resourceManager.getString( |
| "collections", "itemNotFound"); |
| throw new CollectionViewError(message); |
| } |
| |
| if (localIndex.length == 0) |
| return insertIndex ? 0 : -1; |
| |
| try |
| { |
| return sort.findItem(localIndex, values, mode, insertIndex); |
| } |
| catch (e:SortError) |
| { |
| // usually because the find criteria is not compatible with the sort. |
| } |
| |
| return -1; |
| } |
| |
| /** |
| * Create a bookmark for this view. This method is called by |
| * ListCollectionViewCursor. |
| * |
| * @param index the index to bookmark |
| * @return a new bookmark instance |
| * @throws a CollectionViewError if the index is out of bounds |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| mx_internal function getBookmark(index:int):ListCollectionViewBookmark |
| { |
| if (index < 0 || index > length) |
| { |
| var message:String = resourceManager.getString( |
| "collections", "invalidIndex", [ index ]); |
| throw new CollectionViewError(message); |
| } |
| |
| var value:Object; |
| try |
| { |
| value = getItemAt(index); |
| } |
| catch(e:Error) |
| { |
| } |
| return new ListCollectionViewBookmark(value, |
| this, |
| revision, |
| index); |
| |
| } |
| |
| /** |
| * Given a bookmark find the location for the value. If the |
| * view has been modified since the bookmark was created attempt |
| * to relocate the item. If the bookmark represents an item |
| * that is no longer in the view (removed or filtered out) return |
| * -1. |
| * |
| * @param bookmark the bookmark to locate |
| * @return the new location of the bookmark, -1 if not in the view anymore |
| * @throws CollectionViewError if the bookmark is invalid |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| mx_internal function getBookmarkIndex(bookmark:CursorBookmark):int |
| { |
| if (!(bookmark is ListCollectionViewBookmark) |
| || ListCollectionViewBookmark(bookmark).view != this) |
| { |
| var message:String = resourceManager.getString( |
| "collections", "bookmarkNotFound"); |
| throw new CollectionViewError(message); |
| } |
| |
| var bm:ListCollectionViewBookmark = ListCollectionViewBookmark(bookmark); |
| |
| if (bm.viewRevision != revision) |
| { |
| // getItemAt has a side-effect of throwing an exception if the index is out- |
| // of-range, here we are checking to see if the index falls with-in the range |
| // and only then calling getItemAt. |
| if (bm.index < 0 || bm.index >= length || getItemAt(bm.index) != bm.value) |
| { |
| try |
| { |
| bm.index = getItemIndex(bm.value); |
| } |
| catch (e:SortError) |
| { |
| bm.index = getLocalItemIndex(bm.value); |
| } |
| } |
| |
| bm.viewRevision = revision; |
| } |
| return bm.index; |
| } |
| |
| /** |
| * The view is a listener of CollectionEvents on its underlying IList |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| private function listChangeHandler(event:CollectionEvent):void |
| { |
| if (autoUpdateCounter > 0) |
| { |
| if (!pendingUpdates) |
| { |
| pendingUpdates = []; |
| } |
| pendingUpdates.push(event); |
| } |
| else |
| { |
| switch (event.kind) |
| { |
| case CollectionEventKind.ADD: |
| addItemsToView(event.items, event.location); |
| break; |
| |
| case CollectionEventKind.MOVE: |
| var n:int = event.items.length; |
| for (var i:int = 0; i < n; i++) |
| moveItemInView(event.items[i]); |
| break; |
| |
| case CollectionEventKind.RESET: |
| reset(); |
| break; |
| |
| case CollectionEventKind.REMOVE: |
| removeItemsFromView(event.items, event.location); |
| break; |
| |
| case CollectionEventKind.REPLACE: |
| replaceItemsInView(event.items, event.location); |
| break; |
| |
| case CollectionEventKind.UPDATE: |
| handlePropertyChangeEvents(event.items); |
| break; |
| |
| default: |
| dispatchEvent(event); |
| } // switch |
| } |
| } |
| |
| /** |
| * Given a set of <code>PropertyChangeEvent</code>s go through and update the view. |
| * This is currently not optimized. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| private function handlePropertyChangeEvents(events:Array):void |
| { |
| var eventItems:Array = events; |
| if (sort || filterFunction != null) |
| { |
| //go through the events and find all the individual objects |
| //that have been updated |
| //then for each one determine whether we should move it or |
| //just fire an update event |
| var updatedItems:Array = []; |
| var updateEntry:Object; |
| var i:int; |
| for (i = 0; i < events.length; i++) |
| { |
| var updateInfo:PropertyChangeEvent = events[i]; |
| var item:Object; |
| var defaultMove:Boolean; |
| if (updateInfo.target) |
| { |
| item = updateInfo.target; |
| //if the target != source that means the update |
| //happened to some subprop of the item in the collection |
| //if we have a custom comparator this will affect |
| //the sort so for now say we should move but |
| //maybe we could optimize further |
| defaultMove = updateInfo.target != updateInfo.source; |
| } |
| else |
| { |
| item = updateInfo.source; |
| defaultMove = false; |
| } |
| |
| //see if the item is already in the list |
| var j:int = 0; |
| for (; j < updatedItems.length; j++) |
| { |
| if (updatedItems[j].item == item) |
| { |
| // even if it is, if a different property changed, track that too. |
| var evts:Array = updatedItems[j].events as Array; |
| var l:int = evts.length; |
| for (var k:int = 0; k < l; k++) |
| { |
| if (evts[k].property != updateInfo.property) |
| { |
| evts.push(updateInfo); |
| break; |
| } |
| // we could also merge events for changes to the same |
| // property but that's hopefully low probability |
| // and leads to questions of event order. |
| } |
| break; |
| } |
| } |
| |
| if (j < updatedItems.length) |
| { |
| updateEntry = updatedItems[j]; |
| } |
| else |
| { |
| updateEntry = {item: item, move: defaultMove, events: [updateInfo], |
| entireObjectChanged: updateInfo.property == null, oldItem: updateInfo.property == null ? updateInfo.oldValue : null}; |
| updatedItems.push(updateEntry); |
| } |
| |
| //if we've already set replace don't unset it |
| //if there's a filterFunction need to go through replace |
| //if there's no property specified for the sort we'll need |
| //to assume we have to replace |
| //if there is a property see if it affects the sort |
| updateEntry.move = |
| updateEntry.move |
| || filterFunction != null |
| || updateEntry.entireObjectChanged |
| || (sort && sort.propertyAffectsSort(String(updateInfo.property))); |
| } |
| |
| // go through the items and move and send move events for ones that moved |
| // and build the list of remaining items we need to send UPDATE events for |
| eventItems = []; |
| for (i = 0; i < updatedItems.length; i++) |
| { |
| updateEntry = updatedItems[i]; |
| if (updateEntry.move) |
| { |
| if(updateEntry.entireObjectChanged) |
| { |
| removeItemsFromView([updateEntry.oldItem], -1, true); |
| } |
| moveItemInView(updateEntry.item, updateEntry.item, eventItems); |
| } |
| else |
| { |
| eventItems.push(updateEntry.item); |
| } |
| } |
| |
| // now go through the updated items and add all events for all |
| // properties that changed in that item (except for those items |
| // we moved |
| var temp:Array = []; |
| for (var ctr:int = 0; ctr < eventItems.length; ctr++) |
| for (var ctr1:int = 0; ctr1 < updatedItems.length; ctr1++) |
| if (eventItems[ctr] == updatedItems[ctr1].item) |
| { |
| temp = temp.concat(updatedItems[ctr1].events); |
| } |
| eventItems = temp; |
| } |
| |
| if (eventItems.length > 0) |
| { |
| |
| var updateEvent:CollectionEvent = |
| new CollectionEvent(CollectionEvent.COLLECTION_CHANGE); |
| updateEvent.kind = CollectionEventKind.UPDATE; |
| updateEvent.items = eventItems; |
| dispatchEvent(updateEvent); |
| } |
| } |
| |
| /** |
| * When enableAutoUpdates pushes autoUpdateCounter back down to 0 |
| * this method will execute to consolidate the pending update |
| * events or turn it into a massive refresh(). |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| private function handlePendingUpdates():void |
| { |
| if (pendingUpdates) |
| { |
| var pu:Array = pendingUpdates; |
| pendingUpdates = null; |
| |
| // Could further optimize to consolidate various events |
| // and make a decision if there are too many updates |
| // and we should just refresh. |
| var singleUpdateEvent:CollectionEvent; |
| for (var i:int = 0; i < pu.length; i++) |
| { |
| var event:CollectionEvent = pu[i]; |
| if (event.kind == CollectionEventKind.UPDATE) |
| { |
| if (!singleUpdateEvent) |
| { |
| singleUpdateEvent = event; |
| } |
| else |
| { |
| for (var j:int = 0; j < event.items.length; j++) |
| { |
| singleUpdateEvent.items.push(event.items[j]); |
| } |
| } |
| } |
| else |
| { |
| listChangeHandler(event); |
| } |
| } |
| |
| if (singleUpdateEvent) |
| { |
| listChangeHandler(singleUpdateEvent); |
| } |
| } |
| } |
| |
| private function internalRefresh(dispatch:Boolean):Boolean |
| { |
| if (sort || filterFunction != null) |
| { |
| try |
| { |
| populateLocalIndex(); |
| } |
| catch(pending:ItemPendingError) |
| { |
| pending.addResponder(new ItemResponder( |
| function(data:Object, token:Object = null):void |
| { |
| internalRefresh(dispatch); |
| }, |
| function(info:Object, token:Object = null):void |
| { |
| //no-op |
| })); |
| return false; |
| } |
| |
| if (filterFunction != null) |
| { |
| var tmp:Array = []; |
| var len:int = localIndex.length; |
| for (var i:int = 0; i < len; i++) |
| { |
| var item:Object = localIndex[i]; |
| if (filterFunction(item)) |
| { |
| tmp.push(item); |
| } |
| } |
| localIndex = tmp; |
| } |
| |
| if (sort) |
| { |
| sort.sort(localIndex); |
| dispatch = true; |
| } |
| } |
| else if (localIndex) |
| { |
| localIndex = null; |
| } |
| |
| revision++; |
| pendingUpdates = null; |
| if (dispatch) |
| { |
| var refreshEvent:CollectionEvent = |
| new CollectionEvent(CollectionEvent.COLLECTION_CHANGE); |
| refreshEvent.kind = CollectionEventKind.REFRESH; |
| dispatchEvent(refreshEvent); |
| } |
| return true; |
| } |
| |
| /** |
| * Remove the old value from the view and replace it with the value |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| private function moveItemInView(item:Object, dispatch:Boolean = true, updateEventItems:Array = null):void |
| { |
| if (localIndex) |
| { |
| //we're guaranteed that removeItemsFromView isn't going |
| //to work here because the item has probably |
| //already been updated so getItemIndex is going to fail |
| //so we'll just do a linear search and find it if it's here |
| var removeLocation:int = -1; |
| for (var i:int = 0; i < localIndex.length; i++) |
| { |
| if (localIndex[i] == item) |
| { |
| removeLocation = i; |
| break; |
| } |
| } |
| if (removeLocation > -1) |
| { |
| localIndex.splice(removeLocation, 1); |
| } |
| |
| var addLocation:int = addItemsToView([item], removeLocation, false); |
| |
| if (dispatch) |
| { |
| var event:CollectionEvent = |
| new CollectionEvent(CollectionEvent.COLLECTION_CHANGE); |
| event.items.push(item); |
| if (updateEventItems && addLocation == removeLocation && addLocation > -1) |
| { |
| updateEventItems.push(item); |
| return; |
| } |
| if (addLocation > -1 && removeLocation > -1) |
| { |
| event.kind = CollectionEventKind.MOVE; |
| event.location = addLocation; |
| event.oldLocation = removeLocation; |
| } |
| else if (addLocation > -1) |
| { |
| event.kind = CollectionEventKind.ADD; |
| event.location = addLocation; |
| } |
| else if (removeLocation > -1) |
| { |
| event.kind = CollectionEventKind.REMOVE; |
| event.location = removeLocation; |
| } |
| else |
| { |
| dispatch = false; |
| } |
| |
| if (dispatch) |
| { |
| dispatchEvent(event); |
| } |
| } |
| } |
| } |
| |
| /** |
| * Copy all of the data from the source list into the local index. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| private function populateLocalIndex():void |
| { |
| if (list) |
| { |
| localIndex = list.toArray(); |
| } |
| else |
| { |
| localIndex = []; |
| } |
| } |
| |
| /** |
| * Take the item and remove it from the view. If we don't have a sort |
| * use the sourceLocation. Dispatch the CollectionEvent with kind REMOVE |
| * if dispatch is true. |
| * |
| * @param items the items to remove from the view |
| * @param sourceLocation the location within the list where the item was removed |
| * @param dispatch true if the view should dispatch a corresponding |
| * CollectionEvent with kind REMOVE (default is true) |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| private function removeItemsFromView(items:Array, sourceLocation:int, dispatch:Boolean = true):void |
| { |
| var removedItems:Array = localIndex ? [] : items; |
| var removeLocation:int = sourceLocation; |
| if (localIndex) |
| { |
| for (var i:int = 0; i < items.length; i++) |
| { |
| var item:Object = items[i]; |
| var loc:int = getItemIndex(item); |
| if (loc > -1) |
| { |
| localIndex.splice(loc, 1); |
| removedItems.push(item); |
| removeLocation = loc; |
| } |
| } |
| } |
| if (dispatch && removedItems.length > 0) |
| { |
| var event:CollectionEvent = |
| new CollectionEvent(CollectionEvent.COLLECTION_CHANGE); |
| event.kind = CollectionEventKind.REMOVE; |
| event.location = (!localIndex || removedItems.length == 1) |
| ? removeLocation |
| : -1; |
| event.items = removedItems; |
| dispatchEvent(event); |
| } |
| } |
| |
| /** |
| * Items is an array of PropertyChangeEvents so replace the oldValues with the new |
| * newValues. Start at the location specified and move forward, it's unlikely |
| * that the length of items is > 1. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| private function replaceItemsInView(changeEvents:Array, |
| location:int, |
| dispatch:Boolean = true):void |
| { |
| if (localIndex) |
| { |
| var len:int = changeEvents.length; |
| var oldItems:Array = []; |
| var newItems:Array = []; |
| for (var i:int = 0; i < len; i++) |
| { |
| var propertyEvent:PropertyChangeEvent = changeEvents[i]; |
| oldItems.push(propertyEvent.oldValue); |
| newItems.push(propertyEvent.newValue); |
| } |
| removeItemsFromView(oldItems, location, dispatch); |
| addItemsToView(newItems, location, dispatch); |
| } |
| else |
| { |
| var event:CollectionEvent = |
| new CollectionEvent(CollectionEvent.COLLECTION_CHANGE); |
| event.kind = CollectionEventKind.REPLACE; |
| event.location = location; |
| event.items = changeEvents; |
| dispatchEvent(event); |
| } |
| } |
| |
| /** |
| * @private |
| * When the source list is replaced, reset. |
| */ |
| mx_internal function reset():void |
| { |
| internalRefresh(false); |
| if (dispatchResetEvent) |
| { |
| var event:CollectionEvent = |
| new CollectionEvent(CollectionEvent.COLLECTION_CHANGE); |
| event.kind = CollectionEventKind.RESET; |
| dispatchEvent(event); |
| } |
| } |
| |
| public function get complexFieldWatcher():ComplexFieldChangeWatcher |
| { |
| return _complexFieldWatcher; |
| } |
| |
| public function set complexFieldWatcher(value:ComplexFieldChangeWatcher):void |
| { |
| if(_complexFieldWatcher != value) |
| { |
| stopWatchingForComplexFieldsChanges(); |
| |
| _complexFieldWatcher = value; |
| if(_complexFieldWatcher) |
| _complexFieldWatcher.mx_internal::list = this; |
| |
| startWatchingForComplexFieldsChanges(); |
| } |
| } |
| |
| private function startWatchingForComplexFieldsChanges():void |
| { |
| if(complexFieldWatcher && sort && sort.fields) |
| { |
| _complexFieldWatcher.addEventListener(PropertyChangeEvent.PROPERTY_CHANGE, onComplexFieldValueChanged, false, 0, true); |
| _complexFieldWatcher.startWatchingForComplexFieldChanges(); |
| } |
| } |
| |
| private function stopWatchingForComplexFieldsChanges():void |
| { |
| if(complexFieldWatcher) |
| { |
| _complexFieldWatcher.removeEventListener(PropertyChangeEvent.PROPERTY_CHANGE, onComplexFieldValueChanged); |
| _complexFieldWatcher.stopWatchingForComplexFieldChanges(); |
| } |
| } |
| |
| private function onComplexFieldValueChanged(changeEvent:PropertyChangeEvent):void |
| { |
| if(sort) |
| { |
| moveItemInView(changeEvent.source); |
| } |
| } |
| } |
| |
| } |
| |
| import flash.events.Event; |
| import flash.events.EventDispatcher; |
| |
| import mx.collections.*; |
| import mx.collections.errors.*; |
| import mx.core.mx_internal; |
| import mx.events.*; |
| import mx.resources.IResourceManager; |
| import mx.resources.ResourceManager; |
| |
| use namespace mx_internal; |
| |
| /** |
| * Dispatched whenever the cursor position is updated. |
| * |
| * @eventType mx.events.FlexEvent.CURSOR_UPDATE |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| [Event(name="cursorUpdate", type="mx.events.FlexEvent")] |
| |
| [ResourceBundle("collections")] |
| |
| /** |
| * @private |
| * The internal implementation of cursor for the ListCollectionView. |
| */ |
| class ListCollectionViewCursor extends EventDispatcher implements IViewCursor |
| { |
| //-------------------------------------------------------------------------- |
| // |
| // Class constants |
| // |
| //-------------------------------------------------------------------------- |
| |
| /** |
| * @private |
| */ |
| private static const BEFORE_FIRST_INDEX:int = -1; |
| |
| /** |
| * @private |
| */ |
| private static const AFTER_LAST_INDEX:int = -2; |
| |
| //-------------------------------------------------------------------------- |
| // |
| // Constructor |
| // |
| //-------------------------------------------------------------------------- |
| |
| /** |
| * Constructor. |
| * |
| * <p>Creates the cursor for the view.</p> |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function ListCollectionViewCursor(view:ListCollectionView) |
| { |
| super(); |
| |
| _view = view; |
| _view.addEventListener(CollectionEvent.COLLECTION_CHANGE, collectionEventHandler, false, 0, true); |
| currentIndex = view.length > 0 ? 0 : AFTER_LAST_INDEX; |
| if (currentIndex == 0) |
| { |
| try |
| { |
| setCurrent(view.getItemAt(0), false); |
| } |
| catch(e:ItemPendingError) |
| { |
| currentIndex = BEFORE_FIRST_INDEX; |
| setCurrent(null, false); |
| } |
| } |
| } |
| |
| //-------------------------------------------------------------------------- |
| // |
| // Variables |
| // |
| //-------------------------------------------------------------------------- |
| |
| /** |
| * @private |
| */ |
| private var _view:ListCollectionView; |
| |
| /** |
| * @private |
| */ |
| private var currentIndex:int; |
| |
| /** |
| * @private |
| */ |
| private var currentValue:Object; |
| |
| /** |
| * @private |
| */ |
| private var invalid:Boolean; |
| |
| /** |
| * @private |
| * Used for accessing localized Error messages. |
| */ |
| private var resourceManager:IResourceManager = |
| ResourceManager.getInstance(); |
| |
| //-------------------------------------------------------------------------- |
| // |
| // Properties |
| // |
| //-------------------------------------------------------------------------- |
| |
| /** |
| * Get a reference to the view that this cursor is associated with. |
| * @return the associated <code>ICollectionView</code> |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function get view():ICollectionView |
| { |
| checkValid(); |
| return _view; |
| } |
| |
| [Bindable("cursorUpdate")] |
| /** |
| * Provides access the object at the current location referenced by |
| * this cursor within the source collection. |
| * If the cursor is beyond the ends of the collection (beforeFirst, |
| * afterLast) this will return <code>null</code>. |
| * |
| * @see mx.collections.IViewCursor#moveNext |
| * @see mx.collections.IViewCursor#movePrevious |
| * @see mx.collections.IViewCursor#seek |
| * @see mx.collections.IViewCursor#beforeFirst |
| * @see mx.collections.IViewCursor#afterLast |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function get current():Object |
| { |
| checkValid(); |
| |
| return currentValue; |
| } |
| |
| [Bindable("cursorUpdate")] |
| |
| /** |
| * Provides access to the bookmark of the item returned by the |
| * <code>current</code> property. |
| * The bookmark can be used to move the cursor to a previously visited |
| * item, or one relative to it (see the <code>seek()</code> method for |
| * more information). |
| * |
| * @see mx.collections.IViewCursor#current |
| * @see mx.collections.IViewCursor#seek |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function get bookmark():CursorBookmark |
| { |
| checkValid(); |
| if (view.length == 0 || beforeFirst) return CursorBookmark.FIRST; |
| else if (afterLast) return CursorBookmark.LAST; |
| //if currentIndex > view.length this is a bug in cursor and i want the |
| //exception thrown to track it down |
| else return ListCollectionView(view).getBookmark(currentIndex); |
| } |
| |
| |
| [Bindable("cursorUpdate")] |
| /** |
| * true if the current is sitting before the first item in the view. |
| * If the ICollectionView is empty (length == 0) this will always |
| * be true. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function get beforeFirst():Boolean |
| { |
| checkValid(); |
| return currentIndex == BEFORE_FIRST_INDEX || view.length == 0; |
| } |
| |
| |
| [Bindable("cursorUpdate")] |
| /** |
| * true if the cursor is sitting after the last item in the view. |
| * If the ICollectionView is empty (length == 0) this will always |
| * be true. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function get afterLast():Boolean |
| { |
| checkValid(); |
| return currentIndex == AFTER_LAST_INDEX || view.length == 0; |
| } |
| |
| /** |
| * Finds the item with the specified properties within the |
| * collection and positions the cursor on that item. |
| * If the item can not be found no change to the current location will be |
| * made. |
| * <code>findAny()</code> can only be called on sorted views, if the view |
| * isn't sorted, or items in the view do not contain properties used |
| * to compute the sort order, a <code>CursorError</code> will be thrown. |
| * <p> |
| * If the associated collection is remote, and not all of the items have |
| * been cached locally this method will begin an asynchronous fetch from the |
| * remote collection, or if one is already in progress wait for it to |
| * complete before making another fetch request. |
| * If multiple items can match the search criteria then the item found is |
| * non-deterministic. |
| * If it is important to find the first or last occurrence of an item in a |
| * non-unique index use the <code>findFirst()</code> or |
| * <code>findLast()</code>. |
| * The values specified must be configured as name-value pairs, as in an |
| * associative array (or the actual object to search for). |
| * The values of the names specified must match those properties specified in |
| * the sort. for example |
| * If properties "x", "y", and "z" are the in the current index, the values |
| * specified should be {x:x-value, y:y-value,z:z-value}. |
| * When all of the data is local this method will return <code>true</code> if |
| * the item can be found and false otherwise. |
| * If the data is not local and an asynchronous operation must be performed, |
| * an <code>ItemPendingError</code> will be thrown. |
| * |
| * @see mx.collections.IViewCursor#findFirst |
| * @see mx.collections.IViewCursor#findLast |
| * @see mx.collections.errors.ItemPendingError |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function findAny(values:Object):Boolean |
| { |
| checkValid(); |
| var lcView:ListCollectionView = ListCollectionView(view); |
| var index:int; |
| try |
| { |
| index = lcView.findItem(values, Sort.ANY_INDEX_MODE); |
| } |
| catch(e:SortError) |
| { |
| //this is because the find critieria is not compatible with the |
| //sort |
| throw new CursorError(e.message); |
| } |
| if (index > -1) |
| { |
| currentIndex = index; |
| setCurrent(lcView.getItemAt(currentIndex)); |
| } |
| return index > -1; |
| } |
| |
| /** |
| * Finds the first item with the specified properties |
| * within the collection and positions the cursor on that item. |
| * If the item can not be found no change to the current location will be |
| * made. |
| * <code>findFirst()</code> can only be called on sorted views, if the view |
| * isn't sorted, or items in the view do not contain properties used |
| * to compute the sort order, a <code>CursorError</code> will be thrown. |
| * <p> |
| * If the associated collection is remote, and not all of the items have been |
| * cached locally this method will begin an asynchronous fetch from the |
| * remote collection, or if one is already in progress wait for it to |
| * complete before making another fetch request. |
| * If it is not important to find the first occurrence of an item in a |
| * non-unique index use <code>findAny()</code> as it may be a little faster. |
| * The values specified must be configured as name-value pairs, as in an |
| * associative array (or the actual object to search for). |
| * The values of the names specified must match those properties specified in |
| * the sort. for example If properties "x", "y", and "z" are the in the current |
| * index, the values specified should be {x:x-value, y:y-value,z:z-value}. |
| * When all of the data is local this method will |
| * return <code>true</code> if the item can be found and false otherwise. |
| * If the data is not local and an asynchronous operation must be performed, |
| * an <code>ItemPendingError</code> will be thrown. |
| * |
| * @see mx.collections.IViewCursor#findAny |
| * @see mx.collections.IViewCursor#findLast |
| * @see mx.collections.errors.ItemPendingError |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function findFirst(values:Object):Boolean |
| { |
| checkValid(); |
| var lcView:ListCollectionView = ListCollectionView(view); |
| var index:int; |
| try |
| { |
| index = lcView.findItem(values, Sort.FIRST_INDEX_MODE); |
| } |
| catch(sortError:SortError) |
| { |
| //this is because the find critieria is not compatible with the |
| //sort |
| throw new CursorError(sortError.message); |
| } |
| if (index > -1) |
| { |
| currentIndex = index; |
| setCurrent(lcView.getItemAt(currentIndex)); |
| } |
| return index > -1; |
| } |
| |
| |
| /** |
| * Finds the last item with the specified properties |
| * within the collection and positions the cursor on that item. |
| * If the item can not be found no change to the current location will be |
| * made. |
| * <code>findLast()</code> can only be called on sorted views, if the view |
| * isn't sorted, or items in the view do not contain properties used |
| * to compute the sort order, a <code>CursorError</code> will be thrown. |
| * <p> |
| * If the associated collection is remote, and not all of the items have been |
| * cached locally this method will begin an asynchronous fetch from the |
| * remote collection, or if one is already in progress wait for it to |
| * complete before making another fetch request. |
| * If it is not important to find the last occurrence of an item in a |
| * non-unique index use <code>findAny()</code> as it may be a little faster. |
| * The values specified must be configured as name-value pairs, as in an |
| * associative array (or the actual object to search for). |
| * The values of the names specified must match those properties specified in |
| * the sort. for example If properties "x", "y", and "z" are the in the current |
| * index, the values specified should be {x:x-value, y:y-value,z:z-value}. |
| * When all of the data is local this method will |
| * return <code>true</code> if the item can be found and false otherwise. |
| * If the data is not local and an asynchronous operation must be performed, |
| * an <code>ItemPendingError</code> will be thrown. |
| * |
| * @see mx.collections.IViewCursor#findAny |
| * @see mx.collections.IViewCursor#findFirst |
| * @see mx.collections.errors.ItemPendingError |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function findLast(values:Object):Boolean |
| { |
| checkValid(); |
| var lcView:ListCollectionView = ListCollectionView(view); |
| var index:int; |
| try |
| { |
| index = lcView.findItem(values, Sort.LAST_INDEX_MODE); |
| } |
| catch(sortError:SortError) |
| { |
| //this is because the find critieria is not compatible with the |
| //sort |
| throw new CursorError(sortError.message); |
| } |
| if (index > -1) |
| { |
| currentIndex = index; |
| setCurrent(lcView.getItemAt(currentIndex)); |
| } |
| return index > -1; |
| } |
| |
| /** |
| * Insert the specified item before the cursor's current position. |
| * If the cursor is <code>afterLast</code> the insertion |
| * will happen at the end of the View. If the cursor is |
| * <code>beforeFirst</code> on a non-empty view an error will be thrown. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function insert(item:Object):void |
| { |
| var insertIndex:int; |
| if (afterLast) |
| { |
| insertIndex = view.length; |
| } |
| else if (beforeFirst) |
| { |
| if (view.length > 0) |
| { |
| var message:String = resourceManager.getString( |
| "collections", "invalidInsert"); |
| throw new CursorError(message); |
| } |
| else |
| { |
| insertIndex = 0; |
| } |
| } |
| else |
| { |
| insertIndex = currentIndex; |
| } |
| ListCollectionView(view).addItemAt(item, insertIndex); |
| } |
| |
| /** |
| * Moves the cursor to the next item within the collection. On success |
| * the <code>current</code> property will be updated to reference the object at this |
| * new location. Returns true if current is valid, false if not (afterLast). |
| * If the data is not local and an asynchronous operation must be performed, an |
| * <code>ItemPendingError</code> will be thrown. See the ItemPendingError docs |
| * as well as the collections documentation for more information on using the |
| * ItemPendingError. |
| * |
| * @return true if still in the list, false if current is now afterLast |
| * |
| * @see mx.collections.IViewCursor#current |
| * @see mx.collections.IViewCursor#movePrevious |
| * @see mx.collections.errors.ItemPendingError |
| * @example |
| * <pre> |
| * var myArrayCollection:ICollectionView = new ArrayCollection(["Bobby", "Mark", "Trevor", "Jacey", "Tyler"]); |
| * var cursor:IViewCursor = myArrayCollection.createCursor(); |
| * while (!cursor.afterLast) |
| * { |
| * trace(cursor.current); |
| * cursor.moveNext(); |
| * } |
| * </pre> |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function moveNext():Boolean |
| { |
| //the afterLast getter checks validity and also checks length > 0 |
| if (afterLast) |
| { |
| return false; |
| } |
| // we can't set the index until we know that we can move there first. |
| var tempIndex:int = beforeFirst ? 0 : currentIndex + 1; |
| if (tempIndex >= view.length) |
| { |
| tempIndex = AFTER_LAST_INDEX; |
| setCurrent(null); |
| } |
| else |
| { |
| setCurrent(ListCollectionView(view).getItemAt(tempIndex)); |
| } |
| currentIndex = tempIndex; |
| return !afterLast; |
| } |
| |
| /** |
| * Moves the cursor to the previous item within the collection. On success |
| * the <code>current</code> property will be updated to reference the object at this |
| * new location. Returns true if current is valid, false if not (beforeFirst). |
| * If the data is not local and an asynchronous operation must be performed, an |
| * <code>ItemPendingError</code> will be thrown. See the ItemPendingError docs |
| * as well as the collections documentation for more information on using the |
| * ItemPendingError. |
| * |
| * @return true if still in the list, false if current is now beforeFirst |
| * |
| * @see mx.collections.IViewCursor#current |
| * @see mx.collections.IViewCursor#moveNext |
| * @see mx.collections.errors.ItemPendingError |
| * @example |
| * <pre> |
| * var myArrayCollection:ICollectionView = new ArrayCollection(["Bobby", "Mark", "Trevor", "Jacey", "Tyler"]); |
| * var cursor:ICursor = myArrayCollection.createCursor(); |
| * cursor.seek(CursorBookmark.last); |
| * while (!cursor.beforeFirst) |
| * { |
| * trace(current); |
| * cursor.movePrevious(); |
| * } |
| * </pre> |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| |
| public function movePrevious():Boolean |
| { |
| //the afterLast getter checks validity and also checks length > 0 |
| if (beforeFirst) |
| { |
| return false; |
| } |
| // we can't set the index until we know that we can move there first |
| var tempIndex:int = afterLast ? view.length - 1 : currentIndex - 1; |
| if (tempIndex == -1) |
| { |
| tempIndex = BEFORE_FIRST_INDEX; |
| setCurrent(null); |
| } |
| else |
| { |
| setCurrent(ListCollectionView(view).getItemAt(tempIndex)); |
| } |
| currentIndex = tempIndex; |
| return !beforeFirst; |
| } |
| |
| /** |
| * Remove the current item and return it. If the cursor is |
| * <code>beforeFirst</code> or <code>afterLast</code> throw a |
| * CursorError. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function remove():Object |
| { |
| if (beforeFirst || afterLast) |
| { |
| var message:String = resourceManager.getString( |
| "collections", "invalidRemove"); |
| throw new CursorError(message); |
| } |
| var oldIndex:int = currentIndex; |
| currentIndex++; |
| if (currentIndex >= view.length) |
| { |
| currentIndex = AFTER_LAST_INDEX; |
| setCurrent(null); |
| } |
| else |
| { |
| try |
| { |
| setCurrent(ListCollectionView(view).getItemAt(currentIndex)); |
| } |
| catch(e:ItemPendingError) |
| { |
| setCurrent(null, false); |
| ListCollectionView(view).removeItemAt(oldIndex); |
| throw e; |
| } |
| } |
| var removed:Object = ListCollectionView(view).removeItemAt(oldIndex); |
| return removed; |
| } |
| |
| /** |
| * Moves the cursor to a location at an offset from the specified |
| * bookmark. |
| * The offset can be negative in which case the cursor is positioned an |
| * offset number of items prior to the specified bookmark. |
| * If the associated collection is remote, and not all of the items have been |
| * cached locally this method will begin an asynchronous fetch from the |
| * remote collection. |
| * |
| * If the data is not local and an asynchronous operation must be performed, an |
| * <code>ItemPendingError</code> will be thrown. See the ItemPendingError docs |
| * as well as the collections documentation for more information on using the |
| * ItemPendingError. |
| * |
| * |
| * @param bookmark <code>CursorBookmark</code> reference to marker information that |
| * allows repositioning to a specific location. |
| * In addition to supplying a value returned from the <code>bookmark</code> |
| * property, there are three constant bookmark values that can be |
| * specified: |
| * <ul> |
| * <li><code>CursorBookmark.FIRST</code> - seek from |
| * the start (first element) of the collection</li> |
| * <li><code>CursorBookmark.CURRENT</code> - seek from |
| * the current position in the collection</li> |
| * <li><code>CursorBookmark.LAST</code> - seek from the |
| * end (last element) of the collection</li> |
| * </ul> |
| * @param offset indicates how far from the specified bookmark to seek. |
| * If the specified number is negative the cursor will attempt to |
| * move prior to the specified bookmark, if the offset specified is |
| * beyond the end points of the collection the cursor will be |
| * positioned off the end (beforeFirst or afterLast). |
| * @param prefetch indicates the intent to iterate in a specific direction once the |
| * seek operation completes, this reduces the number of required |
| * network round trips during a seek. |
| * If the iteration direction is known at the time of the request |
| * the appropriate amount of data can be returned ahead of the |
| * request to iterate it. |
| * |
| * @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 |
| { |
| checkValid(); |
| if (view.length == 0) |
| { |
| currentIndex = AFTER_LAST_INDEX; |
| setCurrent(null, false); |
| return; |
| } |
| |
| var newIndex:int = currentIndex; |
| if (bookmark == CursorBookmark.FIRST) |
| { |
| newIndex = 0; |
| } |
| else if (bookmark == CursorBookmark.LAST) |
| { |
| newIndex = view.length - 1; |
| } |
| else if (bookmark != CursorBookmark.CURRENT) |
| { |
| var message:String; |
| try |
| { |
| newIndex = ListCollectionView(view).getBookmarkIndex(bookmark); |
| if (newIndex < 0) |
| { |
| setCurrent(null); |
| |
| message = resourceManager.getString( |
| "collections", "bookmarkInvalid"); |
| throw new CursorError(message); |
| } |
| } |
| catch(bmError:CollectionViewError) |
| { |
| message = resourceManager.getString( |
| "collections", "bookmarkInvalid"); |
| throw new CursorError(message); |
| } |
| } |
| |
| newIndex += offset; |
| |
| var newCurrent:Object = null; |
| if (newIndex >= view.length) |
| { |
| currentIndex = AFTER_LAST_INDEX; |
| } |
| else if (newIndex < 0) |
| { |
| currentIndex = BEFORE_FIRST_INDEX; |
| } |
| else |
| { |
| newCurrent = ListCollectionView(view).getItemAt(newIndex, prefetch); |
| currentIndex = newIndex; |
| } |
| setCurrent(newCurrent); |
| } |
| |
| //-------------------------------------------------------------------------- |
| // |
| // Internal methods |
| // |
| //-------------------------------------------------------------------------- |
| |
| private function checkValid():void |
| { |
| if (invalid) |
| { |
| var message:String = resourceManager.getString( |
| "collections", "invalidCursor"); |
| throw new CursorError(message); |
| } |
| } |
| |
| private function collectionEventHandler(event:CollectionEvent):void |
| { |
| switch (event.kind) |
| { |
| case CollectionEventKind.ADD: |
| if (event.location <= currentIndex) |
| { |
| currentIndex += event.items.length; |
| } |
| break; |
| |
| case CollectionEventKind.REMOVE: |
| if (event.location < currentIndex) |
| { |
| currentIndex -= event.items.length; |
| } |
| else if (event.location == currentIndex) |
| { |
| if (currentIndex < view.length) |
| { |
| try |
| { |
| setCurrent(ListCollectionView(view).getItemAt(currentIndex)); |
| } |
| catch(error:ItemPendingError) |
| { |
| setCurrent(null, false); |
| } |
| } |
| else // currentIndex == view.length |
| { |
| //we were removed! is this an error? |
| //should cursor move to now last item, view.length - 1?? |
| currentIndex = AFTER_LAST_INDEX; |
| setCurrent(null); //dispatch the updated at least |
| } |
| } |
| break; |
| |
| case CollectionEventKind.MOVE: |
| if (event.oldLocation == currentIndex) |
| { |
| currentIndex = event.location; |
| } |
| else |
| { |
| if (event.oldLocation < currentIndex) |
| { |
| currentIndex -= event.items.length; |
| } |
| if (event.location <= currentIndex) |
| { |
| currentIndex += event.items.length; |
| } |
| } |
| break; |
| |
| case CollectionEventKind.REFRESH: |
| if (!(beforeFirst || afterLast)) |
| { |
| try |
| { |
| currentIndex = ListCollectionView(view).getItemIndex(currentValue); |
| } |
| catch (e:SortError) |
| { |
| if (ListCollectionView(view).sort) |
| { |
| currentIndex = ListCollectionView(view).getLocalItemIndex(currentValue); |
| } |
| } |
| if (currentIndex == -1) |
| { |
| setCurrent(null); |
| } |
| } |
| break; |
| |
| case CollectionEventKind.REPLACE: |
| if (event.location == currentIndex) |
| { |
| try |
| { |
| setCurrent(ListCollectionView(view).getItemAt(currentIndex)); |
| } |
| catch(error:ItemPendingError) |
| { |
| setCurrent(null, false); |
| } |
| } |
| break; |
| |
| case CollectionEventKind.RESET: |
| //just move to the beginning |
| currentIndex = BEFORE_FIRST_INDEX; |
| setCurrent(null); |
| break; |
| } |
| } |
| |
| /** |
| * @private |
| */ |
| private function setCurrent(value:Object, dispatch:Boolean = true):void |
| { |
| currentValue = value; |
| |
| if (dispatch) |
| dispatchEvent(new FlexEvent(FlexEvent.CURSOR_UPDATE)); |
| } |
| } |
| |
| /** |
| * @private |
| * Encapsulates the positional aspects of a cursor within an ListCollectionView. |
| * Only the ListCollectionView should construct this. |
| */ |
| class ListCollectionViewBookmark extends CursorBookmark |
| { |
| mx_internal var index:int; |
| mx_internal var view:ListCollectionView; |
| mx_internal var viewRevision:int; |
| |
| /** |
| * @private |
| */ |
| public function ListCollectionViewBookmark(value:Object, |
| view:ListCollectionView, |
| viewRevision:int, |
| index:int) |
| { |
| super(value); |
| this.view = view; |
| this.viewRevision = viewRevision; |
| this.index = index; |
| } |
| |
| /** |
| * Get the approximate index of the item represented by this bookmark |
| * in its view. If the item has been paged out this may throw an |
| * ItemPendingError. If the item is not in the current view -1 will be |
| * returned. This method may also return -1 if index-based location is not |
| * possible. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| override public function getViewIndex():int |
| { |
| return view.getBookmarkIndex(this); |
| } |
| } |