1//////////////////////////////////////////////////////////////////////////////// 2// 3// ADOBE SYSTEMS INCORPORATED 4// Copyright 2006-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.controls.fileSystemClasses 13{ 14 15import flash.events.Event; 16import flash.events.KeyboardEvent; 17import flash.filesystem.File; 18import flash.system.Capabilities; 19import flash.ui.Keyboard; 20import mx.collections.ArrayCollection; 21import mx.controls.FileSystemEnumerationMode; 22import mx.controls.dataGridClasses.DataGridColumn; 23import mx.core.mx_internal; 24import mx.events.FileEvent; 25import mx.events.FlexEvent; 26import mx.events.ListEvent; 27import mx.resources.IResourceManager; 28import mx.resources.ResourceManager; 29import mx.utils.DirectoryEnumeration; 30 31use namespace mx_internal; 32 33[ExcludeClass] 34 35/** 36 * @private 37 */ 38public class FileSystemControlHelper 39{ 40 include "../../core/Version.as"; 41 42 //-------------------------------------------------------------------------- 43 // 44 // Class initialization 45 // 46 //-------------------------------------------------------------------------- 47 48 /** 49 * @private 50 */ 51 public static var COMPUTER:File; 52 53 /** 54 * @private 55 */ 56 private static function initClass():void 57 { 58 if (Capabilities.os.substring(0, 3) == "Win") 59 COMPUTER = new File("root$:\\Computer"); 60 else // Mac or Unix 61 COMPUTER = new File("/Computer"); 62 } 63 64 initClass(); 65 66 //-------------------------------------------------------------------------- 67 // 68 // Class methods 69 // 70 //-------------------------------------------------------------------------- 71 72 /** 73 * @private 74 */ 75 private static function fileSystemIsCaseInsensitive():Boolean 76 { 77 var os:String = Capabilities.os.substring(0, 3); 78 return os == "Win" || os == "Mac"; 79 } 80 81 //-------------------------------------------------------------------------- 82 // 83 // Constructor 84 // 85 //-------------------------------------------------------------------------- 86 87 /** 88 * Constructor. 89 * 90 * @langversion 3.0 91 * @playerversion AIR 1.1 92 * @productversion Flex 3 93 */ 94 public function FileSystemControlHelper(owner:Object, hierarchical:Boolean) 95 { 96 super(); 97 98 this.owner = owner; 99 this.hierarchical = hierarchical; 100 101 owner.addEventListener(FlexEvent.UPDATE_COMPLETE, 102 updateCompleteHandler); 103 } 104 105 //-------------------------------------------------------------------------- 106 // 107 // Variables 108 // 109 //-------------------------------------------------------------------------- 110 111 /** 112 * @private 113 * A reference to the FileSystemList, FileSystemDataGrid, 114 * FileSystemTree, or FileSystemComboBox using this object. 115 */ 116 mx_internal var owner:Object; 117 118 /** 119 * @private 120 * A flag indicating whether the dataProvider of the owner 121 * is hierarchical or flat. 122 * In other words, this flag is true if the owner 123 * is a FileSystemTree and false otherwise. 124 */ 125 mx_internal var hierarchical:Boolean; 126 127 /** 128 * @private 129 */ 130 mx_internal var resourceManager:IResourceManager = 131 ResourceManager.getInstance(); 132 133 //-------------------------------------------------------------------------- 134 // 135 // Properties 136 // 137 //-------------------------------------------------------------------------- 138 139 //---------------------------------- 140 // backHistory 141 //---------------------------------- 142 143 /** 144 * @private 145 */ 146 public function get backHistory():Array 147 { 148 return historyIndex > 0 ? 149 history.slice(0, historyIndex).reverse() : 150 []; 151 } 152 153 //---------------------------------- 154 // canNavigateBack 155 //---------------------------------- 156 157 /** 158 * @private 159 */ 160 public function get canNavigateBack():Boolean 161 { 162 return historyIndex > 0; 163 } 164 165 //---------------------------------- 166 // canNavigateDown 167 //---------------------------------- 168 169 /** 170 * @private 171 */ 172 public function get canNavigateDown():Boolean 173 { 174 var selectedFile:File = File(owner.selectedItem); 175 return selectedFile && selectedFile.isDirectory; 176 } 177 178 //---------------------------------- 179 // canNavigateForward 180 //---------------------------------- 181 182 /** 183 * @private 184 */ 185 public function get canNavigateForward():Boolean 186 { 187 return historyIndex < history.length - 1; 188 } 189 190 //---------------------------------- 191 // canNavigateUp 192 //---------------------------------- 193 194 /** 195 * @private 196 */ 197 public function get canNavigateUp():Boolean 198 { 199 return !isComputer(directory); 200 } 201 202 //---------------------------------- 203 // directory 204 //---------------------------------- 205 206 /** 207 * @private 208 * Storage for the directory property. 209 */ 210 private var _directory:File; 211 212 /** 213 * @private 214 */ 215 private var directoryChanged:Boolean = false; 216 217 /** 218 * @private 219 */ 220 public function get directory():File 221 { 222 return _directory; 223 } 224 225 /** 226 * @private 227 */ 228 public function set directory(value:File):void 229 { 230 if (!value || 231 (!isComputer(value) && 232 (!value.exists || !value.isDirectory))) 233 { 234 throw(new Error("No such directory: " + value.nativePath)); 235 } 236 237 resetHistory(value); 238 239 setDirectory(value); 240 } 241 242 //---------------------------------- 243 // directoryEnumeration 244 //---------------------------------- 245 246 /** 247 * @private 248 */ 249 mx_internal var directoryEnumeration:DirectoryEnumeration = 250 new DirectoryEnumeration(); 251 252 //---------------------------------- 253 // enumerationMode 254 //---------------------------------- 255 256 /** 257 * @private 258 * Storage for the enumerationMode property. 259 */ 260 private var _enumerationMode:String = 261 FileSystemEnumerationMode.DIRECTORIES_FIRST; 262 263 /** 264 * @private 265 */ 266 private var enumerationModeChanged:Boolean = false; 267 268 /** 269 * @private 270 */ 271 public function get enumerationMode():String 272 { 273 return _enumerationMode; 274 } 275 276 /** 277 * @private 278 */ 279 public function set enumerationMode(value:String):void 280 { 281 _enumerationMode = value; 282 enumerationModeChanged = true; 283 284 owner.invalidateProperties(); 285 } 286 287 //---------------------------------- 288 // extensions 289 //---------------------------------- 290 291 /** 292 * @private 293 * Storage for the extensions property. 294 */ 295 private var _extensions:Array /* of String */; 296 297 /** 298 * @private 299 */ 300 private var extensionsChanged:Boolean = false; 301 302 /** 303 * @private 304 */ 305 public function get extensions():Array /* of String */ 306 { 307 return _extensions; 308 } 309 310 /** 311 * @private 312 */ 313 public function set extensions(value:Array /* of String */):void 314 { 315 _extensions = value; 316 extensionsChanged = true; 317 318 owner.invalidateProperties(); 319 } 320 321 //---------------------------------- 322 // filterFunction 323 //---------------------------------- 324 325 /** 326 * @private 327 * Storage for the filterFunction property. 328 */ 329 private var _filterFunction:Function; 330 331 /** 332 * @private 333 */ 334 private var filterFunctionChanged:Boolean = false; 335 336 /** 337 * @private 338 */ 339 public function get filterFunction():Function 340 { 341 return _filterFunction; 342 } 343 344 /** 345 * @private 346 */ 347 public function set filterFunction(value:Function):void 348 { 349 _filterFunction = value; 350 filterFunctionChanged = true; 351 352 owner.invalidateProperties(); 353 } 354 355 //---------------------------------- 356 // forwardHistory 357 //---------------------------------- 358 359 [Bindable("historyChanged")] 360 361 /** 362 * @private 363 */ 364 public function get forwardHistory():Array 365 { 366 return historyIndex < history.length - 1 ? 367 history.slice(historyIndex + 1) : 368 []; 369 } 370 371 //---------------------------------- 372 // history 373 //---------------------------------- 374 375 /** 376 * @private 377 */ 378 public var history:Array; 379 380 //---------------------------------- 381 // historyIndex 382 //---------------------------------- 383 384 /** 385 * @private 386 */ 387 public var historyIndex:int; 388 389 //---------------------------------- 390 // nativePathToIndexMap 391 //---------------------------------- 392 393 /** 394 * @private 395 * Storage for the nativePathToIndexMap property. 396 */ 397 private var _nativePathToIndexMap:Object; 398 399 /** 400 * @private 401 * Maps nativePath (String) -> index (int). 402 * This map is used to implement findIndex() as a simple lookup, 403 * so that multiple finds are fast. 404 * It is freed whenever an operation changes which items 405 * are displayed in the control, or their order, 406 * and rebuilt tne next time it or <code>items</code> is accessed. 407 */ 408 mx_internal function get nativePathToIndexMap():Object 409 { 410 if (!_nativePathToIndexMap) 411 rebuildEnumerationInfo(); 412 413 return _nativePathToIndexMap; 414 } 415 416 //---------------------------------- 417 // itemArray 418 //---------------------------------- 419 420 /** 421 * @private 422 * Storage for the itemArray property. 423 */ 424 private var _itemArray:Array /* of File */; 425 426 /** 427 * @private 428 * An array of all the File items displayed in the control, 429 * in the order in which they appear. 430 * This array is used together with <code>nativePathToIndexMap</code> 431 * to implement findItem() as a simple lookup, 432 * so that multiple finds are fast. 433 * It is freed whenever an operation changes which items 434 * are displayed in the control, or their order, 435 * and rebuilt tne next time it 436 * or <code>nativePathToIndexMap</code> is accessed. 437 */ 438 mx_internal function get itemArray():Array /* of File */ 439 { 440 if (!_itemArray) 441 rebuildEnumerationInfo(); 442 443 return _itemArray; 444 } 445 446 //---------------------------------- 447 // nameCompareFunction 448 //---------------------------------- 449 450 /** 451 * @private 452 * Storage for the nameCompareFunction property. 453 */ 454 private var _nameCompareFunction:Function; 455 456 /** 457 * @private 458 */ 459 private var nameCompareFunctionChanged:Boolean = false; 460 461 /** 462 * @private 463 */ 464 public function get nameCompareFunction():Function 465 { 466 return _nameCompareFunction; 467 } 468 469 /** 470 * @private 471 */ 472 public function set nameCompareFunction(value:Function):void 473 { 474 _nameCompareFunction = value; 475 nameCompareFunctionChanged = true; 476 477 owner.invalidateProperties(); 478 } 479 480 //---------------------------------- 481 // openPaths 482 //---------------------------------- 483 484 /** 485 * @private 486 */ 487 private var pendingOpenPaths:Array /* of String */; 488 489 /** 490 * An Array of <code>nativePath</code> Strings for the File items 491 * representing the open subdirectories. 492 * This Array is empty if no subdirectories are open. 493 * 494 * @default [] 495 * 496 * @langversion 3.0 497 * @playerversion AIR 1.1 498 * @productversion Flex 3 499 */ 500 public function get openPaths():Array /* of String */ 501 { 502 return pendingOpenPaths ? 503 pendingOpenPaths : 504 getOpenPaths(); 505 } 506 507 /** 508 * @private 509 */ 510 public function set openPaths(value:Array /* of String */):void 511 { 512 pendingOpenPaths = value; 513 514 owner.invalidateProperties(); 515 } 516 517 //---------------------------------- 518 // selectedPath 519 //---------------------------------- 520 521 /** 522 * @private 523 */ 524 public function get selectedPath():String 525 { 526 return selectedPaths[0]; 527 } 528 529 /** 530 * @private 531 */ 532 public function set selectedPath(value:String):void 533 { 534 selectedPaths = [ value ]; 535 } 536 537 //---------------------------------- 538 // selectedPaths 539 //---------------------------------- 540 541 /** 542 * @private 543 */ 544 private var pendingSelectedPaths:Array /* of String */; 545 546 /** 547 * @private 548 */ 549 public function get selectedPaths():Array /* of String */ 550 { 551 return pendingSelectedPaths ? 552 pendingSelectedPaths : 553 getSelectedPaths(); 554 } 555 556 /** 557 * @private 558 */ 559 public function set selectedPaths(value:Array /* of String */):void 560 { 561 pendingSelectedPaths = value; 562 563 owner.invalidateProperties(); 564 } 565 566 //---------------------------------- 567 // showExtensions 568 //---------------------------------- 569 570 /** 571 * @private 572 * Storage for the showExtensions property. 573 */ 574 private var _showExtensions:Boolean = true; 575 576 /** 577 * @private 578 */ 579 public function get showExtensions():Boolean 580 { 581 return _showExtensions; 582 } 583 584 /** 585 * @private 586 */ 587 public function set showExtensions(value:Boolean):void 588 { 589 _showExtensions = value; 590 591 owner.invalidateList(); 592 } 593 594 //---------------------------------- 595 // showHidden 596 //---------------------------------- 597 598 /** 599 * @private 600 * Storage for the showHidden property. 601 */ 602 private var _showHidden:Boolean = false; 603 604 /** 605 * @private 606 */ 607 private var showHiddenChanged:Boolean = false; 608 609 /** 610 * @private 611 */ 612 public function get showHidden():Boolean 613 { 614 return _showHidden; 615 } 616 617 /** 618 * @private 619 */ 620 public function set showHidden(value:Boolean):void 621 { 622 _showHidden = value; 623 showHiddenChanged = true; 624 625 owner.invalidateProperties(); 626 } 627 628 //---------------------------------- 629 // showIcons 630 //---------------------------------- 631 632 /** 633 * @private 634 * Storage for the showIcons property. 635 */ 636 private var _showIcons:Boolean = true; 637 638 /** 639 * @private 640 */ 641 public function get showIcons():Boolean 642 { 643 return _showIcons; 644 } 645 646 /** 647 * @private 648 */ 649 public function set showIcons(value:Boolean):void 650 { 651 _showIcons = value; 652 653 owner.invalidateList(); 654 } 655 656 //-------------------------------------------------------------------------- 657 // 658 // Methods 659 // 660 //-------------------------------------------------------------------------- 661 662 /** 663 * @private 664 */ 665 public function commitProperties():void 666 { 667 if (enumerationModeChanged || 668 extensionsChanged || 669 filterFunctionChanged || 670 nameCompareFunctionChanged || 671 showHiddenChanged) 672 { 673 directoryEnumeration.enumerationMode = enumerationMode; 674 directoryEnumeration.extensions = extensions; 675 directoryEnumeration.filterFunction = filterFunction; 676 directoryEnumeration.nameCompareFunction = nameCompareFunction; 677 directoryEnumeration.showHidden = showHidden; 678 directoryEnumeration.refresh(); 679 680 // For a List or DataGrid, refreshing its collection 681 // (which is what directoryEnumeration.refresh() does) 682 // is enough to make the control update properly 683 // with the newly filtered/sorted collection. 684 // But a Tree doesn't properly handle having its 685 // collection refreshed; for example, if the new 686 // filter reduces the number of items, the Tree 687 // can display blank renderers. 688 // So instead we simply reset the dataProvider. 689 owner.dataProvider = directoryEnumeration.collection; 690 691 itemsChanged(); 692 693 extensionsChanged = false; 694 enumerationModeChanged = false; 695 filterFunctionChanged = false; 696 nameCompareFunctionChanged = false; 697 showHiddenChanged = false; 698 } 699 700 if (directoryChanged) 701 { 702 fill(); 703 704 var event:FileEvent = new FileEvent(FileEvent.DIRECTORY_CHANGE); 705 event.file = directory; 706 owner.dispatchEvent(event); 707 708 directoryChanged = false; 709 } 710 } 711 712 /** 713 * Fills the list by enumerating the current directory 714 * and setting the dataProvider. 715 * 716 * @langversion 3.0 717 * @playerversion AIR 1.1 718 * @productversion Flex 3 719 */ 720 mx_internal function fill():void 721 { 722 setDataProvider(isComputer(directory) ? 723 getRootDirectories() : 724 directory.getDirectoryListing()); 725 } 726 727 /** 728 * @private 729 */ 730 public function styleChanged(styleProp:String):void 731 { 732 if (styleProp == "fileIcon" || styleProp == "directoryIcon") 733 owner.invalidateList(); 734 } 735 736 /** 737 * @private 738 */ 739 mx_internal function setDirectory(value:File):void 740 { 741 _directory = value; 742 directoryChanged = true; 743 744 // Clear the now-stale contents of the list. 745 // The list will repopulate after the new directory 746 // is enumerated. 747 owner.dataProvider = null; 748 749 if (hierarchical) 750 owner.dataDescriptor.reset(); 751 752 owner.invalidateProperties(); 753 754 // Trigger databindings. 755 owner.dispatchEvent(new Event("directoryChanged")); 756 } 757 758 /** 759 * @private 760 */ 761 mx_internal function setDataProvider(value:Array):void 762 { 763 directoryEnumeration.enumerationMode = enumerationMode; 764 directoryEnumeration.extensions = extensions; 765 directoryEnumeration.filterFunction = filterFunction; 766 directoryEnumeration.nameCompareFunction = nameCompareFunction; 767 directoryEnumeration.showHidden = showHidden; 768 769 directoryEnumeration.source = value; 770 771 owner.dataProvider = directoryEnumeration.collection; 772 773 itemsChanged(); 774 } 775 776 /** 777 * @private 778 */ 779 public function itemToUID(data:Object):String 780 { 781 return data ? File(data).nativePath : "null"; 782 } 783 784 /** 785 * @private 786 */ 787 public function isComputer(f:File):Boolean 788 { 789 if (Capabilities.os.substr(0, 3) =="Win") 790 return f.nativePath.substring(0, 6) == "root$:"; 791 return f.nativePath == "/Computer"; 792 } 793 794 /** 795 * @private 796 */ 797 private function getRootDirectories():Array 798 { 799 var a:Array = []; 800 801 for each (var f:File in File.getRootDirectories()) 802 { 803 if (f.isDirectory) 804 a.push(f); 805 } 806 807 return a; 808 } 809 810 /** 811 * @private 812 */ 813 public function fileIconFunction(item:File):Class 814 { 815 if (!showIcons) 816 return null; 817 818 return owner.getStyle(item.isDirectory ? "directoryIcon" : "fileIcon"); 819 } 820 821 /** 822 * @private 823 */ 824 public function fileLabelFunction(item:File, 825 column:DataGridColumn = null):String 826 { 827 if (isComputer(item)) 828 { 829 return resourceManager.getString( 830 "aircontrols", "computer"); 831 } 832 833 var label:String = item.name; 834 835 // The name of the / directory on Mac is the empty string. 836 // In this case, display the nativePath, which will be "/". 837 if (label == "") 838 label = item.nativePath; 839 840 if (!item.isDirectory && !showExtensions) 841 { 842 var index:int = label.lastIndexOf("."); 843 if (index != -1) 844 label = label.substring(0, index); 845 } 846 847 return label; 848 } 849 850 /** 851 * @private 852 */ 853 public function findIndex(nativePath:String):int 854 { 855 if (!nativePath) 856 return -1; 857 858 if (fileSystemIsCaseInsensitive()) 859 nativePath = nativePath.toLowerCase(); 860 861 var value:* = nativePathToIndexMap[nativePath]; 862 return value === undefined ? -1 : int(value); 863 } 864 865 /** 866 * @private 867 */ 868 public function findItem(nativePath:String):File 869 { 870 var index:int = findIndex(nativePath); 871 if (index == -1) 872 return null; 873 874 return itemArray[index]; 875 } 876 877 /** 878 * @private 879 * This method is called whenever something happens 880 * that affects which items are displayed by the 881 * control, or the order in which they are displayed. 882 */ 883 mx_internal function itemsChanged():void 884 { 885 // These two data structures are now invalid, so free them. 886 // They will be rebuilt the next time they are needed. 887 _itemArray = null; 888 _nativePathToIndexMap = null; 889 } 890 891 /** 892 * @private 893 */ 894 private function rebuildEnumerationInfo():void 895 { 896 _itemArray = []; 897 _nativePathToIndexMap = {}; 898 899 enumerateItems(addItemToEnumerationInfo); 900 } 901 902 /** 903 * @private 904 */ 905 private function addItemToEnumerationInfo(index:int, item:File):void 906 { 907 var nativePath:String = item.nativePath; 908 909 if (fileSystemIsCaseInsensitive()) 910 nativePath = nativePath.toLowerCase(); 911 912 _itemArray.push(item); 913 _nativePathToIndexMap[nativePath] = index; 914 } 915 916 /** 917 * @private 918 */ 919 private function enumerateItems(itemCallback:Function):int 920 { 921 return enumerate(ArrayCollection(owner.dataProvider), 922 0, itemCallback); 923 } 924 925 /** 926 * @private 927 */ 928 private function enumerate(items:ArrayCollection, index:int, 929 itemCallback:Function):int 930 { 931 var n:int = items.length; 932 for (var i:int = 0; i < n; i++) 933 { 934 var item:File = File(items.getItemAt(i)); 935 itemCallback(index, item); 936 index++; 937 938 if (hierarchical && item.isDirectory && owner.isItemOpen(item)) 939 { 940 var childItems:ArrayCollection = 941 owner.dataDescriptor.getChildren(item); 942 943 index = enumerate(childItems, index, itemCallback); 944 } 945 } 946 return index; 947 } 948 949 /** 950 * @private 951 */ 952 public function navigateDown():void 953 { 954 if (canNavigateDown) 955 navigateTo(File(owner.selectedItem)); 956 } 957 958 /** 959 * @private 960 */ 961 public function navigateUp():void 962 { 963 if (canNavigateUp) 964 navigateTo(directory.parent ? directory.parent : COMPUTER); 965 } 966 967 /** 968 * @private 969 */ 970 public function navigateBack(index:int = 0):void 971 { 972 if (canNavigateBack) 973 navigateBy(-1 - index); 974 } 975 976 /** 977 * @private 978 */ 979 public function navigateForward(index:int = 0):void 980 { 981 if (canNavigateForward) 982 navigateBy(1 + index) 983 } 984 985 /** 986 * @private 987 */ 988 private function navigateBy(n:int):void 989 { 990 historyIndex += n; 991 992 if (historyIndex < 0) 993 historyIndex = 0; 994 else if (historyIndex > history.length - 1) 995 historyIndex = history.length - 1; 996 997 setDirectory(history[historyIndex]); 998 999 owner.dispatchEvent(new Event("historyChanged")); 1000 } 1001 1002 /** 1003 * @private 1004 */ 1005 public function navigateTo(directory:File):void 1006 { 1007 setDirectory(directory); 1008 1009 pushHistory(directory); 1010 } 1011 1012 /** 1013 * @private 1014 */ 1015 public function refresh():void 1016 { 1017 var openPaths:Array /* of String */ 1018 var selectedPaths:Array /* of String */; 1019 var firstVisiblePath:String; 1020 var oldHorizontalScrollPosition:int; 1021 1022 if (hierarchical) 1023 openPaths = getOpenPaths(); 1024 selectedPaths = getSelectedPaths(); 1025 firstVisiblePath = getFirstVisiblePath(); 1026 oldHorizontalScrollPosition = owner.horizontalScrollPosition; 1027 1028 fill(); 1029 1030 // Tree must be revalidated after its dataProvider 1031 // changes for expandItem() to work. 1032 if (hierarchical) 1033 owner.validateNow(); 1034 1035 if (hierarchical) 1036 setOpenPaths(openPaths); 1037 setSelectedPaths(selectedPaths); 1038 if (setFirstVisiblePath(firstVisiblePath)) 1039 owner.horizontalScrollPosition = oldHorizontalScrollPosition; 1040 } 1041 1042 /** 1043 * @private 1044 */ 1045 private function getOpenPaths():Array /* of String */ 1046 { 1047 var openPaths:Array /* of String */ = []; 1048 var n:int = owner.openItems.length; 1049 for (var i:int = 0; i < n; i++) 1050 { 1051 openPaths.push(File(owner.openItems[i]).nativePath); 1052 } 1053 return openPaths; 1054 } 1055 1056 /** 1057 * @private 1058 * Returns an Array of nativePath Strings for the selected items. 1059 * This method is called by refresh() before repopulating the control. 1060 */ 1061 private function getSelectedPaths():Array /* of String */ 1062 { 1063 var selectedPaths:Array /* of String */ = []; 1064 var n:int = owner.selectedItems.length; 1065 for (var i:int = 0; i < n; i++) 1066 { 1067 selectedPaths.push(File(owner.selectedItems[i]).nativePath); 1068 } 1069 return selectedPaths; 1070 } 1071 1072 /** 1073 * @private 1074 * Returns the nativePath of the first visible item. 1075 * This method is called by refresh() before repopulating the control. 1076 */ 1077 private function getFirstVisiblePath():String 1078 { 1079 if (owner.dataProvider == null || owner.dataProvider.length == 0) 1080 return null; 1081 1082 var index:int = owner.verticalScrollPosition; 1083 var item:File = itemArray[index]; 1084 return item ? item.nativePath : null; 1085 } 1086 1087 /** 1088 * @private 1089 */ 1090 private function setOpenPaths(openPaths:Array /* of String */):void 1091 { 1092 var n:int = openPaths.length; 1093 for (var i:int = 0; i < n; i++) 1094 { 1095 owner.openSubdirectory(openPaths[i]); 1096 } 1097 } 1098 1099 /** 1100 * @private 1101 * Selects items whose nativePaths are in the specified Array. 1102 * This method is called by refresh() after repopulating the control. 1103 */ 1104 private function setSelectedPaths(selectedPaths:Array /* of String */):void 1105 { 1106 var indices:Array /* of int */ = []; 1107 1108 var n:int = selectedPaths.length; 1109 for (var i:int = 0; i < n; i++) 1110 { 1111 var path:String = selectedPaths[i]; 1112 var index:int = findIndex(path); 1113 if (index != -1) 1114 indices.push(index); 1115 } 1116 1117 owner.selectedIndices = indices; 1118 } 1119 1120 /** 1121 * @private 1122 * Scrolls the list to the item with the specified nativePath. 1123 * This method is by refresh() after repopulating the control. 1124 */ 1125 private function setFirstVisiblePath(path:String):Boolean 1126 { 1127 if (path == null) 1128 return false; 1129 1130 var index:int = findIndex(path); 1131 if (index == -1) 1132 return false; 1133 1134 owner.verticalScrollPosition = index; 1135 return true; 1136 } 1137 1138 /** 1139 * @private 1140 */ 1141 public function clear():void 1142 { 1143 owner.dataProvider = null; 1144 1145 itemsChanged(); 1146 } 1147 1148 /** 1149 * @private 1150 */ 1151 public function resetHistory(directory:File):void 1152 { 1153 history = [ directory ]; 1154 historyIndex = 0; 1155 1156 owner.dispatchEvent(new Event("historyChanged")); 1157 } 1158 1159 /** 1160 * @private 1161 */ 1162 private function pushHistory(directory:File):void 1163 { 1164 historyIndex++; 1165 history.splice(historyIndex); 1166 history.push(directory); 1167 1168 owner.dispatchEvent(new Event("historyChanged")); 1169 } 1170 1171 /** 1172 * @private 1173 * Returns an Array of File objects 1174 * representing the path to the specified directory. 1175 * The first File represents a root directory. 1176 * The last File represents the specified file's parent directory. 1177 */ 1178 public function getParentChain(file:File):Array 1179 { 1180 if (!file) 1181 return []; 1182 1183 var a:Array = []; 1184 1185 for (var f:File = file; f != null; f = f.parent) 1186 { 1187 a.unshift(f); 1188 } 1189 1190 return a; 1191 } 1192 1193 /** 1194 * @private 1195 * Dispatches a cancelable "directoryChanging" event 1196 * and returns true if it wasn't canceled. 1197 */ 1198 mx_internal function dispatchDirectoryChangingEvent(newDirectory:File):Boolean 1199 { 1200 var event:FileEvent = 1201 new FileEvent(FileEvent.DIRECTORY_CHANGING, false, true); 1202 event.file = newDirectory; 1203 owner.dispatchEvent(event); 1204 1205 return !event.isDefaultPrevented(); 1206 } 1207 1208 /** 1209 * @private 1210 * Dispatches a "fileChoose" event. 1211 */ 1212 mx_internal function dispatchFileChooseEvent(file:File):void 1213 { 1214 var event:FileEvent = new FileEvent(FileEvent.FILE_CHOOSE); 1215 event.file = file; 1216 owner.dispatchEvent(event); 1217 } 1218 1219 /** 1220 * @private 1221 */ 1222 private function getBackDirectory():File 1223 { 1224 return historyIndex == 0 ? 1225 null : 1226 history[historyIndex - 1]; 1227 } 1228 1229 /** 1230 * @private 1231 */ 1232 private function getForwardDirectory():File 1233 { 1234 return historyIndex == history.length - 1 ? 1235 null : 1236 history[historyIndex + 1]; 1237 } 1238 1239 //-------------------------------------------------------------------------- 1240 // 1241 // Event handlers 1242 // 1243 //-------------------------------------------------------------------------- 1244 1245 /** 1246 * @private 1247 */ 1248 private function updateCompleteHandler(event:FlexEvent):void 1249 { 1250 if (pendingOpenPaths != null) 1251 { 1252 setOpenPaths(pendingOpenPaths); 1253 1254 pendingOpenPaths = null; 1255 } 1256 1257 if (pendingSelectedPaths != null) 1258 { 1259 setSelectedPaths(pendingSelectedPaths); 1260 1261 pendingSelectedPaths = null; 1262 } 1263 } 1264 1265 /** 1266 * @private 1267 */ 1268 public function itemDoubleClickHandler(event:ListEvent):void 1269 { 1270 var selectedFile:File = File(owner.selectedItem); 1271 1272 if (selectedFile.isDirectory) 1273 { 1274 if (dispatchDirectoryChangingEvent(selectedFile)) 1275 navigateDown(); 1276 } 1277 else 1278 { 1279 dispatchFileChooseEvent(selectedFile); 1280 } 1281 } 1282 1283 /** 1284 * @private 1285 */ 1286 public function handleKeyDown(event:KeyboardEvent):Boolean 1287 { 1288 switch (event.keyCode) 1289 { 1290 case Keyboard.ENTER: 1291 { 1292 var selectedFile:File = File(owner.selectedItem); 1293 1294 if (canNavigateDown && 1295 dispatchDirectoryChangingEvent(selectedFile)) 1296 { 1297 navigateDown(); 1298 } 1299 else 1300 { 1301 dispatchFileChooseEvent(selectedFile); 1302 } 1303 return true; 1304 } 1305 1306 case Keyboard.BACKSPACE: 1307 { 1308 if (canNavigateUp && 1309 dispatchDirectoryChangingEvent(directory.parent)) 1310 { 1311 navigateUp(); 1312 } 1313 return true; 1314 } 1315 } 1316 1317 return false; 1318 } 1319} 1320 1321} 1322