Implement watching directory correctly in MSW wxFileSystemWatcher.
The directories used to be always monitored recursively, even when this wasn't requested, in wxMSW implementation. Change this but also implement efficient support for monitoring the entire hierarchies using the native support for this. Also update the sample to allow monitoring directories recursively as well. See #12847. git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@67693 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
This commit is contained in:
@@ -55,6 +55,16 @@ enum
|
||||
wxFSW_EVENT_WARNING | wxFSW_EVENT_ERROR
|
||||
};
|
||||
|
||||
// Type of the path watched, used only internally for now.
|
||||
enum wxFSWPathType
|
||||
{
|
||||
wxFSWPath_None, // Invalid value for an initialized watch.
|
||||
wxFSWPath_File, // Plain file.
|
||||
wxFSWPath_Dir, // Watch a directory and the files in it.
|
||||
wxFSWPath_Tree // Watch a directory and all its children recursively.
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Event containing information about file system change.
|
||||
*/
|
||||
@@ -179,19 +189,17 @@ typedef void (wxEvtHandler::*wxFileSystemWatcherEventFunction)
|
||||
// wxFileSystemWatcherBase: interface for wxFileSystemWatcher
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Simple container to store information about one watched file
|
||||
*/
|
||||
// Simple container to store information about one watched path.
|
||||
class wxFSWatchInfo
|
||||
{
|
||||
public:
|
||||
wxFSWatchInfo() :
|
||||
m_path(wxEmptyString), m_events(-1)
|
||||
m_events(-1), m_type(wxFSWPath_None)
|
||||
{
|
||||
}
|
||||
|
||||
wxFSWatchInfo(const wxString& path, int events) :
|
||||
m_path(path), m_events(events)
|
||||
wxFSWatchInfo(const wxString& path, int events, wxFSWPathType type) :
|
||||
m_path(path), m_events(events), m_type(type)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -205,9 +213,15 @@ public:
|
||||
return m_events;
|
||||
}
|
||||
|
||||
wxFSWPathType GetType() const
|
||||
{
|
||||
return m_type;
|
||||
}
|
||||
|
||||
protected:
|
||||
wxString m_path;
|
||||
int m_events;
|
||||
wxFSWPathType m_type;
|
||||
};
|
||||
|
||||
WX_DECLARE_STRING_HASH_MAP(wxFSWatchInfo, wxFSWatchInfoMap);
|
||||
@@ -304,6 +318,11 @@ protected:
|
||||
return path_copy.GetFullPath();
|
||||
}
|
||||
|
||||
// Delegates the real work of adding the path to wxFSWatcherImpl::Add() and
|
||||
// updates m_watches if the new path was successfully added.
|
||||
bool DoAdd(const wxFileName& path, int events, wxFSWPathType type);
|
||||
|
||||
|
||||
wxFSWatchInfoMap m_watches; // path=>wxFSWatchInfo map
|
||||
wxFSWatcherImpl* m_service; // file system events service
|
||||
wxEvtHandler* m_owner; // handler for file system events
|
||||
|
@@ -23,6 +23,12 @@ public:
|
||||
wxMSWFileSystemWatcher(const wxFileName& path,
|
||||
int events = wxFSW_EVENT_ALL);
|
||||
|
||||
// Override the base class function to provide a much more efficient
|
||||
// implementation for it using the platform native support for watching the
|
||||
// entire directory trees.
|
||||
virtual bool AddTree(const wxFileName& path, int events = wxFSW_EVENT_ALL,
|
||||
const wxString& filter = wxEmptyString);
|
||||
|
||||
protected:
|
||||
bool Init();
|
||||
};
|
||||
|
@@ -32,7 +32,9 @@ public:
|
||||
MyFrame(const wxString& title);
|
||||
virtual ~MyFrame();
|
||||
|
||||
void AddDirectory(const wxString& dir);
|
||||
// Add an entry of the specified type asking the user for the filename if
|
||||
// the one passed to this function is empty.
|
||||
void AddEntry(wxFSWPathType type, wxString filename = wxString());
|
||||
|
||||
bool CreateWatcherIfNecessary();
|
||||
|
||||
@@ -47,6 +49,7 @@ private:
|
||||
void OnAbout(wxCommandEvent& event);
|
||||
|
||||
void OnAdd(wxCommandEvent& event);
|
||||
void OnAddTree(wxCommandEvent& event);
|
||||
void OnRemove(wxCommandEvent& event);
|
||||
|
||||
void OnFileSystemEvent(wxFileSystemWatcherEvent& event);
|
||||
@@ -87,7 +90,7 @@ public:
|
||||
if ( m_frame->CreateWatcherIfNecessary() )
|
||||
{
|
||||
if ( !m_dirToWatch.empty() )
|
||||
m_frame->AddDirectory(m_dirToWatch);
|
||||
m_frame->AddEntry(wxFSWPath_Dir, m_dirToWatch);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -144,7 +147,8 @@ MyFrame::MyFrame(const wxString& title)
|
||||
MENU_ID_WATCH = 101,
|
||||
|
||||
BTN_ID_ADD = 200,
|
||||
BTN_ID_REMOVE = 201,
|
||||
BTN_ID_ADD_TREE,
|
||||
BTN_ID_REMOVE
|
||||
};
|
||||
|
||||
// ================================================================
|
||||
@@ -194,9 +198,11 @@ MyFrame::MyFrame(const wxString& title)
|
||||
|
||||
// buttons
|
||||
wxButton* buttonAdd = new wxButton(panel, BTN_ID_ADD, "&Add");
|
||||
wxButton* buttonAddTree = new wxButton(panel, BTN_ID_ADD_TREE, "Add &tree");
|
||||
wxButton* buttonRemove = new wxButton(panel, BTN_ID_REMOVE, "&Remove");
|
||||
wxSizer *btnSizer = new wxGridSizer(2);
|
||||
btnSizer->Add(buttonAdd, wxSizerFlags().Center().Border(wxALL));
|
||||
btnSizer->Add(buttonAddTree, wxSizerFlags().Center().Border(wxALL));
|
||||
btnSizer->Add(buttonRemove, wxSizerFlags().Center().Border(wxALL));
|
||||
|
||||
// and put it all together
|
||||
@@ -253,6 +259,8 @@ MyFrame::MyFrame(const wxString& title)
|
||||
// buttons
|
||||
Connect(BTN_ID_ADD, wxEVT_COMMAND_BUTTON_CLICKED,
|
||||
wxCommandEventHandler(MyFrame::OnAdd));
|
||||
Connect(BTN_ID_ADD_TREE, wxEVT_COMMAND_BUTTON_CLICKED,
|
||||
wxCommandEventHandler(MyFrame::OnAddTree));
|
||||
Connect(BTN_ID_REMOVE, wxEVT_COMMAND_BUTTON_CLICKED,
|
||||
wxCommandEventHandler(MyFrame::OnRemove));
|
||||
|
||||
@@ -318,29 +326,54 @@ void MyFrame::OnWatch(wxCommandEvent& event)
|
||||
|
||||
void MyFrame::OnAdd(wxCommandEvent& WXUNUSED(event))
|
||||
{
|
||||
AddEntry(wxFSWPath_Dir);
|
||||
}
|
||||
|
||||
void MyFrame::OnAddTree(wxCommandEvent& WXUNUSED(event))
|
||||
{
|
||||
AddEntry(wxFSWPath_Tree);
|
||||
}
|
||||
|
||||
void MyFrame::AddEntry(wxFSWPathType type, wxString filename)
|
||||
{
|
||||
if ( filename.empty() )
|
||||
{
|
||||
// TODO account for adding the files as well
|
||||
filename = wxDirSelector("Choose a folder to watch", "",
|
||||
wxDD_DEFAULT_STYLE | wxDD_DIR_MUST_EXIST);
|
||||
if ( filename.empty() )
|
||||
return;
|
||||
}
|
||||
|
||||
wxCHECK_RET(m_watcher, "Watcher not initialized");
|
||||
|
||||
// TODO account for adding the files as well
|
||||
const wxString& dir = wxDirSelector("Choose a folder to watch", "",
|
||||
wxDD_DEFAULT_STYLE | wxDD_DIR_MUST_EXIST);
|
||||
if ( dir.empty() )
|
||||
wxLogDebug("Adding %s: '%s'",
|
||||
filename,
|
||||
type == wxFSWPath_Dir ? "directory" : "directory tree");
|
||||
|
||||
bool ok = false;
|
||||
switch ( type )
|
||||
{
|
||||
case wxFSWPath_Dir:
|
||||
ok = m_watcher->Add(wxFileName::DirName(filename));
|
||||
break;
|
||||
|
||||
case wxFSWPath_Tree:
|
||||
ok = m_watcher->AddTree(wxFileName::DirName(filename));
|
||||
break;
|
||||
|
||||
case wxFSWPath_File:
|
||||
case wxFSWPath_None:
|
||||
wxFAIL_MSG( "Unexpected path type." );
|
||||
}
|
||||
|
||||
if (!ok)
|
||||
{
|
||||
wxLogError("Error adding '%s' to watched paths", filename);
|
||||
return;
|
||||
|
||||
AddDirectory(dir);
|
||||
}
|
||||
|
||||
void MyFrame::AddDirectory(const wxString& dir)
|
||||
{
|
||||
wxLogDebug("Adding directory: '%s'", dir);
|
||||
|
||||
if (!m_watcher->Add(wxFileName::DirName(dir), wxFSW_EVENT_ALL))
|
||||
{
|
||||
wxLogError("Error adding '%s' to watched paths", dir);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_filesList->InsertItem(m_filesList->GetItemCount(), dir);
|
||||
}
|
||||
m_filesList->InsertItem(m_filesList->GetItemCount(), filename);
|
||||
}
|
||||
|
||||
void MyFrame::OnRemove(wxCommandEvent& WXUNUSED(event))
|
||||
|
@@ -79,10 +79,30 @@ wxFileSystemWatcherBase::~wxFileSystemWatcherBase()
|
||||
|
||||
bool wxFileSystemWatcherBase::Add(const wxFileName& path, int events)
|
||||
{
|
||||
// args validation & consistency checks
|
||||
if (!path.FileExists() && !path.DirExists())
|
||||
wxFSWPathType type = wxFSWPath_None;
|
||||
if ( path.FileExists() )
|
||||
{
|
||||
type = wxFSWPath_File;
|
||||
}
|
||||
else if ( path.DirExists() )
|
||||
{
|
||||
type = wxFSWPath_Dir;
|
||||
}
|
||||
else
|
||||
{
|
||||
wxLogError(_("Can't monitor non-existent path \"%s\" for changes."),
|
||||
path.GetFullPath());
|
||||
return false;
|
||||
}
|
||||
|
||||
return DoAdd(path, events, type);
|
||||
}
|
||||
|
||||
bool
|
||||
wxFileSystemWatcherBase::DoAdd(const wxFileName& path,
|
||||
int events,
|
||||
wxFSWPathType type)
|
||||
{
|
||||
wxString canonical = GetCanonicalPath(path);
|
||||
if (canonical.IsEmpty())
|
||||
return false;
|
||||
@@ -91,7 +111,7 @@ bool wxFileSystemWatcherBase::Add(const wxFileName& path, int events)
|
||||
wxString::Format("Path '%s' is already watched", canonical));
|
||||
|
||||
// adding a path in a platform specific way
|
||||
wxFSWatchInfo watch(canonical, events);
|
||||
wxFSWatchInfo watch(canonical, events, type);
|
||||
if ( !m_service->Add(watch) )
|
||||
return false;
|
||||
|
||||
|
@@ -136,9 +136,32 @@ void wxFSWatcherImplMSW::SendEvent(wxFileSystemWatcherEvent& evt)
|
||||
|
||||
bool wxFSWatcherImplMSW::DoSetUpWatch(wxFSWatchEntryMSW& watch)
|
||||
{
|
||||
BOOL bWatchSubtree wxDUMMY_INITIALIZE(FALSE);
|
||||
|
||||
switch ( watch.GetType() )
|
||||
{
|
||||
case wxFSWPath_File:
|
||||
wxLogError(_("Monitoring individual files for changes is not "
|
||||
"supported currently."));
|
||||
return false;
|
||||
|
||||
case wxFSWPath_Dir:
|
||||
bWatchSubtree = FALSE;
|
||||
break;
|
||||
|
||||
case wxFSWPath_Tree:
|
||||
bWatchSubtree = TRUE;
|
||||
break;
|
||||
|
||||
case wxFSWPath_None:
|
||||
wxFAIL_MSG( "Invalid watch type." );
|
||||
return false;
|
||||
}
|
||||
|
||||
int flags = Watcher2NativeFlags(watch.GetFlags());
|
||||
int ret = ReadDirectoryChangesW(watch.GetHandle(), watch.GetBuffer(),
|
||||
wxFSWatchEntryMSW::BUFFER_SIZE, FALSE,
|
||||
wxFSWatchEntryMSW::BUFFER_SIZE,
|
||||
bWatchSubtree,
|
||||
flags, NULL,
|
||||
watch.GetOverlapped(), NULL);
|
||||
if (!ret)
|
||||
@@ -385,4 +408,30 @@ bool wxMSWFileSystemWatcher::Init()
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool
|
||||
wxMSWFileSystemWatcher::AddTree(const wxFileName& path,
|
||||
int events,
|
||||
const wxString& filter)
|
||||
{
|
||||
if ( !filter.empty() )
|
||||
{
|
||||
// Use the inefficient generic version as we can only monitor
|
||||
// everything under the given directory.
|
||||
//
|
||||
// Notice that it would probably be better to still monitor everything
|
||||
// natively and filter out the changes we're not interested in.
|
||||
return wxFileSystemWatcherBase::AddTree(path, events, filter);
|
||||
}
|
||||
|
||||
|
||||
if ( !path.DirExists() )
|
||||
{
|
||||
wxLogError(_("Can't monitor non-existent directory \"%s\" for changes."),
|
||||
path.GetFullPath());
|
||||
return false;
|
||||
}
|
||||
|
||||
return DoAdd(path, events, wxFSWPath_Tree);
|
||||
}
|
||||
|
||||
#endif // wxUSE_FSWATCHER
|
||||
|
Reference in New Issue
Block a user