filesremote
filesremote copied to clipboard
Drag-n-Drop Support?
This tool is amazing and thank you for your great job in this project! I think it would be great if drag-n-drop support could be added to the already impressive feature set. Basically, drag files/folders from the main window to a file manager window (explorer in Windows, finder in Mac ...) will automatically download the files to the target folder, and uploading works the same way but in a different direction. This would greatly improve usability.
Thanks for the feedback!
Yea I really would love to have full proper drag-and-drop support. I actually tried for quite a while about a year ago, but hit roadblocks with the UI framework I am using, wxWidgets.
If I recall correctly, it was especially challenging because the API for drag-and-drop goes something like: OS calls an event on the app when the mouse cursor is released over a file manager window, and the app needs to immediately respond with a path to local file the OS can copy or move to the target location. The problem is that it will take maybe a second or more to download the file over SSH, and if the OS doesn't get a response immediately it just times out and ignores the drag-and-drop attempt.
I remember there was also some special challenge I tried to work around for Mac OS, but don't remember exactly what it was.
Can't promise I will get time to revisit it any time soon, as I remember trying hard and giving up. I think it may require doing some trickery for each platform natively, as I think it's beyond the capabilities of wxWidgets. For sure it can be done natively somehow, as I remember seeing WinSCP do it quite nicely.
If any one is up for this coding challenge, let me know!
Here is the branch from a year ago where I attempted it: https://github.com/allanrbo/filesremote/commit/3a934af6ada66a865026ab1cfa2253e0a62030ab
Thanks for your explanation! C++ is not my primary language, but from your description it seems that the OS requires the application to give a path whenever a drag-and-drop event happens.
Is it possible to give a fake / placeholder path (for example, somewhere in a temporary folder, using current timestamp / GUID as file / folder name) to OS, using it as an intermediate location? For example, when a file is dragged outside the app into a file manager (indicating a download operation), first a placeholder file is copied to the target path, then a thread is initiated and begin the download in the background (with a progress bar), finally the file is moved to the target path and the placeholder file is deleted.
As you mentioned WinSCP, this reminded me with another type of application that requires a long time for drag-and-drop operation: zip file managers (7zip, Bandizip, peazip...). I am a user of Bandizip, and if I try to drag a large file from the app to a file manager window, there're 2 stages. In the first stage, Bandizip shows a progress bar for extracting the file. In the next stage, a Windows file copy progress bar is displayed, showing the file being copied from a temporary location to the desired target location. Will this be easier to implement?
Again, this is a just an enhancement. Feel free to delay it later and there is no pressure to finish it now. You're already doing an amazing job!
Hi @jerrylususu . Thanks for the suggestion. You are right that the OS requires the path when the drag-and-drop event starts, but that is actually part of the problem. It would be much easier if the OS instead just gave a destination where the app could copy to.
I was considering the idea of a temp file, but I think the problem was the following. At least with the wxWidgets API.
- App gives the API a path to copy/move from (for example
C:\Users\user1\AppData\Local\Temp\somefile.txt
) - The OS now copies/moves from this path (
C:\Users\user1\AppData\Local\Temp\somefile.txt
) to the actual destination corresponding to the window we dragged to (for exampleC:\Users\user1\Desktop
). - Now meanwhile if we in parallel download and write to for example
C:\Users\user1\AppData\Local\Temp\somefile.txt
, it is already too late. The OS has already copied from src to dst.
The API never notified back to the app what the actual dst was. That is hidden to the app. It's the app telling the OS where to copy from, not the OS telling the app where to copy to, unfortunately.
In the before mentioned branch, I was trying a trick with detecting when a drag was initiated, and then at that moment do the download in a synchronous fashion (freeze the UI), until the download is complete, in order for the file to be able to give the API a usable local src path. Obviously this will only fly for small files, as it's hanging the UI, causing the OS to think the app has crashed if it goes on for too long. And even with small files, I couldn't get it to work reliably every time. It was very flaky...
I hope to revisit this at some point, and try if something can be done directly with Win32 / Cocoa / GTK, since wxWidgets seems unable by itself, but can't promise it will be any time soon unfortunately. When I do, I have noted your suggestion of taking a look at 7zip and similar for inspiration. That's a good idea.
After reading more about how Windows Explorer's drag and drop works, I found my understanding misleading and your explanation is indeed correct.
On the FAQ page of 7zip, it says
Why does drag-and-drop archive extraction from 7-Zip to Explorer use temp files?
7-Zip doesn't know folder path of drop target. Only Windows Explorer knows exact drop target. And Windows Explorer needs files (drag source) as decompressed files on disk. So 7-Zip extracts files from archive to temp folder and then 7-Zip notifies Windows Explorer about paths of these temp files. Then Windows Explorer copies these files to drop target folder.
And in the source code of 7zip (CPP/7zip/UI/FileManger/PanelDrag.cpp: 451)
Now we don't know any good way to detect destination path for D&D to Explorer.
I think some notification mechanism might be the only possible way for implementing this feature (in Windows). I tried to manually go through the drag-and-drop support part of 7zip source code, and it seems that they just create a temporary path and decompress the files into it. Unfortunately I failed to find how the notification works.
Another nasty hack would be creating a dummy file, create a file watcher on that file, let Windows copy it to some path, and then utilize that path. But that also doesn't sound very elegant.
More Info:
- A stack overflow question on this drag-and-drop problem: https://stackoverflow.com/questions/20971127/get-file-path-for-drag-n-drop-file-on-windows-explorer
- A project using the file watcher method to determine the destination path for a drag-and-drop operation: https://www.codeproject.com/Articles/23207/Drag-and-Drop-to-Windows-Folder-C?msg=5397030#xx5397030xx
So after some more digging I think I finally found out what is the name of such operations: Async Drag and Drop. Here is a related MSDN document: https://docs.microsoft.com/en-us/windows/win32/shell/datascenarios#dragging-and-dropping-shell-objects-asynchronously
Basically in Windows it's possible to define a Drag-n-Drop operation as async variant, then it's possible to create the file in a background thread, and notify the OS to copy or move the file after it's been populated. Hope that helps.
Another way (adopted by Filezilla) is to create a shell extension to monitor the destination path. See https://stackoverflow.com/questions/38804272/qt-drag-and-drop-to-windows-explorer-how-do-i-know-the-dropped-destination-path . This method is also adopted by WinSCP. Here is a link: https://winscp.net/eng/docs/dragext#technical_background
However all these methods seems to be platform specific (Windows only). As I am not a Mac user and only works with Linux without GUI, unfortunately I am not able to provide more information on cross platform compatible solutions.
P.S. Further reading WinSCP source code shows that when their shell extension is not enabled, they fallback to use fake folder + file system watcher. (Doc Page)
WinSCP: source/forms/CustomScpExplorer.cpp:7406
void __fastcall TCustomScpExplorerForm::DDFakeFileInitDrag(TFileList * FileList,
bool & Created)
{
FFakeFileDropTarget = UnicodeString();
FDragFakeDirectory = CreateFakeTransferDirectory();
FileList->AddItem(NULL, FDragFakeDirectory);
Created = true;
if (!WinConfiguration->IsDDExtRunning() || WinConfiguration->IsDDExtBroken())
{
FDragFakeMonitors = StartCreationDirectoryMonitorsOnEachDrive(FILE_NOTIFY_CHANGE_DIR_NAME, DDFakeCreated);
}
Nice find with the async drag-and-drop API. But yea, it's a shame it's Windows only.
Hadn't thought of the file watcher idea. Interesting that they do that in WinSCP. Though it is a slight hack as you said, it may actually be the best cross platform option. I was just looking at github.com/emcrisostomo/fswatch , and it seems to support watching for file modifications, but unsure about also copy/move operations.
But as mentioned, I got my hands full these days so can't guarantee I will get time to try to implement this in the foreseeable future. But these are great data points for in the future where I will hopefully get time. Or if you want to take a stab at it as mentioned, that would be welcome :-)
- App gives the API a path to copy/move from (for example
C:\Users\user1\AppData\Local\Temp\somefile.txt
)- The OS now copies/moves from this path (
C:\Users\user1\AppData\Local\Temp\somefile.txt
) to the actual destination corresponding to the window we dragged to (for exampleC:\Users\user1\Desktop
).- Now meanwhile if we in parallel download and write to for example
C:\Users\user1\AppData\Local\Temp\somefile.txt
, it is already too late. The OS has already copied from src to dst.
Sorry if this is not revelant anymore, i just found out about your program, not even tried it yet...
Anyways, got couple ideas about this:
-
What if you immediately place exclusive lock on the dummy file. This way the OS would display an error dialog, and the user can click on "Retry" after the transfer is finished. Of course, this relies on the error dialog having a "Retry" button, and not just a single "OK, error acknowledged, close" button.
-
Can you just take note of the drop target and postpone sending the drag'n'drop event notification? Maybe cover the whole desktop with an invisible window immediately when you detect dragging? If so, you can show a progress dialog, finish the transfer, and then notify the actual target process.