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