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