]> git.saurik.com Git - wxWidgets.git/blob - src/common/fswatchercmn.cpp
dfa8730cf3a7c4e95603690705d17a86c5bc77c3
[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 case wxFSW_EVENT_ATTRIB: // Currently this is wxGTK-only
44 return "ATTRIBUTE";
45 case wxFSW_EVENT_WARNING:
46 return "WARNING";
47 case wxFSW_EVENT_ERROR:
48 return "ERROR";
49 }
50
51 // should never be reached!
52 wxFAIL_MSG("Unknown change type");
53 return "INVALID_TYPE";
54 }
55
56
57 // ============================================================================
58 // wxFileSystemWatcherEvent implementation
59 // ============================================================================
60
61 wxString wxFileSystemWatcherEvent::ToString() const
62 {
63 return wxString::Format("FSW_EVT type=%d (%s) path='%s'", m_changeType,
64 GetFSWEventChangeTypeName(m_changeType), GetPath().GetFullPath());
65 }
66
67
68 // ============================================================================
69 // wxFileSystemWatcherEvent implementation
70 // ============================================================================
71
72 wxFileSystemWatcherBase::wxFileSystemWatcherBase() :
73 m_service(0), m_owner(this)
74 {
75 }
76
77 wxFileSystemWatcherBase::~wxFileSystemWatcherBase()
78 {
79 RemoveAll();
80 if (m_service)
81 {
82 delete m_service;
83 }
84 }
85
86 bool wxFileSystemWatcherBase::Add(const wxFileName& path, int events)
87 {
88 wxFSWPathType type = wxFSWPath_None;
89 if ( path.FileExists() )
90 {
91 type = wxFSWPath_File;
92 }
93 else if ( path.DirExists() )
94 {
95 type = wxFSWPath_Dir;
96 }
97 else
98 {
99 wxLogError(_("Can't monitor non-existent path \"%s\" for changes."),
100 path.GetFullPath());
101 return false;
102 }
103
104 return AddAny(path, events, type);
105 }
106
107 bool
108 wxFileSystemWatcherBase::AddAny(const wxFileName& path,
109 int events,
110 wxFSWPathType type,
111 const wxString& filespec)
112 {
113 wxString canonical = GetCanonicalPath(path);
114 if (canonical.IsEmpty())
115 return false;
116
117 // adding a path in a platform specific way
118 wxFSWatchInfo watch(canonical, events, type, filespec);
119 if ( !m_service->Add(watch) )
120 return false;
121
122 // on success, either add path to our 'watch-list'
123 // or, if already watched, inc the refcount. This may happen if
124 // a dir is Add()ed, then later AddTree() is called on a parent dir
125 wxFSWatchInfoMap::iterator it = m_watches.find(canonical);
126 if ( it == m_watches.end() )
127 {
128 wxFSWatchInfoMap::value_type val(canonical, watch);
129 m_watches.insert(val).second;
130 }
131 else
132 {
133 wxFSWatchInfo& watch = it->second;
134 int count = watch.IncRef();
135 wxLogTrace(wxTRACE_FSWATCHER,
136 "'%s' is now watched %d times", canonical, count);
137 }
138 return true;
139 }
140
141 bool wxFileSystemWatcherBase::Remove(const wxFileName& path)
142 {
143 // args validation & consistency checks
144 wxString canonical = GetCanonicalPath(path);
145 if (canonical.IsEmpty())
146 return false;
147
148 wxFSWatchInfoMap::iterator it = m_watches.find(canonical);
149 wxCHECK_MSG(it != m_watches.end(), false,
150 wxString::Format("Path '%s' is not watched", canonical));
151
152 // Decrement the watch's refcount and remove from watch-list if 0
153 bool ret = true;
154 wxFSWatchInfo& watch = it->second;
155 if ( !watch.DecRef() )
156 {
157 // remove in a platform specific way
158 ret = m_service->Remove(watch);
159
160 m_watches.erase(it);
161 }
162 return ret;
163 }
164
165 bool wxFileSystemWatcherBase::AddTree(const wxFileName& path, int events,
166 const wxString& filespec)
167 {
168 if (!path.DirExists())
169 return false;
170
171 // OPT could be optimised if we stored information about relationships
172 // between paths
173 class AddTraverser : public wxDirTraverser
174 {
175 public:
176 AddTraverser(wxFileSystemWatcherBase* watcher, int events,
177 const wxString& filespec) :
178 m_watcher(watcher), m_events(events), m_filespec(filespec)
179 {
180 }
181
182 virtual wxDirTraverseResult OnFile(const wxString& WXUNUSED(filename))
183 {
184 // There is no need to watch individual files as we watch the
185 // parent directory which will notify us about any changes in them.
186 return wxDIR_CONTINUE;
187 }
188
189 virtual wxDirTraverseResult OnDir(const wxString& dirname)
190 {
191 if ( m_watcher->AddAny(wxFileName::DirName(dirname),
192 m_events, wxFSWPath_Tree, m_filespec) )
193 {
194 wxLogTrace(wxTRACE_FSWATCHER,
195 "--- AddTree adding directory '%s' ---", dirname);
196 }
197 return wxDIR_CONTINUE;
198 }
199
200 private:
201 wxFileSystemWatcherBase* m_watcher;
202 int m_events;
203 wxString m_filespec;
204 };
205
206 wxDir dir(path.GetFullPath());
207 // Prevent asserts or infinite loops in trees containing symlinks
208 int flags = wxDIR_DIRS;
209 if ( !path.ShouldFollowLink() )
210 {
211 flags |= wxDIR_NO_FOLLOW;
212 }
213 AddTraverser traverser(this, events, filespec);
214 dir.Traverse(traverser, filespec, flags);
215
216 // Add the path itself explicitly as Traverse() doesn't return it.
217 AddAny(path.GetPathWithSep(), events, wxFSWPath_Tree, filespec);
218
219 return true;
220 }
221
222 bool wxFileSystemWatcherBase::RemoveTree(const wxFileName& path)
223 {
224 if (!path.DirExists())
225 return false;
226
227 // OPT could be optimised if we stored information about relationships
228 // between paths
229 class RemoveTraverser : public wxDirTraverser
230 {
231 public:
232 RemoveTraverser(wxFileSystemWatcherBase* watcher,
233 const wxString& filespec) :
234 m_watcher(watcher), m_filespec(filespec)
235 {
236 }
237
238 virtual wxDirTraverseResult OnFile(const wxString& WXUNUSED(filename))
239 {
240 // We never watch the individual files when watching the tree, so
241 // nothing to do here.
242 return wxDIR_CONTINUE;
243 }
244
245 virtual wxDirTraverseResult OnDir(const wxString& dirname)
246 {
247 m_watcher->Remove(wxFileName::DirName(dirname));
248 return wxDIR_CONTINUE;
249 }
250
251 private:
252 wxFileSystemWatcherBase* m_watcher;
253 wxString m_filespec;
254 };
255
256 // If AddTree() used a filespec, we must use the same one
257 wxString canonical = GetCanonicalPath(path);
258 wxFSWatchInfoMap::iterator it = m_watches.find(canonical);
259 wxCHECK_MSG( it != m_watches.end(), false,
260 wxString::Format("Path '%s' is not watched", canonical) );
261 wxFSWatchInfo watch = it->second;
262 const wxString filespec = watch.GetFilespec();
263
264 #if defined(__WINDOWS__)
265 // When there's no filespec, the wxMSW AddTree() would have set a watch
266 // on only the passed 'path'. We must therefore remove only this
267 if (filespec.empty())
268 {
269 return Remove(path);
270 }
271 // Otherwise fall through to the generic implementation
272 #endif // __WINDOWS__
273
274 wxDir dir(path.GetFullPath());
275 // AddTree() might have used the wxDIR_NO_FOLLOW to prevent asserts or
276 // infinite loops in trees containing symlinks. We need to do the same
277 // or we'll try to remove unwatched items. Let's hope the caller used
278 // the same ShouldFollowLink() setting as in AddTree()...
279 int flags = wxDIR_DIRS;
280 if ( !path.ShouldFollowLink() )
281 {
282 flags |= wxDIR_NO_FOLLOW;
283 }
284 RemoveTraverser traverser(this, filespec);
285 dir.Traverse(traverser, filespec, flags);
286
287 // As in AddTree() above, handle the path itself explicitly.
288 Remove(path);
289
290 return true;
291 }
292
293 bool wxFileSystemWatcherBase::RemoveAll()
294 {
295 m_service->RemoveAll();
296 m_watches.clear();
297 return true;
298 }
299
300 int wxFileSystemWatcherBase::GetWatchedPathsCount() const
301 {
302 return m_watches.size();
303 }
304
305 int wxFileSystemWatcherBase::GetWatchedPaths(wxArrayString* paths) const
306 {
307 wxCHECK_MSG( paths != NULL, -1, "Null array passed to retrieve paths");
308
309 wxFSWatchInfoMap::const_iterator it = m_watches.begin();
310 for ( ; it != m_watches.end(); ++it)
311 {
312 paths->push_back(it->first);
313 }
314
315 return m_watches.size();
316 }
317
318 #endif // wxUSE_FSWATCHER