1//////////////////////////////////////////////////////////////////////////////// 2// 3// ADOBE SYSTEMS INCORPORATED 4// Copyright 2005-2007 Adobe Systems Incorporated 5// All Rights Reserved. 6// 7// NOTICE: Adobe permits you to use, modify, and distribute this file 8// in accordance with the terms of the license agreement accompanying it. 9// 10//////////////////////////////////////////////////////////////////////////////// 11 12package mx.collections 13{ 14 15import flash.events.Event; 16import flash.events.EventDispatcher; 17import flash.utils.Proxy; 18import flash.utils.flash_proxy; 19import flash.utils.getQualifiedClassName; 20import mx.collections.errors.CollectionViewError; 21import mx.collections.errors.CursorError; 22import mx.collections.errors.ItemPendingError; 23import mx.collections.errors.SortError; 24import mx.core.IMXMLObject; 25import mx.core.mx_internal; 26import mx.events.CollectionEvent; 27import mx.events.CollectionEventKind; 28import mx.events.FlexEvent; 29import mx.events.PropertyChangeEvent; 30import mx.managers.ISystemManager; 31import mx.managers.SystemManager; 32import mx.resources.IResourceManager; 33import mx.resources.ResourceManager; 34import mx.utils.ObjectUtil; 35 36use namespace mx_internal; 37 38/** 39 * Dispatched when the ICollectionView has been updated in some way. 40 * 41 * @eventType mx.events.CollectionEvent.COLLECTION_CHANGE 42 * 43 * @langversion 3.0 44 * @playerversion Flash 9 45 * @playerversion AIR 1.1 46 * @productversion Flex 3 47 */ 48[Event(name="collectionChange", type="mx.events.CollectionEvent")] 49 50[ResourceBundle("collections")] 51 52/** 53 * The ListCollectionView class adds the properties and methods of the 54 * <code>ICollectionView</code> interface to an object that conforms to the 55 * <code>IList</code> interface. As a result, you can pass an object of this class 56 * to anything that requires an <code>IList</code> or <code>ICollectionView</code>. 57 * 58 * <p>This class also lets you use [ ] array notation 59 * to access the <code>getItemAt()</code> and <code>setItemAt()</code> methods. 60 * If you use code such as <code>myListCollectionView[index]</code> 61 * Flex calls the <code>myListCollectionView</code> object's 62 * <code>getItemAt()</code> or <code>setItemAt()</code> method.</p> 63 * 64 * @mxml 65 * 66 * <p>The <code><mx:ListCollectionView></code> has the following attributes, 67 * which all of its subclasses inherit:</p> 68 * 69 * <pre> 70 * <mx:ListCollectionView 71 * <b>Properties</b> 72 * filterFunction="null" 73 * list="null" 74 * sort="null" 75 * <b>Events</b> 76 * collectionChange="<i>No default</i>" 77 * /> 78 * </pre> 79 * 80 * @langversion 3.0 81 * @playerversion Flash 9 82 * @playerversion AIR 1.1 83 * @productversion Flex 3 84 */ 85public class ListCollectionView extends Proxy 86 implements ICollectionView, IList, IMXMLObject 87{ 88 include "../core/Version.as"; 89 90 //-------------------------------------------------------------------------- 91 // 92 // Private variables 93 // 94 //-------------------------------------------------------------------------- 95 96 /** 97 * @private 98 * Internal event dispatcher. 99 */ 100 private var eventDispatcher:EventDispatcher; 101 102 /** 103 * @private 104 * Revisions are used for bookmark maintenace, 105 * see getBookmark() and getBookmarkIndex() along with reset(). 106 */ 107 private var revision:int; 108 109 /** 110 * @private 111 * Used internally for managing disableAutoUpdate and enableAutoUpdate 112 * calls. disableAutoUpdate increments the counter, enable decrements. 113 * When the counter reaches 0 handlePendingUpdates is called. 114 */ 115 private var autoUpdateCounter:int; 116 117 /** 118 * @private 119 * Any update events that occured while autoUpdateCounter > 0 120 * are stored here. 121 * This may be null when there are no updates. 122 */ 123 private var pendingUpdates:Array; 124 125 /** 126 * @private 127 * Flag that indicates whether a RESET type of collectionChange 128 * event should be emitted when reset() is called. 129 */ 130 mx_internal var dispatchResetEvent:Boolean = true; 131 132 /** 133 * @private 134 * Used for accessing localized Error messages. 135 */ 136 private var resourceManager:IResourceManager = 137 ResourceManager.getInstance(); 138 139 //-------------------------------------------------------------------------- 140 // 141 // Protected variables 142 // 143 //-------------------------------------------------------------------------- 144 145 /** 146 * When the view is sorted or filtered the <code>localIndex</code> property 147 * contains an array of items in the sorted or filtered (ordered, reduced) 148 * view, in the sorted order. 149 * The ListCollectionView class uses this property to access the items in 150 * the view. 151 * The <code>localIndex</code> property should never contain anything 152 * that is not in the source, but may not have everything in the source. 153 * This property is <code>null</code> when there is no sort. 154 * 155 * @langversion 3.0 156 * @playerversion Flash 9 157 * @playerversion AIR 1.1 158 * @productversion Flex 3 159 */ 160 protected var localIndex:Array; 161 162 //-------------------------------------------------------------------------- 163 // 164 // Constructor 165 // 166 //-------------------------------------------------------------------------- 167 168 /** 169 * The ListCollectionView constructor. 170 * 171 * @param list the IList this ListCollectionView is meant to wrap. 172 * 173 * @langversion 3.0 174 * @playerversion Flash 9 175 * @playerversion AIR 1.1 176 * @productversion Flex 3 177 */ 178 public function ListCollectionView(list:IList = null) 179 { 180 super(); 181 182 eventDispatcher = new EventDispatcher(this); 183 this.list = list; 184 } 185 186 /** 187 * Called automatically by the MXML compiler when the ListCollectionView 188 * is created using an MXML tag. 189 * If you create the ListCollectionView through ActionScript, you 190 * must call this method passing in the MXML document and 191 * <code>null</code> for the <code>id</code>. 192 * 193 * @param document The MXML document containing this ListCollectionView. 194 * 195 * @param id Ignored. 196 * 197 * @langversion 3.0 198 * @playerversion Flash 9 199 * @playerversion AIR 1.1 200 * @productversion Flex 3 201 */ 202 public function initialized(document:Object, id:String):void 203 { 204 refresh(); 205 } 206 207 //-------------------------------------------------------------------------- 208 // 209 // Properties 210 // 211 //-------------------------------------------------------------------------- 212 213 //---------------------------------- 214 // length 215 //---------------------------------- 216 217 [Bindable("collectionChange")] 218 219 /** 220 * @inheritDoc 221 * 222 * @langversion 3.0 223 * @playerversion Flash 9 224 * @playerversion AIR 1.1 225 * @productversion Flex 3 226 */ 227 public function get length():int 228 { 229 if (localIndex) 230 { 231 return localIndex.length; 232 } 233 else if (list) 234 { 235 return list.length; 236 } 237 else 238 { 239 return 0; 240 } 241 } 242 243 //---------------------------------- 244 // list 245 //---------------------------------- 246 247 /** 248 * @private 249 * Storage for the list property. 250 */ 251 private var _list:IList; 252 253 [Inspectable(category="General")] 254 [Bindable("listChanged")] 255 256 /** 257 * The IList that this collection view wraps. 258 * 259 * @langversion 3.0 260 * @playerversion Flash 9 261 * @playerversion AIR 1.1 262 * @productversion Flex 3 263 */ 264 public function get list():IList 265 { 266 return _list; 267 } 268 269 /** 270 * @private 271 */ 272 public function set list(value:IList):void 273 { 274 if (_list != value) 275 { 276 var oldHasItems:Boolean; 277 var newHasItems:Boolean; 278 if (_list) 279 { 280 _list.removeEventListener(CollectionEvent.COLLECTION_CHANGE, 281 listChangeHandler); 282 oldHasItems = _list.length > 0; 283 } 284 285 _list = value; 286 287 if (_list) 288 { 289 // weak listeners to collections and dataproviders 290 _list.addEventListener(CollectionEvent.COLLECTION_CHANGE, 291 listChangeHandler, false, 0, true); 292 newHasItems = _list.length > 0; 293 } 294 295 if (oldHasItems || newHasItems) 296 reset(); 297 dispatchEvent(new Event("listChanged")); 298 } 299 } 300 301 //---------------------------------- 302 // filterFunction 303 //---------------------------------- 304 305 /** 306 * @private 307 * Storage for the filterFunction property. 308 */ 309 private var _filterFunction:Function; 310 311 [Bindable("filterFunctionChanged")] 312 [Inspectable(category="General")] 313 314 /** 315 * @inheritDoc 316 * 317 * @see #refresh() 318 * 319 * @langversion 3.0 320 * @playerversion Flash 9 321 * @playerversion AIR 1.1 322 * @productversion Flex 3 323 */ 324 public function get filterFunction():Function 325 { 326 return _filterFunction; 327 } 328 329 /** 330 * @private 331 */ 332 public function set filterFunction(f:Function):void 333 { 334 _filterFunction = f; 335 dispatchEvent(new Event("filterFunctionChanged")); 336 } 337 338 //---------------------------------- 339 // sort 340 //---------------------------------- 341 342 /** 343 * @private 344 * Storage for the sort property. 345 */ 346 private var _sort:ISort; 347 348 [Bindable("sortChanged")] 349 [Inspectable(category="General")] 350 351 /** 352 * @inheritDoc 353 * 354 * @see #refresh() 355 * 356 * @langversion 3.0 357 * @playerversion Flash 9 358 * @playerversion AIR 1.1 359 * @productversion Flex 3 360 */ 361 public function get sort():ISort 362 { 363 return _sort; 364 } 365 366 /** 367 * @private 368 */ 369 public function set sort(s:ISort):void 370 { 371 _sort = s; 372 dispatchEvent(new Event("sortChanged")); 373 } 374 375 //-------------------------------------------------------------------------- 376 // 377 // ICollectionView Methods 378 // 379 //-------------------------------------------------------------------------- 380 381 /** 382 * @inheritDoc 383 * 384 * @see #enableAutoUpdate() 385 * @see mx.events.CollectionEvent 386 * 387 * @langversion 3.0 388 * @playerversion Flash 9 389 * @playerversion AIR 1.1 390 * @productversion Flex 3 391 */ 392 public function contains(item:Object):Boolean 393 { 394 return getItemIndex(item) != -1; 395 } 396 397 /** 398 * @inheritDoc 399 * 400 * @see mx.collections.ICollectionView#enableAutoUpdate() 401 * @see mx.events.CollectionEvent 402 * 403 * @langversion 3.0 404 * @playerversion Flash 9 405 * @playerversion AIR 1.1 406 * @productversion Flex 3 407 */ 408 public function disableAutoUpdate():void 409 { 410 autoUpdateCounter++; 411 } 412 413 /** 414 * @inheritDoc 415 * 416 * @see mx.collections.ICollectionView#disableAutoUpdate() 417 * 418 * @langversion 3.0 419 * @playerversion Flash 9 420 * @playerversion AIR 1.1 421 * @productversion Flex 3 422 */ 423 public function enableAutoUpdate():void 424 { 425 if (autoUpdateCounter > 0) 426 { 427 autoUpdateCounter--; 428 if (autoUpdateCounter == 0) 429 { 430 handlePendingUpdates(); 431 } 432 } 433 } 434 435 /** 436 * @inheritDoc 437 * 438 * @langversion 3.0 439 * @playerversion Flash 9 440 * @playerversion AIR 1.1 441 * @productversion Flex 3 442 */ 443 public function createCursor():IViewCursor 444 { 445 return new ListCollectionViewCursor(this); 446 } 447 448 /** 449 * @inheritDoc 450 * 451 * @see mx.events.CollectionEvent 452 * @see mx.core.IPropertyChangeNotifier 453 * @see mx.events.PropertyChangeEvent 454 * 455 * @langversion 3.0 456 * @playerversion Flash 9 457 * @playerversion AIR 1.1 458 * @productversion Flex 3 459 */ 460 public function itemUpdated(item:Object, property:Object = null, 461 oldValue:Object = null, 462 newValue:Object = null):void 463 { 464 list.itemUpdated(item, property, oldValue, newValue); 465 } 466 467 /** 468 * @inheritDoc 469 * 470 * @langversion 3.0 471 * @playerversion Flash 9 472 * @playerversion AIR 1.1 473 * @productversion Flex 3 474 */ 475 public function refresh():Boolean 476 { 477 return internalRefresh(true); 478 } 479 480 //-------------------------------------------------------------------------- 481 // 482 // IList Methods 483 // 484 //-------------------------------------------------------------------------- 485 486 [Bindable("collectionChange")] 487 488 /** 489 * @inheritDoc 490 * 491 * @langversion 3.0 492 * @playerversion Flash 9 493 * @playerversion AIR 1.1 494 * @productversion Flex 3 495 */ 496 public function getItemAt(index:int, prefetch:int = 0):Object 497 { 498 if (index < 0 || index >= length) 499 { 500 var message:String = resourceManager.getString( 501 "collections", "outOfBounds", [ index ]); 502 throw new RangeError(message); 503 } 504 505 if (localIndex) 506 { 507 return localIndex[index]; 508 } 509 else if (list) 510 { 511 return list.getItemAt(index, prefetch); 512 } 513 514 return null; 515 } 516 517 /** 518 * @inheritDoc 519 * 520 * @langversion 3.0 521 * @playerversion Flash 9 522 * @playerversion AIR 1.1 523 * @productversion Flex 3 524 */ 525 public function setItemAt(item:Object, index:int):Object 526 { 527 if (index < 0 || !list || index >= length) 528 { 529 var message:String = resourceManager.getString( 530 "collections", "outOfBounds", [ index ]); 531 throw new RangeError(message); 532 } 533 534 var listIndex:int = index; 535 if (localIndex) 536 { 537 if (index > localIndex.length) 538 { 539 listIndex = list.length; 540 } 541 else 542 { 543 var oldItem:Object = localIndex[index]; 544 listIndex = list.getItemIndex(oldItem); 545 } 546 } 547 return list.setItemAt(item, listIndex); 548 } 549 550 /** 551 * @inheritDoc 552 * 553 * @langversion 3.0 554 * @playerversion Flash 9 555 * @playerversion AIR 1.1 556 * @productversion Flex 3 557 */ 558 public function addItem(item:Object):void 559 { 560 addItemAt(item, length); 561 } 562 563 /** 564 * @inheritDoc 565 * 566 * @langversion 3.0 567 * @playerversion Flash 9 568 * @playerversion AIR 1.1 569 * @productversion Flex 3 570 */ 571 public function addItemAt(item:Object, index:int):void 572 { 573 if (index < 0 || !list || index > length) 574 { 575 var message:String = resourceManager.getString( 576 "collections", "outOfBounds", [ index ]); 577 throw new RangeError(message); 578 } 579 580 var listIndex:int = index; 581 //if we're sorted addItemAt is meaningless, just add to the end 582 if (localIndex && sort) 583 { 584 listIndex = list.length; 585 } 586 else if (localIndex && filterFunction != null) 587 { 588 // if end of filtered list, put at end of source list 589 if (listIndex == localIndex.length) 590 listIndex = list.length; 591 // if somewhere in filtered list, find it and insert before it 592 // or at beginning 593 else 594 listIndex = list.getItemIndex(localIndex[index]); 595 } 596 list.addItemAt(item, listIndex); 597 } 598 599 /** 600 * Adds a list of items to the current list, placing them at the end of 601 * the list in the order they are passed. 602 * 603 * @param IList The list of items to add to the current list 604 * 605 * @langversion 3.0 606 * @playerversion Flash 9 607 * @playerversion AIR 1.1 608 * @productversion Flex 3 609 */ 610 public function addAll(addList:IList):void 611 { 612 addAllAt(addList, length); 613 } 614 615 /** 616 * Adds a list of items to the current list, placing them at the position 617 * index passed in to the function. The items are placed at the index location 618 * and placed in the order they are recieved. 619 * 620 * @param IList The list of items to add to the current list 621 * @param index The location of the current list to place the new items. 622 * @throws RangeError if index is less than 0 or greater than the length of the list. 623 * 624 * @langversion 3.0 625 * @playerversion Flash 9 626 * @playerversion AIR 1.1 627 * @productversion Flex 3 628 */ 629 public function addAllAt(addList:IList, index:int):void 630 { 631 var length:int = addList.length; 632 for (var i:int=0; i < length; i++) 633 { 634 this.addItemAt(addList.getItemAt(i), i+index); 635 } 636 } 637 638 /** 639 * @inheritDoc 640 * 641 * @langversion 3.0 642 * @playerversion Flash 9 643 * @playerversion AIR 1.1 644 * @productversion Flex 3 645 */ 646 public function getItemIndex(item:Object):int 647 { 648 var i:int; 649 650 if (localIndex && sort) 651 { 652 var startIndex:int = findItem(item, Sort.FIRST_INDEX_MODE); 653 if (startIndex == -1) 654 return -1; 655 656 var endIndex:int = findItem(item, Sort.LAST_INDEX_MODE); 657 for (i = startIndex; i <= endIndex; i++) 658 { 659 if (localIndex[i] == item) 660 return i; 661 } 662 663 return -1; 664 } 665 else if (localIndex && filterFunction != null) 666 { 667 var len:int = localIndex.length; 668 for (i = 0; i < len; i++) 669 { 670 if (localIndex[i] == item) 671 return i; 672 } 673 674 return -1; 675 } 676 677 // fallback 678 return list.getItemIndex(item); 679 } 680 681 /** 682 * @inheritDoc 683 */ 684 mx_internal function getLocalItemIndex(item:Object):int 685 { 686 var i:int; 687 688 var len:int = localIndex.length; 689 for (i = 0; i < len; i++) 690 { 691 if (localIndex[i] == item) 692 return i; 693 } 694 695 return -1; 696 } 697 698 /** 699 * @private 700 */ 701 private function getFilteredItemIndex(item:Object):int 702 { 703 //loc is wrong 704 //the intent of this function is to find where this new item 705 //should be in the filtered list, by looking at the main list 706 //for it's neighbor that is also in this filtered list 707 //and trying to insert item after that neighbor in the insert locao filtered list 708 709 //1st get the position in the original list 710 var loc:int = list.getItemIndex(item); 711 712 //if it's 0 then item must be also the first in the filtered list 713 if (loc == 0) 714 return 0; 715 716 // scan backwards for an item that also in the filtered list 717 for (var i:int = loc - 1; i >= 0; i--) 718 { 719 var prevItem:Object = list.getItemAt(i); 720 if (filterFunction(prevItem)) 721 { 722 var len:int = localIndex.length; 723 // get the index of the item in the filtered set 724 //for (var j:int = 0; j < len; j++) 725 for (var j:int = 0; j < len; j++) 726 { 727 if (localIndex[j] == prevItem) 728 return j + 1; 729 } 730 } 731 } 732 733 //turns out that there are no neighbors of item in the filtered 734 //list, so item is the 1st item 735 return 0; 736 } 737 738 739 /** 740 * @inheritDoc 741 * 742 * @langversion 3.0 743 * @playerversion Flash 9 744 * @playerversion AIR 1.1 745 * @productversion Flex 3 746 */ 747 public function removeItemAt(index:int):Object 748 { 749 if (index < 0 || index >= length) 750 { 751 var message:String = resourceManager.getString( 752 "collections", "outOfBounds", [ index ]); 753 throw new RangeError(message); 754 } 755 756 var listIndex:int = index; 757 if (localIndex) 758 { 759 var oldItem:Object = localIndex[index]; 760 listIndex = list.getItemIndex(oldItem); 761 } 762 return list.removeItemAt(listIndex); 763 } 764 765 /** 766 * Remove all items from the list. 767 * 768 * @langversion 3.0 769 * @playerversion Flash 9 770 * @playerversion AIR 1.1 771 * @productversion Flex 3 772 */ 773 public function removeAll():void 774 { 775 var len:int = length; 776 if (len > 0) 777 { 778 if (localIndex) 779 { 780 for (var i:int = len - 1; i >= 0; i--) 781 { 782 removeItemAt(i); 783 } 784 } 785 else 786 { 787 list.removeAll(); 788 } 789 } 790 } 791 792 /** 793 * @inheritDoc 794 * 795 * @langversion 3.0 796 * @playerversion Flash 9 797 * @playerversion AIR 1.1 798 * @productversion Flex 3 799 */ 800 public function toArray():Array 801 { 802 var ret:Array; 803 if (localIndex) 804 { 805 ret = localIndex.concat(); 806 } 807 else 808 { 809 ret = list.toArray(); 810 } 811 return ret; 812 } 813 814 /** 815 * Prints the contents of this view to a string and returns it. 816 * 817 * @return The contents of this view, in string form. 818 * 819 * @langversion 3.0 820 * @playerversion Flash 9 821 * @playerversion AIR 1.1 822 * @productversion Flex 3 823 */ 824 public function toString():String 825 { 826 if (localIndex) 827 { 828 return ObjectUtil.toString(localIndex); 829 } 830 else 831 { 832 if (list && Object(list).toString) 833 return Object(list).toString(); 834 else 835 return getQualifiedClassName(this); 836 } 837 } 838 839 //-------------------------------------------------------------------------- 840 // 841 // Proxy methods 842 // 843 //-------------------------------------------------------------------------- 844 845 /** 846 * @private 847 * Attempts to call getItemAt(), converting the property name into an int. 848 */ 849 override flash_proxy function getProperty(name:*):* 850 { 851 if (name is QName) 852 name = name.localName; 853 854 var index:int = -1; 855 try 856 { 857 // If caller passed in a number such as 5.5, it will be floored. 858 var n:Number = parseInt(String(name)); 859 if (!isNaN(n)) 860 index = int(n); 861 } 862 catch(e:Error) // localName was not a number 863 { 864 } 865 866 if (index == -1) 867 { 868 var message:String = resourceManager.getString( 869 "collections", "unknownProperty", [ name ]); 870 throw new Error(message); 871 } 872 else 873 { 874 return getItemAt(index); 875 } 876 } 877 878 /** 879 * @private 880 * Attempts to call setItemAt(), converting the property name into an int. 881 */ 882 override flash_proxy function setProperty(name:*, value:*):void 883 { 884 if (name is QName) 885 name = name.localName; 886 887 var index:int = -1; 888 try 889 { 890 // If caller passed in a number such as 5.5, it will be floored. 891 var n:Number = parseInt(String(name)); 892 if (!isNaN(n)) 893 index = int(n); 894 } 895 catch(e:Error) // localName was not a number 896 { 897 } 898 899 if (index == -1) 900 { 901 var message:String = resourceManager.getString( 902 "collections", "unknownProperty", [ name ]); 903 throw new Error(message); 904 } 905 else 906 { 907 setItemAt(value, index); 908 } 909 } 910 911 /** 912 * @private 913 * This is an internal function. 914 * The VM will call this method for code like <code>"foo" in bar</code> 915 * 916 * @param name The property name that should be tested for existence. 917 */ 918 override flash_proxy function hasProperty(name:*):Boolean 919 { 920 if (name is QName) 921 name = name.localName; 922 923 var index:int = -1; 924 try 925 { 926 // If caller passed in a number such as 5.5, it will be floored. 927 var n:Number = parseInt(String(name)); 928 if (!isNaN(n)) 929 index = int(n); 930 } 931 catch(e:Error) // localName was not a number 932 { 933 } 934 935 if (index == -1) 936 return false; 937 938 return index >= 0 && index < length; 939 } 940 941 /** 942 * @private 943 */ 944 override flash_proxy function nextNameIndex(index:int):int 945 { 946 return index < length ? index + 1 : 0; 947 } 948 949 /** 950 * @private 951 */ 952 override flash_proxy function nextName(index:int):String 953 { 954 return (index - 1).toString(); 955 } 956 957 /** 958 * @private 959 */ 960 override flash_proxy function nextValue(index:int):* 961 { 962 return getItemAt(index - 1); 963 } 964 965 /** 966 * @private 967 * Any methods that can't be found on this class shouldn't be called, 968 * so return null 969 */ 970 override flash_proxy function callProperty(name:*, ... rest):* 971 { 972 return null; 973 } 974 975 //-------------------------------------------------------------------------- 976 // 977 // EventDispatcher methods 978 // 979 //-------------------------------------------------------------------------- 980 981 /** 982 * @inheritDoc 983 * 984 * @langversion 3.0 985 * @playerversion Flash 9 986 * @playerversion AIR 1.1 987 * @productversion Flex 3 988 */ 989 public function addEventListener(type:String, 990 listener:Function, 991 useCapture:Boolean = false, 992 priority:int = 0, 993 useWeakReference:Boolean = false):void 994 { 995 eventDispatcher.addEventListener(type, listener, useCapture, 996 priority, useWeakReference); 997 } 998 999 /** 1000 * @inheritDoc 1001 * 1002 * @langversion 3.0 1003 * @playerversion Flash 9 1004 * @playerversion AIR 1.1 1005 * @productversion Flex 3 1006 */ 1007 public function removeEventListener(type:String, 1008 listener:Function, 1009 useCapture:Boolean = false):void 1010 { 1011 eventDispatcher.removeEventListener(type, listener, useCapture); 1012 } 1013 1014 /** 1015 * @inheritDoc 1016 * 1017 * @langversion 3.0 1018 * @playerversion Flash 9 1019 * @playerversion AIR 1.1 1020 * @productversion Flex 3 1021 */ 1022 public function dispatchEvent(event:Event):Boolean 1023 { 1024 return eventDispatcher.dispatchEvent(event); 1025 } 1026 1027 /** 1028 * @inheritDoc 1029 * 1030 * @langversion 3.0 1031 * @playerversion Flash 9 1032 * @playerversion AIR 1.1 1033 * @productversion Flex 3 1034 */ 1035 public function hasEventListener(type:String):Boolean 1036 { 1037 return eventDispatcher.hasEventListener(type); 1038 } 1039 1040 /** 1041 * @inheritDoc 1042 * 1043 * @langversion 3.0 1044 * @playerversion Flash 9 1045 * @playerversion AIR 1.1 1046 * @productversion Flex 3 1047 */ 1048 public function willTrigger(type:String):Boolean 1049 { 1050 return eventDispatcher.willTrigger(type); 1051 } 1052 1053 //-------------------------------------------------------------------------- 1054 // 1055 // Internal methods 1056 // 1057 //-------------------------------------------------------------------------- 1058 1059 /** 1060 * Take the item and insert it into the view. If we don't have a sort 1061 * use the sourceLocation. Dispatch the CollectionEvent with kind ADD 1062 * if dispatch is true. 1063 * 1064 * @param items the items to add into the view 1065 * @param sourceLocation the location within the list where the items were added 1066 * @param extendedInfo Object reference to any additional event information 1067 * that needs to be preserved. 1068 * @param dispatch true if the view should dispatch a corresponding 1069 * CollectionEvent with kind ADD (default is true) 1070 * 1071 * @langversion 3.0 1072 * @playerversion Flash 9 1073 * @playerversion AIR 1.1 1074 * @productversion Flex 3 1075 */ 1076 private function addItemsToView(items:Array, sourceLocation:int, 1077 dispatch:Boolean = true):int 1078 { 1079 var addedItems:Array = localIndex ? [] : items; 1080 var addLocation:int = sourceLocation; 1081 var firstOne:Boolean = true; 1082 1083 if (localIndex) 1084 { 1085 var loc:int = sourceLocation; 1086 for (var i:int = 0; i < items.length; i++) 1087 { 1088 var item:Object = items[i]; 1089 if (filterFunction == null || filterFunction(item)) 1090 { 1091 if (sort) 1092 { 1093 loc = findItem(item, Sort.ANY_INDEX_MODE, true); 1094 if (firstOne) 1095 { 1096 addLocation = loc; 1097 firstOne = false; 1098 } 1099 } 1100 else 1101 { 1102 loc = getFilteredItemIndex(item); 1103 if (firstOne) 1104 { 1105 addLocation = loc; 1106 firstOne = false; 1107 } 1108 } 1109 1110 if (sort && sort.unique && sort.compareFunction(item, localIndex[loc]) == 0) 1111 { 1112 // We cause all adds to fail here, not just the one. 1113 var message:String = resourceManager.getString( 1114 "collections", "incorrectAddition"); 1115 throw new CollectionViewError(message); 1116 } 1117 localIndex.splice(loc++, 0, item); 1118 addedItems.push(item); 1119 } 1120 else 1121 addLocation = -1; 1122 } 1123 } 1124 1125 if (localIndex && addedItems.length > 1) 1126 { 1127 addLocation = -1; 1128 } 1129 1130 if (dispatch && addedItems.length > 0) 1131 { 1132 var event:CollectionEvent = 1133 new CollectionEvent(CollectionEvent.COLLECTION_CHANGE); 1134 event.kind = CollectionEventKind.ADD; 1135 event.location = addLocation; 1136 event.items = addedItems; 1137 dispatchEvent(event); 1138 } 1139 1140 return addLocation; 1141 } 1142 1143 /** 1144 * Find the item specified using the Sort find mode constants. 1145 * If there is no sort assigned throw an error. 1146 * 1147 * @param values the values object that can be passed into Sort.findItem 1148 * @param mode the mode to pass to Sort.findItem (see Sort) 1149 * @param insertIndex true if it should find the insertion point 1150 * @return the index where the item is located, -1 if not found or SortError 1151 * caught 1152 * 1153 * @langversion 3.0 1154 * @playerversion Flash 9 1155 * @playerversion AIR 1.1 1156 * @productversion Flex 3 1157 */ 1158 mx_internal function findItem(values:Object, mode:String, insertIndex:Boolean = false):int 1159 { 1160 if (!sort || !localIndex) 1161 { 1162 var message:String = resourceManager.getString( 1163 "collections", "itemNotFound"); 1164 throw new CollectionViewError(message); 1165 } 1166 1167 if (localIndex.length == 0) 1168 return insertIndex ? 0 : -1; 1169 1170 try 1171 { 1172 return sort.findItem(localIndex, values, mode, insertIndex); 1173 } 1174 catch (e:SortError) 1175 { 1176 // usually because the find critieria is not compatible with the sort. 1177 } 1178 1179 return -1; 1180 } 1181 1182 /** 1183 * Create a bookmark for this view. This method is called by 1184 * ListCollectionViewCursor. 1185 * 1186 * @param index the index to bookmark 1187 * @return a new bookmark instance 1188 * @throws a CollectionViewError if the index is out of bounds 1189 * 1190 * @langversion 3.0 1191 * @playerversion Flash 9 1192 * @playerversion AIR 1.1 1193 * @productversion Flex 3 1194 */ 1195 mx_internal function getBookmark(index:int):ListCollectionViewBookmark 1196 { 1197 if (index < 0 || index > length) 1198 { 1199 var message:String = resourceManager.getString( 1200 "collections", "invalidIndex", [ index ]); 1201 throw new CollectionViewError(message); 1202 } 1203 1204 var value:Object; 1205 try 1206 { 1207 value = getItemAt(index); 1208 } 1209 catch(e:Error) 1210 { 1211 // the cursor was over something that is not yet on the client 1212 value = null; 1213 } 1214 return new ListCollectionViewBookmark(value, 1215 this, 1216 revision, 1217 index); 1218 1219 } 1220 1221 /** 1222 * Given a bookmark find the location for the value. If the 1223 * view has been modified since the bookmark was created attempt 1224 * to relocate the item. If the bookmark represents an item 1225 * that is no longer in the view (removed or filtered out) return 1226 * -1. 1227 * 1228 * @param bookmark the bookmark to locate 1229 * @return the new location of the bookmark, -1 if not in the view anymore 1230 * @throws CollectionViewError if the bookmark is invalid 1231 * 1232 * @langversion 3.0 1233 * @playerversion Flash 9 1234 * @playerversion AIR 1.1 1235 * @productversion Flex 3 1236 */ 1237 mx_internal function getBookmarkIndex(bookmark:CursorBookmark):int 1238 { 1239 if (!(bookmark is ListCollectionViewBookmark) 1240 || ListCollectionViewBookmark(bookmark).view != this) 1241 { 1242 var message:String = resourceManager.getString( 1243 "collections", "bookmarkNotFound"); 1244 throw new CollectionViewError(message); 1245 } 1246 1247 var bm:ListCollectionViewBookmark = ListCollectionViewBookmark(bookmark); 1248 1249 if (bm.viewRevision != revision) 1250 { 1251 // getItemAt has a side-effect of throwing an exception if the index is out- 1252 // of-range, here we are checking to see if the index falls with-in the range 1253 // and only then calling getItemAt. 1254 if (bm.index < 0 || bm.index >= length || getItemAt(bm.index) != bm.value) 1255 { 1256 try 1257 { 1258 bm.index = getItemIndex(bm.value); 1259 } 1260 catch (e:SortError) 1261 { 1262 bm.index = getLocalItemIndex(bm.value); 1263 } 1264 } 1265 1266 bm.viewRevision = revision; 1267 } 1268 return bm.index; 1269 } 1270 1271 /** 1272 * The view is a listener of CollectionEvents on its underlying IList 1273 * 1274 * @langversion 3.0 1275 * @playerversion Flash 9 1276 * @playerversion AIR 1.1 1277 * @productversion Flex 3 1278 */ 1279 private function listChangeHandler(event:CollectionEvent):void 1280 { 1281 if (autoUpdateCounter > 0) 1282 { 1283 if (!pendingUpdates) 1284 { 1285 pendingUpdates = []; 1286 } 1287 pendingUpdates.push(event); 1288 } 1289 else 1290 { 1291 switch (event.kind) 1292 { 1293 case CollectionEventKind.ADD: 1294 addItemsToView(event.items, event.location); 1295 break; 1296 1297 case CollectionEventKind.MOVE: 1298 var n:int = event.items.length; 1299 for (var i:int = 0; i < n; i++) 1300 moveItemInView(event.items[i]); 1301 break; 1302 1303 case CollectionEventKind.RESET: 1304 reset(); 1305 break; 1306 1307 case CollectionEventKind.REMOVE: 1308 removeItemsFromView(event.items, event.location); 1309 break; 1310 1311 case CollectionEventKind.REPLACE: 1312 replaceItemsInView(event.items, event.location); 1313 break; 1314 1315 case CollectionEventKind.UPDATE: 1316 handlePropertyChangeEvents(event.items); 1317 break; 1318 1319 default: 1320 dispatchEvent(event); 1321 } // switch 1322 } 1323 } 1324 1325 /** 1326 * Given a set of PropertyChangeEvents go through and update the view. 1327 * This is currently not optimized. 1328 * 1329 * @langversion 3.0 1330 * @playerversion Flash 9 1331 * @playerversion AIR 1.1 1332 * @productversion Flex 3 1333 */ 1334 private function handlePropertyChangeEvents(events:Array):void 1335 { 1336 var eventItems:Array = events; 1337 if (sort || filterFunction != null) 1338 { 1339 //go through the events and find all the individual objects 1340 //that have been updated 1341 //then for each one determine whether we should move it or 1342 //just fire an update event 1343 var updatedItems:Array = []; 1344 var updateEntry:Object; 1345 var i:int; 1346 for (i = 0; i < events.length; i++) 1347 { 1348 var updateInfo:PropertyChangeEvent = events[i]; 1349 var item:Object; 1350 var defaultMove:Boolean; 1351 if (updateInfo.target) 1352 { 1353 item = updateInfo.target; 1354 //if the target != source that means the update 1355 //happened to some subprop of the item in the collection 1356 //if we have a custom comparator this will affect 1357 //the sort so for now say we should move but 1358 //maybe we could optimize further 1359 defaultMove = updateInfo.target != updateInfo.source; 1360 } 1361 else 1362 { 1363 item = updateInfo.source; 1364 defaultMove = false; 1365 } 1366 1367 //see if the item is already in the list 1368 var j:int = 0; 1369 for (; j < updatedItems.length; j++) 1370 { 1371 if (updatedItems[j].item == item) 1372 { 1373 // even if it is, if a different property changed, track that too. 1374 var events:Array = updatedItems[j].events; 1375 var l:int = events.length; 1376 for (var k:int = 0; k < l; k++) 1377 { 1378 if (events[k].property != updateInfo.property) 1379 { 1380 events.push(updateInfo); 1381 break; 1382 } 1383 // we could also merge events for changes to the same 1384 // property but that's hopefully low probability 1385 // and leads to questions of event order. 1386 } 1387 break; 1388 } 1389 } 1390 1391 if (j < updatedItems.length) 1392 { 1393 updateEntry = updatedItems[j]; 1394 } 1395 else 1396 { 1397 updateEntry = { item: item, move: defaultMove, events: [ updateInfo ] }; 1398 updatedItems.push(updateEntry); 1399 } 1400 1401 //if we've already set replace don't unset it 1402 //if there's a filterFunction need to go through replace 1403 //if there's no property specified for the sort we'll need 1404 //to assume we have to replace 1405 //if there is a property see if it affects the sort 1406 updateEntry.move = 1407 updateEntry.move 1408 || filterFunction 1409 || !updateInfo.property 1410 || (sort && sort.propertyAffectsSort(String(updateInfo.property))); 1411 } 1412 1413 // go through the items and move and send move events for ones that moved 1414 // and build the list of remaining items we need to send UPDATE events for 1415 eventItems = []; 1416 for (i = 0; i < updatedItems.length; i++) 1417 { 1418 updateEntry = updatedItems[i]; 1419 if (updateEntry.move) 1420 { 1421 moveItemInView(updateEntry.item, updateEntry.item, eventItems); 1422 } 1423 else 1424 { 1425 eventItems.push(updateEntry.item); 1426 } 1427 } 1428 1429 // now go through the updated items and add all events for all 1430 // properties that changed in that item (except for those items 1431 // we moved 1432 var temp:Array = []; 1433 for (var ctr:int = 0; ctr < eventItems.length; ctr++) 1434 for (var ctr1:int = 0; ctr1 < updatedItems.length; ctr1++) 1435 if (eventItems[ctr] == updatedItems[ctr1].item) 1436 { 1437 temp = temp.concat(updatedItems[ctr1].events); 1438 } 1439 eventItems = temp; 1440 } 1441 1442 if (eventItems.length > 0) 1443 { 1444 1445 var updateEvent:CollectionEvent = 1446 new CollectionEvent(CollectionEvent.COLLECTION_CHANGE); 1447 updateEvent.kind = CollectionEventKind.UPDATE; 1448 updateEvent.items = eventItems; 1449 dispatchEvent(updateEvent); 1450 } 1451 } 1452 1453 /** 1454 * When enableAutoUpdates pushes autoUpdateCounter back down to 0 1455 * this method will execute to consolidate the pending update 1456 * events or turn it into a massive refresh(). 1457 * 1458 * @langversion 3.0 1459 * @playerversion Flash 9 1460 * @playerversion AIR 1.1 1461 * @productversion Flex 3 1462 */ 1463 private function handlePendingUpdates():void 1464 { 1465 if (pendingUpdates) 1466 { 1467 var pu:Array = pendingUpdates; 1468 pendingUpdates = null; 1469 1470 // Could further optimize to consolidate various events 1471 // and make a decision if there are too many updates 1472 // and we should just refresh. 1473 var singleUpdateEvent:CollectionEvent; 1474 for (var i:int = 0; i < pu.length; i++) 1475 { 1476 var event:CollectionEvent = pu[i]; 1477 if (event.kind == CollectionEventKind.UPDATE) 1478 { 1479 if (!singleUpdateEvent) 1480 { 1481 singleUpdateEvent = event; 1482 } 1483 else 1484 { 1485 for (var j:int = 0; j < event.items.length; j++) 1486 { 1487 singleUpdateEvent.items.push(event.items[j]); 1488 } 1489 } 1490 } 1491 else 1492 { 1493 listChangeHandler(event); 1494 } 1495 } 1496 1497 if (singleUpdateEvent) 1498 { 1499 listChangeHandler(singleUpdateEvent); 1500 } 1501 } 1502 } 1503 1504 private function internalRefresh(dispatch:Boolean):Boolean 1505 { 1506 if (sort || filterFunction != null) 1507 { 1508 try 1509 { 1510 populateLocalIndex(); 1511 } 1512 catch(pending:ItemPendingError) 1513 { 1514 pending.addResponder(new ItemResponder( 1515 function(data:Object, token:Object = null):void 1516 { 1517 internalRefresh(dispatch); 1518 }, 1519 function(info:Object, token:Object = null):void 1520 { 1521 //no-op 1522 })); 1523 return false; 1524 } 1525 1526 if (filterFunction != null) 1527 { 1528 var tmp:Array = []; 1529 var len:int = localIndex.length; 1530 for (var i:int = 0; i < len; i++) 1531 { 1532 var item:Object = localIndex[i]; 1533 if (filterFunction(item)) 1534 { 1535 tmp.push(item); 1536 } 1537 } 1538 localIndex = tmp; 1539 } 1540 if (sort) 1541 { 1542 sort.sort(localIndex); 1543 dispatch = true; 1544 } 1545 } 1546 else if (localIndex) 1547 { 1548 localIndex = null; 1549 } 1550 1551 revision++; 1552 pendingUpdates = null; 1553 if (dispatch) 1554 { 1555 var refreshEvent:CollectionEvent = 1556 new CollectionEvent(CollectionEvent.COLLECTION_CHANGE); 1557 refreshEvent.kind = CollectionEventKind.REFRESH; 1558 dispatchEvent(refreshEvent); 1559 } 1560 return true; 1561 } 1562 1563 /** 1564 * Remove the old value from the view and replace it with the value 1565 * 1566 * @langversion 3.0 1567 * @playerversion Flash 9 1568 * @playerversion AIR 1.1 1569 * @productversion Flex 3 1570 */ 1571 private function moveItemInView(item:Object, 1572 dispatch:Boolean = true, updateEventItems:Array = null):void 1573 { 1574 if (localIndex) 1575 { 1576 //we're guaranteed that removeItemsFromView isn't going 1577 //to work here because the item has probably 1578 //already been updated so getItemIndex is going to fail 1579 //so we'll just do a linear search and find it if it's here 1580 var removeLocation:int = -1; 1581 for (var i:int = 0; i < localIndex.length; i++) 1582 { 1583 if (localIndex[i] == item) 1584 { 1585 removeLocation = i; 1586 break; 1587 } 1588 } 1589 if (removeLocation > -1) 1590 { 1591 localIndex.splice(removeLocation, 1); 1592 } 1593 1594 var addLocation:int = addItemsToView([item], removeLocation, false); 1595 1596 if (dispatch) 1597 { 1598 var event:CollectionEvent = 1599 new CollectionEvent(CollectionEvent.COLLECTION_CHANGE); 1600 event.items.push(item); 1601 if (updateEventItems && addLocation == removeLocation && addLocation > -1) 1602 { 1603 updateEventItems.push(item); 1604 return; 1605 } 1606 if (addLocation > -1 && removeLocation > -1) 1607 { 1608 event.kind = CollectionEventKind.MOVE; 1609 event.location = addLocation; 1610 event.oldLocation = removeLocation; 1611 } 1612 else if (addLocation > -1) 1613 { 1614 event.kind = CollectionEventKind.ADD; 1615 event.location = addLocation; 1616 } 1617 else if (removeLocation > -1) 1618 { 1619 event.kind = CollectionEventKind.REMOVE; 1620 event.location = removeLocation; 1621 } 1622 else 1623 { 1624 dispatch = false; 1625 } 1626 1627 if (dispatch) 1628 { 1629 dispatchEvent(event); 1630 } 1631 } 1632 } 1633 } 1634 1635 /** 1636 * Copy all of the data from the source list into the local index. 1637 * 1638 * @langversion 3.0 1639 * @playerversion Flash 9 1640 * @playerversion AIR 1.1 1641 * @productversion Flex 3 1642 */ 1643 private function populateLocalIndex():void 1644 { 1645 if (list) 1646 { 1647 localIndex = list.toArray(); 1648 } 1649 else 1650 { 1651 localIndex = []; 1652 } 1653 } 1654 1655 /** 1656 * Take the item and remove it from the view. If we don't have a sort 1657 * use the sourceLocation. Dispatch the CollectionEvent with kind REMOVE 1658 * if dispatch is true. 1659 * 1660 * @param items the items to remove from the view 1661 * @param sourceLocation the location within the list where the item was removed 1662 * @param dispatch true if the view should dispatch a corresponding 1663 * CollectionEvent with kind REMOVE (default is true) 1664 * 1665 * @langversion 3.0 1666 * @playerversion Flash 9 1667 * @playerversion AIR 1.1 1668 * @productversion Flex 3 1669 */ 1670 private function removeItemsFromView(items:Array, sourceLocation:int, dispatch:Boolean = true):void 1671 { 1672 var removedItems:Array = localIndex ? [] : items; 1673 var removeLocation:int = sourceLocation; 1674 if (localIndex) 1675 { 1676 for (var i:int = 0; i < items.length; i++) 1677 { 1678 var item:Object = items[i]; 1679 var loc:int = getItemIndex(item); 1680 if (loc > -1) 1681 { 1682 localIndex.splice(loc, 1); 1683 removedItems.push(item); 1684 removeLocation = loc; 1685 } 1686 } 1687 } 1688 if (dispatch && removedItems.length > 0) 1689 { 1690 var event:CollectionEvent = 1691 new CollectionEvent(CollectionEvent.COLLECTION_CHANGE); 1692 event.kind = CollectionEventKind.REMOVE; 1693 event.location = (!localIndex || removedItems.length == 1) 1694 ? removeLocation 1695 : -1; 1696 event.items = removedItems; 1697 dispatchEvent(event); 1698 } 1699 } 1700 1701 /** 1702 * Items is an array of PropertyChangeEvents so replace the oldValues with the new 1703 * newValues. Start at the location specified and move forward, it's unlikely 1704 * that the length of items is > 1. 1705 * 1706 * @langversion 3.0 1707 * @playerversion Flash 9 1708 * @playerversion AIR 1.1 1709 * @productversion Flex 3 1710 */ 1711 private function replaceItemsInView(items:Array, 1712 location:int, 1713 dispatch:Boolean = true):void 1714 { 1715 if (localIndex) 1716 { 1717 var len:int = items.length; 1718 var oldItems:Array = []; 1719 var newItems:Array = []; 1720 for (var i:int = 0; i < len; i++) 1721 { 1722 var propertyEvent:PropertyChangeEvent = items[i]; 1723 oldItems.push(propertyEvent.oldValue); 1724 newItems.push(propertyEvent.newValue); 1725 } 1726 removeItemsFromView(oldItems, location, dispatch); 1727 addItemsToView(newItems, location, dispatch); 1728 } 1729 else 1730 { 1731 var event:CollectionEvent = 1732 new CollectionEvent(CollectionEvent.COLLECTION_CHANGE); 1733 event.kind = CollectionEventKind.REPLACE; 1734 event.location = location; 1735 event.items = items; 1736 dispatchEvent(event); 1737 } 1738 } 1739 1740 /** 1741 * @private 1742 * When the source list is replaced, reset. 1743 */ 1744 mx_internal function reset():void 1745 { 1746 internalRefresh(false); 1747 if (dispatchResetEvent) 1748 { 1749 var event:CollectionEvent = 1750 new CollectionEvent(CollectionEvent.COLLECTION_CHANGE); 1751 event.kind = CollectionEventKind.RESET; 1752 dispatchEvent(event); 1753 } 1754 } 1755 1756} 1757 1758} 1759 1760import flash.events.EventDispatcher; 1761import flash.events.Event; 1762import flash.events.IEventDispatcher; 1763 1764import mx.events.*; 1765import mx.collections.*; 1766import mx.collections.errors.*; 1767import mx.core.mx_internal; 1768import mx.managers.*; 1769import mx.resources.IResourceManager; 1770import mx.resources.ResourceManager; 1771 1772use namespace mx_internal; 1773 1774/** 1775 * Dispatched whenever the cursor position is updated. 1776 * 1777 * @eventType mx.events.FlexEvent.CURSOR_UPDATE 1778 * 1779 * @langversion 3.0 1780 * @playerversion Flash 9 1781 * @playerversion AIR 1.1 1782 * @productversion Flex 3 1783 */ 1784[Event(name="cursorUpdate", type="mx.events.FlexEvent")] 1785 1786[ResourceBundle("collections")] 1787 1788/** 1789 * @private 1790 * The internal implementation of cursor for the ListCollectionView. 1791 */ 1792class ListCollectionViewCursor extends EventDispatcher implements IViewCursor 1793{ 1794 //-------------------------------------------------------------------------- 1795 // 1796 // Class constants 1797 // 1798 //-------------------------------------------------------------------------- 1799 1800 /** 1801 * @private 1802 */ 1803 private static const BEFORE_FIRST_INDEX:int = -1; 1804 1805 /** 1806 * @private 1807 */ 1808 private static const AFTER_LAST_INDEX:int = -2; 1809 1810 //-------------------------------------------------------------------------- 1811 // 1812 // Constructor 1813 // 1814 //-------------------------------------------------------------------------- 1815 1816 /** 1817 * Constructor. 1818 * 1819 * <p>Creates the cursor for the view.</p> 1820 * 1821 * @langversion 3.0 1822 * @playerversion Flash 9 1823 * @playerversion AIR 1.1 1824 * @productversion Flex 3 1825 */ 1826 public function ListCollectionViewCursor(view:ListCollectionView) 1827 { 1828 super(); 1829 1830 _view = view; 1831 _view.addEventListener(CollectionEvent.COLLECTION_CHANGE, collectionEventHandler, false, 0, true); 1832 currentIndex = view.length > 0 ? 0 : AFTER_LAST_INDEX; 1833 if (currentIndex == 0) 1834 { 1835 try 1836 { 1837 setCurrent(view.getItemAt(0), false); 1838 } 1839 catch(e:ItemPendingError) 1840 { 1841 currentIndex = BEFORE_FIRST_INDEX; 1842 setCurrent(null, false); 1843 } 1844 } 1845 } 1846 1847 //-------------------------------------------------------------------------- 1848 // 1849 // Variables 1850 // 1851 //-------------------------------------------------------------------------- 1852 1853 /** 1854 * @private 1855 */ 1856 private var _view:ListCollectionView; 1857 1858 /** 1859 * @private 1860 */ 1861 private var currentIndex:int; 1862 1863 /** 1864 * @private 1865 */ 1866 private var currentValue:Object; 1867 1868 /** 1869 * @private 1870 */ 1871 private var invalid:Boolean; 1872 1873 /** 1874 * @private 1875 * Used for accessing localized Error messages. 1876 */ 1877 private var resourceManager:IResourceManager = 1878 ResourceManager.getInstance(); 1879 1880 //-------------------------------------------------------------------------- 1881 // 1882 // Properties 1883 // 1884 //-------------------------------------------------------------------------- 1885 1886 /** 1887 * Get a reference to the view that this cursor is associated with. 1888 * @return the associated <code>ICollectionView</code> 1889 * 1890 * @langversion 3.0 1891 * @playerversion Flash 9 1892 * @playerversion AIR 1.1 1893 * @productversion Flex 3 1894 */ 1895 public function get view():ICollectionView 1896 { 1897 checkValid(); 1898 return _view; 1899 } 1900 1901 [Bindable("cursorUpdate")] 1902 /** 1903 * Provides access the object at the current location referenced by 1904 * this cursor within the source collection. 1905 * If the cursor is beyond the ends of the collection (beforeFirst, 1906 * afterLast) this will return <code>null</code>. 1907 * 1908 * @see mx.collections.IViewCursor#moveNext 1909 * @see mx.collections.IViewCursor#movePrevious 1910 * @see mx.collections.IViewCursor#seek 1911 * @see mx.collections.IViewCursor#beforeFirst 1912 * @see mx.collections.IViewCursor#afterLast 1913 * 1914 * @langversion 3.0 1915 * @playerversion Flash 9 1916 * @playerversion AIR 1.1 1917 * @productversion Flex 3 1918 */ 1919 public function get current():Object 1920 { 1921 checkValid(); 1922 1923 return currentValue; 1924 } 1925 1926 [Bindable("cursorUpdate")] 1927 1928 /** 1929 * Provides access to the bookmark of the item returned by the 1930 * <code>current</code> property. 1931 * The bookmark can be used to move the cursor to a previously visited 1932 * item, or one relative to it (see the <code>seek()</code> method for 1933 * more information). 1934 * 1935 * @see mx.collections.IViewCursor#current 1936 * @see mx.collections.IViewCursor#seek 1937 * 1938 * @langversion 3.0 1939 * @playerversion Flash 9 1940 * @playerversion AIR 1.1 1941 * @productversion Flex 3 1942 */ 1943 public function get bookmark():CursorBookmark 1944 { 1945 checkValid(); 1946 if (view.length == 0 || beforeFirst) return CursorBookmark.FIRST; 1947 else if (afterLast) return CursorBookmark.LAST; 1948 //if currentIndex > view.length this is a bug in cursor and i want the 1949 //exception thrown to track it down 1950 else return ListCollectionView(view).getBookmark(currentIndex); 1951 } 1952 1953 1954 [Bindable("cursorUpdate")] 1955 /** 1956 * true if the current is sitting before the first item in the view. 1957 * If the ICollectionView is empty (length == 0) this will always 1958 * be true. 1959 * 1960 * @langversion 3.0 1961 * @playerversion Flash 9 1962 * @playerversion AIR 1.1 1963 * @productversion Flex 3 1964 */ 1965 public function get beforeFirst():Boolean 1966 { 1967 checkValid(); 1968 return currentIndex == BEFORE_FIRST_INDEX || view.length == 0; 1969 } 1970 1971 1972 [Bindable("cursorUpdate")] 1973 /** 1974 * true if the cursor is sitting after the last item in the view. 1975 * If the ICollectionView is empty (length == 0) this will always 1976 * be true. 1977 * 1978 * @langversion 3.0 1979 * @playerversion Flash 9 1980 * @playerversion AIR 1.1 1981 * @productversion Flex 3 1982 */ 1983 public function get afterLast():Boolean 1984 { 1985 checkValid(); 1986 return currentIndex == AFTER_LAST_INDEX || view.length == 0; 1987 } 1988 1989 /** 1990 * Finds the item with the specified properties within the 1991 * collection and positions the cursor on that item. 1992 * If the item can not be found no change to the current location will be 1993 * made. 1994 * <code>findAny()</code> can only be called on sorted views, if the view 1995 * isn't sorted, or items in the view do not contain properties used 1996 * to compute the sort order, a <code>CursorError</code> will be thrown. 1997 * <p> 1998 * If the associated collection is remote, and not all of the items have 1999 * been cached locally this method will begin an asynchronous fetch from the 2000 * remote collection, or if one is already in progress wait for it to 2001 * complete before making another fetch request. 2002 * If multiple items can match the search criteria then the item found is 2003 * non-deterministic. 2004 * If it is important to find the first or last occurrence of an item in a 2005 * non-unique index use the <code>findFirst()</code> or 2006 * <code>findLast()</code>. 2007 * The values specified must be configured as name-value pairs, as in an 2008 * associative array (or the actual object to search for). 2009 * The values of the names specified must match those properties specified in 2010 * the sort. for example 2011 * If properties "x", "y", and "z" are the in the current index, the values 2012 * specified should be {x:x-value, y:y-value,z:z-value}. 2013 * When all of the data is local this method will return <code>true</code> if 2014 * the item can be found and false otherwise. 2015 * If the data is not local and an asynchronous operation must be performed, 2016 * an <code>ItemPendingError</code> will be thrown. 2017 * 2018 * @see mx.collections.IViewCursor#findFirst 2019 * @see mx.collections.IViewCursor#findLast 2020 * @see mx.collections.errors.ItemPendingError 2021 * 2022 * @langversion 3.0 2023 * @playerversion Flash 9 2024 * @playerversion AIR 1.1 2025 * @productversion Flex 3 2026 */ 2027 public function findAny(values:Object):Boolean 2028 { 2029 checkValid(); 2030 var lcView:ListCollectionView = ListCollectionView(view); 2031 var index:int; 2032 try 2033 { 2034 index = lcView.findItem(values, Sort.ANY_INDEX_MODE); 2035 } 2036 catch(e:SortError) 2037 { 2038 //this is because the find critieria is not compatible with the 2039 //sort 2040 throw new CursorError(e.message); 2041 } 2042 if (index > -1) 2043 { 2044 currentIndex = index; 2045 setCurrent(lcView.getItemAt(currentIndex)); 2046 } 2047 return index > -1; 2048 } 2049 2050 /** 2051 * Finds the first item with the specified properties 2052 * within the collection and positions the cursor on that item. 2053 * If the item can not be found no change to the current location will be 2054 * made. 2055 * <code>findFirst()</code> can only be called on sorted views, if the view 2056 * isn't sorted, or items in the view do not contain properties used 2057 * to compute the sort order, a <code>CursorError</code> will be thrown. 2058 * <p> 2059 * If the associated collection is remote, and not all of the items have been 2060 * cached locally this method will begin an asynchronous fetch from the 2061 * remote collection, or if one is already in progress wait for it to 2062 * complete before making another fetch request. 2063 * If it is not important to find the first occurrence of an item in a 2064 * non-unique index use <code>findAny()</code> as it may be a little faster. 2065 * The values specified must be configured as name-value pairs, as in an 2066 * associative array (or the actual object to search for). 2067 * The values of the names specified must match those properties specified in 2068 * the sort. for example If properties "x", "y", and "z" are the in the current 2069 * index, the values specified should be {x:x-value, y:y-value,z:z-value}. 2070 * When all of the data is local this method will 2071 * return <code>true</code> if the item can be found and false otherwise. 2072 * If the data is not local and an asynchronous operation must be performed, 2073 * an <code>ItemPendingError</code> will be thrown. 2074 * 2075 * @see mx.collections.IViewCursor#findAny 2076 * @see mx.collections.IViewCursor#findLast 2077 * @see mx.collections.errors.ItemPendingError 2078 * 2079 * @langversion 3.0 2080 * @playerversion Flash 9 2081 * @playerversion AIR 1.1 2082 * @productversion Flex 3 2083 */ 2084 public function findFirst(values:Object):Boolean 2085 { 2086 checkValid(); 2087 var lcView:ListCollectionView = ListCollectionView(view); 2088 var index:int; 2089 try 2090 { 2091 index = lcView.findItem(values, Sort.FIRST_INDEX_MODE); 2092 } 2093 catch(sortError:SortError) 2094 { 2095 //this is because the find critieria is not compatible with the 2096 //sort 2097 throw new CursorError(sortError.message); 2098 } 2099 if (index > -1) 2100 { 2101 currentIndex = index; 2102 setCurrent(lcView.getItemAt(currentIndex)); 2103 } 2104 return index > -1; 2105 } 2106 2107 2108 /** 2109 * Finds the last item with the specified properties 2110 * within the collection and positions the cursor on that item. 2111 * If the item can not be found no change to the current location will be 2112 * made. 2113 * <code>findLast()</code> can only be called on sorted views, if the view 2114 * isn't sorted, or items in the view do not contain properties used 2115 * to compute the sort order, a <code>CursorError</code> will be thrown. 2116 * <p> 2117 * If the associated collection is remote, and not all of the items have been 2118 * cached locally this method will begin an asynchronous fetch from the 2119 * remote collection, or if one is already in progress wait for it to 2120 * complete before making another fetch request. 2121 * If it is not important to find the last occurrence of an item in a 2122 * non-unique index use <code>findAny()</code> as it may be a little faster. 2123 * The values specified must be configured as name-value pairs, as in an 2124 * associative array (or the actual object to search for). 2125 * The values of the names specified must match those properties specified in 2126 * the sort. for example If properties "x", "y", and "z" are the in the current 2127 * index, the values specified should be {x:x-value, y:y-value,z:z-value}. 2128 * When all of the data is local this method will 2129 * return <code>true</code> if the item can be found and false otherwise. 2130 * If the data is not local and an asynchronous operation must be performed, 2131 * an <code>ItemPendingError</code> will be thrown. 2132 * 2133 * @see mx.collections.IViewCursor#findAny 2134 * @see mx.collections.IViewCursor#findFirst 2135 * @see mx.collections.errors.ItemPendingError 2136 * 2137 * @langversion 3.0 2138 * @playerversion Flash 9 2139 * @playerversion AIR 1.1 2140 * @productversion Flex 3 2141 */ 2142 public function findLast(values:Object):Boolean 2143 { 2144 checkValid(); 2145 var lcView:ListCollectionView = ListCollectionView(view); 2146 var index:int; 2147 try 2148 { 2149 index = lcView.findItem(values, Sort.LAST_INDEX_MODE); 2150 } 2151 catch(sortError:SortError) 2152 { 2153 //this is because the find critieria is not compatible with the 2154 //sort 2155 throw new CursorError(sortError.message); 2156 } 2157 if (index > -1) 2158 { 2159 currentIndex = index; 2160 setCurrent(lcView.getItemAt(currentIndex)); 2161 } 2162 return index > -1; 2163 } 2164 2165 /** 2166 * Insert the specified item before the cursor's current position. 2167 * If the cursor is <code>afterLast</code> the insertion 2168 * will happen at the end of the View. If the cursor is 2169 * <code>beforeFirst</code> on a non-empty view an error will be thrown. 2170 * 2171 * @langversion 3.0 2172 * @playerversion Flash 9 2173 * @playerversion AIR 1.1 2174 * @productversion Flex 3 2175 */ 2176 public function insert(item:Object):void 2177 { 2178 var insertIndex:int; 2179 if (afterLast) 2180 { 2181 insertIndex = view.length; 2182 } 2183 else if (beforeFirst) 2184 { 2185 if (view.length > 0) 2186 { 2187 var message:String = resourceManager.getString( 2188 "collections", "invalidInsert"); 2189 throw new CursorError(message); 2190 } 2191 else 2192 { 2193 insertIndex = 0; 2194 } 2195 } 2196 else 2197 { 2198 insertIndex = currentIndex; 2199 } 2200 ListCollectionView(view).addItemAt(item, insertIndex); 2201 } 2202 2203 /** 2204 * Moves the cursor to the next item within the collection. On success 2205 * the <code>current</code> property will be updated to reference the object at this 2206 * new location. Returns true if current is valid, false if not (afterLast). 2207 * If the data is not local and an asynchronous operation must be performed, an 2208 * <code>ItemPendingError</code> will be thrown. See the ItemPendingError docs 2209 * as well as the collections documentation for more information on using the 2210 * ItemPendingError. 2211 * 2212 * @return true if still in the list, false if current is now afterLast 2213 * 2214 * @see mx.collections.IViewCursor#current 2215 * @see mx.collections.IViewCursor#movePrevious 2216 * @see mx.collections.errors.ItemPendingError 2217 * @see mx.collectoins.events.ItemAvailableEvent 2218 * @example 2219 * <pre> 2220 * var myArrayCollection:ICollectionView = new ArrayCollection(["Bobby", "Mark", "Trevor", "Jacey", "Tyler"]); 2221 * var cursor:IViewCursor = myArrayCollection.createCursor(); 2222 * while (!cursor.afterLast) 2223 * { 2224 * trace(cursor.current); 2225 * cursor.moveNext(); 2226 * } 2227 * </pre> 2228 * 2229 * @langversion 3.0 2230 * @playerversion Flash 9 2231 * @playerversion AIR 1.1 2232 * @productversion Flex 3 2233 */ 2234 public function moveNext():Boolean 2235 { 2236 //the afterLast getter checks validity and also checks length > 0 2237 if (afterLast) 2238 { 2239 return false; 2240 } 2241 // we can't set the index until we know that we can move there first. 2242 var tempIndex:int = beforeFirst ? 0 : currentIndex + 1; 2243 if (tempIndex >= view.length) 2244 { 2245 tempIndex = AFTER_LAST_INDEX; 2246 setCurrent(null); 2247 } 2248 else 2249 { 2250 setCurrent(ListCollectionView(view).getItemAt(tempIndex)); 2251 } 2252 currentIndex = tempIndex; 2253 return !afterLast; 2254 } 2255 2256 /** 2257 * Moves the cursor to the previous item within the collection. On success 2258 * the <code>current</code> property will be updated to reference the object at this 2259 * new location. Returns true if current is valid, false if not (beforeFirst). 2260 * If the data is not local and an asynchronous operation must be performed, an 2261 * <code>ItemPendingError</code> will be thrown. See the ItemPendingError docs 2262 * as well as the collections documentation for more information on using the 2263 * ItemPendingError. 2264 * 2265 * @return true if still in the list, false if current is now beforeFirst 2266 * 2267 * @see mx.collections.IViewCursor#current 2268 * @see mx.collections.IViewCursor#moveNext 2269 * @see mx.collections.errors.ItemPendingError 2270 * @see mx.collectoins.events.ItemAvailableEvent 2271 * @example 2272 * <pre> 2273 * var myArrayCollection:ICollectionView = new ArrayCollection(["Bobby", "Mark", "Trevor", "Jacey", "Tyler"]); 2274 * var cursor:ICursor = myArrayCollection.createCursor(); 2275 * cursor.seek(CursorBookmark.last); 2276 * while (!cursor.beforeFirst) 2277 * { 2278 * trace(current); 2279 * cursor.movePrevious(); 2280 * } 2281 * </pre> 2282 * 2283 * @langversion 3.0 2284 * @playerversion Flash 9 2285 * @playerversion AIR 1.1 2286 * @productversion Flex 3 2287 */ 2288 2289 public function movePrevious():Boolean 2290 { 2291 //the afterLast getter checks validity and also checks length > 0 2292 if (beforeFirst) 2293 { 2294 return false; 2295 } 2296 // we can't set the index until we know that we can move there first 2297 var tempIndex:int = afterLast ? view.length - 1 : currentIndex - 1; 2298 if (tempIndex == -1) 2299 { 2300 tempIndex = BEFORE_FIRST_INDEX; 2301 setCurrent(null); 2302 } 2303 else 2304 { 2305 setCurrent(ListCollectionView(view).getItemAt(tempIndex)); 2306 } 2307 currentIndex = tempIndex; 2308 return !beforeFirst; 2309 } 2310 2311 /** 2312 * Remove the current item and return it. If the cursor is 2313 * <code>beforeFirst</code> or <code>afterLast</code> throw a 2314 * CursorError. 2315 * 2316 * @langversion 3.0 2317 * @playerversion Flash 9 2318 * @playerversion AIR 1.1 2319 * @productversion Flex 3 2320 */ 2321 public function remove():Object 2322 { 2323 if (beforeFirst || afterLast) 2324 { 2325 var message:String = resourceManager.getString( 2326 "collections", "invalidRemove"); 2327 throw new CursorError(message); 2328 } 2329 var oldIndex:int = currentIndex; 2330 currentIndex++; 2331 if (currentIndex >= view.length) 2332 { 2333 currentIndex = AFTER_LAST_INDEX; 2334 setCurrent(null); 2335 } 2336 else 2337 { 2338 try 2339 { 2340 setCurrent(ListCollectionView(view).getItemAt(currentIndex)); 2341 } 2342 catch(e:ItemPendingError) 2343 { 2344 setCurrent(null, false); 2345 ListCollectionView(view).removeItemAt(oldIndex); 2346 throw e; 2347 } 2348 } 2349 var removed:Object = ListCollectionView(view).removeItemAt(oldIndex); 2350 return removed; 2351 } 2352 2353 /** 2354 * Moves the cursor to a location at an offset from the specified 2355 * bookmark. 2356 * The offset can be negative in which case the cursor is positioned an 2357 * offset number of items prior to the specified bookmark. 2358 * If the associated collection is remote, and not all of the items have been 2359 * cached locally this method will begin an asynchronous fetch from the 2360 * remote collection. 2361 * 2362 * If the data is not local and an asynchronous operation must be performed, an 2363 * <code>ItemPendingError</code> will be thrown. See the ItemPendingError docs 2364 * as well as the collections documentation for more information on using the 2365 * ItemPendingError. 2366 * 2367 * 2368 * @param bookmark <code>CursorBookmark</code> reference to marker information that 2369 * allows repositioning to a specific location. 2370 * In addition to supplying a value returned from the <code>bookmark</code> 2371 * property, there are three constant bookmark values that can be 2372 * specified: 2373 * <ul> 2374 * <li><code>CursorBookmark.FIRST</code> - seek from 2375 * the start (first element) of the collection</li> 2376 * <li><code>CursorBookmark.CURRENT</code> - seek from 2377 * the current position in the collection</li> 2378 * <li><code>CursorBookmark.LAST</code> - seek from the 2379 * end (last element) of the collection</li> 2380 * </ul> 2381 * @param offset indicates how far from the specified bookmark to seek. 2382 * If the specified number is negative the cursor will attempt to 2383 * move prior to the specified bookmark, if the offset specified is 2384 * beyond the end points of the collection the cursor will be 2385 * positioned off the end (beforeFirst or afterLast). 2386 * @param prefetch indicates the intent to iterate in a specific direction once the 2387 * seek operation completes, this reduces the number of required 2388 * network round trips during a seek. 2389 * If the iteration direction is known at the time of the request 2390 * the appropriate amount of data can be returned ahead of the 2391 * request to iterate it. 2392 * 2393 * @langversion 3.0 2394 * @playerversion Flash 9 2395 * @playerversion AIR 1.1 2396 * @productversion Flex 3 2397 */ 2398 public function seek(bookmark:CursorBookmark, offset:int = 0, prefetch:int = 0):void 2399 { 2400 checkValid(); 2401 if (view.length == 0) 2402 { 2403 currentIndex = AFTER_LAST_INDEX; 2404 setCurrent(null, false); 2405 return; 2406 } 2407 2408 var newIndex:int = currentIndex; 2409 if (bookmark == CursorBookmark.FIRST) 2410 { 2411 newIndex = 0; 2412 } 2413 else if (bookmark == CursorBookmark.LAST) 2414 { 2415 newIndex = view.length - 1; 2416 } 2417 else if (bookmark != CursorBookmark.CURRENT) 2418 { 2419 var message:String; 2420 try 2421 { 2422 newIndex = ListCollectionView(view).getBookmarkIndex(bookmark); 2423 if (newIndex < 0) 2424 { 2425 setCurrent(null); 2426 2427 message = resourceManager.getString( 2428 "collections", "bookmarkInvalid"); 2429 throw new CursorError(message); 2430 } 2431 } 2432 catch(bmError:CollectionViewError) 2433 { 2434 message = resourceManager.getString( 2435 "collections", "bookmarkInvalid"); 2436 throw new CursorError(message); 2437 } 2438 } 2439 2440 newIndex += offset; 2441 2442 var newCurrent:Object = null; 2443 if (newIndex >= view.length) 2444 { 2445 currentIndex = AFTER_LAST_INDEX; 2446 } 2447 else if (newIndex < 0) 2448 { 2449 currentIndex = BEFORE_FIRST_INDEX; 2450 } 2451 else 2452 { 2453 newCurrent = ListCollectionView(view).getItemAt(newIndex, prefetch); 2454 currentIndex = newIndex; 2455 } 2456 setCurrent(newCurrent); 2457 } 2458 2459 //-------------------------------------------------------------------------- 2460 // 2461 // Internal methods 2462 // 2463 //-------------------------------------------------------------------------- 2464 2465 private function checkValid():void 2466 { 2467 if (invalid) 2468 { 2469 var message:String = resourceManager.getString( 2470 "collections", "invalidCursor"); 2471 throw new CursorError(message); 2472 } 2473 } 2474 2475 private function collectionEventHandler(event:CollectionEvent):void 2476 { 2477 switch (event.kind) 2478 { 2479 case CollectionEventKind.ADD: 2480 if (event.location <= currentIndex) 2481 { 2482 currentIndex += event.items.length; 2483 } 2484 break; 2485 2486 case CollectionEventKind.REMOVE: 2487 if (event.location < currentIndex) 2488 { 2489 currentIndex -= event.items.length; 2490 } 2491 else if (event.location == currentIndex) 2492 { 2493 if (currentIndex < view.length) 2494 { 2495 try 2496 { 2497 setCurrent(ListCollectionView(view).getItemAt(currentIndex)); 2498 } 2499 catch(error:ItemPendingError) 2500 { 2501 setCurrent(null, false); 2502 } 2503 } 2504 else // currentIndex == view.length 2505 { 2506 //we were removed! is this an error? 2507 //should cursor move to now last item, view.length - 1?? 2508 currentIndex = AFTER_LAST_INDEX; 2509 setCurrent(null); //dispatch the updated at least 2510 } 2511 } 2512 break; 2513 2514 case CollectionEventKind.MOVE: 2515 if (event.oldLocation == currentIndex) 2516 { 2517 currentIndex = event.location; 2518 } 2519 else 2520 { 2521 if (event.oldLocation < currentIndex) 2522 { 2523 currentIndex -= event.items.length; 2524 } 2525 if (event.location <= currentIndex) 2526 { 2527 currentIndex += event.items.length; 2528 } 2529 } 2530 break; 2531 2532 case CollectionEventKind.REFRESH: 2533 if (!(beforeFirst || afterLast)) 2534 { 2535 try 2536 { 2537 currentIndex = ListCollectionView(view).getItemIndex(currentValue); 2538 } 2539 catch (e:SortError) 2540 { 2541 if (ListCollectionView(view).sort) 2542 { 2543 currentIndex = ListCollectionView(view).getLocalItemIndex(currentValue); 2544 } 2545 } 2546 if (currentIndex == -1) 2547 { 2548 setCurrent(null); 2549 } 2550 } 2551 break; 2552 2553 case CollectionEventKind.REPLACE: 2554 if (event.location == currentIndex) 2555 { 2556 try 2557 { 2558 setCurrent(ListCollectionView(view).getItemAt(currentIndex)); 2559 } 2560 catch(error:ItemPendingError) 2561 { 2562 setCurrent(null, false); 2563 } 2564 } 2565 break; 2566 2567 case CollectionEventKind.RESET: 2568 //just move to the beginning 2569 currentIndex = BEFORE_FIRST_INDEX; 2570 setCurrent(null); 2571 break; 2572 } 2573 } 2574 2575 /** 2576 * @private 2577 */ 2578 private function setCurrent(value:Object, dispatch:Boolean = true):void 2579 { 2580 currentValue = value; 2581 2582 if (dispatch) 2583 dispatchEvent(new FlexEvent(FlexEvent.CURSOR_UPDATE)); 2584 } 2585} 2586 2587/** 2588 * @private 2589 * Encapsulates the positional aspects of a cursor within an ListCollectionView. 2590 * Only the ListCollectionView should construct this. 2591 */ 2592class ListCollectionViewBookmark extends CursorBookmark 2593{ 2594 mx_internal var index:int; 2595 mx_internal var view:ListCollectionView; 2596 mx_internal var viewRevision:int; 2597 2598 /** 2599 * @private 2600 */ 2601 public function ListCollectionViewBookmark(value:Object, 2602 view:ListCollectionView, 2603 viewRevision:int, 2604 index:int) 2605 { 2606 super(value); 2607 this.view = view; 2608 this.viewRevision = viewRevision; 2609 this.index = index; 2610 } 2611 2612 /** 2613 * Get the approximate index of the item represented by this bookmark 2614 * in its view. If the item has been paged out this may throw an 2615 * ItemPendingError. If the item is not in the current view -1 will be 2616 * returned. This method may also return -1 if index-based location is not 2617 * possible. 2618 * 2619 * @langversion 3.0 2620 * @playerversion Flash 9 2621 * @playerversion AIR 1.1 2622 * @productversion Flex 3 2623 */ 2624 override public function getViewIndex():int 2625 { 2626 return view.getBookmarkIndex(this); 2627 } 2628} 2629