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