Improve handling of file spec in wxFileSystemWatcher::AddTree().
[wxWidgets.git] / src / common / fswatchercmn.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/common/fswatchercmn.cpp
3 // Purpose: wxMswFileSystemWatcher
4 // Author: Bartosz Bekier
5 // Created: 2009-05-26
6 // RCS-ID: $Id$
7 // Copyright: (c) 2009 Bartosz Bekier <bartosz.bekier@gmail.com>
8 // Licence: wxWindows licence
9 /////////////////////////////////////////////////////////////////////////////
10
11 // For compilers that support precompilation, includes "wx.h".
12 #include "wx/wxprec.h"
13
14 #ifdef __BORLANDC__
15 #pragma hdrstop
16 #endif
17
18 #if wxUSE_FSWATCHER
19
20 #include "wx/fswatcher.h"
21 #include "wx/private/fswatcher.h"
22
23 // ============================================================================
24 // helpers
25 // ============================================================================
26
27 wxDEFINE_EVENT(wxEVT_FSWATCHER, wxFileSystemWatcherEvent);
28
29 static wxString GetFSWEventChangeTypeName(int type)
30 {
31 switch (type)
32 {
33 case wxFSW_EVENT_CREATE:
34 return "CREATE";
35 case wxFSW_EVENT_DELETE:
36 return "DELETE";
37 case wxFSW_EVENT_RENAME:
38 return "RENAME";
39 case wxFSW_EVENT_MODIFY:
40 return "MODIFY";
41 case wxFSW_EVENT_ACCESS:
42 return "ACCESS";
43 }
44
45 // should never be reached!
46 wxFAIL_MSG("Unknown change type");
47 return "INVALID_TYPE";
48 }
49
50
51 // ============================================================================
52 // wxFileSystemWatcherEvent implementation
53 // ============================================================================
54
55 wxString wxFileSystemWatcherEvent::ToString() const
56 {
57 return wxString::Format("FSW_EVT type=%d (%s) path='%s'", m_changeType,
58 GetFSWEventChangeTypeName(m_changeType), GetPath().GetFullPath());
59 }
60
61
62 // ============================================================================
63 // wxFileSystemWatcherEvent implementation
64 // ============================================================================
65
66 wxFileSystemWatcherBase::wxFileSystemWatcherBase() :
67 m_service(0), m_owner(this)
68 {
69 }
70
71 wxFileSystemWatcherBase::~wxFileSystemWatcherBase()
72 {
73 RemoveAll();
74 if (m_service)
75 {
76 delete m_service;
77 }
78 }
79
80 bool wxFileSystemWatcherBase::Add(const wxFileName& path, int events)
81 {
82 wxFSWPathType type = wxFSWPath_None;
83 if ( path.FileExists() )
84 {
85 type = wxFSWPath_File;
86 }
87 else if ( path.DirExists() )
88 {
89 type = wxFSWPath_Dir;
90 }
91 else
92 {
93 wxLogError(_("Can't monitor non-existent path \"%s\" for changes."),
94 path.GetFullPath());
95 return false;
96 }
97
98 return AddAny(path, events, type);
99 }
100
101 bool
102 wxFileSystemWatcherBase::AddAny(const wxFileName& path,
103 int events,
104 wxFSWPathType type,
105 const wxString& filespec)
106 {
107 wxString canonical = GetCanonicalPath(path);
108 if (canonical.IsEmpty())
109 return false;
110
111 wxCHECK_MSG(m_watches.find(canonical) == m_watches.end(), false,
112 wxString::Format("Path '%s' is already watched", canonical));
113
114 // adding a path in a platform specific way
115 wxFSWatchInfo watch(canonical, events, type, filespec);
116 if ( !m_service->Add(watch) )
117 return false;
118
119 // on success, add path to our 'watch-list'
120 wxFSWatchInfoMap::value_type val(canonical, watch);
121 return m_watches.insert(val).second;
122 }
123
124 bool wxFileSystemWatcherBase::Remove(const wxFileName& path)
125 {
126 // args validation & consistency checks
127 wxString canonical = GetCanonicalPath(path);
128 if (canonical.IsEmpty())
129 return false;
130
131 wxFSWatchInfoMap::iterator it = m_watches.find(canonical);
132 wxCHECK_MSG(it != m_watches.end(), false,
133 wxString::Format("Path '%s' is not watched", canonical));
134
135 // remove from watch-list
136 wxFSWatchInfo watch = it->second;
137 m_watches.erase(it);
138
139 // remove in a platform specific way
140 return m_service->Remove(watch);
141 }
142
143 bool wxFileSystemWatcherBase::AddTree(const wxFileName& path, int events,
144 const wxString& filespec)
145 {
146 if (!path.DirExists())
147 return false;
148
149 // OPT could be optimised if we stored information about relationships
150 // between paths
151 class AddTraverser : public wxDirTraverser
152 {
153 public:
154 AddTraverser(wxFileSystemWatcherBase* watcher, int events,
155 const wxString& filespec) :
156 m_watcher(watcher), m_events(events), m_filespec(filespec)
157 {
158 }
159
160 // CHECK we choose which files to delegate to Add(), maybe we should pass
161 // all of them to Add() and let it choose? this is useful when adding a
162 // file to a dir that is already watched, then not only should we know
163 // about that, but Add() should also behave well then
164 virtual wxDirTraverseResult OnFile(const wxString& filename)
165 {
166 if ( m_watcher->AddAny(wxFileName::FileName(filename),
167 m_events, wxFSWPath_File) )
168 {
169 wxLogTrace(wxTRACE_FSWATCHER,
170 "--- AddTree adding file '%s' ---", filename);
171 }
172 return wxDIR_CONTINUE;
173 }
174
175 virtual wxDirTraverseResult OnDir(const wxString& dirname)
176 {
177 // We can't currently watch only the files with the given filespec
178 // in the subdirectories so we only watch subdirectories at all if
179 // we want to watch everything.
180 if ( m_filespec.empty() )
181 {
182 if ( m_watcher->AddAny(wxFileName::DirName(dirname),
183 m_events, wxFSWPath_Dir) )
184 {
185 wxLogTrace(wxTRACE_FSWATCHER,
186 "--- AddTree adding directory '%s' ---", dirname);
187 }
188 }
189 return wxDIR_CONTINUE;
190 }
191
192 private:
193 wxFileSystemWatcherBase* m_watcher;
194 int m_events;
195 wxString m_filespec;
196 };
197
198 wxDir dir(path.GetFullPath());
199 AddTraverser traverser(this, events, filespec);
200 dir.Traverse(traverser, filespec);
201
202 // Add the path itself explicitly as Traverse() doesn't return it.
203 AddAny(path.GetPathWithSep(), events, wxFSWPath_Dir, filespec);
204
205 return true;
206 }
207
208 bool wxFileSystemWatcherBase::RemoveTree(const wxFileName& path)
209 {
210 if (!path.DirExists())
211 return false;
212
213 // OPT could be optimised if we stored information about relationships
214 // between paths
215 class RemoveTraverser : public wxDirTraverser
216 {
217 public:
218 RemoveTraverser(wxFileSystemWatcherBase* watcher,
219 const wxString& filespec) :
220 m_watcher(watcher), m_filespec(filespec)
221 {
222 }
223
224 virtual wxDirTraverseResult OnFile(const wxString& filename)
225 {
226 m_watcher->Remove(wxFileName(filename));
227 return wxDIR_CONTINUE;
228 }
229
230 virtual wxDirTraverseResult OnDir(const wxString& dirname)
231 {
232 // Currently the subdirectories would have been added only if there
233 // is no filespec.
234 //
235 // Notice that we still need to recurse into them even if we're
236 // using a filespec because they can contain files matching it.
237 if ( m_filespec.empty() )
238 {
239 m_watcher->Remove(wxFileName::DirName(dirname));
240 }
241
242 return wxDIR_CONTINUE;
243 }
244
245 private:
246 wxFileSystemWatcherBase* m_watcher;
247 wxString m_filespec;
248 };
249
250 // If AddTree() used a filespec, we must use the same one
251 wxString canonical = GetCanonicalPath(path);
252 wxFSWatchInfoMap::iterator it = m_watches.find(canonical);
253 wxCHECK_MSG( it != m_watches.end(), false,
254 wxString::Format("Path '%s' is not watched", canonical) );
255 wxFSWatchInfo watch = it->second;
256 const wxString filespec = watch.GetFilespec();
257
258 #if defined(__WINDOWS__)
259 // When there's no filespec, the wxMSW AddTree() would have set a watch
260 // on only the passed 'path'. We must therefore remove only this
261 if (filespec.empty())
262 {
263 return Remove(path);
264 }
265 // Otherwise fall through to the generic implementation
266 #endif // __WINDOWS__
267
268 wxDir dir(path.GetFullPath());
269 RemoveTraverser traverser(this, filespec);
270 dir.Traverse(traverser, filespec);
271
272 // As in AddTree() above, handle the path itself explicitly.
273 Remove(path);
274
275 return true;
276 }
277
278 bool wxFileSystemWatcherBase::RemoveAll()
279 {
280 m_service->RemoveAll();
281 m_watches.clear();
282 return true;
283 }
284
285 int wxFileSystemWatcherBase::GetWatchedPathsCount() const
286 {
287 return m_watches.size();
288 }
289
290 int wxFileSystemWatcherBase::GetWatchedPaths(wxArrayString* paths) const
291 {
292 wxCHECK_MSG( paths != NULL, -1, "Null array passed to retrieve paths");
293
294 wxFSWatchInfoMap::const_iterator it = m_watches.begin();
295 for ( ; it != m_watches.end(); ++it)
296 {
297 paths->push_back(it->first);
298 }
299
300 return m_watches.size();
301 }
302
303 #endif // wxUSE_FSWATCHER