nextcloud-desktop/src/mirall/folderwatcher.cpp

261 lines
7.3 KiB
C++
Raw Normal View History

2011-04-06 13:48:02 +04:00
/*
* Copyright (C) by Duncan Mac-Vicar P. <duncan@kde.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*/
2011-02-17 02:21:45 +03:00
// event masks
#include <sys/inotify.h>
2011-02-17 02:21:45 +03:00
#include <QFileInfo>
#include <QFlags>
#include <QDebug>
#include <QDir>
#include <QMutexLocker>
#include <QStringList>
#include <QTimer>
2011-02-17 02:21:45 +03:00
#include "mirall/inotify.h"
2011-02-17 02:21:45 +03:00
#include "mirall/folderwatcher.h"
#include "mirall/fileutils.h"
2011-02-17 02:21:45 +03:00
static const uint32_t standard_event_mask =
IN_CLOSE_WRITE | IN_ATTRIB | IN_MOVE | IN_CREATE | IN_DELETE | IN_DELETE_SELF | IN_MOVE_SELF | IN_UNMOUNT | IN_ONLYDIR | IN_DONT_FOLLOW;
/* minimum amount of seconds between two
events to consider it a new event */
#define DEFAULT_EVENT_INTERVAL_SEC 5
2011-02-17 02:21:45 +03:00
namespace Mirall {
FolderWatcher::FolderWatcher(const QString &root, QObject *parent)
: QObject(parent),
_eventsEnabled(true),
_eventInterval(DEFAULT_EVENT_INTERVAL_SEC),
_root(root),
_processTimer(new QTimer(this)),
_lastMask(0)
2011-02-17 02:21:45 +03:00
{
// this is not the best place for this
addIgnore("/**/.unison*");
_processTimer->setSingleShot(true);
QObject::connect(_processTimer, SIGNAL(timeout()), this, SLOT(slotProcessTimerTimeout()));
_inotify = new INotify(standard_event_mask);
slotAddFolderRecursive(root);
QObject::connect(_inotify, SIGNAL(notifyEvent(int, int, const QString &)),
SLOT(slotINotifyEvent(int, int, const QString &)));
// do a first synchronization to get changes while
// the application was not running
setProcessTimer();
2011-02-17 02:21:45 +03:00
}
FolderWatcher::~FolderWatcher()
{
}
2011-03-21 00:18:38 +03:00
QString FolderWatcher::root() const
{
return _root;
}
void FolderWatcher::addIgnore(const QString &pattern)
{
_ignores.append(pattern);
}
bool FolderWatcher::eventsEnabled() const
{
return _eventsEnabled;
}
void FolderWatcher::setEventsEnabled(bool enabled)
{
qDebug() << " * event notification " << (enabled ? "enabled" : "disabled");
_eventsEnabled = enabled;
if (_eventsEnabled) {
// schedule a queue cleanup for accumulated events
if ( _pendingPaths.empty() )
return;
setProcessTimer();
}
else
{
// if we are disabling events, clear any ongoing timer
if (_processTimer->isActive())
_processTimer->stop();
}
}
void FolderWatcher::clearPendingEvents()
{
if (_processTimer->isActive())
_processTimer->stop();
_pendingPaths.clear();
}
int FolderWatcher::eventInterval() const
{
return _eventInterval;
}
void FolderWatcher::setEventInterval(int seconds)
{
_eventInterval = seconds;
}
QStringList FolderWatcher::folders() const
{
return _inotify->directories();
}
void FolderWatcher::slotAddFolderRecursive(const QString &path)
2011-02-17 02:21:45 +03:00
{
2011-03-30 00:23:42 +04:00
int subdirs = 0;
qDebug() << "(+) Watcher:" << path;
_inotify->addPath(path);
QStringList watchedFolders(_inotify->directories());
//qDebug() << "currently watching " << watchedFolders;
QStringListIterator subfoldersIt(FileUtils::subFoldersList(path, FileUtils::SubFolderRecursive));
2011-02-17 02:21:45 +03:00
while (subfoldersIt.hasNext()) {
QDir folder (subfoldersIt.next());
if (folder.exists() && !watchedFolders.contains(folder.path())) {
2011-03-30 00:23:42 +04:00
subdirs++;
//qDebug() << "(+) Watcher:" << folder.path();
// check that it does not match the ignore list
foreach (QString pattern, _ignores) {
QRegExp regexp(pattern);
regexp.setPatternSyntax(QRegExp::Wildcard);
if (regexp.exactMatch(folder.path())) {
qDebug() << "* Not adding" << folder.path();
continue;
}
}
_inotify->addPath(folder.path());
2011-02-17 02:21:45 +03:00
}
else
2011-03-30 00:23:42 +04:00
qDebug() << " `-> discarded:" << folder.path();
2011-02-17 02:21:45 +03:00
}
2011-03-30 00:23:42 +04:00
if (subdirs >0)
qDebug() << " `-> and" << subdirs << "subdirectories";
}
2011-02-17 02:21:45 +03:00
void FolderWatcher::slotINotifyEvent(int mask, int cookie, const QString &path)
{
int lastMask = _lastMask;
QString lastPath = _lastPath;
_lastMask = mask;
_lastPath = path;
// cancel close write events that come after create
if (lastMask == IN_CREATE && mask == IN_CLOSE_WRITE
&& lastPath == path ) {
return;
}
if (IN_IGNORED & mask) {
//qDebug() << "IGNORE event";
return;
}
if (IN_Q_OVERFLOW & mask) {
//qDebug() << "OVERFLOW";
}
2011-02-17 02:21:45 +03:00
if (mask & IN_CREATE) {
//qDebug() << cookie << " CREATE: " << path;
if (QFileInfo(path).isDir()) {
//setEventsEnabled(false);
slotAddFolderRecursive(path);
//setEventsEnabled(true);
}
}
else if (mask & IN_DELETE) {
//qDebug() << cookie << " DELETE: " << path;
if (_inotify->directories().contains(path) &&
QFileInfo(path).isDir());
qDebug() << "(-) Watcher:" << path;
_inotify->removePath(path);
}
else if (mask & IN_CLOSE_WRITE) {
//qDebug() << cookie << " WRITABLE CLOSED: " << path;
}
else if (mask & IN_MOVE) {
//qDebug() << cookie << " MOVE: " << path;
}
else {
//qDebug() << cookie << " OTHER " << mask << " :" << path;
}
foreach (QString pattern, _ignores) {
QRegExp regexp(pattern);
regexp.setPatternSyntax(QRegExp::Wildcard);
if (regexp.exactMatch(path)) {
qDebug() << "* Discarded" << path;
return;
}
}
_pendingPaths.append(path);
slotProcessPaths();
}
void FolderWatcher::slotProcessTimerTimeout()
{
qDebug() << "* Scheduled processing of event queue for" << root();
if (!_pendingPaths.empty())
slotProcessPaths();
}
void FolderWatcher::setProcessTimer()
{
if (!_processTimer->isActive()) {
qDebug() << "* Pending events for" << root() << "will be processed in" << eventInterval() << "seconds (" << QTime::currentTime().addSecs(eventInterval()).toString("HH:mm:ss") << ")." << _pendingPaths.size() << "events until now )";
}
_processTimer->start(eventInterval() * 1000);
}
void FolderWatcher::slotProcessPaths()
{
QTime eventTime = QTime::currentTime();
QTime lastEventTime = _lastEventTime;
_lastEventTime = eventTime;
// if the events are disabled or the last event happened
// recently eg: copying lot of ifles
if (!eventsEnabled() ||
( !lastEventTime.isNull() &&
(lastEventTime.secsTo(eventTime) < eventInterval()) ))
{
// in case this is the last file from a bulk copy
// set the process timer again so that we process the
// queue we are not processing now
setProcessTimer();
return;
}
QStringList notifyPaths(_pendingPaths);
_pendingPaths.clear();
//qDebug() << lastEventTime << eventTime;
qDebug() << " * Notify" << notifyPaths.size() << "changed items for" << root();
emit folderChanged(notifyPaths);
2011-02-17 02:21:45 +03:00
}
}
#include "folderwatcher.moc"