mirror of
https://github.com/d47081/qBittorrent.git
synced 2025-01-11 15:27:54 +00:00
Fix coding style (Issue #2192).
Also split filesystemwatcher.h into .h/.cpp files.
This commit is contained in:
parent
d32bb52390
commit
60c0939e05
@ -26,6 +26,7 @@ SOURCES += \
|
|||||||
$$PWD/downloadthread.cpp \
|
$$PWD/downloadthread.cpp \
|
||||||
$$PWD/scannedfoldersmodel.cpp \
|
$$PWD/scannedfoldersmodel.cpp \
|
||||||
$$PWD/torrentpersistentdata.cpp \
|
$$PWD/torrentpersistentdata.cpp \
|
||||||
|
$$PWD/filesystemwatcher.cpp \
|
||||||
$$PWD/misc.cpp \
|
$$PWD/misc.cpp \
|
||||||
$$PWD/fs_utils.cpp \
|
$$PWD/fs_utils.cpp \
|
||||||
$$PWD/logger.cpp \
|
$$PWD/logger.cpp \
|
||||||
|
278
src/core/filesystemwatcher.cpp
Normal file
278
src/core/filesystemwatcher.cpp
Normal file
@ -0,0 +1,278 @@
|
|||||||
|
#include <QtGlobal>
|
||||||
|
#ifndef Q_OS_WIN
|
||||||
|
#include <QSet>
|
||||||
|
#include <iostream>
|
||||||
|
#include <errno.h>
|
||||||
|
#if defined(Q_OS_MAC) || defined(Q_OS_FREEBSD)
|
||||||
|
#include <sys/param.h>
|
||||||
|
#include <sys/mount.h>
|
||||||
|
#include <string.h>
|
||||||
|
#elif !defined Q_OS_HAIKU
|
||||||
|
#include <sys/vfs.h>
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "fs_utils.h"
|
||||||
|
#include "misc.h"
|
||||||
|
|
||||||
|
#ifndef CIFS_MAGIC_NUMBER
|
||||||
|
#define CIFS_MAGIC_NUMBER 0xFF534D42
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef NFS_SUPER_MAGIC
|
||||||
|
#define NFS_SUPER_MAGIC 0x6969
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef SMB_SUPER_MAGIC
|
||||||
|
#define SMB_SUPER_MAGIC 0x517B
|
||||||
|
#endif
|
||||||
|
|
||||||
|
const int WATCH_INTERVAL = 10000; // 10 sec
|
||||||
|
const int MAX_PARTIAL_RETRIES = 5;
|
||||||
|
|
||||||
|
#include "filesystemwatcher.h"
|
||||||
|
|
||||||
|
FileSystemWatcher::FileSystemWatcher(QObject *parent)
|
||||||
|
: QFileSystemWatcher(parent)
|
||||||
|
{
|
||||||
|
m_filters << "*.torrent" << "*.magnet";
|
||||||
|
connect(this, SIGNAL(directoryChanged(QString)), SLOT(scanLocalFolder(QString)));
|
||||||
|
}
|
||||||
|
|
||||||
|
FileSystemWatcher::~FileSystemWatcher()
|
||||||
|
{
|
||||||
|
#ifndef Q_OS_WIN
|
||||||
|
if (m_watchTimer)
|
||||||
|
delete m_watchTimer;
|
||||||
|
#endif
|
||||||
|
if (m_partialTorrentTimer)
|
||||||
|
delete m_partialTorrentTimer;
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList FileSystemWatcher::directories() const
|
||||||
|
{
|
||||||
|
QStringList dirs;
|
||||||
|
#ifndef Q_OS_WIN
|
||||||
|
if (m_watchTimer) {
|
||||||
|
foreach (const QDir &dir, m_watchedFolders)
|
||||||
|
dirs << dir.canonicalPath();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
dirs << QFileSystemWatcher::directories();
|
||||||
|
return dirs;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileSystemWatcher::addPath(const QString &path)
|
||||||
|
{
|
||||||
|
#if !defined Q_OS_WIN && !defined Q_OS_HAIKU
|
||||||
|
QDir dir(path);
|
||||||
|
if (!dir.exists()) return;
|
||||||
|
|
||||||
|
// Check if the path points to a network file system or not
|
||||||
|
if (isNetworkFileSystem(path)) {
|
||||||
|
// Network mode
|
||||||
|
qDebug("Network folder detected: %s", qPrintable(path));
|
||||||
|
qDebug("Using file polling mode instead of inotify...");
|
||||||
|
m_watchedFolders << dir;
|
||||||
|
// Set up the watch timer
|
||||||
|
if (!m_watchTimer) {
|
||||||
|
m_watchTimer = new QTimer(this);
|
||||||
|
connect(m_watchTimer, SIGNAL(timeout()), SLOT(scanNetworkFolders()));
|
||||||
|
m_watchTimer->start(WATCH_INTERVAL); // 5 sec
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
#endif
|
||||||
|
// Normal mode
|
||||||
|
qDebug("FS Watching is watching %s in normal mode", qPrintable(path));
|
||||||
|
QFileSystemWatcher::addPath(path);
|
||||||
|
scanLocalFolder(path);
|
||||||
|
#if !defined Q_OS_WIN && !defined Q_OS_HAIKU
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileSystemWatcher::removePath(const QString &path)
|
||||||
|
{
|
||||||
|
#ifndef Q_OS_WIN
|
||||||
|
QDir dir(path);
|
||||||
|
for (int i = 0; i < m_watchedFolders.count(); ++i) {
|
||||||
|
if (QDir(m_watchedFolders.at(i)) == dir) {
|
||||||
|
m_watchedFolders.removeAt(i);
|
||||||
|
if (m_watchedFolders.isEmpty())
|
||||||
|
delete m_watchTimer;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
// Normal mode
|
||||||
|
QFileSystemWatcher::removePath(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileSystemWatcher::scanLocalFolder(QString path)
|
||||||
|
{
|
||||||
|
qDebug("scanLocalFolder(%s) called", qPrintable(path));
|
||||||
|
QStringList torrents;
|
||||||
|
// Local folders scan
|
||||||
|
addTorrentsFromDir(QDir(path), torrents);
|
||||||
|
// Report detected torrent files
|
||||||
|
if (!torrents.empty()) {
|
||||||
|
qDebug("The following files are being reported: %s", qPrintable(torrents.join("\n")));
|
||||||
|
emit torrentsAdded(torrents);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileSystemWatcher::scanNetworkFolders()
|
||||||
|
{
|
||||||
|
#ifndef Q_OS_WIN
|
||||||
|
qDebug("scanNetworkFolders() called");
|
||||||
|
QStringList torrents;
|
||||||
|
// Network folders scan
|
||||||
|
foreach (const QDir &dir, m_watchedFolders) {
|
||||||
|
//qDebug("FSWatcher: Polling manually folder %s", qPrintable(dir.path()));
|
||||||
|
addTorrentsFromDir(dir, torrents);
|
||||||
|
}
|
||||||
|
// Report detected torrent files
|
||||||
|
if (!torrents.empty()) {
|
||||||
|
qDebug("The following files are being reported: %s", qPrintable(torrents.join("\n")));
|
||||||
|
emit torrentsAdded(torrents);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileSystemWatcher::processPartialTorrents()
|
||||||
|
{
|
||||||
|
QStringList noLongerPartial;
|
||||||
|
|
||||||
|
// Check which torrents are still partial
|
||||||
|
foreach (const QString &torrentPath, m_partialTorrents.keys()) {
|
||||||
|
if (!QFile::exists(torrentPath)) {
|
||||||
|
m_partialTorrents.remove(torrentPath);
|
||||||
|
}
|
||||||
|
else if (fsutils::isValidTorrentFile(torrentPath)) {
|
||||||
|
noLongerPartial << torrentPath;
|
||||||
|
m_partialTorrents.remove(torrentPath);
|
||||||
|
}
|
||||||
|
else if (m_partialTorrents[torrentPath] >= MAX_PARTIAL_RETRIES) {
|
||||||
|
m_partialTorrents.remove(torrentPath);
|
||||||
|
QFile::rename(torrentPath, torrentPath + ".invalid");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
++m_partialTorrents[torrentPath];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stop the partial timer if necessary
|
||||||
|
if (m_partialTorrents.empty()) {
|
||||||
|
m_partialTorrentTimer->stop();
|
||||||
|
m_partialTorrentTimer->deleteLater();
|
||||||
|
qDebug("No longer any partial torrent.");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
qDebug("Still %d partial torrents after delayed processing.", m_partialTorrents.count());
|
||||||
|
m_partialTorrentTimer->start(WATCH_INTERVAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Notify of new torrents
|
||||||
|
if (!noLongerPartial.isEmpty())
|
||||||
|
emit torrentsAdded(noLongerPartial);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileSystemWatcher::startPartialTorrentTimer()
|
||||||
|
{
|
||||||
|
Q_ASSERT(!m_partialTorrents.isEmpty());
|
||||||
|
if (!m_partialTorrentTimer) {
|
||||||
|
m_partialTorrentTimer = new QTimer();
|
||||||
|
connect(m_partialTorrentTimer, SIGNAL(timeout()), SLOT(processPartialTorrents()));
|
||||||
|
m_partialTorrentTimer->setSingleShot(true);
|
||||||
|
m_partialTorrentTimer->start(WATCH_INTERVAL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileSystemWatcher::addTorrentsFromDir(const QDir &dir, QStringList &torrents)
|
||||||
|
{
|
||||||
|
const QStringList files = dir.entryList(m_filters, QDir::Files, QDir::Unsorted);
|
||||||
|
foreach (const QString &file, files) {
|
||||||
|
const QString fileAbsPath = dir.absoluteFilePath(file);
|
||||||
|
if (fileAbsPath.endsWith(".magnet")) {
|
||||||
|
QFile f(fileAbsPath);
|
||||||
|
if (f.open(QIODevice::ReadOnly)
|
||||||
|
&& !misc::magnetUriToHash(QString::fromLocal8Bit(f.readAll())).isEmpty()) {
|
||||||
|
torrents << fileAbsPath;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (fsutils::isValidTorrentFile(fileAbsPath)) {
|
||||||
|
torrents << fileAbsPath;
|
||||||
|
}
|
||||||
|
else if (!m_partialTorrents.contains(fileAbsPath)) {
|
||||||
|
qDebug("Partial torrent detected at: %s", qPrintable(fileAbsPath));
|
||||||
|
qDebug("Delay the file's processing...");
|
||||||
|
m_partialTorrents.insert(fileAbsPath, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!m_partialTorrents.empty())
|
||||||
|
startPartialTorrentTimer();
|
||||||
|
}
|
||||||
|
|
||||||
|
#if !defined Q_OS_WIN && !defined Q_OS_HAIKU
|
||||||
|
bool FileSystemWatcher::isNetworkFileSystem(QString path)
|
||||||
|
{
|
||||||
|
QString file = path;
|
||||||
|
if (!file.endsWith("/"))
|
||||||
|
file += "/";
|
||||||
|
file += ".";
|
||||||
|
struct statfs buf;
|
||||||
|
if (!statfs(file.toLocal8Bit().constData(), &buf)) {
|
||||||
|
#ifdef Q_OS_MAC
|
||||||
|
// XXX: should we make sure HAVE_STRUCT_FSSTAT_F_FSTYPENAME is defined?
|
||||||
|
return ((strcmp(buf.f_fstypename, "nfs") == 0) || (strcmp(buf.f_fstypename, "cifs") == 0) || (strcmp(buf.f_fstypename, "smbfs") == 0));
|
||||||
|
#else
|
||||||
|
return ((buf.f_type == (long)CIFS_MAGIC_NUMBER) || (buf.f_type == (long)NFS_SUPER_MAGIC) || (buf.f_type == (long)SMB_SUPER_MAGIC));
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
std::cerr << "Error: statfs() call failed for " << qPrintable(file) << ". Supposing it is a local folder..." << std::endl;
|
||||||
|
switch(errno) {
|
||||||
|
case EACCES:
|
||||||
|
std::cerr << "Search permission is denied for a component of the path prefix of the path" << std::endl;
|
||||||
|
break;
|
||||||
|
case EFAULT:
|
||||||
|
std::cerr << "Buf or path points to an invalid address" << std::endl;
|
||||||
|
break;
|
||||||
|
case EINTR:
|
||||||
|
std::cerr << "This call was interrupted by a signal" << std::endl;
|
||||||
|
break;
|
||||||
|
case EIO:
|
||||||
|
std::cerr << "I/O Error" << std::endl;
|
||||||
|
break;
|
||||||
|
case ELOOP:
|
||||||
|
std::cerr << "Too many symlinks" << std::endl;
|
||||||
|
break;
|
||||||
|
case ENAMETOOLONG:
|
||||||
|
std::cerr << "path is too long" << std::endl;
|
||||||
|
break;
|
||||||
|
case ENOENT:
|
||||||
|
std::cerr << "The file referred by path does not exist" << std::endl;
|
||||||
|
break;
|
||||||
|
case ENOMEM:
|
||||||
|
std::cerr << "Insufficient kernel memory" << std::endl;
|
||||||
|
break;
|
||||||
|
case ENOSYS:
|
||||||
|
std::cerr << "The file system does not detect this call" << std::endl;
|
||||||
|
break;
|
||||||
|
case ENOTDIR:
|
||||||
|
std::cerr << "A component of the path is not a directory" << std::endl;
|
||||||
|
break;
|
||||||
|
case EOVERFLOW:
|
||||||
|
std::cerr << "Some values were too large to be represented in the struct" << std::endl;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
std::cerr << "Unknown error" << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cerr << "Errno: " << errno << std::endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
@ -8,290 +8,45 @@
|
|||||||
#include <QStringList>
|
#include <QStringList>
|
||||||
#include <QHash>
|
#include <QHash>
|
||||||
|
|
||||||
#ifndef Q_OS_WIN
|
|
||||||
#include <QSet>
|
|
||||||
#include <iostream>
|
|
||||||
#include <errno.h>
|
|
||||||
#if defined(Q_OS_MAC) || defined(Q_OS_FREEBSD)
|
|
||||||
#include <sys/param.h>
|
|
||||||
#include <sys/mount.h>
|
|
||||||
#include <string.h>
|
|
||||||
#elif !defined Q_OS_HAIKU
|
|
||||||
#include <sys/vfs.h>
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "fs_utils.h"
|
|
||||||
#include "misc.h"
|
|
||||||
|
|
||||||
#ifndef CIFS_MAGIC_NUMBER
|
|
||||||
#define CIFS_MAGIC_NUMBER 0xFF534D42
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef NFS_SUPER_MAGIC
|
|
||||||
#define NFS_SUPER_MAGIC 0x6969
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef SMB_SUPER_MAGIC
|
|
||||||
#define SMB_SUPER_MAGIC 0x517B
|
|
||||||
#endif
|
|
||||||
|
|
||||||
const int WATCH_INTERVAL = 10000; // 10 sec
|
|
||||||
const int MAX_PARTIAL_RETRIES = 5;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Subclassing QFileSystemWatcher in order to support Network File
|
* Subclassing QFileSystemWatcher in order to support Network File
|
||||||
* System watching (NFS, CIFS) on Linux and Mac OS.
|
* System watching (NFS, CIFS) on Linux and Mac OS.
|
||||||
*/
|
*/
|
||||||
class FileSystemWatcher: public QFileSystemWatcher {
|
class FileSystemWatcher : public QFileSystemWatcher
|
||||||
Q_OBJECT
|
{
|
||||||
|
Q_OBJECT
|
||||||
private:
|
|
||||||
#ifndef Q_OS_WIN
|
|
||||||
QList<QDir> watched_folders;
|
|
||||||
QPointer<QTimer> watch_timer;
|
|
||||||
#endif
|
|
||||||
QStringList m_filters;
|
|
||||||
// Partial torrents
|
|
||||||
QHash<QString, int> m_partialTorrents;
|
|
||||||
QPointer<QTimer> m_partialTorrentTimer;
|
|
||||||
|
|
||||||
#if !defined Q_OS_WIN && !defined Q_OS_HAIKU
|
|
||||||
private:
|
|
||||||
static bool isNetworkFileSystem(QString path) {
|
|
||||||
QString file = path;
|
|
||||||
if (!file.endsWith("/"))
|
|
||||||
file += "/";
|
|
||||||
file += ".";
|
|
||||||
struct statfs buf;
|
|
||||||
if (!statfs(file.toLocal8Bit().constData(), &buf)) {
|
|
||||||
#ifdef Q_OS_MAC
|
|
||||||
// XXX: should we make sure HAVE_STRUCT_FSSTAT_F_FSTYPENAME is defined?
|
|
||||||
return (strcmp(buf.f_fstypename, "nfs") == 0 || strcmp(buf.f_fstypename, "cifs") == 0 || strcmp(buf.f_fstypename, "smbfs") == 0);
|
|
||||||
#else
|
|
||||||
return (buf.f_type == (long)CIFS_MAGIC_NUMBER || buf.f_type == (long)NFS_SUPER_MAGIC || buf.f_type == (long)SMB_SUPER_MAGIC);
|
|
||||||
#endif
|
|
||||||
} else {
|
|
||||||
std::cerr << "Error: statfs() call failed for " << qPrintable(file) << ". Supposing it is a local folder..." << std::endl;
|
|
||||||
switch(errno) {
|
|
||||||
case EACCES:
|
|
||||||
std::cerr << "Search permission is denied for a component of the path prefix of the path" << std::endl;
|
|
||||||
break;
|
|
||||||
case EFAULT:
|
|
||||||
std::cerr << "Buf or path points to an invalid address" << std::endl;
|
|
||||||
break;
|
|
||||||
case EINTR:
|
|
||||||
std::cerr << "This call was interrupted by a signal" << std::endl;
|
|
||||||
break;
|
|
||||||
case EIO:
|
|
||||||
std::cerr << "I/O Error" << std::endl;
|
|
||||||
break;
|
|
||||||
case ELOOP:
|
|
||||||
std::cerr << "Too many symlinks" << std::endl;
|
|
||||||
break;
|
|
||||||
case ENAMETOOLONG:
|
|
||||||
std::cerr << "path is too long" << std::endl;
|
|
||||||
break;
|
|
||||||
case ENOENT:
|
|
||||||
std::cerr << "The file referred by path does not exist" << std::endl;
|
|
||||||
break;
|
|
||||||
case ENOMEM:
|
|
||||||
std::cerr << "Insufficient kernel memory" << std::endl;
|
|
||||||
break;
|
|
||||||
case ENOSYS:
|
|
||||||
std::cerr << "The file system does not detect this call" << std::endl;
|
|
||||||
break;
|
|
||||||
case ENOTDIR:
|
|
||||||
std::cerr << "A component of the path is not a directory" << std::endl;
|
|
||||||
break;
|
|
||||||
case EOVERFLOW:
|
|
||||||
std::cerr << "Some values were too large to be represented in the struct" << std::endl;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
std::cerr << "Unknown error" << std::endl;
|
|
||||||
}
|
|
||||||
std::cerr << "Errno: " << errno << std::endl;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
FileSystemWatcher(QObject *parent): QFileSystemWatcher(parent) {
|
explicit FileSystemWatcher(QObject *parent = 0);
|
||||||
m_filters << "*.torrent" << "*.magnet";
|
~FileSystemWatcher();
|
||||||
connect(this, SIGNAL(directoryChanged(QString)), this, SLOT(scanLocalFolder(QString)));
|
|
||||||
}
|
|
||||||
|
|
||||||
~FileSystemWatcher() {
|
QStringList directories() const;
|
||||||
#ifndef Q_OS_WIN
|
void addPath(const QString &path);
|
||||||
if (watch_timer)
|
void removePath(const QString &path);
|
||||||
delete watch_timer;
|
|
||||||
#endif
|
|
||||||
if (m_partialTorrentTimer)
|
|
||||||
delete m_partialTorrentTimer;
|
|
||||||
}
|
|
||||||
|
|
||||||
QStringList directories() const {
|
|
||||||
QStringList dirs;
|
|
||||||
#ifndef Q_OS_WIN
|
|
||||||
if (watch_timer) {
|
|
||||||
foreach (const QDir &dir, watched_folders)
|
|
||||||
dirs << dir.canonicalPath();
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
dirs << QFileSystemWatcher::directories();
|
|
||||||
return dirs;
|
|
||||||
}
|
|
||||||
|
|
||||||
void addPath(const QString & path) {
|
|
||||||
#if !defined Q_OS_WIN && !defined Q_OS_HAIKU
|
|
||||||
QDir dir(path);
|
|
||||||
if (!dir.exists())
|
|
||||||
return;
|
|
||||||
// Check if the path points to a network file system or not
|
|
||||||
if (isNetworkFileSystem(path)) {
|
|
||||||
// Network mode
|
|
||||||
qDebug("Network folder detected: %s", qPrintable(path));
|
|
||||||
qDebug("Using file polling mode instead of inotify...");
|
|
||||||
watched_folders << dir;
|
|
||||||
// Set up the watch timer
|
|
||||||
if (!watch_timer) {
|
|
||||||
watch_timer = new QTimer(this);
|
|
||||||
connect(watch_timer, SIGNAL(timeout()), this, SLOT(scanNetworkFolders()));
|
|
||||||
watch_timer->start(WATCH_INTERVAL); // 5 sec
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
#endif
|
|
||||||
// Normal mode
|
|
||||||
qDebug("FS Watching is watching %s in normal mode", qPrintable(path));
|
|
||||||
QFileSystemWatcher::addPath(path);
|
|
||||||
scanLocalFolder(path);
|
|
||||||
#if !defined Q_OS_WIN && !defined Q_OS_HAIKU
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
void removePath(const QString & path) {
|
|
||||||
#ifndef Q_OS_WIN
|
|
||||||
QDir dir(path);
|
|
||||||
for (int i = 0; i < watched_folders.count(); ++i) {
|
|
||||||
if (QDir(watched_folders.at(i)) == dir) {
|
|
||||||
watched_folders.removeAt(i);
|
|
||||||
if (watched_folders.isEmpty())
|
|
||||||
delete watch_timer;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
// Normal mode
|
|
||||||
QFileSystemWatcher::removePath(path);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected slots:
|
|
||||||
void scanLocalFolder(QString path) {
|
|
||||||
qDebug("scanLocalFolder(%s) called", qPrintable(path));
|
|
||||||
QStringList torrents;
|
|
||||||
// Local folders scan
|
|
||||||
addTorrentsFromDir(QDir(path), torrents);
|
|
||||||
// Report detected torrent files
|
|
||||||
if (!torrents.empty()) {
|
|
||||||
qDebug("The following files are being reported: %s", qPrintable(torrents.join("\n")));
|
|
||||||
emit torrentsAdded(torrents);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void scanNetworkFolders() {
|
|
||||||
#ifndef Q_OS_WIN
|
|
||||||
qDebug("scanNetworkFolders() called");
|
|
||||||
QStringList torrents;
|
|
||||||
// Network folders scan
|
|
||||||
foreach (const QDir &dir, watched_folders) {
|
|
||||||
//qDebug("FSWatcher: Polling manually folder %s", qPrintable(dir.path()));
|
|
||||||
addTorrentsFromDir(dir, torrents);
|
|
||||||
}
|
|
||||||
// Report detected torrent files
|
|
||||||
if (!torrents.empty()) {
|
|
||||||
qDebug("The following files are being reported: %s", qPrintable(torrents.join("\n")));
|
|
||||||
emit torrentsAdded(torrents);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
void processPartialTorrents() {
|
|
||||||
QStringList no_longer_partial;
|
|
||||||
|
|
||||||
// Check which torrents are still partial
|
|
||||||
foreach (const QString& torrent_path, m_partialTorrents.keys()) {
|
|
||||||
if (!QFile::exists(torrent_path)) {
|
|
||||||
m_partialTorrents.remove(torrent_path);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (fsutils::isValidTorrentFile(torrent_path)) {
|
|
||||||
no_longer_partial << torrent_path;
|
|
||||||
m_partialTorrents.remove(torrent_path);
|
|
||||||
} else {
|
|
||||||
if (m_partialTorrents[torrent_path] >= MAX_PARTIAL_RETRIES) {
|
|
||||||
m_partialTorrents.remove(torrent_path);
|
|
||||||
QFile::rename(torrent_path, torrent_path+".invalid");
|
|
||||||
} else {
|
|
||||||
m_partialTorrents[torrent_path]++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Stop the partial timer if necessary
|
|
||||||
if (m_partialTorrents.empty()) {
|
|
||||||
m_partialTorrentTimer->stop();
|
|
||||||
m_partialTorrentTimer->deleteLater();
|
|
||||||
qDebug("No longer any partial torrent.");
|
|
||||||
} else {
|
|
||||||
qDebug("Still %d partial torrents after delayed processing.", m_partialTorrents.count());
|
|
||||||
m_partialTorrentTimer->start(WATCH_INTERVAL);
|
|
||||||
}
|
|
||||||
// Notify of new torrents
|
|
||||||
if (!no_longer_partial.isEmpty())
|
|
||||||
emit torrentsAdded(no_longer_partial);
|
|
||||||
}
|
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void torrentsAdded(QStringList &pathList);
|
void torrentsAdded(QStringList &pathList);
|
||||||
|
|
||||||
|
protected slots:
|
||||||
|
void scanLocalFolder(QString path);
|
||||||
|
void scanNetworkFolders();
|
||||||
|
void processPartialTorrents();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void startPartialTorrentTimer() {
|
void startPartialTorrentTimer();
|
||||||
Q_ASSERT(!m_partialTorrents.isEmpty());
|
void addTorrentsFromDir(const QDir &dir, QStringList &torrents);
|
||||||
if (!m_partialTorrentTimer) {
|
#if !defined Q_OS_WIN && !defined Q_OS_HAIKU
|
||||||
m_partialTorrentTimer = new QTimer();
|
static bool isNetworkFileSystem(QString path);
|
||||||
connect(m_partialTorrentTimer, SIGNAL(timeout()), SLOT(processPartialTorrents()));
|
#endif
|
||||||
m_partialTorrentTimer->setSingleShot(true);
|
|
||||||
m_partialTorrentTimer->start(WATCH_INTERVAL);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void addTorrentsFromDir(const QDir &dir, QStringList &torrents) {
|
|
||||||
const QStringList files = dir.entryList(m_filters, QDir::Files, QDir::Unsorted);
|
|
||||||
foreach (const QString &file, files) {
|
|
||||||
const QString file_abspath = dir.absoluteFilePath(file);
|
|
||||||
if (file_abspath.endsWith(".magnet")) {
|
|
||||||
QFile f(file_abspath);
|
|
||||||
if (f.open(QIODevice::ReadOnly)
|
|
||||||
&& !misc::magnetUriToHash(QString::fromLocal8Bit(f.readAll())).isEmpty()) {
|
|
||||||
torrents << file_abspath;
|
|
||||||
}
|
|
||||||
} else if (fsutils::isValidTorrentFile(file_abspath)) {
|
|
||||||
torrents << file_abspath;
|
|
||||||
} else {
|
|
||||||
if (!m_partialTorrents.contains(file_abspath)) {
|
|
||||||
qDebug("Partial torrent detected at: %s", qPrintable(file_abspath));
|
|
||||||
qDebug("Delay the file's processing...");
|
|
||||||
m_partialTorrents.insert(file_abspath, 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!m_partialTorrents.empty())
|
|
||||||
startPartialTorrentTimer();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
#ifndef Q_OS_WIN
|
||||||
|
QList<QDir> m_watchedFolders;
|
||||||
|
QPointer<QTimer> m_watchTimer;
|
||||||
|
#endif
|
||||||
|
QStringList m_filters;
|
||||||
|
// Partial torrents
|
||||||
|
QHash<QString, int> m_partialTorrents;
|
||||||
|
QPointer<QTimer> m_partialTorrentTimer;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // FILESYSTEMWATCHER_H
|
#endif // FILESYSTEMWATCHER_H
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Bittorrent Client using Qt4 and libtorrent.
|
* Bittorrent Client using Qt and libtorrent.
|
||||||
* Copyright (C) 2010 Christian Kandeler, Christophe Dumez
|
* Copyright (C) 2010 Christian Kandeler, Christophe Dumez
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or
|
* This program is free software; you can redistribute it and/or
|
||||||
@ -28,171 +28,206 @@
|
|||||||
* Contact : chris@qbittorrent.org
|
* Contact : chris@qbittorrent.org
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "scannedfoldersmodel.h"
|
|
||||||
#include "preferences.h"
|
|
||||||
#include "filesystemwatcher.h"
|
|
||||||
|
|
||||||
#include <QDir>
|
#include <QDir>
|
||||||
#include <QFileInfo>
|
#include <QFileInfo>
|
||||||
#include <QString>
|
#include <QString>
|
||||||
|
#include <QStringList>
|
||||||
#include <QTemporaryFile>
|
#include <QTemporaryFile>
|
||||||
#include "misc.h"
|
|
||||||
|
|
||||||
namespace {
|
#include "misc.h"
|
||||||
const int PathColumn = 0;
|
#include "fs_utils.h"
|
||||||
const int DownloadAtTorrentColumn = 1;
|
#include "preferences.h"
|
||||||
|
#include "filesystemwatcher.h"
|
||||||
|
#include "scannedfoldersmodel.h"
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
const int PathColumn = 0;
|
||||||
|
const int DownloadAtTorrentColumn = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
class ScanFoldersModel::PathData {
|
class ScanFoldersModel::PathData
|
||||||
|
{
|
||||||
public:
|
public:
|
||||||
PathData(const QString &path) : path(path), downloadAtPath(false) {}
|
PathData(const QString &path)
|
||||||
PathData(const QString &path, bool download_at_path) : path(path), downloadAtPath(download_at_path) {}
|
: path(path)
|
||||||
const QString path;
|
, downloadAtPath(false)
|
||||||
bool downloadAtPath;
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
PathData(const QString &path, bool download_at_path)
|
||||||
|
: path(path)
|
||||||
|
, downloadAtPath(download_at_path)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
const QString path;
|
||||||
|
bool downloadAtPath;
|
||||||
};
|
};
|
||||||
|
|
||||||
ScanFoldersModel *ScanFoldersModel::instance(QObject *parent) {
|
|
||||||
//Q_ASSERT(!parent != !m_instance);
|
|
||||||
if (!m_instance)
|
|
||||||
m_instance = new ScanFoldersModel(parent);
|
|
||||||
return m_instance;
|
|
||||||
}
|
|
||||||
|
|
||||||
ScanFoldersModel::ScanFoldersModel(QObject *parent) :
|
|
||||||
QAbstractTableModel(parent), m_fsWatcher(0)
|
|
||||||
{ }
|
|
||||||
|
|
||||||
ScanFoldersModel::~ScanFoldersModel() {
|
|
||||||
qDeleteAll(m_pathList);
|
|
||||||
}
|
|
||||||
|
|
||||||
int ScanFoldersModel::rowCount(const QModelIndex &parent) const {
|
|
||||||
return parent.isValid() ? 0 : m_pathList.count();
|
|
||||||
}
|
|
||||||
|
|
||||||
int ScanFoldersModel::columnCount(const QModelIndex &parent) const {
|
|
||||||
Q_UNUSED(parent);
|
|
||||||
return 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
QVariant ScanFoldersModel::data(const QModelIndex &index, int role) const {
|
|
||||||
if (!index.isValid() || index.row() >= rowCount())
|
|
||||||
return QVariant();
|
|
||||||
|
|
||||||
const PathData* pathData = m_pathList.at(index.row());
|
|
||||||
if (index.column() == PathColumn && role == Qt::DisplayRole) {
|
|
||||||
|
|
||||||
return fsutils::toNativePath(pathData->path);
|
|
||||||
}
|
|
||||||
if (index.column() == DownloadAtTorrentColumn && role == Qt::CheckStateRole)
|
|
||||||
return pathData->downloadAtPath ? Qt::Checked : Qt::Unchecked;
|
|
||||||
return QVariant();
|
|
||||||
}
|
|
||||||
|
|
||||||
QVariant ScanFoldersModel::headerData(int section, Qt::Orientation orientation, int role) const {
|
|
||||||
if (orientation != Qt::Horizontal || role != Qt::DisplayRole || section < 0 || section >= columnCount())
|
|
||||||
return QVariant();
|
|
||||||
|
|
||||||
if (section == PathColumn)
|
|
||||||
return tr("Watched Folder");
|
|
||||||
return tr("Download here");
|
|
||||||
}
|
|
||||||
|
|
||||||
Qt::ItemFlags ScanFoldersModel::flags(const QModelIndex &index) const {
|
|
||||||
if (!index.isValid() || index.row() >= rowCount() || index.column() != DownloadAtTorrentColumn)
|
|
||||||
return QAbstractTableModel::flags(index);
|
|
||||||
return QAbstractTableModel::flags(index) | Qt::ItemIsUserCheckable;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ScanFoldersModel::setData(const QModelIndex &index, const QVariant &value, int role) {
|
|
||||||
if (!index.isValid() || index.row() >= rowCount() || index.column() > DownloadAtTorrentColumn || role != Qt::CheckStateRole)
|
|
||||||
return false;
|
|
||||||
Q_ASSERT(index.column() == DownloadAtTorrentColumn);
|
|
||||||
m_pathList[index.row()]->downloadAtPath = (value.toInt() == Qt::Checked);
|
|
||||||
emit dataChanged(index, index);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
ScanFoldersModel::PathStatus ScanFoldersModel::addPath(const QString &path, bool download_at_path) {
|
|
||||||
QDir dir(path);
|
|
||||||
if (!dir.exists())
|
|
||||||
return DoesNotExist;
|
|
||||||
if (!dir.isReadable())
|
|
||||||
return CannotRead;
|
|
||||||
const QString &canonicalPath = dir.canonicalPath();
|
|
||||||
if (findPathData(canonicalPath) != -1)
|
|
||||||
return AlreadyInList;
|
|
||||||
if (!m_fsWatcher) {
|
|
||||||
m_fsWatcher = new FileSystemWatcher(this);
|
|
||||||
connect(m_fsWatcher, SIGNAL(torrentsAdded(QStringList&)), this, SIGNAL(torrentsAdded(QStringList&)));
|
|
||||||
}
|
|
||||||
beginInsertRows(QModelIndex(), rowCount(), rowCount());
|
|
||||||
m_pathList << new PathData(canonicalPath, download_at_path);
|
|
||||||
endInsertRows();
|
|
||||||
// Start scanning
|
|
||||||
m_fsWatcher->addPath(canonicalPath);
|
|
||||||
return Ok;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ScanFoldersModel::removePath(int row) {
|
|
||||||
Q_ASSERT(row >= 0 && row < rowCount());
|
|
||||||
beginRemoveRows(QModelIndex(), row, row);
|
|
||||||
m_fsWatcher->removePath(m_pathList.at(row)->path);
|
|
||||||
m_pathList.removeAt(row);
|
|
||||||
endRemoveRows();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ScanFoldersModel::removePath(const QString &path) {
|
|
||||||
const int row = findPathData(path);
|
|
||||||
if (row == -1)
|
|
||||||
return false;
|
|
||||||
removePath(row);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
ScanFoldersModel::PathStatus ScanFoldersModel::setDownloadAtPath(int row, bool downloadAtPath) {
|
|
||||||
Q_ASSERT(row >= 0 && row < rowCount());
|
|
||||||
|
|
||||||
bool &oldValue = m_pathList[row]->downloadAtPath;
|
|
||||||
if (oldValue != downloadAtPath) {
|
|
||||||
if (downloadAtPath) {
|
|
||||||
QTemporaryFile testFile(m_pathList[row]->path + "/tmpFile");
|
|
||||||
if (!testFile.open())
|
|
||||||
return CannotWrite;
|
|
||||||
}
|
|
||||||
oldValue = downloadAtPath;
|
|
||||||
const QModelIndex changedIndex = index(row, DownloadAtTorrentColumn);
|
|
||||||
emit dataChanged(changedIndex, changedIndex);
|
|
||||||
}
|
|
||||||
return Ok;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ScanFoldersModel::downloadInTorrentFolder(const QString &filePath) const {
|
|
||||||
const int row = findPathData(QFileInfo(filePath).dir().path());
|
|
||||||
Q_ASSERT(row != -1);
|
|
||||||
return m_pathList.at(row)->downloadAtPath;
|
|
||||||
}
|
|
||||||
|
|
||||||
int ScanFoldersModel::findPathData(const QString &path) const {
|
|
||||||
for (int i = 0; i < m_pathList.count(); ++i) {
|
|
||||||
const PathData* pathData = m_pathList.at(i);
|
|
||||||
if (pathData->path == fsutils::fromNativePath(path))
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ScanFoldersModel::makePersistent() {
|
|
||||||
Preferences* const pref = Preferences::instance();
|
|
||||||
QStringList paths;
|
|
||||||
QList<bool> downloadInFolderInfo;
|
|
||||||
foreach (const PathData* pathData, m_pathList) {
|
|
||||||
paths << pathData->path;
|
|
||||||
downloadInFolderInfo << pathData->downloadAtPath;
|
|
||||||
}
|
|
||||||
pref->setScanDirs(paths);
|
|
||||||
pref->setDownloadInScanDirs(downloadInFolderInfo);
|
|
||||||
}
|
|
||||||
|
|
||||||
ScanFoldersModel *ScanFoldersModel::m_instance = 0;
|
ScanFoldersModel *ScanFoldersModel::m_instance = 0;
|
||||||
|
|
||||||
|
ScanFoldersModel *ScanFoldersModel::instance(QObject *parent)
|
||||||
|
{
|
||||||
|
//Q_ASSERT(!parent != !m_instance);
|
||||||
|
if (!m_instance)
|
||||||
|
m_instance = new ScanFoldersModel(parent);
|
||||||
|
return m_instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
ScanFoldersModel::ScanFoldersModel(QObject *parent)
|
||||||
|
: QAbstractTableModel(parent)
|
||||||
|
, m_fsWatcher(0)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
ScanFoldersModel::~ScanFoldersModel()
|
||||||
|
{
|
||||||
|
qDeleteAll(m_pathList);
|
||||||
|
}
|
||||||
|
|
||||||
|
int ScanFoldersModel::rowCount(const QModelIndex &parent) const
|
||||||
|
{
|
||||||
|
return parent.isValid() ? 0 : m_pathList.count();
|
||||||
|
}
|
||||||
|
|
||||||
|
int ScanFoldersModel::columnCount(const QModelIndex &parent) const
|
||||||
|
{
|
||||||
|
Q_UNUSED(parent);
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariant ScanFoldersModel::data(const QModelIndex &index, int role) const
|
||||||
|
{
|
||||||
|
if (!index.isValid() || (index.row() >= rowCount()))
|
||||||
|
return QVariant();
|
||||||
|
|
||||||
|
const PathData *pathData = m_pathList.at(index.row());
|
||||||
|
if ((index.column() == PathColumn) && (role == Qt::DisplayRole))
|
||||||
|
return fsutils::toNativePath(pathData->path);
|
||||||
|
|
||||||
|
if ((index.column() == DownloadAtTorrentColumn) && (role == Qt::CheckStateRole))
|
||||||
|
return (pathData->downloadAtPath ? Qt::Checked : Qt::Unchecked);
|
||||||
|
|
||||||
|
return QVariant();
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariant ScanFoldersModel::headerData(int section, Qt::Orientation orientation, int role) const
|
||||||
|
{
|
||||||
|
if ((orientation != Qt::Horizontal) || (role != Qt::DisplayRole) || (section < 0) || (section >= columnCount()))
|
||||||
|
return QVariant();
|
||||||
|
|
||||||
|
if (section == PathColumn)
|
||||||
|
return tr("Watched Folder");
|
||||||
|
|
||||||
|
return tr("Download here");
|
||||||
|
}
|
||||||
|
|
||||||
|
Qt::ItemFlags ScanFoldersModel::flags(const QModelIndex &index) const
|
||||||
|
{
|
||||||
|
if (!index.isValid() || (index.row() >= rowCount()) || (index.column() != DownloadAtTorrentColumn))
|
||||||
|
return QAbstractTableModel::flags(index);
|
||||||
|
|
||||||
|
return (QAbstractTableModel::flags(index) | Qt::ItemIsUserCheckable);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ScanFoldersModel::setData(const QModelIndex &index, const QVariant &value, int role)
|
||||||
|
{
|
||||||
|
if (!index.isValid() || (index.row() >= rowCount()) || (index.column() > DownloadAtTorrentColumn) || (role != Qt::CheckStateRole))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
Q_ASSERT(index.column() == DownloadAtTorrentColumn);
|
||||||
|
m_pathList[index.row()]->downloadAtPath = (value.toInt() == Qt::Checked);
|
||||||
|
emit dataChanged(index, index);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
ScanFoldersModel::PathStatus ScanFoldersModel::addPath(const QString &path, bool downloadAtPath)
|
||||||
|
{
|
||||||
|
QDir dir(path);
|
||||||
|
if (!dir.exists()) return DoesNotExist;
|
||||||
|
if (!dir.isReadable()) return CannotRead;
|
||||||
|
|
||||||
|
const QString &canonicalPath = dir.canonicalPath();
|
||||||
|
if (findPathData(canonicalPath) != -1) return AlreadyInList;
|
||||||
|
|
||||||
|
if (!m_fsWatcher) {
|
||||||
|
m_fsWatcher = new FileSystemWatcher(this);
|
||||||
|
connect(m_fsWatcher, SIGNAL(torrentsAdded(QStringList&)), this, SIGNAL(torrentsAdded(QStringList&)));
|
||||||
|
}
|
||||||
|
|
||||||
|
beginInsertRows(QModelIndex(), rowCount(), rowCount());
|
||||||
|
m_pathList << new PathData(canonicalPath, downloadAtPath);
|
||||||
|
endInsertRows();
|
||||||
|
|
||||||
|
// Start scanning
|
||||||
|
m_fsWatcher->addPath(canonicalPath);
|
||||||
|
return Ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScanFoldersModel::removePath(int row)
|
||||||
|
{
|
||||||
|
Q_ASSERT((row >= 0) && (row < rowCount()));
|
||||||
|
beginRemoveRows(QModelIndex(), row, row);
|
||||||
|
m_fsWatcher->removePath(m_pathList.at(row)->path);
|
||||||
|
m_pathList.removeAt(row);
|
||||||
|
endRemoveRows();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ScanFoldersModel::removePath(const QString &path)
|
||||||
|
{
|
||||||
|
const int row = findPathData(path);
|
||||||
|
if (row == -1) return false;
|
||||||
|
|
||||||
|
removePath(row);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
ScanFoldersModel::PathStatus ScanFoldersModel::setDownloadAtPath(int row, bool downloadAtPath)
|
||||||
|
{
|
||||||
|
Q_ASSERT((row >= 0) && (row < rowCount()));
|
||||||
|
|
||||||
|
bool &oldValue = m_pathList[row]->downloadAtPath;
|
||||||
|
if (oldValue != downloadAtPath) {
|
||||||
|
if (downloadAtPath) {
|
||||||
|
QTemporaryFile testFile(m_pathList[row]->path + "/tmpFile");
|
||||||
|
if (!testFile.open()) return CannotWrite;
|
||||||
|
}
|
||||||
|
|
||||||
|
oldValue = downloadAtPath;
|
||||||
|
const QModelIndex changedIndex = index(row, DownloadAtTorrentColumn);
|
||||||
|
emit dataChanged(changedIndex, changedIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ScanFoldersModel::downloadInTorrentFolder(const QString &filePath) const
|
||||||
|
{
|
||||||
|
const int row = findPathData(QFileInfo(filePath).dir().path());
|
||||||
|
Q_ASSERT(row != -1);
|
||||||
|
return m_pathList.at(row)->downloadAtPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ScanFoldersModel::findPathData(const QString &path) const
|
||||||
|
{
|
||||||
|
for (int i = 0; i < m_pathList.count(); ++i)
|
||||||
|
if (m_pathList.at(i)->path == fsutils::fromNativePath(path))
|
||||||
|
return i;
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScanFoldersModel::makePersistent()
|
||||||
|
{
|
||||||
|
Preferences *const pref = Preferences::instance();
|
||||||
|
QStringList paths;
|
||||||
|
QList<bool> downloadInFolderInfo;
|
||||||
|
foreach (const PathData *pathData, m_pathList) {
|
||||||
|
paths << pathData->path;
|
||||||
|
downloadInFolderInfo << pathData->downloadAtPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
pref->setScanDirs(paths);
|
||||||
|
pref->setDownloadInScanDirs(downloadInFolderInfo);
|
||||||
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Bittorrent Client using Qt4 and libtorrent.
|
* Bittorrent Client using Qt and libtorrent.
|
||||||
* Copyright (C) 2010 Christian Kandeler, Christophe Dumez
|
* Copyright (C) 2010 Christian Kandeler, Christophe Dumez
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or
|
* This program is free software; you can redistribute it and/or
|
||||||
@ -33,48 +33,60 @@
|
|||||||
|
|
||||||
#include <QAbstractTableModel>
|
#include <QAbstractTableModel>
|
||||||
#include <QList>
|
#include <QList>
|
||||||
#include <QStringList>
|
|
||||||
|
QT_BEGIN_NAMESPACE
|
||||||
|
class QStringList;
|
||||||
|
QT_END_NAMESPACE
|
||||||
|
|
||||||
class FileSystemWatcher;
|
class FileSystemWatcher;
|
||||||
|
|
||||||
class ScanFoldersModel : public QAbstractTableModel {
|
class ScanFoldersModel : public QAbstractTableModel
|
||||||
Q_OBJECT
|
{
|
||||||
Q_DISABLE_COPY(ScanFoldersModel)
|
Q_OBJECT
|
||||||
|
Q_DISABLE_COPY(ScanFoldersModel)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
enum PathStatus { Ok, DoesNotExist, CannotRead, CannotWrite, AlreadyInList };
|
enum PathStatus
|
||||||
static ScanFoldersModel *instance(QObject *parent = 0);
|
{
|
||||||
virtual ~ScanFoldersModel();
|
Ok,
|
||||||
|
DoesNotExist,
|
||||||
|
CannotRead,
|
||||||
|
CannotWrite,
|
||||||
|
AlreadyInList
|
||||||
|
};
|
||||||
|
|
||||||
virtual int rowCount(const QModelIndex & parent = QModelIndex()) const;
|
static ScanFoldersModel *instance(QObject *parent = 0);
|
||||||
virtual int columnCount(const QModelIndex &parent = QModelIndex()) const;
|
virtual ~ScanFoldersModel();
|
||||||
virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
|
|
||||||
virtual QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;
|
|
||||||
virtual Qt::ItemFlags flags(const QModelIndex &index) const;
|
|
||||||
|
|
||||||
// TODO: removePaths(); singular version becomes private helper functions;
|
virtual int rowCount(const QModelIndex &parent = QModelIndex()) const;
|
||||||
// also: remove functions should take modelindexes
|
virtual int columnCount(const QModelIndex &parent = QModelIndex()) const;
|
||||||
PathStatus addPath(const QString &path, bool download_at_path);
|
virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
|
||||||
void removePath(int row);
|
virtual QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;
|
||||||
bool removePath(const QString &path);
|
virtual Qt::ItemFlags flags(const QModelIndex &index) const;
|
||||||
PathStatus setDownloadAtPath(int row, bool downloadAtPath);
|
|
||||||
|
|
||||||
bool downloadInTorrentFolder(const QString &filePath) const;
|
// TODO: removePaths(); singular version becomes private helper functions;
|
||||||
void makePersistent();
|
// also: remove functions should take modelindexes
|
||||||
|
PathStatus addPath(const QString &path, bool downloadAtPath);
|
||||||
|
void removePath(int row);
|
||||||
|
bool removePath(const QString &path);
|
||||||
|
PathStatus setDownloadAtPath(int row, bool downloadAtPath);
|
||||||
|
|
||||||
|
bool downloadInTorrentFolder(const QString &filePath) const;
|
||||||
|
void makePersistent();
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
// The absolute paths of new torrent files in the scanned directories.
|
// The absolute paths of new torrent files in the scanned directories.
|
||||||
void torrentsAdded(QStringList &pathList);
|
void torrentsAdded(QStringList &pathList);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
explicit ScanFoldersModel(QObject *parent);
|
explicit ScanFoldersModel(QObject *parent = 0);
|
||||||
virtual bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole);
|
virtual bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole);
|
||||||
static ScanFoldersModel *m_instance;
|
static ScanFoldersModel *m_instance;
|
||||||
class PathData;
|
class PathData;
|
||||||
int findPathData(const QString &path) const;
|
int findPathData(const QString &path) const;
|
||||||
|
|
||||||
QList<PathData*> m_pathList;
|
QList<PathData*> m_pathList;
|
||||||
FileSystemWatcher *m_fsWatcher;
|
FileSystemWatcher *m_fsWatcher;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // SCANNEDFOLDERSMODEL_H
|
#endif // SCANNEDFOLDERSMODEL_H
|
||||||
|
Loading…
Reference in New Issue
Block a user