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; - - AddDirectory(dir); + AddEntry(wxFSWPath_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)) 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