From 6294bc4505f0f600c8933a5b883ea4a8f582929e Mon Sep 17 00:00:00 2001 From: David Given Date: Thu, 8 Sep 2022 00:08:31 +0200 Subject: [PATCH] Drag-and-drop for moving one file at a time works... badly. --- lib/vfs/vfs.cc | 7 ++ lib/vfs/vfs.h | 1 + src/gui/layout.cpp | 8 +- src/gui/layout.fbp | 7 +- src/gui/layout.h | 3 + src/gui/mainwindow.cc | 168 ++++++++++++++++++++++++++++++++---------- 6 files changed, 151 insertions(+), 43 deletions(-) diff --git a/lib/vfs/vfs.cc b/lib/vfs/vfs.cc index 23f512cb..db10c97c 100644 --- a/lib/vfs/vfs.cc +++ b/lib/vfs/vfs.cc @@ -50,6 +50,13 @@ Path Path::parent() const return p; } +Path Path::concat(const std::string& s) const +{ + Path p(*this); + p.push_back(s); + return p; +} + std::string Path::to_str(const std::string sep) const { return join(*this, sep); diff --git a/lib/vfs/vfs.h b/lib/vfs/vfs.h index 0095c2fe..1efc972b 100644 --- a/lib/vfs/vfs.h +++ b/lib/vfs/vfs.h @@ -19,6 +19,7 @@ public: public: Path parent() const; + Path concat(const std::string& s) const; std::string to_str(const std::string sep = "/") const; }; diff --git a/src/gui/layout.cpp b/src/gui/layout.cpp index 9aa262db..ed909479 100644 --- a/src/gui/layout.cpp +++ b/src/gui/layout.cpp @@ -330,7 +330,7 @@ MainWindowGen::MainWindowGen( wxWindow* parent, wxWindowID id, const wxString& t m_dataViewColumn3 = browserTree->AppendTextColumn( wxT("Mode"), 2, wxDATAVIEW_CELL_INERT, -1, static_cast(wxALIGN_LEFT), wxDATAVIEW_COL_RESIZABLE ); fgSizer23->Add( browserTree, 0, wxALL|wxEXPAND, 5 ); - diskSpaceGauge = new wxGauge( browsePanel, wxID_ANY, 100, wxDefaultPosition, wxDefaultSize, wxGA_HORIZONTAL ); + diskSpaceGauge = new wxGauge( browsePanel, wxID_ANY, 100, wxDefaultPosition, wxDefaultSize, wxGA_HORIZONTAL|wxGA_SMOOTH ); diskSpaceGauge->SetValue( 0 ); fgSizer23->Add( diskSpaceGauge, 0, wxALL|wxEXPAND, 5 ); @@ -400,6 +400,9 @@ MainWindowGen::MainWindowGen( wxWindow* parent, wxWindowID id, const wxString& t browserMoreMenu->Bind(wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainWindowGen::OnBrowserRenameMenuItem ), this, browserRenameMenuItem->GetId()); browserMoreMenu->Bind(wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainWindowGen::OnBrowserDeleteMenuItem ), this, browserDeleteMenuItem->GetId()); this->Connect( browserFormatTool->GetId(), wxEVT_COMMAND_TOOL_CLICKED, wxCommandEventHandler( MainWindowGen::OnBrowserFormatButton ) ); + browserTree->Connect( wxEVT_COMMAND_DATAVIEW_ITEM_BEGIN_DRAG, wxDataViewEventHandler( MainWindowGen::OnBrowserBeginDrag ), NULL, this ); + browserTree->Connect( wxEVT_COMMAND_DATAVIEW_ITEM_DROP, wxDataViewEventHandler( MainWindowGen::OnBrowserDrop ), NULL, this ); + browserTree->Connect( wxEVT_COMMAND_DATAVIEW_ITEM_DROP_POSSIBLE, wxDataViewEventHandler( MainWindowGen::OnBrowserDropPossible ), NULL, this ); browserTree->Connect( wxEVT_COMMAND_DATAVIEW_ITEM_EDITING_DONE, wxDataViewEventHandler( MainWindowGen::OnBrowserFilenameChanged ), NULL, this ); browserTree->Connect( wxEVT_COMMAND_DATAVIEW_ITEM_EXPANDING, wxDataViewEventHandler( MainWindowGen::OnBrowserDirectoryExpanding ), NULL, this ); browserTree->Connect( wxEVT_COMMAND_DATAVIEW_SELECTION_CHANGED, wxDataViewEventHandler( MainWindowGen::OnBrowserSelectionChanged ), NULL, this ); @@ -433,6 +436,9 @@ MainWindowGen::~MainWindowGen() this->Disconnect( browserViewTool->GetId(), wxEVT_COMMAND_TOOL_CLICKED, wxCommandEventHandler( MainWindowGen::OnBrowserViewButton ) ); this->Disconnect( browserSaveTool->GetId(), wxEVT_COMMAND_TOOL_CLICKED, wxCommandEventHandler( MainWindowGen::OnBrowserSaveButton ) ); this->Disconnect( browserFormatTool->GetId(), wxEVT_COMMAND_TOOL_CLICKED, wxCommandEventHandler( MainWindowGen::OnBrowserFormatButton ) ); + browserTree->Disconnect( wxEVT_COMMAND_DATAVIEW_ITEM_BEGIN_DRAG, wxDataViewEventHandler( MainWindowGen::OnBrowserBeginDrag ), NULL, this ); + browserTree->Disconnect( wxEVT_COMMAND_DATAVIEW_ITEM_DROP, wxDataViewEventHandler( MainWindowGen::OnBrowserDrop ), NULL, this ); + browserTree->Disconnect( wxEVT_COMMAND_DATAVIEW_ITEM_DROP_POSSIBLE, wxDataViewEventHandler( MainWindowGen::OnBrowserDropPossible ), NULL, this ); browserTree->Disconnect( wxEVT_COMMAND_DATAVIEW_ITEM_EDITING_DONE, wxDataViewEventHandler( MainWindowGen::OnBrowserFilenameChanged ), NULL, this ); browserTree->Disconnect( wxEVT_COMMAND_DATAVIEW_ITEM_EXPANDING, wxDataViewEventHandler( MainWindowGen::OnBrowserDirectoryExpanding ), NULL, this ); browserTree->Disconnect( wxEVT_COMMAND_DATAVIEW_SELECTION_CHANGED, wxDataViewEventHandler( MainWindowGen::OnBrowserSelectionChanged ), NULL, this ); diff --git a/src/gui/layout.fbp b/src/gui/layout.fbp index c6fe56a6..0d25841e 100644 --- a/src/gui/layout.fbp +++ b/src/gui/layout.fbp @@ -2500,11 +2500,14 @@ wxDV_SINGLE - ; ; forward_declare + + OnBrowserBeginDrag + OnBrowserDrop + OnBrowserDropPossible OnBrowserFilenameChanged OnBrowserDirectoryExpanding OnBrowserSelectionChanged @@ -2597,7 +2600,7 @@ Resizable 1 - wxGA_HORIZONTAL + wxGA_HORIZONTAL|wxGA_SMOOTH ; ; forward_declare 0 diff --git a/src/gui/layout.h b/src/gui/layout.h index b92048f2..45b97fd9 100644 --- a/src/gui/layout.h +++ b/src/gui/layout.h @@ -132,6 +132,9 @@ class MainWindowGen : public wxFrame virtual void OnBrowserRenameMenuItem( wxCommandEvent& event ) { event.Skip(); } virtual void OnBrowserDeleteMenuItem( wxCommandEvent& event ) { event.Skip(); } virtual void OnBrowserFormatButton( wxCommandEvent& event ) { event.Skip(); } + virtual void OnBrowserBeginDrag( wxDataViewEvent& event ) { event.Skip(); } + virtual void OnBrowserDrop( wxDataViewEvent& event ) { event.Skip(); } + virtual void OnBrowserDropPossible( wxDataViewEvent& event ) { event.Skip(); } virtual void OnBrowserFilenameChanged( wxDataViewEvent& event ) { event.Skip(); } virtual void OnBrowserDirectoryExpanding( wxDataViewEvent& event ) { event.Skip(); } virtual void OnBrowserSelectionChanged( wxDataViewEvent& event ) { event.Skip(); } diff --git a/src/gui/mainwindow.cc b/src/gui/mainwindow.cc index 9600a650..fa4f87c3 100644 --- a/src/gui/mainwindow.cc +++ b/src/gui/mainwindow.cc @@ -42,13 +42,25 @@ const std::string DEFAULT_EXTRA_CONFIGURATION = "# or the name of a built-in configuration, or the filename\n" "# of a text proto file. Or a comment, of course.\n\n"; +const std::string DND_TYPE = "fluxengine.files"; + +class CancelException +{ +}; + class MainWindow : public MainWindowGen { private: class FilesystemOperation; public: - MainWindow(): MainWindowGen(nullptr), _config("FluxEngine") + MainWindow(): + MainWindowGen(nullptr), + /* This is wrong. Apparently the wxDataViewCtrl doesn't work properly + * with DnD unless the format is wxDF_UNICODETEXT. It should be a custom + * value. */ + _dndFormat(wxDF_UNICODETEXT), + _config("FluxEngine") { Logger::setLogger( [&](std::shared_ptr message) @@ -116,6 +128,13 @@ public: NULL, this); + /* This is a bug workaround for an issue where the calculation of the + * item being dropped on is wrong due to the header not being taken into + * account. See https://forums.wxwidgets.org/viewtopic.php?t=44752. */ + + browserTree->EnableDragSource(_dndFormat); + browserTree->EnableDropTarget(_dndFormat); + /* I have no idea why this is necessary, but on Windows things aren't * laid out correctly without it. */ @@ -612,28 +631,31 @@ public: return path; } - void OnBrowserAddMenuItem(wxCommandEvent&) override + std::shared_ptr GetTargetDirectoryNode(wxDataViewItem& item) { - Path dirPath; - auto item = browserTree->GetSelection(); + Path path; if (item.IsOk()) { - auto dirNode = _filesystemModel->Find(item); - if (!dirNode) - return; - dirPath = dirNode->dirent->path; + auto node = _filesystemModel->Find(item); + if (!node) + return nullptr; + path = node->dirent->path; } - auto dirNode = _filesystemModel->Find(dirPath); + auto node = _filesystemModel->Find(path); + if (!node) + return nullptr; + if (node->dirent->file_type != TYPE_DIRECTORY) + return _filesystemModel->Find(path.parent()); + return node; + } + + void OnBrowserAddMenuItem(wxCommandEvent&) override + { + auto item = browserTree->GetSelection(); + auto dirNode = GetTargetDirectoryNode(item); if (!dirNode) return; - if (dirNode->dirent->file_type != TYPE_DIRECTORY) - { - dirPath = dirPath.parent(); - dirNode = _filesystemModel->Find(dirPath); - if (!dirNode) - return; - } auto localPath = wxFileSelector("Choose the name of the file to add", /* default_path= */ wxEmptyString, @@ -644,11 +666,11 @@ public: .ToStdString(); if (localPath.empty()) return; - Path path = dirPath; - path.push_back(wxFileName(localPath).GetFullName().ToStdString()); + auto path = dirNode->dirent->path.concat( + wxFileName(localPath).GetFullName().ToStdString()); QueueBrowserOperation( - [this, path, localPath, item]() mutable + [this, path, localPath]() mutable { path = ResolveFileConflicts_WT(path); if (path.empty()) @@ -672,6 +694,8 @@ public: { auto item = browserTree->GetSelection(); auto node = _filesystemModel->Find(item); + if (!node) + return; QueueBrowserOperation( [this, node]() @@ -720,13 +744,14 @@ public: if (node->newname.empty()) return; + if (node->newname == node->dirent->filename) + return; QueueBrowserOperation( [this, node]() mutable { auto oldPath = node->dirent->path; - auto newPath = oldPath.parent(); - newPath.push_back(node->newname); + auto newPath = oldPath.parent().concat(node->newname); newPath = ResolveFileConflicts_WT(newPath); if (newPath.empty()) @@ -756,8 +781,12 @@ public: if (d.ShowModal() != wxID_OK) return; - auto oldPath = node->dirent->path; - auto newPath = Path(d.newNameText->GetValue().ToStdString()); + ActuallyMoveFile( + node->dirent->path, Path(d.newNameText->GetValue().ToStdString())); + } + + void ActuallyMoveFile(const Path& oldPath, Path newPath) + { QueueBrowserOperation( [this, oldPath, newPath]() mutable { @@ -780,26 +809,11 @@ public: void OnBrowserNewDirectoryMenuItem(wxCommandEvent& event) { - Path path; auto item = browserTree->GetSelection(); - if (item.IsOk()) - { - auto node = _filesystemModel->Find(item); - if (!node) - return; - path = node->dirent->path; - } - - auto node = _filesystemModel->Find(path); + auto node = GetTargetDirectoryNode(item); if (!node) return; - if (node->dirent->file_type != TYPE_DIRECTORY) - { - path = path.parent(); - node = _filesystemModel->Find(path); - if (!node) - return; - } + auto path = node->dirent->path; CreateDirectoryDialog d(this, wxID_ANY); d.newNameText->SetValue(path.to_str() + "/"); @@ -823,6 +837,79 @@ public: }); } + void OnBrowserBeginDrag(wxDataViewEvent& event) override + { + auto item = browserTree->GetSelection(); + if (!item.IsOk()) + { + event.Veto(); + return; + } + + auto node = _filesystemModel->Find(item); + if (!node) + { + event.Veto(); + return; + } + + wxTextDataObject* obj = new wxTextDataObject(); + obj->SetText(node->dirent->path.to_str()); + event.SetDataObject(obj); + event.SetDataFormat(_dndFormat); + } + + void OnBrowserDropPossible(wxDataViewEvent& event) override + { + if (event.GetDataFormat() != _dndFormat) + { + event.Veto(); + return; + } + } + + void OnBrowserDrop(wxDataViewEvent& event) override + { + try + { + if (event.GetDataFormat() != _dndFormat) + throw CancelException(); + + /* wxWidgets 3.0 data view DnD is borked. See + * https://forums.wxwidgets.org/viewtopic.php?t=44752. The hit + * detection is done against the wrong object, resulting in the + * header size not being taken into account, so we have to manually + * do hit detection correctly. */ + + auto* window = browserTree->GetMainWindow(); + auto screenPos = wxGetMousePosition(); + auto relPos = screenPos - window->GetScreenPosition(); + + wxDataViewItem item; + wxDataViewColumn* column; + browserTree->HitTest(relPos, item, column); + if (!item.IsOk()) + throw CancelException(); + + auto destDirNode = GetTargetDirectoryNode(item); + if (!destDirNode) + throw CancelException(); + auto destDirPath = destDirNode->dirent->path; + + wxTextDataObject obj; + obj.SetData(_dndFormat, event.GetDataSize(), event.GetDataBuffer()); + auto srcPath = Path(obj.GetText().ToStdString()); + if (srcPath.empty()) + throw CancelException(); + + ActuallyMoveFile(srcPath, destDirPath.concat(srcPath.back())); + } + catch (const CancelException& e) + { + event.Veto(); + } + } + void OnBrowserCommitButton(wxCommandEvent&) override { QueueBrowserOperation( @@ -1378,6 +1465,7 @@ private: }; wxConfig _config; + wxDataFormat _dndFormat; std::vector>> _formats; std::vector> _devices;