Files
wxWidgets/src/common/fswatchercmn.cpp
Vadim Zeitlin de22d0def4 Fix wxFileSystemWatcher::RemoveAll() to actually work.
We need to call DoRemove() on all watcher objects to really remove them, just
removing our record of them was not enough and e.g. resulted in errors if we
tried to re-add a previously watched path again.

Closes #15531.

git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@76187 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
2014-03-23 00:57:09 +00:00

332 lines
9.7 KiB
C++

/////////////////////////////////////////////////////////////////////////////
// Name: src/common/fswatchercmn.cpp
// Purpose: wxMswFileSystemWatcher
// Author: Bartosz Bekier
// Created: 2009-05-26
// Copyright: (c) 2009 Bartosz Bekier <bartosz.bekier@gmail.com>
// Licence: wxWindows licence
/////////////////////////////////////////////////////////////////////////////
// For compilers that support precompilation, includes "wx.h".
#include "wx/wxprec.h"
#ifdef __BORLANDC__
#pragma hdrstop
#endif
#if wxUSE_FSWATCHER
#include "wx/fswatcher.h"
#include "wx/private/fswatcher.h"
// ============================================================================
// helpers
// ============================================================================
wxDEFINE_EVENT(wxEVT_FSWATCHER, wxFileSystemWatcherEvent);
static wxString GetFSWEventChangeTypeName(int type)
{
switch (type)
{
case wxFSW_EVENT_CREATE:
return "CREATE";
case wxFSW_EVENT_DELETE:
return "DELETE";
case wxFSW_EVENT_RENAME:
return "RENAME";
case wxFSW_EVENT_MODIFY:
return "MODIFY";
case wxFSW_EVENT_ACCESS:
return "ACCESS";
case wxFSW_EVENT_ATTRIB: // Currently this is wxGTK-only
return "ATTRIBUTE";
#ifdef wxHAS_INOTIFY
case wxFSW_EVENT_UNMOUNT: // Currently this is wxGTK-only
return "UNMOUNT";
#endif
case wxFSW_EVENT_WARNING:
return "WARNING";
case wxFSW_EVENT_ERROR:
return "ERROR";
}
// should never be reached!
wxFAIL_MSG("Unknown change type");
return "INVALID_TYPE";
}
// ============================================================================
// wxFileSystemWatcherEvent implementation
// ============================================================================
IMPLEMENT_DYNAMIC_CLASS(wxFileSystemWatcherEvent, wxEvent);
wxString wxFileSystemWatcherEvent::ToString() const
{
if (IsError())
{
return wxString::Format("FSW_EVT type=%d (%s) message='%s'", m_changeType,
GetFSWEventChangeTypeName(m_changeType), GetErrorDescription());
}
return wxString::Format("FSW_EVT type=%d (%s) path='%s'", m_changeType,
GetFSWEventChangeTypeName(m_changeType), GetPath().GetFullPath());
}
// ============================================================================
// wxFileSystemWatcherEvent implementation
// ============================================================================
wxFileSystemWatcherBase::wxFileSystemWatcherBase() :
m_service(0), m_owner(this)
{
}
wxFileSystemWatcherBase::~wxFileSystemWatcherBase()
{
RemoveAll();
if (m_service)
{
delete m_service;
}
}
bool wxFileSystemWatcherBase::Add(const wxFileName& path, int events)
{
wxFSWPathType type = wxFSWPath_None;
if ( path.FileExists() )
{
type = wxFSWPath_File;
}
else if ( path.DirExists() )
{
type = wxFSWPath_Dir;
}
else
{
// Don't overreact to being passed a non-existent item. It may have
// only just been deleted, in which case doing nothing is correct
wxLogTrace(wxTRACE_FSWATCHER,
"Can't monitor non-existent path \"%s\" for changes.",
path.GetFullPath());
return false;
}
return AddAny(path, events, type);
}
bool
wxFileSystemWatcherBase::AddAny(const wxFileName& path,
int events,
wxFSWPathType type,
const wxString& filespec)
{
wxString canonical = GetCanonicalPath(path);
if (canonical.IsEmpty())
return false;
// adding a path in a platform specific way
wxFSWatchInfo watch(canonical, events, type, filespec);
if ( !m_service->Add(watch) )
return false;
// on success, either add path to our 'watch-list'
// or, if already watched, inc the refcount. This may happen if
// a dir is Add()ed, then later AddTree() is called on a parent dir
wxFSWatchInfoMap::iterator it = m_watches.find(canonical);
if ( it == m_watches.end() )
{
wxFSWatchInfoMap::value_type val(canonical, watch);
m_watches.insert(val);
}
else
{
wxFSWatchInfo& watch = it->second;
int count = watch.IncRef();
wxLogTrace(wxTRACE_FSWATCHER,
"'%s' is now watched %d times", canonical, count);
}
return true;
}
bool wxFileSystemWatcherBase::Remove(const wxFileName& path)
{
// args validation & consistency checks
wxString canonical = GetCanonicalPath(path);
if (canonical.IsEmpty())
return false;
wxFSWatchInfoMap::iterator it = m_watches.find(canonical);
wxCHECK_MSG(it != m_watches.end(), false,
wxString::Format("Path '%s' is not watched", canonical));
// Decrement the watch's refcount and remove from watch-list if 0
bool ret = true;
wxFSWatchInfo& watch = it->second;
if ( !watch.DecRef() )
{
// remove in a platform specific way
ret = m_service->Remove(watch);
m_watches.erase(it);
}
return ret;
}
bool wxFileSystemWatcherBase::AddTree(const wxFileName& path, int events,
const wxString& filespec)
{
if (!path.DirExists())
return false;
// OPT could be optimised if we stored information about relationships
// between paths
class AddTraverser : public wxDirTraverser
{
public:
AddTraverser(wxFileSystemWatcherBase* watcher, int events,
const wxString& filespec) :
m_watcher(watcher), m_events(events), m_filespec(filespec)
{
}
virtual wxDirTraverseResult OnFile(const wxString& WXUNUSED(filename)) wxOVERRIDE
{
// There is no need to watch individual files as we watch the
// parent directory which will notify us about any changes in them.
return wxDIR_CONTINUE;
}
virtual wxDirTraverseResult OnDir(const wxString& dirname) wxOVERRIDE
{
if ( m_watcher->AddAny(wxFileName::DirName(dirname),
m_events, wxFSWPath_Tree, m_filespec) )
{
wxLogTrace(wxTRACE_FSWATCHER,
"--- AddTree adding directory '%s' ---", dirname);
}
return wxDIR_CONTINUE;
}
private:
wxFileSystemWatcherBase* m_watcher;
int m_events;
wxString m_filespec;
};
wxDir dir(path.GetFullPath());
// Prevent asserts or infinite loops in trees containing symlinks
int flags = wxDIR_DIRS;
if ( !path.ShouldFollowLink() )
{
flags |= wxDIR_NO_FOLLOW;
}
AddTraverser traverser(this, events, filespec);
dir.Traverse(traverser, filespec, flags);
// Add the path itself explicitly as Traverse() doesn't return it.
AddAny(path.GetPathWithSep(), events, wxFSWPath_Tree, filespec);
return true;
}
bool wxFileSystemWatcherBase::RemoveTree(const wxFileName& path)
{
if (!path.DirExists())
return false;
// OPT could be optimised if we stored information about relationships
// between paths
class RemoveTraverser : public wxDirTraverser
{
public:
RemoveTraverser(wxFileSystemWatcherBase* watcher,
const wxString& filespec) :
m_watcher(watcher), m_filespec(filespec)
{
}
virtual wxDirTraverseResult OnFile(const wxString& WXUNUSED(filename)) wxOVERRIDE
{
// We never watch the individual files when watching the tree, so
// nothing to do here.
return wxDIR_CONTINUE;
}
virtual wxDirTraverseResult OnDir(const wxString& dirname) wxOVERRIDE
{
m_watcher->Remove(wxFileName::DirName(dirname));
return wxDIR_CONTINUE;
}
private:
wxFileSystemWatcherBase* m_watcher;
wxString m_filespec;
};
// If AddTree() used a filespec, we must use the same one
wxString canonical = GetCanonicalPath(path);
wxFSWatchInfoMap::iterator it = m_watches.find(canonical);
wxCHECK_MSG( it != m_watches.end(), false,
wxString::Format("Path '%s' is not watched", canonical) );
wxFSWatchInfo watch = it->second;
const wxString filespec = watch.GetFilespec();
#if defined(__WINDOWS__)
// When there's no filespec, the wxMSW AddTree() would have set a watch
// on only the passed 'path'. We must therefore remove only this
if (filespec.empty())
{
return Remove(path);
}
// Otherwise fall through to the generic implementation
#endif // __WINDOWS__
wxDir dir(path.GetFullPath());
// AddTree() might have used the wxDIR_NO_FOLLOW to prevent asserts or
// infinite loops in trees containing symlinks. We need to do the same
// or we'll try to remove unwatched items. Let's hope the caller used
// the same ShouldFollowLink() setting as in AddTree()...
int flags = wxDIR_DIRS;
if ( !path.ShouldFollowLink() )
{
flags |= wxDIR_NO_FOLLOW;
}
RemoveTraverser traverser(this, filespec);
dir.Traverse(traverser, filespec, flags);
// As in AddTree() above, handle the path itself explicitly.
Remove(path);
return true;
}
bool wxFileSystemWatcherBase::RemoveAll()
{
const bool ret = m_service->RemoveAll();
m_watches.clear();
return ret;
}
int wxFileSystemWatcherBase::GetWatchedPathsCount() const
{
return m_watches.size();
}
int wxFileSystemWatcherBase::GetWatchedPaths(wxArrayString* paths) const
{
wxCHECK_MSG( paths != NULL, -1, "Null array passed to retrieve paths");
wxFSWatchInfoMap::const_iterator it = m_watches.begin();
for ( ; it != m_watches.end(); ++it)
{
paths->push_back(it->first);
}
return m_watches.size();
}
#endif // wxUSE_FSWATCHER