Browse Source

Fix coding style (Issue #2192).

Also split filesystemwatcher.h into .h/.cpp files.
adaptive-webui-19844
Vladimir Golovnev (Glassez) 10 years ago
parent
commit
60c0939e05
  1. 1
      src/core/core.pri
  2. 278
      src/core/filesystemwatcher.cpp
  3. 295
      src/core/filesystemwatcher.h
  4. 301
      src/core/scannedfoldersmodel.cpp
  5. 72
      src/core/scannedfoldersmodel.h

1
src/core/core.pri

@ -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

@ -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

295
src/core/filesystemwatcher.h

@ -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 { signals:
QStringList dirs; void torrentsAdded(QStringList &pathList);
#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) { protected slots:
#if !defined Q_OS_WIN && !defined Q_OS_HAIKU void scanLocalFolder(QString path);
QDir dir(path); void scanNetworkFolders();
if (!dir.exists()) void processPartialTorrents();
return;
// Check if the path points to a network file system or not private:
if (isNetworkFileSystem(path)) { void startPartialTorrentTimer();
// Network mode void addTorrentsFromDir(const QDir &dir, QStringList &torrents);
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 #if !defined Q_OS_WIN && !defined Q_OS_HAIKU
} static bool isNetworkFileSystem(QString path);
#endif #endif
}
void removePath(const QString & path) {
#ifndef Q_OS_WIN #ifndef Q_OS_WIN
QDir dir(path); QList<QDir> m_watchedFolders;
for (int i = 0; i < watched_folders.count(); ++i) { QPointer<QTimer> m_watchTimer;
if (QDir(watched_folders.at(i)) == dir) {
watched_folders.removeAt(i);
if (watched_folders.isEmpty())
delete watch_timer;
return;
}
}
#endif #endif
// Normal mode QStringList m_filters;
QFileSystemWatcher::removePath(path); // Partial torrents
} QHash<QString, int> m_partialTorrents;
QPointer<QTimer> m_partialTorrentTimer;
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:
void torrentsAdded(QStringList &pathList);
private:
void 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 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();
}
}; };
#endif // FILESYSTEMWATCHER_H #endif // FILESYSTEMWATCHER_H

301
src/core/scannedfoldersmodel.cpp

