From 059df4194f8b0de5d9783b88b3fbb13962138f20 Mon Sep 17 00:00:00 2001 From: Marco Gavelli Date: Sat, 19 Jun 2021 15:45:48 +0200 Subject: [PATCH 1/9] Fix folder opened despite dragleave Fix open exe with args --- Files/BaseLayout.cs | 18 ++++-------------- Files/Filesystem/ListedItem.cs | 4 ++-- Files/Views/LayoutModes/ColumnViewBase.xaml.cs | 2 +- .../LayoutModes/ColumnViewBrowser.xaml.cs | 2 +- .../LayoutModes/DetailsLayoutBrowser.xaml.cs | 2 +- .../Views/LayoutModes/GridViewBrowser.xaml.cs | 2 +- 6 files changed, 10 insertions(+), 20 deletions(-) diff --git a/Files/BaseLayout.cs b/Files/BaseLayout.cs index 811279ef8f2c..d985f2e9de6d 100644 --- a/Files/BaseLayout.cs +++ b/Files/BaseLayout.cs @@ -620,16 +620,6 @@ private void Item_DragLeave(object sender, DragEventArgs e) protected async void Item_DragOver(object sender, DragEventArgs e) { ListedItem item = GetItemFromElement(sender); - - if (item is null && sender is GridViewItem gvi) - { - item = gvi.Content as ListedItem; - } - else if (item is null && sender is ListViewItem lvi) - { - item = lvi.Content as ListedItem; - } - if (item is null) { return; @@ -709,10 +699,10 @@ protected async void Item_Drop(object sender, DragEventArgs e) e.Handled = true; dragOverItem = null; // Reset dragged over item - ListedItem rowItem = GetItemFromElement(sender); - if (rowItem != null) + ListedItem item = GetItemFromElement(sender); + if (item != null) { - await ParentShellPageInstance.FilesystemHelpers.PerformOperationTypeAsync(e.AcceptedOperation, e.DataView, (rowItem as ShortcutItem)?.TargetPath ?? rowItem.ItemPath, false, true, rowItem.IsExecutable); + await ParentShellPageInstance.FilesystemHelpers.PerformOperationTypeAsync(e.AcceptedOperation, e.DataView, (item as ShortcutItem)?.TargetPath ?? item.ItemPath, false, true, item.IsExecutable); } deferral.Complete(); } @@ -727,7 +717,7 @@ protected void InitializeDrag(UIElement element) element.DragOver -= Item_DragOver; element.DragLeave -= Item_DragLeave; element.Drop -= Item_Drop; - if (item.PrimaryItemAttribute == StorageItemTypes.Folder || (item.IsShortcutItem && (Path.GetExtension((item as ShortcutItem).TargetPath)?.Contains("exe") ?? false))) + if (item.PrimaryItemAttribute == StorageItemTypes.Folder || item.IsExecutable) { element.AllowDrop = true; element.DragOver += Item_DragOver; diff --git a/Files/Filesystem/ListedItem.cs b/Files/Filesystem/ListedItem.cs index 6ae246d7a963..917ae6a915fc 100644 --- a/Files/Filesystem/ListedItem.cs +++ b/Files/Filesystem/ListedItem.cs @@ -314,7 +314,7 @@ public override string ToString() public bool IsLibraryItem => this is LibraryItem; public bool IsLinkItem => IsShortcutItem && ((ShortcutItem)this).IsUrl; - public virtual bool IsExecutable => Path.GetExtension(ItemPath)?.Contains("exe") ?? false; + public virtual bool IsExecutable => Path.GetExtension(ItemPath)?.ToLower() == ".exe"; public bool IsPinned => App.SidebarPinnedController.Model.FavoriteItems.Contains(itemPath); private StorageFile itemFile; @@ -382,7 +382,7 @@ public ShortcutItem() : base() public string WorkingDirectory { get; set; } public bool RunAsAdmin { get; set; } public bool IsUrl { get; set; } - public override bool IsExecutable => Path.GetExtension(TargetPath)?.Contains("exe") ?? false; + public override bool IsExecutable => Path.GetExtension(TargetPath)?.ToLower() == ".exe"; } public class LibraryItem : ListedItem diff --git a/Files/Views/LayoutModes/ColumnViewBase.xaml.cs b/Files/Views/LayoutModes/ColumnViewBase.xaml.cs index bf0ea13efaac..c78db9d1adaf 100644 --- a/Files/Views/LayoutModes/ColumnViewBase.xaml.cs +++ b/Files/Views/LayoutModes/ColumnViewBase.xaml.cs @@ -305,7 +305,7 @@ public override void ResetItemOpacity() protected override ListedItem GetItemFromElement(object element) { - return (element as ListViewItem).DataContext as ListedItem; + return (element as ListViewItem).DataContext as ListedItem ?? (element as ListViewItem).Content as ListedItem; } #region IDisposable diff --git a/Files/Views/LayoutModes/ColumnViewBrowser.xaml.cs b/Files/Views/LayoutModes/ColumnViewBrowser.xaml.cs index 7e04b7e28408..036d5a19066f 100644 --- a/Files/Views/LayoutModes/ColumnViewBrowser.xaml.cs +++ b/Files/Views/LayoutModes/ColumnViewBrowser.xaml.cs @@ -364,7 +364,7 @@ public override void ResetItemOpacity() protected override ListedItem GetItemFromElement(object element) { - return (element as ListViewItem).DataContext as ListedItem; + return (element as ListViewItem).DataContext as ListedItem ?? (element as ListViewItem).Content as ListedItem; } #region IDisposable diff --git a/Files/Views/LayoutModes/DetailsLayoutBrowser.xaml.cs b/Files/Views/LayoutModes/DetailsLayoutBrowser.xaml.cs index 7dfc741fec85..3a2ad44352a1 100644 --- a/Files/Views/LayoutModes/DetailsLayoutBrowser.xaml.cs +++ b/Files/Views/LayoutModes/DetailsLayoutBrowser.xaml.cs @@ -484,7 +484,7 @@ protected override void Page_CharacterReceived(CoreWindow sender, CharacterRecei protected override ListedItem GetItemFromElement(object element) { - return (element as ListViewItem).DataContext as ListedItem; + return (element as ListViewItem).DataContext as ListedItem ?? (element as ListViewItem).Content as ListedItem; } private void FileListGridItem_PointerPressed(object sender, PointerRoutedEventArgs e) diff --git a/Files/Views/LayoutModes/GridViewBrowser.xaml.cs b/Files/Views/LayoutModes/GridViewBrowser.xaml.cs index 05026b9d5b48..9fa8cf8e8b65 100644 --- a/Files/Views/LayoutModes/GridViewBrowser.xaml.cs +++ b/Files/Views/LayoutModes/GridViewBrowser.xaml.cs @@ -443,7 +443,7 @@ protected override void Page_CharacterReceived(CoreWindow sender, CharacterRecei protected override ListedItem GetItemFromElement(object element) { - return (element as GridViewItem).DataContext as ListedItem; + return (element as GridViewItem).DataContext as ListedItem ?? (element as GridViewItem).Content as ListedItem; } private void FileListGridItem_PointerPressed(object sender, PointerRoutedEventArgs e) From 2596f9f0fe7cb8be490c8962554dcdb15665c5d4 Mon Sep 17 00:00:00 2001 From: Marco Gavelli Date: Sat, 19 Jun 2021 17:30:16 +0200 Subject: [PATCH 2/9] Handle invalid cliboard format exc --- Files/BaseLayout.cs | 2 +- .../FilesystemOperations/Helpers/FilesystemHelpers.cs | 4 ++-- Files/Interacts/BaseLayoutCommandImplementationModel.cs | 2 +- Files/UserControls/SidebarControl.xaml.cs | 4 ++-- Files/ViewModels/NavToolbarViewModel.cs | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Files/BaseLayout.cs b/Files/BaseLayout.cs index d985f2e9de6d..9971aae41572 100644 --- a/Files/BaseLayout.cs +++ b/Files/BaseLayout.cs @@ -651,7 +651,7 @@ protected async void Item_DragOver(object sender, DragEventArgs e) { draggedItems = await e.DataView.GetStorageItemsAsync(); } - catch (Exception ex) when ((uint)ex.HResult == 0x80040064) + catch (Exception ex) when ((uint)ex.HResult == 0x80040064 || (uint)ex.HResult == 0x8004006A) { e.AcceptedOperation = DataPackageOperation.None; deferral.Complete(); diff --git a/Files/Filesystem/FilesystemOperations/Helpers/FilesystemHelpers.cs b/Files/Filesystem/FilesystemOperations/Helpers/FilesystemHelpers.cs index ecb876941e02..77a891c665cb 100644 --- a/Files/Filesystem/FilesystemOperations/Helpers/FilesystemHelpers.cs +++ b/Files/Filesystem/FilesystemOperations/Helpers/FilesystemHelpers.cs @@ -683,7 +683,7 @@ public async Task CopyItemsFromClipboard(DataPackageView packageVi { source = await packageView.GetStorageItemsAsync(); } - catch (Exception ex) when ((uint)ex.HResult == 0x80040064) + catch (Exception ex) when ((uint)ex.HResult == 0x80040064 || (uint)ex.HResult == 0x8004006A) { return ReturnResult.UnknownException; } @@ -927,7 +927,7 @@ public async Task MoveItemsFromClipboard(DataPackageView packageVi { source = await packageView.GetStorageItemsAsync(); } - catch (Exception ex) when ((uint)ex.HResult == 0x80040064) + catch (Exception ex) when ((uint)ex.HResult == 0x80040064 || (uint)ex.HResult == 0x8004006A) { return ReturnResult.UnknownException; } diff --git a/Files/Interacts/BaseLayoutCommandImplementationModel.cs b/Files/Interacts/BaseLayoutCommandImplementationModel.cs index 090abbfed4d0..4ab8a823be69 100644 --- a/Files/Interacts/BaseLayoutCommandImplementationModel.cs +++ b/Files/Interacts/BaseLayoutCommandImplementationModel.cs @@ -506,7 +506,7 @@ public virtual async void DragEnter(DragEventArgs e) { draggedItems = await e.DataView.GetStorageItemsAsync(); } - catch (Exception dropEx) when ((uint)dropEx.HResult == 0x80040064) + catch (Exception ex) when ((uint)ex.HResult == 0x80040064 || (uint)ex.HResult == 0x8004006A) { if (associatedInstance.ServiceConnection != null) { diff --git a/Files/UserControls/SidebarControl.xaml.cs b/Files/UserControls/SidebarControl.xaml.cs index 5cdd638c722e..70e1d7dbfd5c 100644 --- a/Files/UserControls/SidebarControl.xaml.cs +++ b/Files/UserControls/SidebarControl.xaml.cs @@ -478,7 +478,7 @@ private async void NavigationViewLocationItem_DragOver(object sender, DragEventA { storageItems = await e.DataView.GetStorageItemsAsync(); } - catch (Exception ex) when ((uint)ex.HResult == 0x80040064) + catch (Exception ex) when ((uint)ex.HResult == 0x80040064 || (uint)ex.HResult == 0x8004006A) { e.AcceptedOperation = DataPackageOperation.None; deferral.Complete(); @@ -594,7 +594,7 @@ private async void NavigationViewDriveItem_DragOver(object sender, DragEventArgs { storageItems = await e.DataView.GetStorageItemsAsync(); } - catch (Exception ex) when ((uint)ex.HResult == 0x80040064) + catch (Exception ex) when ((uint)ex.HResult == 0x80040064 || (uint)ex.HResult == 0x8004006A) { e.AcceptedOperation = DataPackageOperation.None; deferral.Complete(); diff --git a/Files/ViewModels/NavToolbarViewModel.cs b/Files/ViewModels/NavToolbarViewModel.cs index 3c0f01f99209..cbebf94324f4 100644 --- a/Files/ViewModels/NavToolbarViewModel.cs +++ b/Files/ViewModels/NavToolbarViewModel.cs @@ -258,7 +258,7 @@ public async void PathBoxItem_DragOver(object sender, DragEventArgs e) { storageItems = await e.DataView.GetStorageItemsAsync(); } - catch (Exception ex) when ((uint)ex.HResult == 0x80040064) + catch (Exception ex) when ((uint)ex.HResult == 0x80040064 || (uint)ex.HResult == 0x8004006A) { e.AcceptedOperation = DataPackageOperation.None; deferral.Complete(); From 083309a4a1a30425b38d522bd8e69153645c6dc6 Mon Sep 17 00:00:00 2001 From: Marco Gavelli Date: Sat, 19 Jun 2021 17:51:00 +0200 Subject: [PATCH 3/9] Switch to dragover, [WIP] remove ftp drop --- Files.Launcher/DragDropForm.cs | 161 ------------------ Files.Launcher/Files.Launcher.csproj | 3 - Files.Launcher/Program.cs | 16 +- Files.Launcher/Win32API.cs | 11 +- Files/BaseLayout.cs | 47 +---- .../FilesystemOperations.cs | 4 +- .../StorageFileExtensions.cs | 11 +- Files/Helpers/PathNormalization.cs | 7 +- .../BaseLayoutCommandImplementationModel.cs | 15 +- .../Interacts/BaseLayoutCommandsViewModel.cs | 4 +- .../IBaseLayoutCommandImplementationModel.cs | 2 +- Files/Strings/en-US/Resources.resw | 3 - Files/Views/LayoutModes/ColumnViewBase.xaml | 2 +- .../Views/LayoutModes/ColumnViewBrowser.xaml | 2 +- .../LayoutModes/DetailsLayoutBrowser.xaml | 2 +- Files/Views/LayoutModes/GridViewBrowser.xaml | 2 +- 16 files changed, 39 insertions(+), 253 deletions(-) delete mode 100644 Files.Launcher/DragDropForm.cs diff --git a/Files.Launcher/DragDropForm.cs b/Files.Launcher/DragDropForm.cs deleted file mode 100644 index a931e3df6da0..000000000000 --- a/Files.Launcher/DragDropForm.cs +++ /dev/null @@ -1,161 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.IO; -using System.Windows.Forms; - -namespace FilesFullTrust -{ - internal class DragDropForm : Form - { - private string dropPath; - - public List DropTargets { get; private set; } = new List(); - - public DragDropForm(string dropPath, string dropText, System.Threading.CancellationToken token) - { - var appTheme = GetAppTheme(); - - this.FormBorderStyle = FormBorderStyle.None; - this.ShowInTaskbar = false; - this.MaximizeBox = false; - this.MinimizeBox = false; - this.Text = "Files"; - this.BackColor = appTheme switch - { - Windows.UI.Xaml.ElementTheme.Light => System.Drawing.Color.White, - _ => System.Drawing.Color.Black - }; - this.Opacity = 0.5; - this.TopMost = true; - this.DragOver += DragDropForm_DragOver; - this.DragDrop += DragDropForm_DragDrop; - this.DragLeave += DragDropForm_DragLeave; - this.AllowDrop = true; - - var label = new Label(); - label.AutoSize = false; - label.Font = new System.Drawing.Font("Segoe UI", 24); - label.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; - label.ForeColor = appTheme switch - { - Windows.UI.Xaml.ElementTheme.Light => System.Drawing.Color.Black, - _ => System.Drawing.Color.White - }; - label.Dock = DockStyle.Fill; - label.Text = dropText; - this.Controls.Add(label); - this.dropPath = dropPath; - - // Create window over Files window - this.StartPosition = FormStartPosition.Manual; - var Handle = Vanara.PInvoke.User32.WindowFromPoint(Cursor.Position); - Vanara.PInvoke.User32.GetWindowRect(Handle, out var lpRect); - this.Size = new System.Drawing.Size(lpRect.Width, lpRect.Height); - this.Location = new System.Drawing.Point(lpRect.Location.X, lpRect.Location.Y); - - token.Register(() => - { - if (this.IsHandleCreated) - { - // If another window is created, close this one - this.Invoke(new InvokeDelegate(() => this.Close())); - } - }); - this.HandleCreated += DragDropForm_HandleCreated; - } - - private Windows.UI.Xaml.ElementTheme GetAppTheme() - { - var appTheme = Windows.UI.Xaml.ElementTheme.Default; - var savedTheme = Windows.Storage.ApplicationData.Current.LocalSettings.Values["theme"]?.ToString(); - if (!string.IsNullOrEmpty(savedTheme)) - { - Enum.TryParse(savedTheme, out appTheme); - } - if (appTheme == Windows.UI.Xaml.ElementTheme.Default) - { - var settings = new Windows.UI.ViewManagement.UISettings(); - appTheme = settings.GetColorValue(Windows.UI.ViewManagement.UIColorType.Background).ToString() switch - { - "#FFFFFFFF" => Windows.UI.Xaml.ElementTheme.Light, - "#FF000000" => Windows.UI.Xaml.ElementTheme.Dark, - _ => Windows.UI.Xaml.ElementTheme.Default // Unknown theme - }; - } - return appTheme; - } - - public delegate void InvokeDelegate(); - - private void DragDropForm_HandleCreated(object sender, EventArgs e) - { - var timer = new Timer(); - timer.Interval = 1000; - timer.Tick += (s, e) => - { - if (!this.DesktopBounds.Contains(Cursor.Position)) - { - // After some time check whether the mouse is still inside the drop window - this.Close(); - (s as Timer).Dispose(); - } - }; - timer.Start(); - } - - private void DragDropForm_DragLeave(object sender, EventArgs e) - { - this.Close(); - } - - private void DragDropForm_DragDrop(object sender, DragEventArgs e) - { - if (e.Data.GetDataPresent("FileDrop")) - { - var str = e.Data.GetData("FileDrop") as string[]; - DropTargets.AddRange(str); - foreach (var file in DropTargets) - { - try - { - // Move files to destination - // Works for 7zip, Winrar which unpack the items in the temp folder - var destName = Path.GetFileName(file.TrimEnd(Path.DirectorySeparatorChar)); - Directory.Move(file, Path.Combine(dropPath, destName)); - } - catch (Exception ex) - { - Program.Logger.Warn(ex, "Failed to drop items"); - } - } - } - this.Close(); - } - - private void DragDropForm_DragOver(object sender, DragEventArgs e) - { - // Should handle "Shell ID List" as well - if (e.Data.GetDataPresent("FileDrop")) - { - e.Effect = DragDropEffects.All; - } - else - { - e.Effect = DragDropEffects.None; - } - } - - protected override CreateParams CreateParams - { - get - { - // Turn on WS_EX_TOOLWINDOW style bit - // Window won't show in alt-tab - CreateParams cp = base.CreateParams; - cp.ExStyle |= 0x80; - return cp; - } - } - } -} \ No newline at end of file diff --git a/Files.Launcher/Files.Launcher.csproj b/Files.Launcher/Files.Launcher.csproj index a922dbcaed62..bd16080b6467 100644 --- a/Files.Launcher/Files.Launcher.csproj +++ b/Files.Launcher/Files.Launcher.csproj @@ -126,9 +126,6 @@ - - Form - diff --git a/Files.Launcher/Program.cs b/Files.Launcher/Program.cs index d88910140a42..768fcf619e72 100644 --- a/Files.Launcher/Program.cs +++ b/Files.Launcher/Program.cs @@ -87,9 +87,6 @@ private static void Main(string[] args) librariesWatcher.Renamed += (object _, RenamedEventArgs e) => OnLibraryChanged(e.ChangeType, e.OldFullPath, e.FullPath); librariesWatcher.EnableRaisingEvents = true; - // Create cancellation token for drop window - cancellation = new CancellationTokenSource(); - // Connect to app service and wait until the connection gets closed appServiceExit = new ManualResetEvent(false); InitializeAppServiceConnection(); @@ -116,8 +113,6 @@ private static void Main(string[] args) handleTable?.Dispose(); deviceWatcher?.Dispose(); librariesWatcher?.Dispose(); - cancellation?.Cancel(); - cancellation?.Dispose(); appServiceExit?.Dispose(); } } @@ -157,7 +152,6 @@ private static async void RecycleBinWatcher_Changed(object sender, FileSystemEve private static NamedPipeServerStream connection; private static ManualResetEvent appServiceExit; - private static CancellationTokenSource cancellation; private static Win32API.DisposableDictionary handleTable; private static IList binWatchers; private static DeviceWatcher deviceWatcher; @@ -872,16 +866,10 @@ await Win32API.StartSTATask(() => break; case "DragDrop": - cancellation.Cancel(); - cancellation.Dispose(); - cancellation = new CancellationTokenSource(); var dropPath = (string)message["droppath"]; - var dropText = (string)message["droptext"]; - var drops = Win32API.StartSTATask>(() => + await Win32API.StartSTATask(() => { - var form = new DragDropForm(dropPath, dropText, cancellation.Token); - System.Windows.Forms.Application.Run(form); - return form.DropTargets; + return false; }); break; diff --git a/Files.Launcher/Win32API.cs b/Files.Launcher/Win32API.cs index 8d088e3c285a..edca4ebdf5dd 100644 --- a/Files.Launcher/Win32API.cs +++ b/Files.Launcher/Win32API.cs @@ -27,6 +27,7 @@ public static Task StartSTATask(Func func) var tcs = new TaskCompletionSource(); Thread thread = new Thread(() => { + Ole32.OleInitialize(); try { tcs.SetResult(func()); @@ -37,7 +38,15 @@ public static Task StartSTATask(Func func) Program.Logger.Info(ex, ex.Message); //tcs.SetException(e); } - }); + finally + { + Ole32.OleUninitialize(); + } + }) + { + IsBackground = true, + Priority = ThreadPriority.Normal + }; thread.SetApartmentState(ApartmentState.STA); thread.Start(); return tcs.Task; diff --git a/Files/BaseLayout.cs b/Files/BaseLayout.cs index 9971aae41572..cb03719039fe 100644 --- a/Files/BaseLayout.cs +++ b/Files/BaseLayout.cs @@ -528,47 +528,6 @@ protected virtual void Page_CharacterReceived(CoreWindow sender, CharacterReceiv } } - private async void Item_DragStarting(object sender, DragStartingEventArgs e) - { - List selectedStorageItems = new List(); - - if (sender is DataGridRow dataGridRow) - { - if (dataGridRow.DataContext is ListedItem item) - { - ParentShellPageInstance.SlimContentPage.SelectedItems.Add(item); - } - } - - foreach (ListedItem item in ParentShellPageInstance.SlimContentPage.SelectedItems) - { - if (item is ShortcutItem) - { - // Can't drag shortcut items - continue; - } - else if (item.PrimaryItemAttribute == StorageItemTypes.File) - { - await ParentShellPageInstance.FilesystemViewModel.GetFileFromPathAsync(item.ItemPath) - .OnSuccess(t => selectedStorageItems.Add(t)); - } - else if (item.PrimaryItemAttribute == StorageItemTypes.Folder) - { - await ParentShellPageInstance.FilesystemViewModel.GetFolderFromPathAsync(item.ItemPath) - .OnSuccess(t => selectedStorageItems.Add(t)); - } - } - - if (selectedStorageItems.Count == 0) - { - e.Cancel = true; - return; - } - - e.Data.SetStorageItems(selectedStorageItems, false); - e.DragUI.SetContentFromDataPackage(); - } - protected async void FileList_DragItemsStarting(object sender, DragItemsStartingEventArgs e) { List selectedStorageItems = new List(); @@ -713,7 +672,6 @@ protected void InitializeDrag(UIElement element) if (item != null) { element.AllowDrop = false; - element.DragStarting -= Item_DragStarting; element.DragOver -= Item_DragOver; element.DragLeave -= Item_DragLeave; element.Drop -= Item_Drop; @@ -730,7 +688,6 @@ protected void InitializeDrag(UIElement element) protected void UninitializeDrag(UIElement element) { element.AllowDrop = false; - element.DragStarting -= Item_DragStarting; element.DragOver -= Item_DragOver; element.DragLeave -= Item_DragLeave; element.Drop -= Item_Drop; @@ -743,9 +700,9 @@ protected void UninitializeDrag(UIElement element) public abstract void Dispose(); - protected void ItemsLayout_DragEnter(object sender, DragEventArgs e) + protected void ItemsLayout_DragOver(object sender, DragEventArgs e) { - CommandsViewModel?.DragEnterCommand?.Execute(e); + CommandsViewModel?.DragOverCommand?.Execute(e); } protected void ItemsLayout_Drop(object sender, DragEventArgs e) diff --git a/Files/Filesystem/FilesystemOperations/FilesystemOperations.cs b/Files/Filesystem/FilesystemOperations/FilesystemOperations.cs index 6c20b4dfaa58..a02c356aaf14 100644 --- a/Files/Filesystem/FilesystemOperations/FilesystemOperations.cs +++ b/Files/Filesystem/FilesystemOperations/FilesystemOperations.cs @@ -264,7 +264,7 @@ await DialogDisplayHelper.ShowDialogAsync( } } - if (Path.GetDirectoryName(destination) == associatedInstance.FilesystemViewModel.WorkingDirectory) + if (Path.GetDirectoryName(destination) == associatedInstance.FilesystemViewModel.WorkingDirectory.TrimPath()) { await Windows.ApplicationModel.Core.CoreApplication.MainView.DispatcherQueue.EnqueueAsync(async () => { @@ -469,7 +469,7 @@ await DialogDisplayHelper.ShowDialogAsync( errorCode?.Report(fsResult.ErrorCode); } - if (Path.GetDirectoryName(destination) == associatedInstance.FilesystemViewModel.WorkingDirectory) + if (Path.GetDirectoryName(destination) == associatedInstance.FilesystemViewModel.WorkingDirectory.TrimPath()) { await Windows.ApplicationModel.Core.CoreApplication.MainView.DispatcherQueue.EnqueueAsync(async () => { diff --git a/Files/Filesystem/StorageFileHelpers/StorageFileExtensions.cs b/Files/Filesystem/StorageFileHelpers/StorageFileExtensions.cs index 06c51167846a..b975ecffc706 100644 --- a/Files/Filesystem/StorageFileHelpers/StorageFileExtensions.cs +++ b/Files/Filesystem/StorageFileHelpers/StorageFileExtensions.cs @@ -1,6 +1,7 @@ using Files.Common; using Files.DataModels.NavigationControlItems; using Files.Extensions; +using Files.Helpers; using Files.UserControls; using Files.ViewModels; using Files.Views; @@ -257,9 +258,9 @@ public static bool AreItemsInSameDrive(this IEnumerable storageIte try { return storageItems.Any(storageItem => - Path.GetPathRoot(storageItem.Path).Equals( - Path.GetPathRoot(destinationPath), - StringComparison.OrdinalIgnoreCase)); + Path.GetPathRoot(storageItem.Path).Equals( + Path.GetPathRoot(destinationPath), + StringComparison.OrdinalIgnoreCase)); } catch { @@ -272,8 +273,8 @@ public static bool AreItemsAlreadyInFolder(this IEnumerable storag try { return storageItems.All(storageItem => - Directory.GetParent(storageItem.Path).FullName.Equals( - destinationPath, StringComparison.OrdinalIgnoreCase)); + Path.GetDirectoryName(storageItem.Path).Equals( + destinationPath.TrimPath(), StringComparison.OrdinalIgnoreCase)); } catch { diff --git a/Files/Helpers/PathNormalization.cs b/Files/Helpers/PathNormalization.cs index 8079147d2005..0c2db2787401 100644 --- a/Files/Helpers/PathNormalization.cs +++ b/Files/Helpers/PathNormalization.cs @@ -3,7 +3,7 @@ namespace Files.Helpers { - public class PathNormalization + public static class PathNormalization { public static string GetPathRoot(string path) { @@ -61,6 +61,11 @@ public static string NormalizePath(string path) } } + public static string TrimPath(this string path) + { + return path?.TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar); + } + public static string GetParentDir(string path) { if (string.IsNullOrEmpty(path)) diff --git a/Files/Interacts/BaseLayoutCommandImplementationModel.cs b/Files/Interacts/BaseLayoutCommandImplementationModel.cs index 4ab8a823be69..1cac73a57260 100644 --- a/Files/Interacts/BaseLayoutCommandImplementationModel.cs +++ b/Files/Interacts/BaseLayoutCommandImplementationModel.cs @@ -492,7 +492,7 @@ public virtual void GridViewSizeIncrease(KeyboardAcceleratorInvokedEventArgs e) } } - public virtual async void DragEnter(DragEventArgs e) + public virtual async void DragOver(DragEventArgs e) { var deferral = e.GetDeferral(); @@ -508,14 +508,7 @@ public virtual async void DragEnter(DragEventArgs e) } catch (Exception ex) when ((uint)ex.HResult == 0x80040064 || (uint)ex.HResult == 0x8004006A) { - if (associatedInstance.ServiceConnection != null) - { - await associatedInstance.ServiceConnection.SendMessageAsync(new ValueSet() { - { "Arguments", "FileOperation" }, - { "fileop", "DragDrop" }, - { "droptext", "DragDropWindowText".GetLocalized() }, - { "droppath", associatedInstance.FilesystemViewModel.WorkingDirectory } }); - } + // Handled by FTP } catch (Exception ex) { @@ -528,9 +521,9 @@ await associatedInstance.ServiceConnection.SendMessageAsync(new ValueSet() { return; } - var folderName = System.IO.Path.GetFileName(associatedInstance.FilesystemViewModel.WorkingDirectory); + var folderName = Path.GetFileName(associatedInstance.FilesystemViewModel.WorkingDirectory.TrimPath()); // As long as one file doesn't already belong to this folder - if (associatedInstance.InstanceViewModel.IsPageTypeSearchResults || draggedItems.All(x => Path.GetDirectoryName(x.Path) == associatedInstance.FilesystemViewModel.WorkingDirectory)) + if (associatedInstance.InstanceViewModel.IsPageTypeSearchResults || draggedItems.AreItemsAlreadyInFolder(associatedInstance.FilesystemViewModel.WorkingDirectory)) { e.AcceptedOperation = DataPackageOperation.None; } diff --git a/Files/Interacts/BaseLayoutCommandsViewModel.cs b/Files/Interacts/BaseLayoutCommandsViewModel.cs index c9df5e453a47..2f00573d142e 100644 --- a/Files/Interacts/BaseLayoutCommandsViewModel.cs +++ b/Files/Interacts/BaseLayoutCommandsViewModel.cs @@ -66,7 +66,7 @@ private void InitializeCommands() PointerWheelChangedCommand = new RelayCommand(commandsModel.PointerWheelChanged); GridViewSizeDecreaseCommand = new RelayCommand(commandsModel.GridViewSizeDecrease); GridViewSizeIncreaseCommand = new RelayCommand(commandsModel.GridViewSizeIncrease); - DragEnterCommand = new RelayCommand(commandsModel.DragEnter); + DragOverCommand = new RelayCommand(commandsModel.DragOver); DropCommand = new RelayCommand(commandsModel.Drop); RefreshCommand = new RelayCommand(commandsModel.RefreshItems); SearchUnindexedItems = new RelayCommand(commandsModel.SearchUnindexedItems); @@ -149,7 +149,7 @@ private void InitializeCommands() public ICommand GridViewSizeIncreaseCommand { get; private set; } - public ICommand DragEnterCommand { get; private set; } + public ICommand DragOverCommand { get; private set; } public ICommand DropCommand { get; private set; } diff --git a/Files/Interacts/IBaseLayoutCommandImplementationModel.cs b/Files/Interacts/IBaseLayoutCommandImplementationModel.cs index 23f81e71c536..8ba7d06a561a 100644 --- a/Files/Interacts/IBaseLayoutCommandImplementationModel.cs +++ b/Files/Interacts/IBaseLayoutCommandImplementationModel.cs @@ -79,7 +79,7 @@ public interface IBaseLayoutCommandImplementationModel : IDisposable void GridViewSizeIncrease(KeyboardAcceleratorInvokedEventArgs e); - void DragEnter(DragEventArgs e); + void DragOver(DragEventArgs e); void Drop(DragEventArgs e); diff --git a/Files/Strings/en-US/Resources.resw b/Files/Strings/en-US/Resources.resw index b43829ac5e05..93f2ab512533 100644 --- a/Files/Strings/en-US/Resources.resw +++ b/Files/Strings/en-US/Resources.resw @@ -1401,9 +1401,6 @@ Learn more about date formats - - Drop here - Search diff --git a/Files/Views/LayoutModes/ColumnViewBase.xaml b/Files/Views/LayoutModes/ColumnViewBase.xaml index 3d1c6c259f03..014642874cbd 100644 --- a/Files/Views/LayoutModes/ColumnViewBase.xaml +++ b/Files/Views/LayoutModes/ColumnViewBase.xaml @@ -1277,8 +1277,8 @@ CanDragItems="{x:Bind InstanceViewModel.IsPageTypeSearchResults, Converter={StaticResource BoolNegationConverter}, Mode=OneWay}" ChoosingItemContainer="FileList_ChoosingItemContainer" DoubleTapped="FileList_DoubleTapped" - DragEnter="ItemsLayout_DragEnter" DragItemsStarting="FileList_DragItemsStarting" + DragOver="ItemsLayout_DragOver" Drop="ItemsLayout_Drop" Holding="FileList_Holding" IsDoubleTapEnabled="True" diff --git a/Files/Views/LayoutModes/ColumnViewBrowser.xaml b/Files/Views/LayoutModes/ColumnViewBrowser.xaml index e4ee56c76bc9..8761d3afcd82 100644 --- a/Files/Views/LayoutModes/ColumnViewBrowser.xaml +++ b/Files/Views/LayoutModes/ColumnViewBrowser.xaml @@ -1281,8 +1281,8 @@ CanDragItems="{x:Bind InstanceViewModel.IsPageTypeSearchResults, Converter={StaticResource BoolNegationConverter}, Mode=OneWay}" ChoosingItemContainer="FileList_ChoosingItemContainer" DoubleTapped="FileList_DoubleTapped" - DragEnter="ItemsLayout_DragEnter" DragItemsStarting="FileList_DragItemsStarting" + DragOver="ItemsLayout_DragOver" Drop="ItemsLayout_Drop" Holding="FileList_Holding" IsDoubleTapEnabled="True" diff --git a/Files/Views/LayoutModes/DetailsLayoutBrowser.xaml b/Files/Views/LayoutModes/DetailsLayoutBrowser.xaml index b1f99897b567..8d8b71a9390d 100644 --- a/Files/Views/LayoutModes/DetailsLayoutBrowser.xaml +++ b/Files/Views/LayoutModes/DetailsLayoutBrowser.xaml @@ -201,8 +201,8 @@ CanDragItems="{x:Bind InstanceViewModel.IsPageTypeSearchResults, Converter={StaticResource BoolNegationConverter}, Mode=OneWay}" ChoosingItemContainer="FileList_ChoosingItemContainer" DoubleTapped="FileList_DoubleTapped" - DragEnter="ItemsLayout_DragEnter" DragItemsStarting="FileList_DragItemsStarting" + DragOver="ItemsLayout_DragOver" Drop="ItemsLayout_Drop" IsDoubleTapEnabled="True" IsItemClickEnabled="True" diff --git a/Files/Views/LayoutModes/GridViewBrowser.xaml b/Files/Views/LayoutModes/GridViewBrowser.xaml index b6879b87e207..2f5a7fa687aa 100644 --- a/Files/Views/LayoutModes/GridViewBrowser.xaml +++ b/Files/Views/LayoutModes/GridViewBrowser.xaml @@ -481,8 +481,8 @@ CanDragItems="{x:Bind InstanceViewModel.IsPageTypeSearchResults, Converter={StaticResource BoolNegationConverter}, Mode=OneWay}" ChoosingItemContainer="FileList_ChoosingItemContainer" DoubleTapped="FileList_DoubleTapped" - DragEnter="ItemsLayout_DragEnter" DragItemsStarting="FileList_DragItemsStarting" + DragOver="ItemsLayout_DragOver" Drop="ItemsLayout_Drop" IsDoubleTapEnabled="True" IsItemClickEnabled="True" From f611e4d3ffaf5831079178562800e487ec32d94e Mon Sep 17 00:00:00 2001 From: Marco Gavelli Date: Sat, 19 Jun 2021 18:23:52 +0200 Subject: [PATCH 4/9] Change default drop action on Ctrl/Shift --- Files/BaseLayout.cs | 41 ++++++++++++------- .../BaseLayoutCommandImplementationModel.cs | 33 +++++++++++---- Files/UserControls/SidebarControl.xaml.cs | 25 ++++++++++- 3 files changed, 74 insertions(+), 25 deletions(-) diff --git a/Files/BaseLayout.cs b/Files/BaseLayout.cs index cb03719039fe..79f82ff5eaa8 100644 --- a/Files/BaseLayout.cs +++ b/Files/BaseLayout.cs @@ -22,6 +22,7 @@ using System.Threading.Tasks; using Windows.ApplicationModel.Core; using Windows.ApplicationModel.DataTransfer; +using Windows.ApplicationModel.DataTransfer.DragDrop; using Windows.Storage; using Windows.System; using Windows.UI.Core; @@ -625,26 +626,38 @@ protected async void Item_DragOver(object sender, DragEventArgs e) } e.Handled = true; - e.DragUIOverride.IsCaptionVisible = true; - if (InstanceViewModel.IsPageTypeSearchResults || draggedItems.Any(draggedItem => draggedItem.Path == item.ItemPath)) { e.AcceptedOperation = DataPackageOperation.None; } - else if (item.IsExecutable) - { - e.DragUIOverride.Caption = $"{"OpenItemsWithCaptionText".GetLocalized()} {item.ItemName}"; - e.AcceptedOperation = DataPackageOperation.Link; - } // Items from the same drive as this folder are dragged into this folder, so we move the items instead of copy - else if (draggedItems.AreItemsInSameDrive(item.ItemPath)) - { - e.DragUIOverride.Caption = string.Format("MoveToFolderCaptionText".GetLocalized(), item.ItemName); - e.AcceptedOperation = DataPackageOperation.Move; - } else { - e.DragUIOverride.Caption = string.Format("CopyToFolderCaptionText".GetLocalized(), item.ItemName); - e.AcceptedOperation = DataPackageOperation.Copy; + e.DragUIOverride.IsCaptionVisible = true; + if (item.IsExecutable) + { + e.DragUIOverride.Caption = $"{"OpenItemsWithCaptionText".GetLocalized()} {item.ItemName}"; + e.AcceptedOperation = DataPackageOperation.Link; + } // Items from the same drive as this folder are dragged into this folder, so we move the items instead of copy + else if (e.Modifiers.HasFlag(DragDropModifiers.Control)) + { + e.DragUIOverride.Caption = string.Format("CopyToFolderCaptionText".GetLocalized(), item.ItemName); + e.AcceptedOperation = DataPackageOperation.Copy; + } + else if (e.Modifiers.HasFlag(DragDropModifiers.Shift)) + { + e.DragUIOverride.Caption = string.Format("MoveToFolderCaptionText".GetLocalized(), item.ItemName); + e.AcceptedOperation = DataPackageOperation.Move; + } + else if (draggedItems.AreItemsInSameDrive(item.ItemPath)) + { + e.DragUIOverride.Caption = string.Format("MoveToFolderCaptionText".GetLocalized(), item.ItemName); + e.AcceptedOperation = DataPackageOperation.Move; + } + else + { + e.DragUIOverride.Caption = string.Format("CopyToFolderCaptionText".GetLocalized(), item.ItemName); + e.AcceptedOperation = DataPackageOperation.Copy; + } } } diff --git a/Files/Interacts/BaseLayoutCommandImplementationModel.cs b/Files/Interacts/BaseLayoutCommandImplementationModel.cs index 1cac73a57260..920d271958db 100644 --- a/Files/Interacts/BaseLayoutCommandImplementationModel.cs +++ b/Files/Interacts/BaseLayoutCommandImplementationModel.cs @@ -12,6 +12,7 @@ using System.IO; using System.Linq; using Windows.ApplicationModel.DataTransfer; +using Windows.ApplicationModel.DataTransfer.DragDrop; using Windows.Foundation; using Windows.Foundation.Collections; using Windows.Storage; @@ -500,7 +501,6 @@ public virtual async void DragOver(DragEventArgs e) if (e.DataView.Contains(StandardDataFormats.StorageItems)) { e.Handled = true; - e.DragUIOverride.IsCaptionVisible = true; IEnumerable draggedItems = new List(); try { @@ -521,21 +521,36 @@ public virtual async void DragOver(DragEventArgs e) return; } - var folderName = Path.GetFileName(associatedInstance.FilesystemViewModel.WorkingDirectory.TrimPath()); + var pwd = associatedInstance.FilesystemViewModel.WorkingDirectory.TrimPath(); + var folderName = (Path.IsPathRooted(pwd) && Path.GetPathRoot(pwd) == pwd) ? Path.GetPathRoot(pwd) : Path.GetFileName(pwd); // As long as one file doesn't already belong to this folder if (associatedInstance.InstanceViewModel.IsPageTypeSearchResults || draggedItems.AreItemsAlreadyInFolder(associatedInstance.FilesystemViewModel.WorkingDirectory)) { e.AcceptedOperation = DataPackageOperation.None; } - else if (draggedItems.AreItemsInSameDrive(associatedInstance.FilesystemViewModel.WorkingDirectory)) - { - e.DragUIOverride.Caption = string.Format("MoveToFolderCaptionText".GetLocalized(), folderName); - e.AcceptedOperation = DataPackageOperation.Move; - } else { - e.DragUIOverride.Caption = string.Format("CopyToFolderCaptionText".GetLocalized(), folderName); - e.AcceptedOperation = DataPackageOperation.Copy; + e.DragUIOverride.IsCaptionVisible = true; + if (e.Modifiers.HasFlag(DragDropModifiers.Control)) + { + e.DragUIOverride.Caption = string.Format("CopyToFolderCaptionText".GetLocalized(), folderName); + e.AcceptedOperation = DataPackageOperation.Copy; + } + else if (e.Modifiers.HasFlag(DragDropModifiers.Shift)) + { + e.DragUIOverride.Caption = string.Format("MoveToFolderCaptionText".GetLocalized(), folderName); + e.AcceptedOperation = DataPackageOperation.Move; + } + else if (draggedItems.AreItemsInSameDrive(associatedInstance.FilesystemViewModel.WorkingDirectory)) + { + e.DragUIOverride.Caption = string.Format("MoveToFolderCaptionText".GetLocalized(), folderName); + e.AcceptedOperation = DataPackageOperation.Move; + } + else + { + e.DragUIOverride.Caption = string.Format("CopyToFolderCaptionText".GetLocalized(), folderName); + e.AcceptedOperation = DataPackageOperation.Copy; + } } } diff --git a/Files/UserControls/SidebarControl.xaml.cs b/Files/UserControls/SidebarControl.xaml.cs index 70e1d7dbfd5c..43bbe9efb16e 100644 --- a/Files/UserControls/SidebarControl.xaml.cs +++ b/Files/UserControls/SidebarControl.xaml.cs @@ -15,6 +15,7 @@ using System.Threading; using System.Windows.Input; using Windows.ApplicationModel.DataTransfer; +using Windows.ApplicationModel.DataTransfer.DragDrop; using Windows.Storage; using Windows.System; using Windows.UI.Core; @@ -502,7 +503,17 @@ private async void NavigationViewLocationItem_DragOver(object sender, DragEventA else { e.DragUIOverride.IsCaptionVisible = true; - if (storageItems.AreItemsInSameDrive(locationItem.Path) || locationItem.IsDefaultLocation) + if (e.Modifiers.HasFlag(DragDropModifiers.Control)) + { + e.DragUIOverride.Caption = string.Format("CopyToFolderCaptionText".GetLocalized(), locationItem.Text); + e.AcceptedOperation = DataPackageOperation.Copy; + } + else if (e.Modifiers.HasFlag(DragDropModifiers.Shift)) + { + e.DragUIOverride.Caption = string.Format("MoveToFolderCaptionText".GetLocalized(), locationItem.Text); + e.AcceptedOperation = DataPackageOperation.Move; + } + else if (storageItems.AreItemsInSameDrive(locationItem.Path) || locationItem.IsDefaultLocation) { e.AcceptedOperation = DataPackageOperation.Move; e.DragUIOverride.Caption = string.Format("MoveToFolderCaptionText".GetLocalized(), locationItem.Text); @@ -617,7 +628,17 @@ private async void NavigationViewDriveItem_DragOver(object sender, DragEventArgs else { e.DragUIOverride.IsCaptionVisible = true; - if (storageItems.AreItemsInSameDrive(driveItem.Path)) + if (e.Modifiers.HasFlag(DragDropModifiers.Control)) + { + e.DragUIOverride.Caption = string.Format("CopyToFolderCaptionText".GetLocalized(), driveItem.Text); + e.AcceptedOperation = DataPackageOperation.Copy; + } + else if (e.Modifiers.HasFlag(DragDropModifiers.Shift)) + { + e.DragUIOverride.Caption = string.Format("MoveToFolderCaptionText".GetLocalized(), driveItem.Text); + e.AcceptedOperation = DataPackageOperation.Move; + } + else if (storageItems.AreItemsInSameDrive(driveItem.Path)) { e.AcceptedOperation = DataPackageOperation.Move; e.DragUIOverride.Caption = string.Format("MoveToFolderCaptionText".GetLocalized(), driveItem.Text); From 5cd0eae0ab42779a965f6a11436a96b48dfc25bb Mon Sep 17 00:00:00 2001 From: Marco Gavelli Date: Sat, 19 Jun 2021 20:05:28 +0200 Subject: [PATCH 5/9] [WIP] Improve drop from external programs --- Files.Launcher/Files.Launcher.csproj | 1 + Files.Launcher/Program.cs | 39 ++- Files.Launcher/RemoteDataObject.cs | 297 ++++++++++++++++++ Files.Launcher/Win32API_ContextMenu.cs | 44 +++ Files/BaseLayout.cs | 11 +- .../Helpers/FilesystemHelpers.cs | 26 +- .../BaseLayoutCommandImplementationModel.cs | 14 +- Files/UserControls/SidebarControl.xaml.cs | 32 +- Files/ViewModels/NavToolbarViewModel.cs | 6 +- 9 files changed, 442 insertions(+), 28 deletions(-) create mode 100644 Files.Launcher/RemoteDataObject.cs diff --git a/Files.Launcher/Files.Launcher.csproj b/Files.Launcher/Files.Launcher.csproj index bd16080b6467..fcc14d48272f 100644 --- a/Files.Launcher/Files.Launcher.csproj +++ b/Files.Launcher/Files.Launcher.csproj @@ -132,6 +132,7 @@ + diff --git a/Files.Launcher/Program.cs b/Files.Launcher/Program.cs index 768fcf619e72..0c203cdf35d6 100644 --- a/Files.Launcher/Program.cs +++ b/Files.Launcher/Program.cs @@ -867,10 +867,45 @@ await Win32API.StartSTATask(() => case "DragDrop": var dropPath = (string)message["droppath"]; - await Win32API.StartSTATask(() => + var result2 = await Win32API.StartSTATask(() => { - return false; + var rdo = new RemoteDataObject(System.Windows.Forms.Clipboard.GetDataObject()); + + foreach (RemoteDataObject.DataPackage package in rdo.GetRemoteData()) + { + try + { + if (package.ItemType == RemoteDataObject.StorageType.File) + { + string directoryPath = Path.GetDirectoryName(dropPath); + if (!Directory.Exists(directoryPath)) + { + Directory.CreateDirectory(directoryPath); + } + + string uniqueName = Win32API.GenerateUniquePath(Path.Combine(dropPath, package.Name)); + using (FileStream stream = new FileStream(uniqueName, FileMode.CreateNew)) + { + package.ContentStream.CopyTo(stream); + } + } + else + { + string directoryPath = Path.Combine(dropPath, package.Name); + if (!Directory.Exists(directoryPath)) + { + Directory.CreateDirectory(directoryPath); + } + } + } + finally + { + package.Dispose(); + } + } + return true; }); + await Win32API.SendMessageAsync(connection, new ValueSet() { { "Success", result2 } }, message.Get("RequestID", (string)null)); break; case "DeleteItem": diff --git a/Files.Launcher/RemoteDataObject.cs b/Files.Launcher/RemoteDataObject.cs new file mode 100644 index 000000000000..95c856ef9de5 --- /dev/null +++ b/Files.Launcher/RemoteDataObject.cs @@ -0,0 +1,297 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Reflection; +using System.Runtime.InteropServices; +using System.Runtime.InteropServices.ComTypes; +using System.Windows.Forms; +using Vanara.PInvoke; +using STATSTG = System.Runtime.InteropServices.ComTypes.STATSTG; + +namespace FilesFullTrust +{ + public class RemoteDataObject + { + /// + /// Holds the that this class is wrapping + /// + private System.Windows.Forms.IDataObject underlyingDataObject; + + /// + /// Holds the interface to the that this class is wrapping. + /// + private System.Runtime.InteropServices.ComTypes.IDataObject comUnderlyingDataObject; + + /// + /// Holds the internal ole to the that this class is wrapping. + /// + private System.Windows.Forms.IDataObject oleUnderlyingDataObject; + + /// + /// Holds the of the "GetDataFromHGLOBAL" method of the internal ole . + /// + private MethodInfo getDataFromHGLOBALMethod; + + /// + /// Initializes a new instance of the class. + /// + /// The underlying data object to wrap. + public RemoteDataObject(System.Windows.Forms.IDataObject underlyingDataObject) + { + //get the underlying dataobject and its ComType IDataObject interface to it + this.underlyingDataObject = underlyingDataObject; + comUnderlyingDataObject = (System.Runtime.InteropServices.ComTypes.IDataObject)this.underlyingDataObject; + + //get the internal ole dataobject and its GetDataFromHGLOBAL so it can be called later + FieldInfo innerDataField = this.underlyingDataObject.GetType().GetField("innerData", BindingFlags.NonPublic | BindingFlags.Instance); + oleUnderlyingDataObject = (System.Windows.Forms.IDataObject)innerDataField.GetValue(this.underlyingDataObject); + getDataFromHGLOBALMethod = oleUnderlyingDataObject.GetType().GetMethod("GetDataFromHGLOBAL", BindingFlags.NonPublic | BindingFlags.Instance); + } + + public IEnumerable GetRemoteData() + { + string FormatName = string.Empty; + + if (GetDataPresent(Shell32.ShellClipboardFormat.CFSTR_FILEDESCRIPTORW)) + { + FormatName = Shell32.ShellClipboardFormat.CFSTR_FILEDESCRIPTORW; + } + else if (GetDataPresent(Shell32.ShellClipboardFormat.CFSTR_FILEDESCRIPTORA)) + { + FormatName = Shell32.ShellClipboardFormat.CFSTR_FILEDESCRIPTORA; + } + + if (string.IsNullOrEmpty(FormatName)) + { + yield break; + } + else + { + if (underlyingDataObject.GetData(FormatName, true) is MemoryStream FileGroupDescriptorStream) + { + try + { + byte[] FileGroupDescriptorBytes = FileGroupDescriptorStream.ToArray(); + + IntPtr FileGroupDescriptorAPointer = Marshal.AllocHGlobal(FileGroupDescriptorBytes.Length); + + try + { + Marshal.Copy(FileGroupDescriptorBytes, 0, FileGroupDescriptorAPointer, FileGroupDescriptorBytes.Length); + + int ItemCount = Marshal.ReadInt32(FileGroupDescriptorAPointer); + + IntPtr FileDescriptorPointer = (IntPtr)(FileGroupDescriptorAPointer.ToInt64() + Marshal.SizeOf(ItemCount)); + + for (int FileDescriptorIndex = 0; FileDescriptorIndex < ItemCount; FileDescriptorIndex++) + { + Shell32.FILEDESCRIPTOR FileDescriptor = Marshal.PtrToStructure(FileDescriptorPointer); + + if (FileDescriptor.dwFileAttributes.HasFlag(FileFlagsAndAttributes.FILE_ATTRIBUTE_DIRECTORY)) + { + yield return new DataPackage(FileDescriptor.cFileName, StorageType.Directroy, null); + } + else + { + yield return new DataPackage(FileDescriptor.cFileName, StorageType.File, GetContentData(Shell32.ShellClipboardFormat.CFSTR_FILECONTENTS, FileDescriptorIndex)); + } + + FileDescriptorPointer = (IntPtr)(FileDescriptorPointer.ToInt64() + Marshal.SizeOf(FileDescriptor)); + } + } + finally + { + Marshal.FreeHGlobal(FileGroupDescriptorAPointer); + } + } + finally + { + FileGroupDescriptorStream.Dispose(); + } + } + else + { + yield break; + } + } + } + + /// + /// Retrieves the data associated with the specified data format at the specified index. + /// + /// The format of the data to retrieve. See for predefined formats. + /// The index of the data to retrieve. + /// + /// A containing the raw data for the specified data format at the specified index. + /// + private MemoryStream GetContentData(string Format, int Index) + { + //create a FORMATETC struct to request the data with + FORMATETC Formatetc = new FORMATETC + { + cfFormat = (short)DataFormats.GetFormat(Format).Id, + dwAspect = DVASPECT.DVASPECT_CONTENT, + lindex = Index, + ptd = new IntPtr(0), + tymed = TYMED.TYMED_ISTREAM | TYMED.TYMED_ISTORAGE | TYMED.TYMED_HGLOBAL + }; + + //using the Com IDataObject interface get the data using the defined FORMATETC + comUnderlyingDataObject.GetData(ref Formatetc, out STGMEDIUM Medium); + + //retrieve the data depending on the returned store type + switch (Medium.tymed) + { + case TYMED.TYMED_ISTORAGE: + { + //to handle a IStorage it needs to be written into a second unmanaged + //memory mapped storage and then the data can be read from memory into + //a managed byte and returned as a MemoryStream + + try + { + //marshal the returned pointer to a IStorage object + Ole32.IStorage IStorageObject = (Ole32.IStorage)Marshal.GetObjectForIUnknown(Medium.unionmember); + + try + { + //create a ILockBytes (unmanaged byte array) and then create a IStorage using the byte array as a backing store + Ole32.CreateILockBytesOnHGlobal(IntPtr.Zero, true, out Ole32.ILockBytes LockBytes); + Ole32.StgCreateDocfileOnILockBytes(LockBytes, STGM.STGM_READWRITE | STGM.STGM_SHARE_EXCLUSIVE | STGM.STGM_CREATE, ppstgOpen: out Ole32.IStorage IStorageObjectCopy); + + try + { + //copy the returned IStorage into the new IStorage + IStorageObject.CopyTo(snbExclude: IntPtr.Zero, pstgDest: IStorageObjectCopy); + LockBytes.Flush(); + IStorageObjectCopy.Commit(Ole32.STGC.STGC_DEFAULT); + + //get the STATSTG of the LockBytes to determine how many bytes were written to it + LockBytes.Stat(out STATSTG LockBytesStat, Ole32.STATFLAG.STATFLAG_NONAME); + + int CbSize = Convert.ToInt32(LockBytesStat.cbSize); + + IntPtr LockBytesContentPtr = Marshal.AllocHGlobal(CbSize); + + try + { + LockBytes.ReadAt(0, LockBytesContentPtr, Convert.ToUInt32(LockBytesStat.cbSize), out _); + + byte[] LockBytesContent = new byte[CbSize]; + + Marshal.Copy(LockBytesContentPtr, LockBytesContent, 0, LockBytesContent.Length); + + return new MemoryStream(LockBytesContent); + } + finally + { + Marshal.FreeHGlobal(LockBytesContentPtr); + } + } + finally + { + Marshal.ReleaseComObject(IStorageObjectCopy); + Marshal.ReleaseComObject(LockBytes); + } + } + finally + { + Marshal.ReleaseComObject(IStorageObject); + } + } + finally + { + Marshal.Release(Medium.unionmember); + } + } + case TYMED.TYMED_ISTREAM: + { + //to handle a IStream it needs to be read into a managed byte and + //returned as a MemoryStream + + IStream IStreamObject = (IStream)Marshal.GetObjectForIUnknown(Medium.unionmember); + + try + { + //get the STATSTG of the IStream to determine how many bytes are in it + IStreamObject.Stat(out STATSTG iStreamStat, 0); + + byte[] IStreamContent = new byte[(Convert.ToInt32(iStreamStat.cbSize))]; + + IStreamObject.Read(IStreamContent, IStreamContent.Length, IntPtr.Zero); + + return new MemoryStream(IStreamContent); + } + finally + { + Marshal.Release(Medium.unionmember); + Marshal.ReleaseComObject(IStreamObject); + } + } + case TYMED.TYMED_HGLOBAL: + { + //to handle a HGlobal the exisitng "GetDataFromHGLOBAL" method is invoked via + //reflection + + try + { + return (MemoryStream)getDataFromHGLOBALMethod.Invoke(oleUnderlyingDataObject, new object[] { DataFormats.GetFormat(Formatetc.cfFormat).Name, Medium.unionmember }); + } + finally + { + Marshal.Release(Medium.unionmember); + } + } + default: + { + return null; + } + } + } + + /// + /// Determines whether data stored in this instance is associated with, or can be converted to, the specified format. + /// + /// The format for which to check. See for predefined formats. + /// + /// true if data stored in this instance is associated with, or can be converted to, the specified format; otherwise false. + /// + public bool GetDataPresent(string format) + { + return underlyingDataObject.GetDataPresent(format); + } + + public sealed class DataPackage : IDisposable + { + public StorageType ItemType { get; } + + public MemoryStream ContentStream { get; } + + public string Name { get; } + + public DataPackage(string Name, StorageType ItemType, MemoryStream ContentStream) + { + this.Name = Name; + this.ItemType = ItemType; + this.ContentStream = ContentStream; + } + + public void Dispose() + { + GC.SuppressFinalize(this); + ContentStream?.Dispose(); + } + + ~DataPackage() + { + Dispose(); + } + } + + public enum StorageType + { + File = 0, + Directroy = 1 + } + } +} diff --git a/Files.Launcher/Win32API_ContextMenu.cs b/Files.Launcher/Win32API_ContextMenu.cs index 0ada15218878..69f2523018fb 100644 --- a/Files.Launcher/Win32API_ContextMenu.cs +++ b/Files.Launcher/Win32API_ContextMenu.cs @@ -8,6 +8,7 @@ using System.IO; using System.Linq; using System.Runtime.InteropServices; +using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; using Vanara.InteropServices; @@ -440,6 +441,49 @@ public void Dispose() } } + public static string GenerateUniquePath(string path) + { + string uniquePath = path; + + if (File.Exists(path)) + { + string nameWithoutExt = Path.GetFileNameWithoutExtension(path); + string extension = Path.GetExtension(path); + string directory = Path.GetDirectoryName(path); + + for (ushort count = 1; File.Exists(uniquePath); count++) + { + if (Regex.IsMatch(nameWithoutExt, @".*\(\d+\)")) + { + uniquePath = Path.Combine(directory, $"{nameWithoutExt.Substring(0, nameWithoutExt.LastIndexOf("(", StringComparison.InvariantCultureIgnoreCase))}({count}){extension}"); + } + else + { + uniquePath = Path.Combine(directory, $"{nameWithoutExt} ({count}){extension}"); + } + } + } + else if (Directory.Exists(path)) + { + string directory = Path.GetDirectoryName(path); + string Name = Path.GetFileName(path); + + for (ushort Count = 1; Directory.Exists(uniquePath); Count++) + { + if (Regex.IsMatch(Name, @".*\(\d+\)")) + { + uniquePath = Path.Combine(directory, $"{Name.Substring(0, Name.LastIndexOf("(", StringComparison.InvariantCultureIgnoreCase))}({Count})"); + } + else + { + uniquePath = Path.Combine(directory, $"{Name} ({Count})"); + } + } + } + + return uniquePath; + } + // There is usually no need to define Win32 COM interfaces/P-Invoke methods here. // The Vanara library contains the definitions for all members of Shell32.dll, User32.dll and more // The ones below are due to bugs in the current version of the library and can be removed once fixed diff --git a/Files/BaseLayout.cs b/Files/BaseLayout.cs index 79f82ff5eaa8..f976afabfdcd 100644 --- a/Files/BaseLayout.cs +++ b/Files/BaseLayout.cs @@ -613,9 +613,8 @@ protected async void Item_DragOver(object sender, DragEventArgs e) } catch (Exception ex) when ((uint)ex.HResult == 0x80040064 || (uint)ex.HResult == 0x8004006A) { - e.AcceptedOperation = DataPackageOperation.None; - deferral.Complete(); - return; + // Handled by FTP + draggedItems = new List(); } catch (Exception ex) { @@ -630,6 +629,12 @@ protected async void Item_DragOver(object sender, DragEventArgs e) { e.AcceptedOperation = DataPackageOperation.None; } + else if (!draggedItems.Any()) + { + e.DragUIOverride.IsCaptionVisible = true; + e.DragUIOverride.Caption = string.Format("MoveToFolderCaptionText".GetLocalized(), item.ItemName); + e.AcceptedOperation = DataPackageOperation.Move; + } else { e.DragUIOverride.IsCaptionVisible = true; diff --git a/Files/Filesystem/FilesystemOperations/Helpers/FilesystemHelpers.cs b/Files/Filesystem/FilesystemOperations/Helpers/FilesystemHelpers.cs index 77a891c665cb..dd6f938245f0 100644 --- a/Files/Filesystem/FilesystemOperations/Helpers/FilesystemHelpers.cs +++ b/Files/Filesystem/FilesystemOperations/Helpers/FilesystemHelpers.cs @@ -1,4 +1,5 @@ -using Files.DataModels; +using Files.Common; +using Files.DataModels; using Files.Dialogs; using Files.Enums; using Files.Extensions; @@ -16,6 +17,7 @@ using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; +using Windows.ApplicationModel.AppService; using Windows.ApplicationModel.DataTransfer; using Windows.Foundation.Collections; using Windows.Graphics.Imaging; @@ -685,7 +687,16 @@ public async Task CopyItemsFromClipboard(DataPackageView packageVi } catch (Exception ex) when ((uint)ex.HResult == 0x80040064 || (uint)ex.HResult == 0x8004006A) { - return ReturnResult.UnknownException; + if (associatedInstance.ServiceConnection != null) + { + var (status, response) = await associatedInstance.ServiceConnection.SendMessageForResponseAsync(new ValueSet() { + { "Arguments", "FileOperation" }, + { "fileop", "DragDrop" }, + { "droptext", "DragDropWindowText".GetLocalized() }, + { "droppath", associatedInstance.FilesystemViewModel.WorkingDirectory } }); + return (status == AppServiceResponseStatus.Success && response.Get("Success", false)) ? ReturnResult.Success : ReturnResult.Failed; + } + return ReturnResult.Failed; } catch (Exception ex) { @@ -929,7 +940,16 @@ public async Task MoveItemsFromClipboard(DataPackageView packageVi } catch (Exception ex) when ((uint)ex.HResult == 0x80040064 || (uint)ex.HResult == 0x8004006A) { - return ReturnResult.UnknownException; + if (associatedInstance.ServiceConnection != null) + { + var (status, response) = await associatedInstance.ServiceConnection.SendMessageForResponseAsync(new ValueSet() { + { "Arguments", "FileOperation" }, + { "fileop", "DragDrop" }, + { "droptext", "DragDropWindowText".GetLocalized() }, + { "droppath", associatedInstance.FilesystemViewModel.WorkingDirectory } }); + return (status == AppServiceResponseStatus.Success && response.Get("Success", false)) ? ReturnResult.Success : ReturnResult.Failed; + } + return ReturnResult.Failed; } catch (Exception ex) { diff --git a/Files/Interacts/BaseLayoutCommandImplementationModel.cs b/Files/Interacts/BaseLayoutCommandImplementationModel.cs index 920d271958db..693880efc3d1 100644 --- a/Files/Interacts/BaseLayoutCommandImplementationModel.cs +++ b/Files/Interacts/BaseLayoutCommandImplementationModel.cs @@ -501,7 +501,7 @@ public virtual async void DragOver(DragEventArgs e) if (e.DataView.Contains(StandardDataFormats.StorageItems)) { e.Handled = true; - IEnumerable draggedItems = new List(); + IEnumerable draggedItems; try { draggedItems = await e.DataView.GetStorageItemsAsync(); @@ -509,13 +509,11 @@ public virtual async void DragOver(DragEventArgs e) catch (Exception ex) when ((uint)ex.HResult == 0x80040064 || (uint)ex.HResult == 0x8004006A) { // Handled by FTP + draggedItems = new List(); } catch (Exception ex) { App.Logger.Warn(ex, ex.Message); - } - if (!draggedItems.Any()) - { e.AcceptedOperation = DataPackageOperation.None; deferral.Complete(); return; @@ -524,10 +522,16 @@ public virtual async void DragOver(DragEventArgs e) var pwd = associatedInstance.FilesystemViewModel.WorkingDirectory.TrimPath(); var folderName = (Path.IsPathRooted(pwd) && Path.GetPathRoot(pwd) == pwd) ? Path.GetPathRoot(pwd) : Path.GetFileName(pwd); // As long as one file doesn't already belong to this folder - if (associatedInstance.InstanceViewModel.IsPageTypeSearchResults || draggedItems.AreItemsAlreadyInFolder(associatedInstance.FilesystemViewModel.WorkingDirectory)) + if (associatedInstance.InstanceViewModel.IsPageTypeSearchResults || (draggedItems.Any() && draggedItems.AreItemsAlreadyInFolder(associatedInstance.FilesystemViewModel.WorkingDirectory))) { e.AcceptedOperation = DataPackageOperation.None; } + else if (!draggedItems.Any()) + { + e.DragUIOverride.IsCaptionVisible = true; + e.DragUIOverride.Caption = string.Format("MoveToFolderCaptionText".GetLocalized(), folderName); + e.AcceptedOperation = DataPackageOperation.Move; + } else { e.DragUIOverride.IsCaptionVisible = true; diff --git a/Files/UserControls/SidebarControl.xaml.cs b/Files/UserControls/SidebarControl.xaml.cs index 43bbe9efb16e..2a5f681324d4 100644 --- a/Files/UserControls/SidebarControl.xaml.cs +++ b/Files/UserControls/SidebarControl.xaml.cs @@ -481,9 +481,8 @@ private async void NavigationViewLocationItem_DragOver(object sender, DragEventA } catch (Exception ex) when ((uint)ex.HResult == 0x80040064 || (uint)ex.HResult == 0x8004006A) { - e.AcceptedOperation = DataPackageOperation.None; - deferral.Complete(); - return; + // Handled by FTP + storageItems = new List(); } catch (Exception ex) { @@ -493,13 +492,18 @@ private async void NavigationViewLocationItem_DragOver(object sender, DragEventA return; } - if (storageItems.Count == 0 || - string.IsNullOrEmpty(locationItem.Path) || + if (string.IsNullOrEmpty(locationItem.Path) || locationItem.Path.Equals(App.AppSettings.RecycleBinPath, StringComparison.OrdinalIgnoreCase) || - storageItems.AreItemsAlreadyInFolder(locationItem.Path)) + (storageItems.Any() && storageItems.AreItemsAlreadyInFolder(locationItem.Path))) { e.AcceptedOperation = DataPackageOperation.None; } + else if (!storageItems.Any()) + { + e.DragUIOverride.IsCaptionVisible = true; + e.DragUIOverride.Caption = string.Format("MoveToFolderCaptionText".GetLocalized(), locationItem.Text); + e.AcceptedOperation = DataPackageOperation.Move; + } else { e.DragUIOverride.IsCaptionVisible = true; @@ -607,9 +611,8 @@ private async void NavigationViewDriveItem_DragOver(object sender, DragEventArgs } catch (Exception ex) when ((uint)ex.HResult == 0x80040064 || (uint)ex.HResult == 0x8004006A) { - e.AcceptedOperation = DataPackageOperation.None; - deferral.Complete(); - return; + // Handled by FTP + storageItems = new List(); } catch (Exception ex) { @@ -619,12 +622,17 @@ private async void NavigationViewDriveItem_DragOver(object sender, DragEventArgs return; } - if (storageItems.Count == 0 || - "DriveCapacityUnknown".GetLocalized().Equals(driveItem.SpaceText, StringComparison.OrdinalIgnoreCase) || - storageItems.AreItemsAlreadyInFolder(driveItem.Path)) + if ("DriveCapacityUnknown".GetLocalized().Equals(driveItem.SpaceText, StringComparison.OrdinalIgnoreCase) || + (storageItems.Any() && storageItems.AreItemsAlreadyInFolder(driveItem.Path))) { e.AcceptedOperation = DataPackageOperation.None; } + else if (!storageItems.Any()) + { + e.DragUIOverride.IsCaptionVisible = true; + e.DragUIOverride.Caption = string.Format("MoveToFolderCaptionText".GetLocalized(), driveItem.Text); + e.AcceptedOperation = DataPackageOperation.Move; + } else { e.DragUIOverride.IsCaptionVisible = true; diff --git a/Files/ViewModels/NavToolbarViewModel.cs b/Files/ViewModels/NavToolbarViewModel.cs index cbebf94324f4..424b88b8d437 100644 --- a/Files/ViewModels/NavToolbarViewModel.cs +++ b/Files/ViewModels/NavToolbarViewModel.cs @@ -273,9 +273,9 @@ public async void PathBoxItem_DragOver(object sender, DragEventArgs e) } if (!storageItems.Any(storageItem => - storageItem.Path.Replace(pathBoxItem.Path, string.Empty). - Trim(Path.DirectorySeparatorChar). - Contains(Path.DirectorySeparatorChar))) + storageItem.Path.Replace(pathBoxItem.Path, string.Empty). + Trim(Path.DirectorySeparatorChar). + Contains(Path.DirectorySeparatorChar))) { e.AcceptedOperation = DataPackageOperation.None; } From 94cf7d84c2208f43d0c672ab8e0b1b51bf320b2f Mon Sep 17 00:00:00 2001 From: Marco Gavelli Date: Sat, 19 Jun 2021 20:41:08 +0200 Subject: [PATCH 6/9] Only copy is supported when dropping from external program --- Files/BaseLayout.cs | 4 ++-- Files/Interacts/BaseLayoutCommandImplementationModel.cs | 4 ++-- Files/UserControls/SidebarControl.xaml.cs | 8 ++++---- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Files/BaseLayout.cs b/Files/BaseLayout.cs index f976afabfdcd..a2185b541bef 100644 --- a/Files/BaseLayout.cs +++ b/Files/BaseLayout.cs @@ -632,8 +632,8 @@ protected async void Item_DragOver(object sender, DragEventArgs e) else if (!draggedItems.Any()) { e.DragUIOverride.IsCaptionVisible = true; - e.DragUIOverride.Caption = string.Format("MoveToFolderCaptionText".GetLocalized(), item.ItemName); - e.AcceptedOperation = DataPackageOperation.Move; + e.DragUIOverride.Caption = string.Format("CopyToFolderCaptionText".GetLocalized(), item.ItemName); + e.AcceptedOperation = DataPackageOperation.Copy; } else { diff --git a/Files/Interacts/BaseLayoutCommandImplementationModel.cs b/Files/Interacts/BaseLayoutCommandImplementationModel.cs index 693880efc3d1..0a498981f4e5 100644 --- a/Files/Interacts/BaseLayoutCommandImplementationModel.cs +++ b/Files/Interacts/BaseLayoutCommandImplementationModel.cs @@ -529,8 +529,8 @@ public virtual async void DragOver(DragEventArgs e) else if (!draggedItems.Any()) { e.DragUIOverride.IsCaptionVisible = true; - e.DragUIOverride.Caption = string.Format("MoveToFolderCaptionText".GetLocalized(), folderName); - e.AcceptedOperation = DataPackageOperation.Move; + e.DragUIOverride.Caption = string.Format("CopyToFolderCaptionText".GetLocalized(), folderName); + e.AcceptedOperation = DataPackageOperation.Copy; } else { diff --git a/Files/UserControls/SidebarControl.xaml.cs b/Files/UserControls/SidebarControl.xaml.cs index 2a5f681324d4..ec847f414b51 100644 --- a/Files/UserControls/SidebarControl.xaml.cs +++ b/Files/UserControls/SidebarControl.xaml.cs @@ -501,8 +501,8 @@ private async void NavigationViewLocationItem_DragOver(object sender, DragEventA else if (!storageItems.Any()) { e.DragUIOverride.IsCaptionVisible = true; - e.DragUIOverride.Caption = string.Format("MoveToFolderCaptionText".GetLocalized(), locationItem.Text); - e.AcceptedOperation = DataPackageOperation.Move; + e.DragUIOverride.Caption = string.Format("CopyToFolderCaptionText".GetLocalized(), locationItem.Text); + e.AcceptedOperation = DataPackageOperation.Copy; } else { @@ -630,8 +630,8 @@ private async void NavigationViewDriveItem_DragOver(object sender, DragEventArgs else if (!storageItems.Any()) { e.DragUIOverride.IsCaptionVisible = true; - e.DragUIOverride.Caption = string.Format("MoveToFolderCaptionText".GetLocalized(), driveItem.Text); - e.AcceptedOperation = DataPackageOperation.Move; + e.DragUIOverride.Caption = string.Format("CopyToFolderCaptionText".GetLocalized(), driveItem.Text); + e.AcceptedOperation = DataPackageOperation.Copy; } else { From a20bbce5a761c73a09d323ac7a0ab8073d5cd6f3 Mon Sep 17 00:00:00 2001 From: Marco Gavelli Date: Sat, 19 Jun 2021 21:08:13 +0200 Subject: [PATCH 7/9] Add support for dropping to recycle bin --- .../FilesystemOperations.cs | 4 +- .../Helpers/FilesystemHelpers.cs | 46 ++++++++++++++----- .../BaseLayoutCommandImplementationModel.cs | 7 ++- Files/UserControls/SidebarControl.xaml.cs | 8 +++- 4 files changed, 49 insertions(+), 16 deletions(-) diff --git a/Files/Filesystem/FilesystemOperations/FilesystemOperations.cs b/Files/Filesystem/FilesystemOperations/FilesystemOperations.cs index a02c356aaf14..cc53f8e602e9 100644 --- a/Files/Filesystem/FilesystemOperations/FilesystemOperations.cs +++ b/Files/Filesystem/FilesystemOperations/FilesystemOperations.cs @@ -124,7 +124,7 @@ public async Task CopyAsync(IStorageItemWithPath source, IProgress errorCode, CancellationToken cancellationToken) { - if (associatedInstance.FilesystemViewModel.WorkingDirectory.StartsWith(App.AppSettings.RecycleBinPath)) + if (destination.StartsWith(App.AppSettings.RecycleBinPath)) { errorCode?.Report(FileSystemStatusCode.Unauthorized); progress?.Report(100.0f); @@ -331,7 +331,7 @@ public async Task MoveAsync(IStorageItemWithPath source, return await CopyAsync(source, destination, collision, progress, errorCode, cancellationToken); } - if (associatedInstance.FilesystemViewModel.WorkingDirectory.StartsWith(App.AppSettings.RecycleBinPath)) + if (destination.StartsWith(App.AppSettings.RecycleBinPath)) { errorCode?.Report(FileSystemStatusCode.Unauthorized); progress?.Report(100.0f); diff --git a/Files/Filesystem/FilesystemOperations/Helpers/FilesystemHelpers.cs b/Files/Filesystem/FilesystemOperations/Helpers/FilesystemHelpers.cs index dd6f938245f0..0f203ac39fd0 100644 --- a/Files/Filesystem/FilesystemOperations/Helpers/FilesystemHelpers.cs +++ b/Files/Filesystem/FilesystemOperations/Helpers/FilesystemHelpers.cs @@ -473,6 +473,10 @@ public async Task PerformOperationTypeAsync(DataPackageOperation o { return default; } + if (destination.StartsWith(App.AppSettings.RecycleBinPath)) + { + return await RecycleItemsFromClipboard(packageView, destination, showDialog, registerHistory); + } else if (operation.HasFlag(DataPackageOperation.Copy)) { return await CopyItemsFromClipboard(packageView, destination, showDialog, registerHistory); @@ -676,6 +680,36 @@ public async Task CopyItemAsync(IStorageItemWithPath source, strin return returnStatus; } + public async Task RecycleItemsFromClipboard(DataPackageView packageView, string destination, bool showDialog, bool registerHistory) + { + if (!packageView.Contains(StandardDataFormats.StorageItems)) + { + // Happens if you copy some text and then you Ctrl+V in Files + return ReturnResult.BadArgumentException; + } + + IReadOnlyList source; + try + { + source = await packageView.GetStorageItemsAsync(); + } + catch (Exception ex) when ((uint)ex.HResult == 0x80040064 || (uint)ex.HResult == 0x8004006A) + { + // Not supported + return ReturnResult.Failed; + } + catch (Exception ex) + { + App.Logger.Warn(ex, ex.Message); + return ReturnResult.UnknownException; + } + ReturnResult returnStatus = ReturnResult.InProgress; + + returnStatus = await DeleteItemsAsync(source, showDialog, false, registerHistory); + + return returnStatus; + } + public async Task CopyItemsFromClipboard(DataPackageView packageView, string destination, bool showDialog, bool registerHistory) { if (packageView.Contains(StandardDataFormats.StorageItems)) @@ -744,7 +778,6 @@ public async Task CopyItemsFromClipboard(DataPackageView packageVi } // Happens if you copy some text and then you Ctrl+V in Files - // Should this be done in ModernShellPage? return ReturnResult.BadArgumentException; } @@ -929,7 +962,6 @@ public async Task MoveItemsFromClipboard(DataPackageView packageVi if (!packageView.Contains(StandardDataFormats.StorageItems)) { // Happens if you copy some text and then you Ctrl+V in Files - // Should this be done in ModernShellPage? return ReturnResult.BadArgumentException; } @@ -940,15 +972,7 @@ public async Task MoveItemsFromClipboard(DataPackageView packageVi } catch (Exception ex) when ((uint)ex.HResult == 0x80040064 || (uint)ex.HResult == 0x8004006A) { - if (associatedInstance.ServiceConnection != null) - { - var (status, response) = await associatedInstance.ServiceConnection.SendMessageForResponseAsync(new ValueSet() { - { "Arguments", "FileOperation" }, - { "fileop", "DragDrop" }, - { "droptext", "DragDropWindowText".GetLocalized() }, - { "droppath", associatedInstance.FilesystemViewModel.WorkingDirectory } }); - return (status == AppServiceResponseStatus.Success && response.Get("Success", false)) ? ReturnResult.Success : ReturnResult.Failed; - } + // Not supported return ReturnResult.Failed; } catch (Exception ex) diff --git a/Files/Interacts/BaseLayoutCommandImplementationModel.cs b/Files/Interacts/BaseLayoutCommandImplementationModel.cs index 0a498981f4e5..a1cc14dde852 100644 --- a/Files/Interacts/BaseLayoutCommandImplementationModel.cs +++ b/Files/Interacts/BaseLayoutCommandImplementationModel.cs @@ -535,7 +535,12 @@ public virtual async void DragOver(DragEventArgs e) else { e.DragUIOverride.IsCaptionVisible = true; - if (e.Modifiers.HasFlag(DragDropModifiers.Control)) + if (pwd.StartsWith(App.AppSettings.RecycleBinPath)) + { + e.DragUIOverride.Caption = string.Format("MoveToFolderCaptionText".GetLocalized(), folderName); + e.AcceptedOperation = DataPackageOperation.Move; + } + else if (e.Modifiers.HasFlag(DragDropModifiers.Control)) { e.DragUIOverride.Caption = string.Format("CopyToFolderCaptionText".GetLocalized(), folderName); e.AcceptedOperation = DataPackageOperation.Copy; diff --git a/Files/UserControls/SidebarControl.xaml.cs b/Files/UserControls/SidebarControl.xaml.cs index ec847f414b51..c8ad57c9210b 100644 --- a/Files/UserControls/SidebarControl.xaml.cs +++ b/Files/UserControls/SidebarControl.xaml.cs @@ -493,7 +493,6 @@ private async void NavigationViewLocationItem_DragOver(object sender, DragEventA } if (string.IsNullOrEmpty(locationItem.Path) || - locationItem.Path.Equals(App.AppSettings.RecycleBinPath, StringComparison.OrdinalIgnoreCase) || (storageItems.Any() && storageItems.AreItemsAlreadyInFolder(locationItem.Path))) { e.AcceptedOperation = DataPackageOperation.None; @@ -507,7 +506,12 @@ private async void NavigationViewLocationItem_DragOver(object sender, DragEventA else { e.DragUIOverride.IsCaptionVisible = true; - if (e.Modifiers.HasFlag(DragDropModifiers.Control)) + if (locationItem.Path.StartsWith(App.AppSettings.RecycleBinPath)) + { + e.DragUIOverride.Caption = string.Format("MoveToFolderCaptionText".GetLocalized(), locationItem.Text); + e.AcceptedOperation = DataPackageOperation.Move; + } + else if (e.Modifiers.HasFlag(DragDropModifiers.Control)) { e.DragUIOverride.Caption = string.Format("CopyToFolderCaptionText".GetLocalized(), locationItem.Text); e.AcceptedOperation = DataPackageOperation.Copy; From 273439037326efa09b006f3fcba240c7b24fb540 Mon Sep 17 00:00:00 2001 From: Marco Gavelli Date: Sat, 19 Jun 2021 21:12:30 +0200 Subject: [PATCH 8/9] Small change --- .../BaseLayoutCommandImplementationModel.cs | 13 ++++++++++--- Files/UserControls/SidebarControl.xaml.cs | 13 ++++++++++--- 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/Files/Interacts/BaseLayoutCommandImplementationModel.cs b/Files/Interacts/BaseLayoutCommandImplementationModel.cs index a1cc14dde852..7c9329f0b0ef 100644 --- a/Files/Interacts/BaseLayoutCommandImplementationModel.cs +++ b/Files/Interacts/BaseLayoutCommandImplementationModel.cs @@ -528,9 +528,16 @@ public virtual async void DragOver(DragEventArgs e) } else if (!draggedItems.Any()) { - e.DragUIOverride.IsCaptionVisible = true; - e.DragUIOverride.Caption = string.Format("CopyToFolderCaptionText".GetLocalized(), folderName); - e.AcceptedOperation = DataPackageOperation.Copy; + if (pwd.StartsWith(App.AppSettings.RecycleBinPath)) + { + e.AcceptedOperation = DataPackageOperation.None; + } + else + { + e.DragUIOverride.IsCaptionVisible = true; + e.DragUIOverride.Caption = string.Format("CopyToFolderCaptionText".GetLocalized(), folderName); + e.AcceptedOperation = DataPackageOperation.Copy; + } } else { diff --git a/Files/UserControls/SidebarControl.xaml.cs b/Files/UserControls/SidebarControl.xaml.cs index c8ad57c9210b..5902b23e6e39 100644 --- a/Files/UserControls/SidebarControl.xaml.cs +++ b/Files/UserControls/SidebarControl.xaml.cs @@ -499,9 +499,16 @@ private async void NavigationViewLocationItem_DragOver(object sender, DragEventA } else if (!storageItems.Any()) { - e.DragUIOverride.IsCaptionVisible = true; - e.DragUIOverride.Caption = string.Format("CopyToFolderCaptionText".GetLocalized(), locationItem.Text); - e.AcceptedOperation = DataPackageOperation.Copy; + if (locationItem.Path.StartsWith(App.AppSettings.RecycleBinPath)) + { + e.AcceptedOperation = DataPackageOperation.None; + } + else + { + e.DragUIOverride.IsCaptionVisible = true; + e.DragUIOverride.Caption = string.Format("CopyToFolderCaptionText".GetLocalized(), locationItem.Text); + e.AcceptedOperation = DataPackageOperation.Copy; + } } else { From 0ebcd47bc8c47ed591e193e3fc3f652f451ce2d4 Mon Sep 17 00:00:00 2001 From: Marco Gavelli Date: Sun, 20 Jun 2021 06:18:09 +0200 Subject: [PATCH 9/9] Add code attribution --- Files.Launcher/RemoteDataObject.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Files.Launcher/RemoteDataObject.cs b/Files.Launcher/RemoteDataObject.cs index 95c856ef9de5..c916e2c26d86 100644 --- a/Files.Launcher/RemoteDataObject.cs +++ b/Files.Launcher/RemoteDataObject.cs @@ -10,6 +10,7 @@ namespace FilesFullTrust { + // Class taken from Rx-Explorer (https://github.com/zhuxb711/RX-Explorer) public class RemoteDataObject { ///