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.
*/
// 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)
{
}
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);
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
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();
};
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();
void OnAbout(wxCommandEvent& event);
void OnAdd(wxCommandEvent& event);
+ void OnAddTree(wxCommandEvent& event);
void OnRemove(wxCommandEvent& event);
void OnFileSystemEvent(wxFileSystemWatcherEvent& event);
if ( m_frame->CreateWatcherIfNecessary() )
{
if ( !m_dirToWatch.empty() )
- m_frame->AddDirectory(m_dirToWatch);
+ m_frame->AddEntry(wxFSWPath_Dir, m_dirToWatch);
}
}
MENU_ID_WATCH = 101,
BTN_ID_ADD = 200,
- BTN_ID_REMOVE = 201,
+ BTN_ID_ADD_TREE,
+ BTN_ID_REMOVE
};
// ================================================================
// 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
// 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));
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))
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;
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;
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)
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