From f8d371481626e3856dbe9696b781f19f7141f1b0 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Tue, 3 May 2011 23:31:39 +0000 Subject: [PATCH] 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 --- include/wx/fswatcher.h | 31 ++++++++++++--- include/wx/msw/fswatcher.h | 6 +++ samples/fswatcher/fswatcher.cpp | 67 ++++++++++++++++++++++++--------- src/common/fswatchercmn.cpp | 26 +++++++++++-- src/msw/fswatcher.cpp | 51 ++++++++++++++++++++++++- 5 files changed, 154 insertions(+), 27 deletions(-) diff --git a/include/wx/fswatcher.h b/include/wx/fswatcher.h index 6622936ff9..34e14fecd6 100644 --- a/include/wx/fswatcher.h +++ b/include/wx/fswatcher.h @@ -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 diff --git a/include/wx/msw/fswatcher.h b/include/wx/msw/fswatcher.h index 761e186b1e..005b35b25b 100644 --- a/include/wx/msw/fswatcher.h +++ b/include/wx/msw/fswatcher.h @@ -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(); }; diff --git a/samples/fswatcher/fswatcher.cpp b/samples/fswatcher/fswatcher.cpp index 8e4132192e..96f724b050 100644 --- a/samples/fswatcher/fswatcher.cpp +++ b/samples/fswatcher/fswatcher.cpp @@ -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)) { - 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() ) - return; + AddEntry(wxFSWPath_Dir); +} - AddDirectory(dir); +void MyFrame::OnAddTree(wxCommandEvent& WXUNUSED(event)) +{ + AddEntry(wxFSWPath_Tree); } -void MyFrame::AddDirectory(const wxString& dir) +void MyFrame::AddEntry(wxFSWPathType type, wxString filename) { - wxLogDebug("Adding directory: '%s'", dir); + 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"); + + wxLogDebug("Adding %s: '%s'", + filename, + type == wxFSWPath_Dir ? "directory" : "directory tree"); - if (!m_watcher->Add(wxFileName::DirName(dir), wxFSW_EVENT_ALL)) + bool ok = false; + switch ( type ) { - wxLogError("Error adding '%s' to watched paths", 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." ); } - else + + if (!ok) { - m_filesList->InsertItem(m_filesList->GetItemCount(), dir); + wxLogError("Error adding '%s' to watched paths", filename); + return; } + + m_filesList->InsertItem(m_filesList->GetItemCount(), filename); } void MyFrame::OnRemove(wxCommandEvent& WXUNUSED(event)) diff --git a/src/common/fswatchercmn.cpp b/src/common/fswatchercmn.cpp index 1f8946be9e..73a608c450 100644 --- a/src/common/fswatchercmn.cpp +++ b/src/common/fswatchercmn.cpp @@ -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; diff --git a/src/msw/fswatcher.cpp b/src/msw/fswatcher.cpp index 4226e001ae..7f2bcd6e89 100644 --- a/src/msw/fswatcher.cpp +++ b/src/msw/fswatcher.cpp @@ -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 -- 2.47.2