Chris@538: /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ Chris@538: Chris@538: /* Chris@538: EasyMercurial Chris@538: Chris@538: Based on HgExplorer by Jari Korhonen Chris@538: Copyright (c) 2010 Jari Korhonen Chris@560: Copyright (c) 2012 Chris Cannam Chris@560: Copyright (c) 2012 Queen Mary, University of London Chris@538: Chris@538: This program is free software; you can redistribute it and/or Chris@538: modify it under the terms of the GNU General Public License as Chris@538: published by the Free Software Foundation; either version 2 of the Chris@538: License, or (at your option) any later version. See the file Chris@538: COPYING included with this distribution for more information. Chris@538: */ Chris@538: Chris@538: #ifndef FSWATCHER_H Chris@538: #define FSWATCHER_H Chris@538: Chris@538: #include Chris@538: #include Chris@538: #include Chris@538: #include Chris@538: #include Chris@538: #include Chris@538: #include Chris@538: #include Chris@538: Chris@538: class FsWatcher : public QObject Chris@538: { Chris@538: Q_OBJECT Chris@538: Chris@538: public: Chris@538: FsWatcher(); Chris@538: virtual ~FsWatcher(); Chris@538: Chris@538: /** Chris@539: * Set the root path of the work directory to be monitored. This Chris@539: * directory and all its subdirectories (recursively) will be Chris@539: * monitored for changes. Chris@539: * Chris@541: * If this path differs from the currently set work dir path, then Chris@541: * the tracked file paths will also be cleared. Call Chris@539: * setTrackedFilePaths afterwards to ensure non-directory files Chris@539: * are monitored. Chris@538: */ Chris@538: void setWorkDirPath(QString path); Chris@538: Chris@538: /** Chris@539: * Provide a set of paths for files which should be tracked. These Chris@542: * will be the only non-directory files monitored for changes. The Chris@542: * paths should be relative to the work directory. Chris@539: */ Chris@539: void setTrackedFilePaths(QStringList paths); Chris@539: Chris@539: /** Chris@538: * Provide a set of prefixes to ignore. Files whose names start Chris@538: * with a prefix in this set will be ignored when they change. Chris@538: */ Chris@538: void setIgnoredFilePrefixes(QStringList prefixes); Chris@538: Chris@538: /** Chris@538: * Provide a set of suffixes to ignore. Files whose names end Chris@538: * with a suffix in this set will be ignored when they change. Chris@538: */ Chris@538: void setIgnoredFileSuffixes(QStringList suffixes); Chris@538: Chris@538: /** Chris@538: * Return a token to identify the current caller in subsequent Chris@538: * calls to getChangedPaths(). Only changes that occur after this Chris@538: * has been called can be detected by the caller. Chris@538: */ Chris@538: int getNewToken(); Chris@538: Chris@538: /** Chris@538: * Return a list of all non-ignored file paths that have been Chris@538: * observed to have changed since the last call to getChangedPaths Chris@538: * with the same token. Chris@538: */ Chris@538: QSet getChangedPaths(int token); Chris@538: Chris@538: signals: Chris@538: /** Chris@538: * Emitted when something has changed. Use the asynchronous Chris@538: * interface to find out what. Chris@538: */ Chris@538: void changed(); Chris@538: Chris@538: private slots: Chris@538: void fsDirectoryChanged(QString); Chris@538: void fsFileChanged(QString); Chris@538: Chris@538: private: Chris@538: // call with lock already held Chris@538: void addWorkDirectory(QString path); Chris@538: Chris@538: // call with lock already held Chris@538: bool shouldIgnore(QString path); Chris@538: Chris@540: // call with lock already held. Returns set of non-ignored files in dir Chris@540: QSet scanDirectory(QString path); Chris@540: Chris@540: // call with lock already held Chris@540: void debugPrint(); Chris@540: Chris@538: private: Chris@538: /** Chris@538: * A change associates a filename with a counter (the Chris@538: * size_t). Each time a file is changed, its counter is assigned Chris@538: * from m_lastCounter and m_lastCounter is incremented. Chris@538: * Chris@538: * This is not especially efficient -- we often want to find "all Chris@538: * files whose counter is greater than X" which involves a Chris@538: * traversal. Maybe something better later. Chris@538: */ Chris@538: QHash m_changes; Chris@538: Chris@538: /** Chris@538: * Associates a token (the client identifier) with a counter. The Chris@538: * counter represents the value of m_lastCounter at the moment Chris@538: * getChangedPaths last completed for that token. Any files in Chris@538: * m_changes whose counters are larger must have been modified. Chris@538: */ Chris@538: QMap m_tokenMap; Chris@538: Chris@540: /** Chris@540: * Associates a directory path with a list of all the files in it Chris@540: * that do not match our ignore patterns. When a directory is Chris@540: * signalled as having changed, then we need to rescan it and Chris@540: * compare against this list before we can determine whether to Chris@540: * notify about the change or not. Chris@540: */ Chris@540: QHash > m_dirContents; Chris@540: Chris@538: QStringList m_ignoredPrefixes; Chris@538: QStringList m_ignoredSuffixes; Chris@538: Chris@538: /// Everything in this class is synchronised. Chris@538: QMutex m_mutex; Chris@538: Chris@538: QString m_workDirPath; Chris@538: int m_lastToken; Chris@538: size_t m_lastCounter; Chris@538: QFileSystemWatcher m_watcher; Chris@538: }; Chris@538: Chris@538: #endif