Initial QSfera import
This commit is contained in:
@@ -0,0 +1,194 @@
|
||||
/*
|
||||
* Copyright (C) by Klaas Freitag <freitag@owncloud.com>
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
// event masks
|
||||
#include "folderwatcher.h"
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include <QFlags>
|
||||
#include <QTimer>
|
||||
|
||||
#if defined(Q_OS_WIN)
|
||||
#include "folderwatcher_win.h"
|
||||
#elif defined(Q_OS_MAC)
|
||||
#include "folderwatcher_mac.h"
|
||||
#elif defined(Q_OS_UNIX)
|
||||
#include "folderwatcher_linux.h"
|
||||
#endif
|
||||
|
||||
#include "folder.h"
|
||||
#include "filesystem.h"
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
namespace {
|
||||
constexpr auto notificationTimeoutC = 10s;
|
||||
}
|
||||
|
||||
namespace OCC {
|
||||
|
||||
Q_LOGGING_CATEGORY(lcFolderWatcher, "gui.folderwatcher", QtInfoMsg)
|
||||
|
||||
FolderWatcher::FolderWatcher(Folder *folder)
|
||||
: QObject(folder)
|
||||
, _folder(folder)
|
||||
{
|
||||
_timer.setInterval(notificationTimeoutC);
|
||||
_timer.setSingleShot(true);
|
||||
connect(&_timer, &QTimer::timeout, this, [this] {
|
||||
auto paths = popChangeSet();
|
||||
Q_ASSERT(!paths.empty());
|
||||
if (!paths.isEmpty()) {
|
||||
qCInfo(lcFolderWatcher) << u"Detected changes in paths:" << paths;
|
||||
Q_EMIT pathChanged(paths);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
FolderWatcher::~FolderWatcher()
|
||||
{
|
||||
}
|
||||
|
||||
void FolderWatcher::init(const QString &root)
|
||||
{
|
||||
_d.reset(new FolderWatcherPrivate(this, root));
|
||||
}
|
||||
|
||||
bool FolderWatcher::pathIsIgnored(const QString &path) const
|
||||
{
|
||||
Q_ASSERT(!path.isEmpty());
|
||||
Q_ASSERT(_folder);
|
||||
if (!QFileInfo::exists(path)) {
|
||||
std::error_code ec;
|
||||
const auto relPath =
|
||||
FileSystem::fromFilesystemPath(std::filesystem::proximate(FileSystem::toFilesystemPath(path), FileSystem::toFilesystemPath(_folder->path()), ec));
|
||||
Q_ASSERT(!ec);
|
||||
if (ec) {
|
||||
qCDebug(lcFolderWatcher) << u"* Failed to lookup record for path:" << path << u"error:" << ec.message();
|
||||
return false;
|
||||
}
|
||||
if (auto record = _folder->journalDb()->getFileRecord(relPath); record.isValid()) {
|
||||
// we know about the file, it got removed, we should not ignore that
|
||||
qCDebug(lcFolderWatcher) << u"* Not ignoring removed file" << path;
|
||||
return false;
|
||||
}
|
||||
// probably a temporary file that no longer exists
|
||||
qCDebug(lcFolderWatcher) << u"* Ignoring file" << path << u"It no longer exists and we don't have it in the database.";
|
||||
return true;
|
||||
}
|
||||
if (_folder->isFileExcludedAbsolute(path) && !Utility::isConflictFile(path)) {
|
||||
qCDebug(lcFolderWatcher) << u"* Ignoring file" << path;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool FolderWatcher::isReliable() const
|
||||
{
|
||||
return _isReliable;
|
||||
}
|
||||
|
||||
void FolderWatcher::startNotificatonTest(const QString &path)
|
||||
{
|
||||
#ifdef Q_OS_MAC
|
||||
// Testing the folder watcher on OSX is harder because the watcher
|
||||
// automatically discards changes that were performed by our process.
|
||||
// It would still be useful to test but the OSX implementation
|
||||
// is deferred until later.
|
||||
return;
|
||||
#endif
|
||||
|
||||
Q_ASSERT(_testNotificationPath.isEmpty());
|
||||
Q_ASSERT(!path.isEmpty());
|
||||
_testNotificationPath = path;
|
||||
|
||||
// Don't do the local file modification immediately:
|
||||
// wait for FolderWatchPrivate::_ready
|
||||
startNotificationTestWhenReady();
|
||||
}
|
||||
|
||||
void FolderWatcher::startNotificationTestWhenReady()
|
||||
{
|
||||
if (!_testNotificationPath.isEmpty()) {
|
||||
// we already received the notification
|
||||
return;
|
||||
}
|
||||
if (!_d->isReady()) {
|
||||
QTimer::singleShot(1s, this, &FolderWatcher::startNotificationTestWhenReady);
|
||||
return;
|
||||
}
|
||||
|
||||
if (OC_ENSURE(QFile::exists(_testNotificationPath))) {
|
||||
const auto mtime = FileSystem::getModTime(_testNotificationPath);
|
||||
FileSystem::setModTime(_testNotificationPath, mtime + 1);
|
||||
} else {
|
||||
QFile f(_testNotificationPath);
|
||||
f.open(QIODevice::WriteOnly | QIODevice::Append);
|
||||
}
|
||||
|
||||
QTimer::singleShot(notificationTimeoutC + 5s, this, [this]() {
|
||||
if (!_testNotificationPath.isEmpty())
|
||||
Q_EMIT becameUnreliable(tr("The watcher did not receive a test notification."));
|
||||
_testNotificationPath.clear();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
int FolderWatcher::testLinuxWatchCount() const
|
||||
{
|
||||
#ifdef Q_OS_LINUX
|
||||
return _d->testWatchCount();
|
||||
#else
|
||||
return -1;
|
||||
#endif
|
||||
}
|
||||
|
||||
void FolderWatcher::addChanges(QSet<QString> &&paths)
|
||||
{
|
||||
Q_ASSERT(thread() == QThread::currentThread());
|
||||
// the timer must be inactive if we haven't received changes yet
|
||||
Q_ASSERT((!_timer.isActive() && _changeSet.isEmpty()) || !_changeSet.isEmpty());
|
||||
Q_ASSERT(!paths.isEmpty());
|
||||
// ------- handle ignores:
|
||||
auto it = paths.cbegin();
|
||||
while (it != paths.cend()) {
|
||||
// we cause a file change from time to time to check whether the folder watcher works as expected
|
||||
if (!_testNotificationPath.isEmpty() && Utility::fileNamesEqual(*it, _testNotificationPath)) {
|
||||
_testNotificationPath.clear();
|
||||
}
|
||||
if (pathIsIgnored(*it)) {
|
||||
it = paths.erase(it);
|
||||
} else {
|
||||
++it;
|
||||
}
|
||||
}
|
||||
if (!paths.isEmpty()) {
|
||||
_changeSet.unite(paths);
|
||||
if (!_timer.isActive()) {
|
||||
_timer.start();
|
||||
// promote that we will report changes once _timer times out
|
||||
Q_EMIT changesDetected();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QSet<QString> FolderWatcher::popChangeSet()
|
||||
{
|
||||
// stop the timer as we pop all queued changes
|
||||
_timer.stop();
|
||||
return std::move(_changeSet);
|
||||
}
|
||||
|
||||
} // namespace OCC
|
||||
Reference in New Issue
Block a user