mirror of
https://github.com/QuasarApp/qca.git
synced 2025-05-08 08:49:34 +00:00
256 lines
6.3 KiB
C++
256 lines
6.3 KiB
C++
/*
|
|
* Copyright (C) 2003-2008 Justin Karneges <justin@affinix.com>
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
*
|
|
* This library 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
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
|
* 02110-1301 USA
|
|
*
|
|
*/
|
|
|
|
#include "qca_support.h"
|
|
|
|
#include <QFileSystemWatcher>
|
|
#include <QFileInfo>
|
|
#include <QDir>
|
|
#include <QList>
|
|
#include <QDateTime>
|
|
#include "qca_safeobj.h"
|
|
|
|
namespace QCA {
|
|
|
|
// this gets us DOR-SS and SR, provided we delete the object between uses.
|
|
// we assume QFileSystemWatcher complies to DS,NE.
|
|
class QFileSystemWatcherRelay : public QObject
|
|
{
|
|
Q_OBJECT
|
|
public:
|
|
QFileSystemWatcher *watcher;
|
|
|
|
QFileSystemWatcherRelay(QFileSystemWatcher *_watcher, QObject *parent = nullptr)
|
|
:QObject(parent), watcher(_watcher)
|
|
{
|
|
connect(watcher, SIGNAL(directoryChanged(const QString &)), SIGNAL(directoryChanged(const QString &)), Qt::QueuedConnection);
|
|
connect(watcher, SIGNAL(fileChanged(const QString &)), SIGNAL(fileChanged(const QString &)), Qt::QueuedConnection);
|
|
}
|
|
|
|
signals:
|
|
void directoryChanged(const QString &path);
|
|
void fileChanged(const QString &path);
|
|
};
|
|
|
|
//----------------------------------------------------------------------------
|
|
// DirWatch
|
|
//----------------------------------------------------------------------------
|
|
class DirWatch::Private : public QObject
|
|
{
|
|
Q_OBJECT
|
|
public:
|
|
DirWatch *q;
|
|
QFileSystemWatcher *watcher;
|
|
QFileSystemWatcherRelay *watcher_relay;
|
|
QString dirName;
|
|
|
|
Private(DirWatch *_q) : QObject(_q), q(_q), watcher(0), watcher_relay(0)
|
|
{
|
|
}
|
|
|
|
private slots:
|
|
void watcher_changed(const QString &path)
|
|
{
|
|
Q_UNUSED(path);
|
|
emit q->changed();
|
|
}
|
|
};
|
|
|
|
DirWatch::DirWatch(const QString &dir, QObject *parent)
|
|
:QObject(parent)
|
|
{
|
|
d = new Private(this);
|
|
setDirName(dir);
|
|
}
|
|
|
|
DirWatch::~DirWatch()
|
|
{
|
|
delete d;
|
|
}
|
|
|
|
QString DirWatch::dirName() const
|
|
{
|
|
return d->dirName;
|
|
}
|
|
|
|
void DirWatch::setDirName(const QString &dir)
|
|
{
|
|
if(d->watcher)
|
|
{
|
|
delete d->watcher;
|
|
delete d->watcher_relay;
|
|
d->watcher = 0;
|
|
d->watcher_relay = 0;
|
|
}
|
|
|
|
d->dirName = dir;
|
|
|
|
if(!d->dirName.isEmpty() && QFileInfo(d->dirName).isDir())
|
|
{
|
|
d->watcher = new QFileSystemWatcher(this);
|
|
d->watcher_relay = new QFileSystemWatcherRelay(d->watcher, this);
|
|
connect(d->watcher_relay, SIGNAL(directoryChanged(const QString &)), d, SLOT(watcher_changed(const QString &)));
|
|
|
|
d->watcher->addPath(d->dirName);
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
// FileWatch
|
|
//----------------------------------------------------------------------------
|
|
|
|
class FileWatch::Private : public QObject
|
|
{
|
|
Q_OBJECT
|
|
public:
|
|
FileWatch *q;
|
|
QFileSystemWatcher *watcher;
|
|
QFileSystemWatcherRelay *watcher_relay;
|
|
QString fileName; // file (optionally w/ path) as provided by user
|
|
QString filePath; // absolute path of file, calculated by us
|
|
bool fileExisted;
|
|
|
|
Private(FileWatch *_q) : QObject(_q), q(_q), watcher(0), watcher_relay(0)
|
|
{
|
|
}
|
|
|
|
void start(const QString &_fileName)
|
|
{
|
|
fileName = _fileName;
|
|
|
|
watcher = new QFileSystemWatcher(this);
|
|
watcher_relay = new QFileSystemWatcherRelay(watcher, this);
|
|
connect(watcher_relay, SIGNAL(directoryChanged(const QString &)), SLOT(dir_changed(const QString &)));
|
|
connect(watcher_relay, SIGNAL(fileChanged(const QString &)), SLOT(file_changed(const QString &)));
|
|
|
|
QFileInfo fi(fileName);
|
|
fi.makeAbsolute();
|
|
filePath = fi.filePath();
|
|
QDir dir = fi.dir();
|
|
|
|
// we watch both the directory and the file itself. the
|
|
// reason we watch the directory is so we can detect when
|
|
// the file is deleted/created
|
|
|
|
// we don't bother checking for dir existence before adding,
|
|
// since there isn't an atomic way to do both at once. if
|
|
// it turns out that the dir doesn't exist, then the
|
|
// monitoring will just silently not work at all.
|
|
|
|
watcher->addPath(dir.path());
|
|
|
|
// can't watch for non-existent directory
|
|
if(!watcher->directories().contains(dir.path()))
|
|
{
|
|
stop();
|
|
return;
|
|
}
|
|
|
|
// save whether or not the file exists
|
|
fileExisted = fi.exists();
|
|
|
|
// add only if file existent
|
|
// if no it will be added on directoryChanged signal
|
|
if(fileExisted)
|
|
watcher->addPath(filePath);
|
|
|
|
// TODO: address race conditions and think about error
|
|
// reporting instead of silently failing. probably this
|
|
// will require a Qt API update.
|
|
}
|
|
|
|
void stop()
|
|
{
|
|
if(watcher)
|
|
{
|
|
delete watcher;
|
|
delete watcher_relay;
|
|
watcher = 0;
|
|
watcher_relay = 0;
|
|
}
|
|
|
|
fileName.clear();
|
|
filePath.clear();
|
|
}
|
|
|
|
private slots:
|
|
void dir_changed(const QString &path)
|
|
{
|
|
Q_UNUSED(path);
|
|
QFileInfo fi(filePath);
|
|
bool exists = fi.exists();
|
|
if(exists && !fileExisted)
|
|
{
|
|
// this means the file was created. put a
|
|
// watch on it.
|
|
fileExisted = true;
|
|
watcher->addPath(filePath);
|
|
emit q->changed();
|
|
}
|
|
}
|
|
|
|
void file_changed(const QString &path)
|
|
{
|
|
Q_UNUSED(path);
|
|
QFileInfo fi(filePath);
|
|
if (!fi.exists() && !fileExisted) {
|
|
// Got a file changed signal on a file that does not exist
|
|
// and is not actively watched. This happens when we
|
|
// previously watched a file but it was deleted and after
|
|
// the original deletion changed-signal we get another one
|
|
// (for example because of bad signal timing). In this scenario
|
|
// we must ignore the change as the change, whatever it may
|
|
// have been, is of no interest to us because we don't watch
|
|
// the file and furthermore the file does not even exist.
|
|
return;
|
|
} else if (!fi.exists()) {
|
|
fileExisted = false;
|
|
};
|
|
emit q->changed();
|
|
}
|
|
};
|
|
|
|
FileWatch::FileWatch(const QString &file, QObject *parent)
|
|
:QObject(parent)
|
|
{
|
|
d = new Private(this);
|
|
d->start(file);
|
|
}
|
|
|
|
FileWatch::~FileWatch()
|
|
{
|
|
delete d;
|
|
}
|
|
|
|
QString FileWatch::fileName() const
|
|
{
|
|
return d->fileName;
|
|
}
|
|
|
|
void FileWatch::setFileName(const QString &file)
|
|
{
|
|
d->stop();
|
|
d->start(file);
|
|
}
|
|
|
|
}
|
|
|
|
#include "dirwatch.moc"
|