@ -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" #include "misc.h"
#include "fs_utils.h"
#include "preferences.h"
#include "filesystemwatcher.h"
#include "scannedfoldersmodel.h"
namespace { namespace
const int PathColumn = 0; {
const int DownloadAtTorrentColumn = 1; 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) { ScanFoldersModel *ScanFoldersModel::m_instance = 0;
//Q_ASSERT(!parent != !m_instance);
if (!m_instance) ScanFoldersModel *ScanFoldersModel::instance(QObject *parent)
m_instance = new ScanFoldersModel(parent); {
return m_instance; //Q_ASSERT(!parent != !m_instance);
if (!m_instance)
m_instance = new ScanFoldersModel(parent);
return m_instance;
} }
ScanFoldersModel::ScanFoldersModel(QObject *parent) : ScanFoldersModel::ScanFoldersModel(QObject *parent)
QAbstractTableModel(parent), m_fsWatcher(0) : QAbstractTableModel(parent)
{ } , m_fsWatcher(0)
{
}
ScanFoldersModel::~ScanFoldersModel() { ScanFoldersModel::~ScanFoldersModel()
qDeleteAll(m_pathList); {
qDeleteAll(m_pathList);
} }
int ScanFoldersModel::rowCount(const QModelIndex &parent) const { int ScanFoldersModel::rowCount(const QModelIndex &parent) const
return parent.isValid() ? 0 : m_pathList.count(); {
return parent.isValid() ? 0 : m_pathList.count();
} }
int ScanFoldersModel::columnCount(const QModelIndex &parent) const { int ScanFoldersModel::columnCount(const QModelIndex &parent) const
Q_UNUSED(parent); {
return 2; Q_UNUSED(parent);
return 2;
} }
QVariant ScanFoldersModel::data(const QModelIndex &index, int role) const { QVariant ScanFoldersModel::data(const QModelIndex &index, int role) const
if (!index.isValid() || index.row() >= rowCount()) {
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(); 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();
const PathData* pathData = m_pathList.at(index.row()); if (section == PathColumn)
if (index.column() == PathColumn && role == Qt::DisplayRole) { return tr("Watched Folder");
return fsutils::toNativePath(pathData->path); return tr("Download here");
}
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 { Qt::ItemFlags ScanFoldersModel::flags(const QModelIndex &index) const
if (orientation != Qt::Horizontal || role != Qt::DisplayRole || section < 0 || section >= columnCount()) {
return QVariant(); if (!index.isValid() || (index.row() >= rowCount()) || (index.column() != DownloadAtTorrentColumn))
return QAbstractTableModel::flags(index);
if (section == PathColumn) return (QAbstractTableModel::flags(index) | Qt::ItemIsUserCheckable);
return tr("Watched Folder"); }
return tr("Download here");
} bool ScanFoldersModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
Qt::ItemFlags ScanFoldersModel::flags(const QModelIndex &index) const { if (!index.isValid() || (index.row() >= rowCount()) || (index.column() > DownloadAtTorrentColumn) || (role != Qt::CheckStateRole))
if (!index.isValid() || index.row() >= rowCount() || index.column() != DownloadAtTorrentColumn) return false;
return QAbstractTableModel::flags(index);
return QAbstractTableModel::flags(index) | Qt::ItemIsUserCheckable; Q_ASSERT(index.column() == DownloadAtTorrentColumn);
} m_pathList[index.row()]->downloadAtPath = (value.toInt() == Qt::Checked);
emit dataChanged(index, index);
bool ScanFoldersModel::setData(const QModelIndex &index, const QVariant &value, int role) { return true;
if (!index.isValid() || index.row() >= rowCount() || index.column() > DownloadAtTorrentColumn || role != Qt::CheckStateRole) }
return false;
Q_ASSERT(index.column() == DownloadAtTorrentColumn); ScanFoldersModel::PathStatus ScanFoldersModel::addPath(const QString &path, bool downloadAtPath)
m_pathList[index.row()]->downloadAtPath = (value.toInt() == Qt::Checked); {
emit dataChanged(index, index); QDir dir(path);
return true; if (!dir.exists()) return DoesNotExist;
} if (!dir.isReadable()) return CannotRead;
ScanFoldersModel::PathStatus ScanFoldersModel::addPath(const QString &path, bool download_at_path) { const QString &canonicalPath = dir.canonicalPath();
QDir dir(path); if (findPathData(canonicalPath) != -1) return AlreadyInList;
if (!dir.exists())
return DoesNotExist; if (!m_fsWatcher) {
if (!dir.isReadable()) m_fsWatcher = new FileSystemWatcher(this);
return CannotRead; connect(m_fsWatcher, SIGNAL(torrentsAdded(QStringList&)), this, SIGNAL(torrentsAdded(QStringList&)));
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); beginInsertRows(QModelIndex(), rowCount(), rowCount());
emit dataChanged(changedIndex, changedIndex); m_pathList << new PathData(canonicalPath, downloadAtPath);
} endInsertRows();
return Ok;
// Start scanning
m_fsWatcher->addPath(canonicalPath);
return Ok;
} }
bool ScanFoldersModel::downloadInTorrentFolder(const QString &filePath) const { void ScanFoldersModel::removePath(int row)
const int row = findPathData(QFileInfo(filePath).dir().path()); {
Q_ASSERT(row != -1); Q_ASSERT((row >= 0) && (row < rowCount()));
return m_pathList.at(row)->downloadAtPath; beginRemoveRows(QModelIndex(), row, row);
m_fsWatcher->removePath(m_pathList.at(row)->path);
m_pathList.removeAt(row);
endRemoveRows();
} }
int ScanFoldersModel::findPathData(const QString &path) const { bool ScanFoldersModel::removePath(const QString &path)
for (int i = 0; i < m_pathList.count(); ++i) { {
const PathData* pathData = m_pathList.at(i); const int row = findPathData(path);
if (pathData->path == fsutils::fromNativePath(path)) if (row == -1) return false;
return i;
}
return -1; removePath(row);
return true;
} }
void ScanFoldersModel::makePersistent() { ScanFoldersModel::PathStatus ScanFoldersModel::setDownloadAtPath(int row, bool downloadAtPath)
Preferences* const pref = Preferences::instance(); {
QStringList paths; Q_ASSERT((row >= 0) && (row < rowCount()));
QList<bool> downloadInFolderInfo;
foreach (const PathData* pathData, m_pathList) { bool &oldValue = m_pathList[row]->downloadAtPath;
paths << pathData->path; if (oldValue != downloadAtPath) {
downloadInFolderInfo << pathData->downloadAtPath; if (downloadAtPath) {
} QTemporaryFile testFile(m_pathList[row]->path + "/tmpFile");
pref->setScanDirs(paths); if (!testFile.open()) return CannotWrite;
pref->setDownloadInScanDirs(downloadInFolderInfo); }
oldValue = downloadAtPath;
const QModelIndex changedIndex = index(row, DownloadAtTorrentColumn);
emit dataChanged(changedIndex, changedIndex);
}
return Ok;
} }
ScanFoldersModel *ScanFoldersModel::m_instance = 0; 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);
}

72
src/core/scannedfoldersmodel.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
@ -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
};
static ScanFoldersModel *instance(QObject *parent = 0);
virtual ~ScanFoldersModel();
virtual int rowCount(const QModelIndex & parent = QModelIndex()) const; virtual int rowCount(const QModelIndex &parent = QModelIndex()) const;
virtual int columnCount(const QModelIndex &parent = QModelIndex()) const; virtual int columnCount(const QModelIndex &parent = QModelIndex()) const;
virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; 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 QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;
virtual Qt::ItemFlags flags(const QModelIndex &index) const; virtual Qt::ItemFlags flags(const QModelIndex &index) const;
// TODO: removePaths(); singular version becomes private helper functions; // TODO: removePaths(); singular version becomes private helper functions;
// also: remove functions should take modelindexes // also: remove functions should take modelindexes
PathStatus addPath(const QString &path, bool download_at_path); PathStatus addPath(const QString &path, bool downloadAtPath);
void removePath(int row); void removePath(int row);
bool removePath(const QString &path); bool removePath(const QString &path);
PathStatus setDownloadAtPath(int row, bool downloadAtPath); PathStatus setDownloadAtPath(int row, bool downloadAtPath);
bool downloadInTorrentFolder(const QString &filePath) const; bool downloadInTorrentFolder(const QString &filePath) const;
void makePersistent(); 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…
Cancel
Save