comparison src/fswatcher.cpp @ 540:fc2df97920e8 fswatcher

Introduce FsWatcher into main window (though it isn't fully rigged up yet)
author Chris Cannam
date Mon, 13 Feb 2012 17:29:06 +0000
parents 3935a7e621ca
children 0a16db274f2c
comparison
equal deleted inserted replaced
539:3935a7e621ca 540:fc2df97920e8
14 License, or (at your option) any later version. See the file 14 License, or (at your option) any later version. See the file
15 COPYING included with this distribution for more information. 15 COPYING included with this distribution for more information.
16 */ 16 */
17 17
18 #include "fswatcher.h" 18 #include "fswatcher.h"
19 #include "debug.h"
19 20
20 #include <QMutexLocker> 21 #include <QMutexLocker>
21 #include <QDir> 22 #include <QDir>
22 23
23 #include <deque> 24 #include <deque>
25
26 #define DEBUG_FSWATCHER 1
24 27
25 /* 28 /*
26 * Watching the filesystem is trickier than it seems at first glance. 29 * Watching the filesystem is trickier than it seems at first glance.
27 * 30 *
28 * We ideally should watch every directory, and every file that is 31 * We ideally should watch every directory, and every file that is
34 * filesystem. This can happen even in "read-only" operations: for 37 * filesystem. This can happen even in "read-only" operations: for
35 * example, hg stat creates files called hg-checklink and hg-checkexec 38 * example, hg stat creates files called hg-checklink and hg-checkexec
36 * to test properties of the filesystem. So we need to know to ignore 39 * to test properties of the filesystem. So we need to know to ignore
37 * those files; unfortunately, when watching a directory (which is how 40 * those files; unfortunately, when watching a directory (which is how
38 * we find out about the creation of new files) we are notified only 41 * we find out about the creation of new files) we are notified only
39 * that the directory has changed -- we aren't told what changed. So 42 * that the directory has changed -- we aren't told what changed.
40 * we need to rescan the directory merely to find out whether to 43 *
41 * ignore the change or not. 44 * This means that, when a directory changes, we need to rescan the
45 * directory to learn whether the set of files in it _excluding_ files
46 * matching our ignore patterns differs from the previous scan, and
47 * ignore the change if it doesn't.
42 */ 48 */
43 49
44 FsWatcher::FsWatcher() : 50 FsWatcher::FsWatcher() :
45 m_lastToken(0), 51 m_lastToken(0),
46 m_lastCounter(0) 52 m_lastCounter(0)
61 QMutexLocker locker(&m_mutex); 67 QMutexLocker locker(&m_mutex);
62 m_watcher.removePaths(m_watcher.directories()); 68 m_watcher.removePaths(m_watcher.directories());
63 m_watcher.removePaths(m_watcher.files()); 69 m_watcher.removePaths(m_watcher.files());
64 m_workDirPath = path; 70 m_workDirPath = path;
65 addWorkDirectory(path); 71 addWorkDirectory(path);
72 debugPrint();
66 } 73 }
67 74
68 void 75 void
69 FsWatcher::setTrackedFilePaths(QStringList paths) 76 FsWatcher::setTrackedFilePaths(QStringList paths)
70 { 77 {
71 QMutexLocker locker(&m_mutex); 78 QMutexLocker locker(&m_mutex);
72 m_watcher.removePaths(m_watcher.files()); 79 m_watcher.removePaths(m_watcher.files());
73 foreach (QString path, paths) { 80 foreach (QString path, paths) {
74 m_watcher.addPath(path); 81 m_watcher.addPath(path);
75 } 82 }
83 debugPrint();
76 } 84 }
77 85
78 void 86 void
79 FsWatcher::addWorkDirectory(QString path) 87 FsWatcher::addWorkDirectory(QString path)
80 { 88 {
92 100
93 QString path = pending.front(); 101 QString path = pending.front();
94 pending.pop_front(); 102 pending.pop_front();
95 if (!alreadyWatched.contains(path)) { 103 if (!alreadyWatched.contains(path)) {
96 m_watcher.addPath(path); 104 m_watcher.addPath(path);
105 m_dirContents[path] = scanDirectory(path);
97 } 106 }
98 107
99 QDir d(path); 108 QDir d(path);
100 if (d.exists()) { 109 if (d.exists()) {
101 d.setFilter(QDir::Dirs | QDir::NoDotAndDotDot | 110 d.setFilter(QDir::Dirs | QDir::NoDotAndDotDot |
151 void 160 void
152 FsWatcher::fsDirectoryChanged(QString path) 161 FsWatcher::fsDirectoryChanged(QString path)
153 { 162 {
154 { 163 {
155 QMutexLocker locker(&m_mutex); 164 QMutexLocker locker(&m_mutex);
165
156 if (shouldIgnore(path)) return; 166 if (shouldIgnore(path)) return;
157 size_t counter = ++m_lastCounter; 167
158 m_changes[path] = counter; 168 QSet<QString> files = scanDirectory(path);
159 } 169 if (files == m_dirContents[path]) {
170 #ifdef DEBUG_FSWATCHER
171 std::cerr << "FsWatcher: Directory " << path << " has changed, but not in a way that we are monitoring" << std::endl;
172 #endif
173 return;
174 }
175 else m_dirContents[path] = files;
176
177 size_t counter = ++m_lastCounter;
178 m_changes[path] = counter;
179 }
180
160 emit changed(); 181 emit changed();
161 } 182 }
162 183
163 void 184 void
164 FsWatcher::fsFileChanged(QString path) 185 FsWatcher::fsFileChanged(QString path)
165 { 186 {
166 QMutexLocker locker(&m_mutex); 187 {
188 QMutexLocker locker(&m_mutex);
189
190 // We don't check whether the file matches an ignore pattern,
191 // because we are only notified for file changes if we are
192 // watching the file explicitly, i.e. the file is in the
193 // tracked file paths list. So we never want to ignore them
194
195 size_t counter = ++m_lastCounter;
196 m_changes[path] = counter;
197 }
198
199 emit changed();
167 } 200 }
168 201
169 bool 202 bool
170 FsWatcher::shouldIgnore(QString path) 203 FsWatcher::shouldIgnore(QString path)
171 { 204 {
172 205 QFileInfo fi(path);
173 } 206 QString fn(fi.fileName());
174 207 foreach (QString pfx, m_ignoredPrefixes) {
208 if (fn.startsWith(pfx)) return true;
209 }
210 foreach (QString sfx, m_ignoredSuffixes) {
211 if (fn.endsWith(sfx)) return true;
212 }
213 return false;
214 }
215
216 QSet<QString>
217 FsWatcher::scanDirectory(QString path)
218 {
219 QSet<QString> files;
220 QDir d(path);
221 if (d.exists()) {
222 d.setFilter(QDir::Files | QDir::NoDotAndDotDot |
223 QDir::Readable | QDir::NoSymLinks);
224 foreach (QString entry, d.entryList()) {
225 if (entry.startsWith('.')) continue;
226 if (shouldIgnore(entry)) continue;
227 files.insert(entry);
228 }
229 }
230 return files;
231 }
232
233 void
234 FsWatcher::debugPrint()
235 {
236 #ifdef DEBUG_FSWATCHER
237 std::cerr << "FsWatcher: Now watching " << m_watcher.directories().size()
238 << " directories and " << m_watcher.files().size()
239 << " files" << std::endl;
240 #endif
241 }