diff --git a/src/core/core.pri b/src/core/core.pri index 43d5ecfd2..6997ca78d 100644 --- a/src/core/core.pri +++ b/src/core/core.pri @@ -26,6 +26,7 @@ SOURCES += \ $$PWD/downloadthread.cpp \ $$PWD/scannedfoldersmodel.cpp \ $$PWD/torrentpersistentdata.cpp \ + $$PWD/filesystemwatcher.cpp \ $$PWD/misc.cpp \ $$PWD/fs_utils.cpp \ $$PWD/logger.cpp \ diff --git a/src/core/filesystemwatcher.cpp b/src/core/filesystemwatcher.cpp new file mode 100644 index 000000000..fb109545e --- /dev/null +++ b/src/core/filesystemwatcher.cpp @@ -0,0 +1,278 @@ +#include +#ifndef Q_OS_WIN +#include +#include +#include +#if defined(Q_OS_MAC) || defined(Q_OS_FREEBSD) +#include +#include +#include +#elif !defined Q_OS_HAIKU +#include +#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 diff --git a/src/core/filesystemwatcher.h b/src/core/filesystemwatcher.h index 2a821d2e3..a6eda5046 100644 --- a/src/core/filesystemwatcher.h +++ b/src/core/filesystemwatcher.h @@ -8,290 +8,45 @@ #include #include -#ifndef Q_OS_WIN -#include -#include -#include -#if defined(Q_OS_MAC) || defined(Q_OS_FREEBSD) -#include -#include -#include -#elif !defined Q_OS_HAIKU -#include -#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 * System watching (NFS, CIFS) on Linux and Mac OS. */ -class FileSystemWatcher: public QFileSystemWatcher { - Q_OBJECT - -private: -#ifndef Q_OS_WIN - QList watched_folders; - QPointer watch_timer; -#endif - QStringList m_filters; - // Partial torrents - QHash m_partialTorrents; - QPointer 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 +class FileSystemWatcher : public QFileSystemWatcher +{ + Q_OBJECT public: - FileSystemWatcher(QObject *parent): QFileSystemWatcher(parent) { - m_filters << "*.torrent" << "*.magnet"; - connect(this, SIGNAL(directoryChanged(QString)), this, SLOT(scanLocalFolder(QString))); - } + explicit FileSystemWatcher(QObject *parent = 0); + ~FileSystemWatcher(); - ~FileSystemWatcher() { -#ifndef Q_OS_WIN - if (watch_timer) - delete watch_timer; -#endif - if (m_partialTorrentTimer) - delete m_partialTorrentTimer; - } + QStringList directories() const; + void addPath(const QString &path); + void removePath(const QString &path); - 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; - } +signals: + void torrentsAdded(QStringList &pathList); - 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); +protected slots: + void scanLocalFolder(QString path); + void scanNetworkFolders(); + void processPartialTorrents(); + +private: + void startPartialTorrentTimer(); + void addTorrentsFromDir(const QDir &dir, QStringList &torrents); #if !defined Q_OS_WIN && !defined Q_OS_HAIKU - } + static bool isNetworkFileSystem(QString path); #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; - } - } + QList m_watchedFolders; + QPointer m_watchTimer; #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: - 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(); - } - + QStringList m_filters; + // Partial torrents + QHash m_partialTorrents; + QPointer m_partialTorrentTimer; }; #endif // FILESYSTEMWATCHER_H diff --git a/src/core/scannedfoldersmodel.cpp b/src/core/scannedfoldersmodel.cpp index b0158f5ec..261609634 100644 --- a/src/core/scannedfoldersmodel.cpp +++ b/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 * * This program is free software; you can redistribute it and/or @@ -28,171 +28,206 @@ * Contact : chris@qbittorrent.org */ -#include "scannedfoldersmodel.h" -#include "preferences.h" -#include "filesystemwatcher.h" - #include #include #include +#include #include + #include "misc.h" +#include "fs_utils.h" +#include "preferences.h" +#include "filesystemwatcher.h" +#include "scannedfoldersmodel.h" -namespace { - const int PathColumn = 0; - const int DownloadAtTorrentColumn = 1; +namespace +{ + const int PathColumn = 0; + const int DownloadAtTorrentColumn = 1; } -class ScanFoldersModel::PathData { +class ScanFoldersModel::PathData +{ public: - PathData(const QString &path) : path(path), downloadAtPath(false) {} - PathData(const QString &path, bool download_at_path) : path(path), downloadAtPath(download_at_path) {} - const QString path; - bool downloadAtPath; + PathData(const QString &path) + : path(path) + , downloadAtPath(false) + { + } + + 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::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(QObject *parent) + : QAbstractTableModel(parent) + , m_fsWatcher(0) +{ +} -ScanFoldersModel::~ScanFoldersModel() { - qDeleteAll(m_pathList); +ScanFoldersModel::~ScanFoldersModel() +{ + qDeleteAll(m_pathList); } -int ScanFoldersModel::rowCount(const QModelIndex &parent) const { - return parent.isValid() ? 0 : m_pathList.count(); +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; +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()) +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(); - const PathData* pathData = m_pathList.at(index.row()); - if (index.column() == PathColumn && role == Qt::DisplayRole) { + if (section == PathColumn) + return tr("Watched Folder"); - return fsutils::toNativePath(pathData->path); - } - if (index.column() == DownloadAtTorrentColumn && role == Qt::CheckStateRole) - return pathData->downloadAtPath ? Qt::Checked : Qt::Unchecked; - return QVariant(); + return tr("Download here"); } -QVariant ScanFoldersModel::headerData(int section, Qt::Orientation orientation, int role) const { - if (orientation != Qt::Horizontal || role != Qt::DisplayRole || section < 0 || section >= columnCount()) - return QVariant(); +Qt::ItemFlags ScanFoldersModel::flags(const QModelIndex &index) const +{ + if (!index.isValid() || (index.row() >= rowCount()) || (index.column() != DownloadAtTorrentColumn)) + return QAbstractTableModel::flags(index); - 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; + 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&))); } - oldValue = downloadAtPath; - const QModelIndex changedIndex = index(row, DownloadAtTorrentColumn); - emit dataChanged(changedIndex, changedIndex); - } - return Ok; + + beginInsertRows(QModelIndex(), rowCount(), rowCount()); + m_pathList << new PathData(canonicalPath, downloadAtPath); + endInsertRows(); + + // Start scanning + m_fsWatcher->addPath(canonicalPath); + 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; +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(); } -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; - } +bool ScanFoldersModel::removePath(const QString &path) +{ + const int row = findPathData(path); + if (row == -1) return false; - return -1; + removePath(row); + return true; } -void ScanFoldersModel::makePersistent() { - Preferences* const pref = Preferences::instance(); - QStringList paths; - QList downloadInFolderInfo; - foreach (const PathData* pathData, m_pathList) { - paths << pathData->path; - downloadInFolderInfo << pathData->downloadAtPath; - } - pref->setScanDirs(paths); - pref->setDownloadInScanDirs(downloadInFolderInfo); +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; } -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 downloadInFolderInfo; + foreach (const PathData *pathData, m_pathList) { + paths << pathData->path; + downloadInFolderInfo << pathData->downloadAtPath; + } + + pref->setScanDirs(paths); + pref->setDownloadInScanDirs(downloadInFolderInfo); +} diff --git a/src/core/scannedfoldersmodel.h b/src/core/scannedfoldersmodel.h index ab2f51960..ad17602b3 100644 --- a/src/core/scannedfoldersmodel.h +++ b/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 * * This program is free software; you can redistribute it and/or @@ -33,48 +33,60 @@ #include #include -#include + +QT_BEGIN_NAMESPACE +class QStringList; +QT_END_NAMESPACE class FileSystemWatcher; -class ScanFoldersModel : public QAbstractTableModel { - Q_OBJECT - Q_DISABLE_COPY(ScanFoldersModel) +class ScanFoldersModel : public QAbstractTableModel +{ + Q_OBJECT + Q_DISABLE_COPY(ScanFoldersModel) public: - enum PathStatus { Ok, DoesNotExist, CannotRead, CannotWrite, AlreadyInList }; - static ScanFoldersModel *instance(QObject *parent = 0); - virtual ~ScanFoldersModel(); + enum PathStatus + { + Ok, + DoesNotExist, + CannotRead, + CannotWrite, + AlreadyInList + }; + + static ScanFoldersModel *instance(QObject *parent = 0); + virtual ~ScanFoldersModel(); - virtual int rowCount(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 headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; - virtual Qt::ItemFlags flags(const QModelIndex &index) const; + virtual int rowCount(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 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; - // also: remove functions should take modelindexes - PathStatus addPath(const QString &path, bool download_at_path); - void removePath(int row); - bool removePath(const QString &path); - PathStatus setDownloadAtPath(int row, bool downloadAtPath); + // TODO: removePaths(); singular version becomes private helper functions; + // 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(); + bool downloadInTorrentFolder(const QString &filePath) const; + void makePersistent(); signals: - // The absolute paths of new torrent files in the scanned directories. - void torrentsAdded(QStringList &pathList); + // The absolute paths of new torrent files in the scanned directories. + void torrentsAdded(QStringList &pathList); private: - explicit ScanFoldersModel(QObject *parent); - virtual bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole); - static ScanFoldersModel *m_instance; - class PathData; - int findPathData(const QString &path) const; + explicit ScanFoldersModel(QObject *parent = 0); + virtual bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole); + static ScanFoldersModel *m_instance; + class PathData; + int findPathData(const QString &path) const; - QList m_pathList; - FileSystemWatcher *m_fsWatcher; + QList m_pathList; + FileSystemWatcher *m_fsWatcher; }; #endif // SCANNEDFOLDERSMODEL_H