|
17 | 17 | using System.Threading.Tasks;
|
18 | 18 | using Windows.ApplicationModel.Core;
|
19 | 19 | using Windows.Storage;
|
| 20 | +using Windows.Storage.BulkAccess; |
20 | 21 | using Windows.Storage.FileProperties;
|
21 | 22 | using Windows.Storage.Search;
|
22 | 23 | using Windows.UI.Core;
|
|
27 | 28 | using Windows.UI.Xaml.Data;
|
28 | 29 | using Windows.UI.Xaml.Media.Animation;
|
29 | 30 | using Windows.UI.Xaml.Media.Imaging;
|
| 31 | +using FileAttributes = System.IO.FileAttributes; |
30 | 32 |
|
31 | 33 | namespace Files.Filesystem
|
32 | 34 | {
|
@@ -477,9 +479,297 @@ public static T GetCurrentSelectedTabInstance<T>()
|
477 | 479 | return default;
|
478 | 480 | }
|
479 | 481 |
|
| 482 | + public enum FINDEX_INFO_LEVELS |
| 483 | + { |
| 484 | + FindExInfoStandard = 0, |
| 485 | + FindExInfoBasic = 1 |
| 486 | + } |
| 487 | + |
| 488 | + public enum FINDEX_SEARCH_OPS |
| 489 | + { |
| 490 | + FindExSearchNameMatch = 0, |
| 491 | + FindExSearchLimitToDirectories = 1, |
| 492 | + FindExSearchLimitToDevices = 2 |
| 493 | + } |
| 494 | + |
| 495 | + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] |
| 496 | + public struct WIN32_FIND_DATA |
| 497 | + { |
| 498 | + public uint dwFileAttributes; |
| 499 | + public System.Runtime.InteropServices.ComTypes.FILETIME ftCreationTime; |
| 500 | + public System.Runtime.InteropServices.ComTypes.FILETIME ftLastAccessTime; |
| 501 | + public System.Runtime.InteropServices.ComTypes.FILETIME ftLastWriteTime; |
| 502 | + public uint nFileSizeHigh; |
| 503 | + public uint nFileSizeLow; |
| 504 | + public uint dwReserved0; |
| 505 | + public uint dwReserved1; |
| 506 | + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)] |
| 507 | + public string cFileName; |
| 508 | + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 14)] |
| 509 | + public string cAlternateFileName; |
| 510 | + } |
| 511 | + |
| 512 | + [DllImport("api-ms-win-core-file-fromapp-l1-1-0.dll", SetLastError = true, CharSet = CharSet.Unicode)] |
| 513 | + public static extern IntPtr FindFirstFileExFromApp( |
| 514 | + string lpFileName, |
| 515 | + FINDEX_INFO_LEVELS fInfoLevelId, |
| 516 | + out WIN32_FIND_DATA lpFindFileData, |
| 517 | + FINDEX_SEARCH_OPS fSearchOp, |
| 518 | + IntPtr lpSearchFilter, |
| 519 | + int dwAdditionalFlags); |
| 520 | + |
| 521 | + public const int FIND_FIRST_EX_CASE_SENSITIVE = 1; |
| 522 | + public const int FIND_FIRST_EX_LARGE_FETCH = 2; |
| 523 | + |
| 524 | + [DllImport("api-ms-win-core-file-l1-1-0.dll", CharSet = CharSet.Unicode)] |
| 525 | + static extern bool FindNextFile(IntPtr hFindFile, out WIN32_FIND_DATA lpFindFileData); |
| 526 | + |
| 527 | + [DllImport("api-ms-win-core-file-l1-1-0.dll")] |
| 528 | + static extern bool FindClose(IntPtr hFindFile); |
| 529 | + |
480 | 530 | bool isLoadingItems = false;
|
| 531 | + |
| 532 | + class PartialStorageItem |
| 533 | + { |
| 534 | + public string ItemName { get; set; } |
| 535 | + public string ContentType { get; set; } |
| 536 | + public StorageItemThumbnail Thumbnail { get; set; } |
| 537 | + public string RelativeId { get; set; } |
| 538 | + } |
| 539 | + |
| 540 | + public async void RapidAddItemsToCollectionAsync(string path) |
| 541 | + { |
| 542 | + App.OccupiedInstance.RibbonArea.Refresh.IsEnabled = false; |
| 543 | + |
| 544 | + Frame rootFrame = Window.Current.Content as Frame; |
| 545 | + var instanceTabsView = rootFrame.Content as InstanceTabsView; |
| 546 | + instanceTabsView.SetSelectedTabInfo(new DirectoryInfo(path).Name, path); |
| 547 | + CancelLoadAndClearFiles(); |
| 548 | + |
| 549 | + isLoadingItems = true; |
| 550 | + EmptyTextState.isVisible = Visibility.Collapsed; |
| 551 | + Universal.path = path; |
| 552 | + _filesAndFolders.Clear(); |
| 553 | + Stopwatch stopwatch = new Stopwatch(); |
| 554 | + stopwatch.Start(); |
| 555 | + LoadIndicator.isVisible = Visibility.Visible; |
| 556 | + |
| 557 | + switch (Universal.path) |
| 558 | + { |
| 559 | + case "Desktop": |
| 560 | + Universal.path = App.DesktopPath; |
| 561 | + break; |
| 562 | + case "Downloads": |
| 563 | + Universal.path = App.DownloadsPath; |
| 564 | + break; |
| 565 | + case "Documents": |
| 566 | + Universal.path = App.DocumentsPath; |
| 567 | + break; |
| 568 | + case "Pictures": |
| 569 | + Universal.path = App.PicturesPath; |
| 570 | + break; |
| 571 | + case "Music": |
| 572 | + Universal.path = App.MusicPath; |
| 573 | + break; |
| 574 | + case "Videos": |
| 575 | + Universal.path = App.VideosPath; |
| 576 | + break; |
| 577 | + case "OneDrive": |
| 578 | + Universal.path = App.OneDrivePath; |
| 579 | + break; |
| 580 | + } |
| 581 | + |
| 582 | + _rootFolder = await StorageFolder.GetFolderFromPathAsync(path); |
| 583 | + QueryOptions options = new QueryOptions() |
| 584 | + { |
| 585 | + IndexerOption = IndexerOption.OnlyUseIndexerAndOptimizeForIndexedProperties, |
| 586 | + FolderDepth = FolderDepth.Shallow |
| 587 | + }; |
| 588 | + var query = _rootFolder.CreateFileQueryWithOptions(options); |
| 589 | + options.SetPropertyPrefetch(PropertyPrefetchOptions.None, null); |
| 590 | + options.SetThumbnailPrefetch(ThumbnailMode.ListView, 40, ThumbnailOptions.ReturnOnlyIfCached); |
| 591 | + FileInformationFactory thumbnailFactory = new FileInformationFactory(query, ThumbnailMode.ListView, 40, ThumbnailOptions.ReturnOnlyIfCached, false); |
| 592 | + |
| 593 | + var singlePurposedFiles = await thumbnailFactory.GetFilesAsync(); |
| 594 | + ObservableCollection<PartialStorageItem> partialFiles = new System.Collections.ObjectModel.ObservableCollection<PartialStorageItem>(); |
| 595 | + foreach(FileInformation info in singlePurposedFiles) |
| 596 | + { |
| 597 | + partialFiles.Add(new PartialStorageItem() { RelativeId = info.FolderRelativeId, Thumbnail = info.Thumbnail, ItemName = info.Name, ContentType = info.DisplayType }); |
| 598 | + } |
| 599 | + |
| 600 | + var singlePurposedFolders = await thumbnailFactory.GetFoldersAsync(); |
| 601 | + ObservableCollection<PartialStorageItem> partialFolders = new System.Collections.ObjectModel.ObservableCollection<PartialStorageItem>(); |
| 602 | + foreach(FolderInformation info in singlePurposedFolders) |
| 603 | + { |
| 604 | + partialFolders.Add(new PartialStorageItem() { RelativeId = info.FolderRelativeId, ItemName = info.Name, ContentType = null, Thumbnail = null }); |
| 605 | + } |
| 606 | + |
| 607 | + WIN32_FIND_DATA findData; |
| 608 | + FINDEX_INFO_LEVELS findInfoLevel = FINDEX_INFO_LEVELS.FindExInfoStandard; |
| 609 | + int additionalFlags = 0; |
| 610 | + findInfoLevel = FINDEX_INFO_LEVELS.FindExInfoBasic; |
| 611 | + additionalFlags = FIND_FIRST_EX_LARGE_FETCH; |
| 612 | + |
| 613 | + IntPtr hFile = FindFirstFileExFromApp(path + "\\*.*", findInfoLevel, out findData, FINDEX_SEARCH_OPS.FindExSearchNameMatch, IntPtr.Zero, |
| 614 | + additionalFlags); |
| 615 | + var count = 0; |
| 616 | + if (hFile.ToInt64() != -1) |
| 617 | + { |
| 618 | + do |
| 619 | + { |
| 620 | + if (((FileAttributes)findData.dwFileAttributes & FileAttributes.Directory) != FileAttributes.Directory) |
| 621 | + { |
| 622 | + AddFile(findData, path, partialFiles.FirstOrDefault(x => x.ItemName == findData.cFileName)); |
| 623 | + ++count; |
| 624 | + } |
| 625 | + else if(((FileAttributes)findData.dwFileAttributes & FileAttributes.Directory) == FileAttributes.Directory) |
| 626 | + { |
| 627 | + AddFolder(findData, path, partialFolders.FirstOrDefault(x => x.ItemName == findData.cFileName)); |
| 628 | + ++count; |
| 629 | + } |
| 630 | + } while (FindNextFile(hFile, out findData)); |
| 631 | + |
| 632 | + FindClose(hFile); |
| 633 | + } |
| 634 | + |
| 635 | + |
| 636 | + if (FilesAndFolders.Count == 0) |
| 637 | + { |
| 638 | + if (_cancellationTokenSource.IsCancellationRequested) |
| 639 | + { |
| 640 | + _cancellationTokenSource = new CancellationTokenSource(); |
| 641 | + isLoadingItems = false; |
| 642 | + return; |
| 643 | + } |
| 644 | + EmptyTextState.isVisible = Visibility.Visible; |
| 645 | + } |
| 646 | + |
| 647 | + |
| 648 | + OrderFiles(); |
| 649 | + stopwatch.Stop(); |
| 650 | + Debug.WriteLine("Loading of items in " + Universal.path + " completed in " + stopwatch.ElapsedMilliseconds + " milliseconds.\n"); |
| 651 | + App.OccupiedInstance.RibbonArea.Refresh.IsEnabled = true; |
| 652 | + LoadIndicator.isVisible = Visibility.Collapsed; |
| 653 | + isLoadingItems = false; |
| 654 | + } |
| 655 | + |
| 656 | + private void AddFolder(WIN32_FIND_DATA findData, string pathRoot, PartialStorageItem partialStorageItem) |
| 657 | + { |
| 658 | + if ((App.OccupiedInstance.ItemDisplayFrame.SourcePageType == typeof(GenericFileBrowser)) || (App.OccupiedInstance.ItemDisplayFrame.SourcePageType == typeof(PhotoAlbum))) |
| 659 | + { |
| 660 | + if (_cancellationTokenSource.IsCancellationRequested) |
| 661 | + { |
| 662 | + isLoadingItems = false; |
| 663 | + return; |
| 664 | + } |
| 665 | + var itemDate = DateTime.FromFileTimeUtc((findData.ftLastWriteTime.dwHighDateTime << 32) + (long)(uint)findData.ftLastWriteTime.dwLowDateTime); |
| 666 | + var itemPath = Path.Combine(pathRoot, findData.cFileName); |
| 667 | + |
| 668 | + _filesAndFolders.Add(new ListedItem(partialStorageItem?.RelativeId) |
| 669 | + { |
| 670 | + //FolderTooltipText = tooltipString, |
| 671 | + FileName = findData.cFileName, |
| 672 | + FileDateReal = itemDate, |
| 673 | + FileType = "Folder", //TODO: Take a look at folder.DisplayType |
| 674 | + FolderImg = Visibility.Visible, |
| 675 | + FileImg = null, |
| 676 | + FileIconVis = Visibility.Collapsed, |
| 677 | + FilePath = itemPath, |
| 678 | + EmptyImgVis = Visibility.Collapsed, |
| 679 | + FileSize = null, |
| 680 | + FileSizeBytes = 0 |
| 681 | + }); |
| 682 | + |
| 683 | + EmptyTextState.isVisible = Visibility.Collapsed; |
| 684 | + } |
| 685 | + } |
| 686 | + |
| 687 | + private async void AddFile(WIN32_FIND_DATA findData, string pathRoot, PartialStorageItem partialStorageFile) |
| 688 | + { |
| 689 | + |
| 690 | + var itemName = findData.cFileName; |
| 691 | + var itemDate = DateTime.FromFileTimeUtc((findData.ftLastWriteTime.dwHighDateTime << 32) + (long) (uint) findData.ftLastWriteTime.dwLowDateTime); |
| 692 | + var itemPath = Path.Combine(pathRoot, findData.cFileName); |
| 693 | + var itemSize = ByteSize.FromBytes((findData.nFileSizeHigh << 32) + (long)(uint)findData.nFileSizeLow).ToString(); |
| 694 | + var itemSizeBytes = (findData.nFileSizeHigh << 32) + (ulong)(uint)findData.nFileSizeLow; |
| 695 | + string itemType = "File"; |
| 696 | + if(partialStorageFile != null) |
| 697 | + { |
| 698 | + itemType = partialStorageFile.ContentType; |
| 699 | + } |
| 700 | + else |
| 701 | + { |
| 702 | + if (findData.cFileName.Contains('.')) |
| 703 | + { |
| 704 | + itemType = findData.cFileName.Split('.')[1].ToUpper() + " File"; |
| 705 | + } |
| 706 | + } |
| 707 | + |
| 708 | + var itemFolderImgVis = Visibility.Collapsed; |
| 709 | + string itemFileExtension = null; |
| 710 | + if (findData.cFileName.Contains('.')) |
| 711 | + { |
| 712 | + itemFileExtension = findData.cFileName.Split('.')[1]; |
| 713 | + } |
| 714 | + |
| 715 | + BitmapImage icon = new BitmapImage(); |
| 716 | + Visibility itemThumbnailImgVis; |
| 717 | + Visibility itemEmptyImgVis; |
| 718 | + |
| 719 | + try |
| 720 | + { |
| 721 | + |
| 722 | + var itemThumbnailImg = partialStorageFile != null ? partialStorageFile.Thumbnail.CloneStream() : null; |
| 723 | + if (itemThumbnailImg != null) |
| 724 | + { |
| 725 | + itemEmptyImgVis = Visibility.Collapsed; |
| 726 | + itemThumbnailImgVis = Visibility.Visible; |
| 727 | + icon.DecodePixelWidth = 40; |
| 728 | + icon.DecodePixelHeight = 40; |
| 729 | + await icon.SetSourceAsync(itemThumbnailImg); |
| 730 | + } |
| 731 | + else |
| 732 | + { |
| 733 | + itemEmptyImgVis = Visibility.Visible; |
| 734 | + itemThumbnailImgVis = Visibility.Collapsed; |
| 735 | + } |
| 736 | + } |
| 737 | + catch |
| 738 | + { |
| 739 | + itemEmptyImgVis = Visibility.Visible; |
| 740 | + itemThumbnailImgVis = Visibility.Collapsed; |
| 741 | + // Catch here to avoid crash |
| 742 | + // TODO maybe some logging could be added in the future... |
| 743 | + } |
| 744 | + |
| 745 | + if (_cancellationTokenSource.IsCancellationRequested) |
| 746 | + { |
| 747 | + isLoadingItems = false; |
| 748 | + return; |
| 749 | + } |
| 750 | + _filesAndFolders.Add(new ListedItem(partialStorageFile?.RelativeId) |
| 751 | + { |
| 752 | + DotFileExtension = itemFileExtension, |
| 753 | + EmptyImgVis = itemEmptyImgVis, |
| 754 | + FileImg = icon, |
| 755 | + FileIconVis = itemThumbnailImgVis, |
| 756 | + FolderImg = itemFolderImgVis, |
| 757 | + FileName = itemName, |
| 758 | + FileDateReal = itemDate, |
| 759 | + FileType = itemType, |
| 760 | + FilePath = itemPath, |
| 761 | + FileSize = itemSize, |
| 762 | + FileSizeBytes = itemSizeBytes |
| 763 | + }); |
| 764 | + |
| 765 | + EmptyTextState.isVisible = Visibility.Collapsed; |
| 766 | + } |
| 767 | + |
481 | 768 | public async void AddItemsToCollectionAsync(string path)
|
482 | 769 | {
|
| 770 | + RapidAddItemsToCollectionAsync(path); |
| 771 | + return; |
| 772 | + |
483 | 773 | App.OccupiedInstance.RibbonArea.Refresh.IsEnabled = false;
|
484 | 774 |
|
485 | 775 | Frame rootFrame = Window.Current.Content as Frame;
|
|
0 commit comments