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
|
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.
|
* Event containing information about file system change.
|
||||||
*/
|
*/
|
||||||
@@ -179,19 +189,17 @@ typedef void (wxEvtHandler::*wxFileSystemWatcherEventFunction)
|
|||||||
// wxFileSystemWatcherBase: interface for wxFileSystemWatcher
|
// wxFileSystemWatcherBase: interface for wxFileSystemWatcher
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
/**
|
// Simple container to store information about one watched path.
|
||||||
* Simple container to store information about one watched file
|
|
||||||
*/
|
|
||||||
class wxFSWatchInfo
|
class wxFSWatchInfo
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
wxFSWatchInfo() :
|
wxFSWatchInfo() :
|
||||||
m_path(wxEmptyString), m_events(-1)
|
m_events(-1), m_type(wxFSWPath_None)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
wxFSWatchInfo(const wxString& path, int events) :
|
wxFSWatchInfo(const wxString& path, int events, wxFSWPathType type) :
|
||||||
m_path(path), m_events(events)
|
m_path(path), m_events(events), m_type(type)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -205,9 +213,15 @@ public:
|
|||||||
return m_events;
|
return m_events;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
wxFSWPathType GetType() const
|
||||||
|
{
|
||||||
|
return m_type;
|
||||||
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
wxString m_path;
|
wxString m_path;
|
||||||
int m_events;
|
int m_events;
|
||||||
|
wxFSWPathType m_type;
|
||||||
};
|
};
|
||||||
|
|
||||||
WX_DECLARE_STRING_HASH_MAP(wxFSWatchInfo, wxFSWatchInfoMap);
|
WX_DECLARE_STRING_HASH_MAP(wxFSWatchInfo, wxFSWatchInfoMap);
|
||||||
@@ -304,6 +318,11 @@ protected:
|
|||||||
return path_copy.GetFullPath();
|
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
|
wxFSWatchInfoMap m_watches; // path=>wxFSWatchInfo map
|
||||||
wxFSWatcherImpl* m_service; // file system events service
|
wxFSWatcherImpl* m_service; // file system events service
|
||||||
wxEvtHandler* m_owner; // handler for file system events
|
wxEvtHandler* m_owner; // handler for file system events
|
||||||
|
@@ -23,6 +23,12 @@ public:
|
|||||||
wxMSWFileSystemWatcher(const wxFileName& path,
|
wxMSWFileSystemWatcher(const wxFileName& path,
|
||||||
int events = wxFSW_EVENT_ALL);
|
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:
|
protected:
|
||||||
bool Init();
|
bool Init();
|
||||||
};
|
};
|
||||||
|
@@ -32,7 +32,9 @@ public:
|
|||||||
MyFrame(const wxString& title);
|
MyFrame(const wxString& title);
|
||||||
virtual ~MyFrame();
|
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();
|
bool CreateWatcherIfNecessary();
|
||||||
|
|
||||||
@@ -47,6 +49,7 @@ private:
|
|||||||
void OnAbout(wxCommandEvent& event);
|
void OnAbout(wxCommandEvent& event);
|
||||||
|
|
||||||
void OnAdd(wxCommandEvent& event);
|
void OnAdd(wxCommandEvent& event);
|
||||||
|
void OnAddTree(wxCommandEvent& event);
|
||||||
void OnRemove(wxCommandEvent& event);
|
void OnRemove(wxCommandEvent& event);
|
||||||
|
|
||||||
void OnFileSystemEvent(wxFileSystemWatcherEvent& event);
|
void OnFileSystemEvent(wxFileSystemWatcherEvent& event);
|
||||||
@@ -87,7 +90,7 @@ public:
|
|||||||
if ( m_frame->CreateWatcherIfNecessary() )
|
if ( m_frame->CreateWatcherIfNecessary() )
|
||||||
{
|
{
|
||||||
if ( !m_dirToWatch.empty() )
|
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,
|
MENU_ID_WATCH = 101,
|
||||||
|
|
||||||
BTN_ID_ADD = 200,
|
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
|
// buttons
|
||||||
wxButton* buttonAdd = new wxButton(panel, BTN_ID_ADD, "&Add");
|
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");
|
wxButton* buttonRemove = new wxButton(panel, BTN_ID_REMOVE, "&Remove");
|
||||||
wxSizer *btnSizer = new wxGridSizer(2);
|
wxSizer *btnSizer = new wxGridSizer(2);
|
||||||
btnSizer->Add(buttonAdd, wxSizerFlags().Center().Border(wxALL));
|
btnSizer->Add(buttonAdd, wxSizerFlags().Center().Border(wxALL));
|
||||||
|
btnSizer->Add(buttonAddTree, wxSizerFlags().Center().Border(wxALL));
|
||||||
btnSizer->Add(buttonRemove, wxSizerFlags().Center().Border(wxALL));
|
btnSizer->Add(buttonRemove, wxSizerFlags().Center().Border(wxALL));
|
||||||
|
|
||||||
// and put it all together
|
// and put it all together
|
||||||
@@ -253,6 +259,8 @@ MyFrame::MyFrame(const wxString& title)
|
|||||||
// buttons
|
// buttons
|
||||||
Connect(BTN_ID_ADD, wxEVT_COMMAND_BUTTON_CLICKED,
|
Connect(BTN_ID_ADD, wxEVT_COMMAND_BUTTON_CLICKED,
|
||||||
wxCommandEventHandler(MyFrame::OnAdd));
|
wxCommandEventHandler(MyFrame::OnAdd));
|
||||||
|
Connect(BTN_ID_ADD_TREE, wxEVT_COMMAND_BUTTON_CLICKED,
|
||||||
|
wxCommandEventHandler(MyFrame::OnAddTree));
|
||||||
Connect(BTN_ID_REMOVE, wxEVT_COMMAND_BUTTON_CLICKED,
|
Connect(BTN_ID_REMOVE, wxEVT_COMMAND_BUTTON_CLICKED,
|
||||||
wxCommandEventHandler(MyFrame::OnRemove));
|
wxCommandEventHandler(MyFrame::OnRemove));
|
||||||
|
|
||||||
@@ -318,29 +326,54 @@ void MyFrame::OnWatch(wxCommandEvent& event)
|
|||||||
|
|
||||||
void MyFrame::OnAdd(wxCommandEvent& WXUNUSED(event))
|
void MyFrame::OnAdd(wxCommandEvent& WXUNUSED(event))
|
||||||
{
|
{
|
||||||
wxCHECK_RET(m_watcher, "Watcher not initialized");
|
AddEntry(wxFSWPath_Dir);
|
||||||
|
|
||||||
// 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() )
|
|
||||||
return;
|
|
||||||
|
|
||||||
AddDirectory(dir);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void MyFrame::AddDirectory(const wxString& dir)
|
void MyFrame::OnAddTree(wxCommandEvent& WXUNUSED(event))
|
||||||
{
|
{
|
||||||
wxLogDebug("Adding directory: '%s'", dir);
|
AddEntry(wxFSWPath_Tree);
|
||||||
|
}
|
||||||
|
|
||||||
if (!m_watcher->Add(wxFileName::DirName(dir), wxFSW_EVENT_ALL))
|
void MyFrame::AddEntry(wxFSWPathType type, wxString filename)
|
||||||
|
{
|
||||||
|
if ( filename.empty() )
|
||||||
{
|
{
|
||||||
wxLogError("Error adding '%s' to watched paths", dir);
|
// 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;
|
||||||
}
|
}
|
||||||
else
|
|
||||||
|
wxCHECK_RET(m_watcher, "Watcher not initialized");
|
||||||
|
|
||||||
|
wxLogDebug("Adding %s: '%s'",
|
||||||
|
filename,
|
||||||
|
type == wxFSWPath_Dir ? "directory" : "directory tree");
|
||||||
|
|
||||||
|
bool ok = false;
|
||||||
|
switch ( type )
|
||||||
{
|
{
|
||||||
m_filesList->InsertItem(m_filesList->GetItemCount(), dir);
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_filesList->InsertItem(m_filesList->GetItemCount(), filename);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MyFrame::OnRemove(wxCommandEvent& WXUNUSED(event))
|
void MyFrame::OnRemove(wxCommandEvent& WXUNUSED(event))
|
||||||
|
@@ -79,10 +79,30 @@ wxFileSystemWatcherBase::~wxFileSystemWatcherBase()
|
|||||||
|
|
||||||
bool wxFileSystemWatcherBase::Add(const wxFileName& path, int events)
|
bool wxFileSystemWatcherBase::Add(const wxFileName& path, int events)
|
||||||
{
|
{
|
||||||
// args validation & consistency checks
|
wxFSWPathType type = wxFSWPath_None;
|
||||||
if (!path.FileExists() && !path.DirExists())
|
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 false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return DoAdd(path, events, type);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
wxFileSystemWatcherBase::DoAdd(const wxFileName& path,
|
||||||
|
int events,
|
||||||
|
wxFSWPathType type)
|
||||||
|
{
|
||||||
wxString canonical = GetCanonicalPath(path);
|
wxString canonical = GetCanonicalPath(path);
|
||||||
if (canonical.IsEmpty())
|
if (canonical.IsEmpty())
|
||||||
return false;
|
return false;
|
||||||
@@ -91,7 +111,7 @@ bool wxFileSystemWatcherBase::Add(const wxFileName& path, int events)
|
|||||||
wxString::Format("Path '%s' is already watched", canonical));
|
wxString::Format("Path '%s' is already watched", canonical));
|
||||||
|
|
||||||
// adding a path in a platform specific way
|
// adding a path in a platform specific way
|
||||||
wxFSWatchInfo watch(canonical, events);
|
wxFSWatchInfo watch(canonical, events, type);
|
||||||
if ( !m_service->Add(watch) )
|
if ( !m_service->Add(watch) )
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
@@ -136,9 +136,32 @@ void wxFSWatcherImplMSW::SendEvent(wxFileSystemWatcherEvent& evt)
|
|||||||
|
|
||||||
bool wxFSWatcherImplMSW::DoSetUpWatch(wxFSWatchEntryMSW& watch)
|
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 flags = Watcher2NativeFlags(watch.GetFlags());
|
||||||
int ret = ReadDirectoryChangesW(watch.GetHandle(), watch.GetBuffer(),
|
int ret = ReadDirectoryChangesW(watch.GetHandle(), watch.GetBuffer(),
|
||||||
wxFSWatchEntryMSW::BUFFER_SIZE, FALSE,
|
wxFSWatchEntryMSW::BUFFER_SIZE,
|
||||||
|
bWatchSubtree,
|
||||||
flags, NULL,
|
flags, NULL,
|
||||||
watch.GetOverlapped(), NULL);
|
watch.GetOverlapped(), NULL);
|
||||||
if (!ret)
|
if (!ret)
|
||||||
@@ -385,4 +408,30 @@ bool wxMSWFileSystemWatcher::Init()
|
|||||||
return ret;
|
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
|
#endif // wxUSE_FSWATCHER
|
||||||
|
Reference in New Issue
Block a user