Chris@538
|
1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
|
Chris@538
|
2
|
Chris@538
|
3 /*
|
Chris@538
|
4 EasyMercurial
|
Chris@538
|
5
|
Chris@538
|
6 Based on hgExplorer by Jari Korhonen
|
Chris@538
|
7 Copyright (c) 2010 Jari Korhonen
|
Chris@538
|
8 Copyright (c) 2011 Chris Cannam
|
Chris@538
|
9 Copyright (c) 2011 Queen Mary, University of London
|
Chris@538
|
10
|
Chris@538
|
11 This program is free software; you can redistribute it and/or
|
Chris@538
|
12 modify it under the terms of the GNU General Public License as
|
Chris@538
|
13 published by the Free Software Foundation; either version 2 of the
|
Chris@538
|
14 License, or (at your option) any later version. See the file
|
Chris@538
|
15 COPYING included with this distribution for more information.
|
Chris@538
|
16 */
|
Chris@538
|
17
|
Chris@538
|
18 #include "fswatcher.h"
|
Chris@538
|
19
|
Chris@538
|
20 #include <QMutexLocker>
|
Chris@538
|
21 #include <QDir>
|
Chris@538
|
22
|
Chris@538
|
23 #include <deque>
|
Chris@538
|
24
|
Chris@539
|
25 /*
|
Chris@539
|
26 * Watching the filesystem is trickier than it seems at first glance.
|
Chris@539
|
27 *
|
Chris@539
|
28 * We ideally should watch every directory, and every file that is
|
Chris@539
|
29 * tracked by Hg. If a new file is created in a directory, then we
|
Chris@539
|
30 * need to respond in order to show it as a potential candidate to be
|
Chris@539
|
31 * added.
|
Chris@539
|
32 *
|
Chris@539
|
33 * Complicating matters though is that Hg itself might modify the
|
Chris@539
|
34 * filesystem. This can happen even in "read-only" operations: for
|
Chris@539
|
35 * example, hg stat creates files called hg-checklink and hg-checkexec
|
Chris@539
|
36 * to test properties of the filesystem. So we need to know to ignore
|
Chris@539
|
37 * those files; unfortunately, when watching a directory (which is how
|
Chris@539
|
38 * we find out about the creation of new files) we are notified only
|
Chris@539
|
39 * that the directory has changed -- we aren't told what changed. So
|
Chris@539
|
40 * we need to rescan the directory merely to find out whether to
|
Chris@539
|
41 * ignore the change or not.
|
Chris@539
|
42 */
|
Chris@539
|
43
|
Chris@538
|
44 FsWatcher::FsWatcher() :
|
Chris@538
|
45 m_lastToken(0),
|
Chris@538
|
46 m_lastCounter(0)
|
Chris@538
|
47 {
|
Chris@538
|
48 connect(&m_watcher, SIGNAL(directoryChanged(QString)),
|
Chris@538
|
49 this, SLOT(fsDirectoryChanged(QString)));
|
Chris@538
|
50 connect(&m_watcher, SIGNAL(fileChanged(QString)),
|
Chris@538
|
51 this, SLOT(fsFileChanged(QString)));
|
Chris@538
|
52 }
|
Chris@538
|
53
|
Chris@538
|
54 FsWatcher::~FsWatcher()
|
Chris@538
|
55 {
|
Chris@538
|
56 }
|
Chris@538
|
57
|
Chris@538
|
58 void
|
Chris@538
|
59 FsWatcher::setWorkDirPath(QString path)
|
Chris@538
|
60 {
|
Chris@538
|
61 QMutexLocker locker(&m_mutex);
|
Chris@538
|
62 m_watcher.removePaths(m_watcher.directories());
|
Chris@538
|
63 m_watcher.removePaths(m_watcher.files());
|
Chris@538
|
64 m_workDirPath = path;
|
Chris@538
|
65 addWorkDirectory(path);
|
Chris@538
|
66 }
|
Chris@538
|
67
|
Chris@538
|
68 void
|
Chris@539
|
69 FsWatcher::setTrackedFilePaths(QStringList paths)
|
Chris@539
|
70 {
|
Chris@539
|
71 QMutexLocker locker(&m_mutex);
|
Chris@539
|
72 m_watcher.removePaths(m_watcher.files());
|
Chris@539
|
73 foreach (QString path, paths) {
|
Chris@539
|
74 m_watcher.addPath(path);
|
Chris@539
|
75 }
|
Chris@539
|
76 }
|
Chris@539
|
77
|
Chris@539
|
78 void
|
Chris@538
|
79 FsWatcher::addWorkDirectory(QString path)
|
Chris@538
|
80 {
|
Chris@538
|
81 // QFileSystemWatcher will refuse to add a file or directory to
|
Chris@538
|
82 // its watch list that it is already watching -- fine -- but it
|
Chris@538
|
83 // prints a warning when this happens, which we wouldn't want. So
|
Chris@538
|
84 // we'll check for duplicates ourselves.
|
Chris@538
|
85 QSet<QString> alreadyWatched =
|
Chris@538
|
86 QSet<QString>::fromList(m_watcher.directories());
|
Chris@538
|
87
|
Chris@538
|
88 std::deque<QString> pending;
|
Chris@538
|
89 pending.push_back(path);
|
Chris@538
|
90
|
Chris@538
|
91 while (!pending.empty()) {
|
Chris@538
|
92
|
Chris@538
|
93 QString path = pending.front();
|
Chris@538
|
94 pending.pop_front();
|
Chris@538
|
95 if (!alreadyWatched.contains(path)) {
|
Chris@538
|
96 m_watcher.addPath(path);
|
Chris@538
|
97 }
|
Chris@538
|
98
|
Chris@538
|
99 QDir d(path);
|
Chris@538
|
100 if (d.exists()) {
|
Chris@538
|
101 d.setFilter(QDir::Dirs | QDir::NoDotAndDotDot |
|
Chris@538
|
102 QDir::Readable | QDir::NoSymLinks);
|
Chris@538
|
103 foreach (QString entry, d.entryList()) {
|
Chris@538
|
104 if (entry.startsWith('.')) continue;
|
Chris@538
|
105 QString entryPath = d.absoluteFilePath(entry);
|
Chris@538
|
106 pending.push_back(entryPath);
|
Chris@538
|
107 }
|
Chris@538
|
108 }
|
Chris@538
|
109 }
|
Chris@538
|
110 }
|
Chris@538
|
111
|
Chris@538
|
112 void
|
Chris@538
|
113 FsWatcher::setIgnoredFilePrefixes(QStringList prefixes)
|
Chris@538
|
114 {
|
Chris@538
|
115 QMutexLocker locker(&m_mutex);
|
Chris@538
|
116 m_ignoredPrefixes = prefixes;
|
Chris@538
|
117 }
|
Chris@538
|
118
|
Chris@538
|
119 void
|
Chris@538
|
120 FsWatcher::setIgnoredFileSuffixes(QStringList suffixes)
|
Chris@538
|
121 {
|
Chris@538
|
122 QMutexLocker locker(&m_mutex);
|
Chris@538
|
123 m_ignoredSuffixes = suffixes;
|
Chris@538
|
124 }
|
Chris@538
|
125
|
Chris@538
|
126 int
|
Chris@538
|
127 FsWatcher::getNewToken()
|
Chris@538
|
128 {
|
Chris@538
|
129 QMutexLocker locker(&m_mutex);
|
Chris@538
|
130 int token = ++m_lastToken;
|
Chris@538
|
131 m_tokenMap[token] = m_lastCounter;
|
Chris@538
|
132 return token;
|
Chris@538
|
133 }
|
Chris@538
|
134
|
Chris@538
|
135 QSet<QString>
|
Chris@538
|
136 FsWatcher::getChangedPaths(int token)
|
Chris@538
|
137 {
|
Chris@538
|
138 QMutexLocker locker(&m_mutex);
|
Chris@538
|
139 size_t lastUpdatedAt = m_tokenMap[token];
|
Chris@538
|
140 QSet<QString> changed;
|
Chris@538
|
141 for (QHash<QString, size_t>::const_iterator i = m_changes.begin();
|
Chris@538
|
142 i != m_changes.end(); ++i) {
|
Chris@538
|
143 if (i.value() > lastUpdatedAt) {
|
Chris@538
|
144 changed.insert(i.key());
|
Chris@538
|
145 }
|
Chris@538
|
146 }
|
Chris@538
|
147 m_tokenMap[token] = m_lastCounter;
|
Chris@538
|
148 return changed;
|
Chris@538
|
149 }
|
Chris@538
|
150
|
Chris@538
|
151 void
|
Chris@538
|
152 FsWatcher::fsDirectoryChanged(QString path)
|
Chris@538
|
153 {
|
Chris@538
|
154 {
|
Chris@538
|
155 QMutexLocker locker(&m_mutex);
|
Chris@538
|
156 if (shouldIgnore(path)) return;
|
Chris@538
|
157 size_t counter = ++m_lastCounter;
|
Chris@538
|
158 m_changes[path] = counter;
|
Chris@538
|
159 }
|
Chris@538
|
160 emit changed();
|
Chris@538
|
161 }
|
Chris@538
|
162
|
Chris@538
|
163 void
|
Chris@538
|
164 FsWatcher::fsFileChanged(QString path)
|
Chris@538
|
165 {
|
Chris@538
|
166 QMutexLocker locker(&m_mutex);
|
Chris@538
|
167 }
|
Chris@538
|
168
|
Chris@538
|
169 bool
|
Chris@538
|
170 FsWatcher::shouldIgnore(QString path)
|
Chris@538
|
171 {
|
Chris@538
|
172
|
Chris@538
|
173 }
|
Chris@538
|
174
|