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