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