Drag-and-drop for moving one file at a time works... badly.

This commit is contained in:
David Given
2022-09-08 00:08:31 +02:00
parent 326bc931ad
commit 6294bc4505
6 changed files with 151 additions and 43 deletions

View File

@@ -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);

View File

@@ -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;
};

View File

@@ -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<wxAlignment>(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 );

View File

@@ -2500,11 +2500,14 @@
<property name="pos"></property>
<property name="size"></property>
<property name="style">wxDV_SINGLE</property>
<property name="subclass">; ; forward_declare</property>
<property name="subclass"></property>
<property name="tooltip"></property>
<property name="window_extra_style"></property>
<property name="window_name"></property>
<property name="window_style"></property>
<event name="OnDataViewCtrlItemBeginDrag">OnBrowserBeginDrag</event>
<event name="OnDataViewCtrlItemDrop">OnBrowserDrop</event>
<event name="OnDataViewCtrlItemDropPossible">OnBrowserDropPossible</event>
<event name="OnDataViewCtrlItemEditingDone">OnBrowserFilenameChanged</event>
<event name="OnDataViewCtrlItemExpanding">OnBrowserDirectoryExpanding</event>
<event name="OnDataViewCtrlSelectionChanged">OnBrowserSelectionChanged</event>
@@ -2597,7 +2600,7 @@
<property name="resize">Resizable</property>
<property name="show">1</property>
<property name="size"></property>
<property name="style">wxGA_HORIZONTAL</property>
<property name="style">wxGA_HORIZONTAL|wxGA_SMOOTH</property>
<property name="subclass">; ; forward_declare</property>
<property name="toolbar_pane">0</property>
<property name="tooltip"></property>

View File

@@ -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(); }

View File

@@ -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<const AnyLogMessage> 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<FilesystemNode> 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<std::pair<std::string, std::unique_ptr<const ConfigProto>>>
_formats;
std::vector<std::unique_ptr<const CandidateDevice>> _devices;