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