]> git.saurik.com Git - wxWidgets.git/commitdiff
Implement watching directory correctly in MSW wxFileSystemWatcher.
authorVadim Zeitlin <vadim@wxwidgets.org>
Tue, 3 May 2011 23:31:39 +0000 (23:31 +0000)
committerVadim Zeitlin <vadim@wxwidgets.org>
Tue, 3 May 2011 23:31:39 +0000 (23:31 +0000)
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
include/wx/msw/fswatcher.h
samples/fswatcher/fswatcher.cpp
src/common/fswatchercmn.cpp
src/msw/fswatcher.cpp

index 6622936ff9e5fd37601ca15ae54eed0628dc4139..34e14fecd6f0b770d12d3e6824e8e1f167754f8f 100644 (file)
@@ -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
index 761e186b1edd472c8244f39001f135830b6ad647..005b35b25b73c05e717c0117e7ea00496daccee6 100644 (file)
@@ -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();
 };
index 8e4132192e0e7efa58c000108891a5e895a379fa..96f724b0500662ff80dbc8b2ccb0a1ba3cbbaead 100644 (file)
@@ -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))
index 1f8946be9e848a311850b700d361d26d7975afc0..73a608c45090781678a85ba4c7ac5b9442f71a13 100644 (file)
@@ -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;
 
index 4226e001aed0b2c2f5b16a7053489866206aa8a2..7f2bcd6e8901e465d1d8c81b8911c7ba6c9de0de 100644 (file)
@@ -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