dolphin-emulator/Externals/wxWidgets3/src/common/fswatchercmn.cpp

331 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))
{
// 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)
{
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))
{
// We never watch the individual files when watching the tree, so
// nothing to do here.
return wxDIR_CONTINUE;
}
virtual wxDirTraverseResult OnDir(const wxString& dirname)
{
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()
{
m_service->RemoveAll();
m_watches.clear();
return true;
}
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