I want a list widget that is a flat view of just files in a single directory. It should be monitored in real time and ideally I should be able to disable it updating itself temporarily if a lot is going to happen in a short period of time. Is there a standard widget that does that? A quick search brings up wxGenericDirCtrl
but, as I understand, there is no way for it to show just files (not directories) of a single directory. It's more of a tree than a list.
Otherwise, what's the best way to make something similar. I can probably subclass wxListBox
and use wxFileSystemWatcher
to keep the view in sync, but it will be tedious. Is there a better way?
I am on Windows; portability is not a concern. I target Windows 7 to 10.
As Igor said above, given the requirements you stated, I don' think there is any other way to do this than to use a wxFileSystemWatcher
.
I pulled together an example of how to keep a list box updated based on reports from the file system watcher. I have to admit that this did turn out to be a more complicated than I thought it would, but I don't think it's excessively tedious.
// For compilers that support precompilation, includes "wx/wx.h".
#include "wx/wxprec.h"
#ifdef __BORLANDC__
#pragma hdrstop
#endif
// for all others, include the necessary headers (this file is usually all you
// need because it includes almost all "standard" wxWidgets headers)
#ifndef WX_PRECOMP
#include "wx/wx.h"
#endif
#include <wx/fswatcher.h>
#include <wx/filepicker.h>
#include <vector>
class MyFrame: public wxFrame
{
public:
MyFrame();
private:
// Event handlers
void OnWatcher(wxFileSystemWatcherEvent& event);
void OnDirChanged(wxFileDirPickerEvent& event);
// Helper functions
wxString StripPath(const wxString&);
void DeleteFromListbox(const wxString&);
wxFileSystemWatcher m_watcher;
wxListBox* m_listBox;
};
MyFrame::MyFrame()
:wxFrame(NULL, wxID_ANY, "Filesystem watcher frame", wxDefaultPosition,
wxSize(600, 400))
{
// Set up the UI elements.
wxPanel* mainPanel = new wxPanel(this,wxID_ANY);
wxDirPickerCtrl* dirPicker = new wxDirPickerCtrl(mainPanel, wxID_ANY);
m_listBox = new wxListBox(mainPanel, wxID_ANY);
wxBoxSizer* sizer = new wxBoxSizer(wxVERTICAL);
sizer->Add(dirPicker, wxSizerFlags(0).Expand().Border(wxALL));
sizer->Add(m_listBox, wxSizerFlags(1).Expand().Border(wxALL&~wxTOP));
mainPanel->SetSizer(sizer);
Layout();
// Set up the event handlers.
m_watcher.SetOwner(this);
dirPicker->Bind(wxEVT_DIRPICKER_CHANGED, &MyFrame::OnDirChanged, this);
Bind(wxEVT_FSWATCHER, &MyFrame::OnWatcher, this);
}
void MyFrame::OnWatcher(wxFileSystemWatcherEvent& event)
{
wxFileName name = event.GetPath();
// Ignore all watch events for folders.
if ( name.IsDir() )
{
return;
}
wxFileName newName = event.GetNewPath();
int changeType = event.GetChangeType();
switch ( changeType )
{
case wxFSW_EVENT_CREATE :
m_listBox->Append( StripPath(name.GetFullPath()) );
break;
case wxFSW_EVENT_DELETE :
DeleteFromListbox( StripPath(name.GetFullPath()) );
break;
case wxFSW_EVENT_RENAME :
DeleteFromListbox( StripPath(name.GetFullPath()) );
m_listBox->Append( StripPath(newName.GetFullPath()) );
break;
case wxFSW_EVENT_MODIFY :
wxFALLTHROUGH;
case wxFSW_EVENT_ACCESS :
wxFALLTHROUGH;
case wxFSW_EVENT_ATTRIB :
wxFALLTHROUGH;
#ifndef __WINDOWS__
case wxFSW_EVENT_UNMOUNT :
wxFALLTHROUGH;
#endif // __WINDOWS__
case wxFSW_EVENT_WARNING :
wxFALLTHROUGH;
case wxFSW_EVENT_ERROR :
wxFALLTHROUGH;
default:
break;
}
}
void MyFrame::OnDirChanged(wxFileDirPickerEvent& event)
{
if ( !::wxDirExists(event.GetPath()) )
{
return;
}
// Remove the current folder from the watch and add the new one.
m_watcher.RemoveAll();
wxString pathWithSep = event.GetPath();
if ( pathWithSep.Right(1) != wxFileName::GetPathSeparator() )
{
pathWithSep << wxFileName::GetPathSeparator();
}
m_watcher.Add(wxFileName(pathWithSep));
// Get a list of the files in new folder.
std::vector<wxString> files;
wxDir dir(pathWithSep);
wxString curName;
if ( dir.GetFirst(&curName, "*", wxDIR_FILES) )
{
files.push_back(StripPath(curName));
}
while ( dir.GetNext(&curName) )
{
files.push_back(StripPath(curName));
}
// Replace the current contents of the listbox with the new filenames.
m_listBox->Clear();
for ( auto it = files.begin() ; it != files.end() ; ++it )
{
m_listBox->Append(*it);
}
}
wxString MyFrame::StripPath(const wxString& name)
{
wxFileName fn(name);
return fn.GetFullName();
}
void MyFrame::DeleteFromListbox(const wxString& name)
{
int index = m_listBox->FindString(name);
if ( index != wxNOT_FOUND )
{
m_listBox->Delete(static_cast<unsigned int>(index));
}
}
class MyApp : public wxApp
{
public:
virtual bool OnInit()
{
MyFrame* frame = new MyFrame();
frame->Show();
return true;
}
};
wxIMPLEMENT_APP(MyApp);
This is just a simple example. There is some room for improvement: