qBittorrent/src/qtlibtorrent/torrentmodel.cpp

575 lines
18 KiB
C++
Raw Normal View History

2010-11-14 22:32:29 +03:00
/*
* Bittorrent Client using Qt4 and libtorrent.
* Copyright (C) 2010 Christophe Dumez
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* In addition, as a special exception, the copyright holders give permission to
* link this program with the OpenSSL project's "OpenSSL" library (or with
* modified versions of it that use the same license as the "OpenSSL" library),
* and distribute the linked executables. You must obey the GNU General Public
* License in all respects for all of the code used other than "OpenSSL". If you
* modify file(s), you may extend this exception to your version of the file(s),
* but you are not obligated to do so. If you do not wish to do so, delete this
* exception statement from your version.
*
* Contact : chris@qbittorrent.org
*/
#include <QDebug>
#include "torrentmodel.h"
#include "torrentpersistentdata.h"
#include "qbtsession.h"
2013-11-10 23:25:16 +04:00
#include "fs_utils.h"
using namespace libtorrent;
2014-05-11 15:29:06 +04:00
namespace
{
QIcon get_paused_icon()
{
static QIcon cached = QIcon(":/Icons/skin/paused.png");
return cached;
}
QIcon get_queued_icon()
{
static QIcon cached = QIcon(":/Icons/skin/queued.png");
return cached;
}
QIcon get_downloading_icon()
{
static QIcon cached = QIcon(":/Icons/skin/downloading.png");
return cached;
}
QIcon get_stalled_downloading_icon()
{
static QIcon cached = QIcon(":/Icons/skin/stalledDL.png");
return cached;
}
QIcon get_uploading_icon()
{
static QIcon cached = QIcon(":/Icons/skin/uploading.png");
return cached;
}
QIcon get_stalled_uploading_icon()
{
static QIcon cached = QIcon(":/Icons/skin/stalledUP.png");
return cached;
}
QIcon get_checking_icon()
{
static QIcon cached = QIcon(":/Icons/skin/checking.png");
return cached;
}
QIcon get_error_icon()
{
static QIcon cached = QIcon(":/Icons/skin/error.png");
return cached;
}
}
TorrentModelItem::TorrentModelItem(const QTorrentHandle &h)
2012-08-25 17:34:00 +04:00
: m_torrent(h)
2014-05-14 02:09:45 +04:00
, m_lastStatus(h.status(torrent_handle::query_accurate_download_counters))
2012-08-25 17:34:00 +04:00
, m_addedTime(TorrentPersistentData::getAddedDate(h.hash()))
, m_seedTime(TorrentPersistentData::getSeedDate(h.hash()))
, m_label(TorrentPersistentData::getLabel(h.hash()))
, m_name(TorrentPersistentData::getName(h.hash()))
, m_hash(h.hash())
{
2012-08-25 17:34:00 +04:00
if (m_name.isEmpty())
m_name = h.name();
}
2014-05-14 02:09:45 +04:00
void TorrentModelItem::refreshStatus()
{
try {
m_lastStatus = m_torrent.status();
} catch(invalid_handle&) {}
}
TorrentModelItem::State TorrentModelItem::state() const
{
try {
// Pause or Queued
2014-05-14 02:09:45 +04:00
if (m_torrent.is_paused(m_lastStatus)) {
2014-05-11 15:29:06 +04:00
m_icon = get_paused_icon();
m_fgColor = QColor("red");
2014-05-14 02:09:45 +04:00
return m_torrent.is_seed(m_lastStatus) ? STATE_PAUSED_UP : STATE_PAUSED_DL;
}
2014-05-14 02:09:45 +04:00
if (m_torrent.is_queued(m_lastStatus)) {
if (m_lastStatus.state != torrent_status::queued_for_checking
&& m_lastStatus.state != torrent_status::checking_resume_data
&& m_lastStatus.state != torrent_status::checking_files) {
2014-05-11 15:29:06 +04:00
m_icon = get_queued_icon();
m_fgColor = QColor("grey");
2014-05-14 02:09:45 +04:00
return m_torrent.is_seed(m_lastStatus) ? STATE_QUEUED_UP : STATE_QUEUED_DL;
}
}
// Other states
2014-05-14 02:09:45 +04:00
switch(m_lastStatus.state) {
case torrent_status::allocating:
2014-05-11 15:29:06 +04:00
m_icon = get_stalled_downloading_icon();
2013-10-22 22:34:27 +04:00
m_fgColor = QColor("grey");
return STATE_ALLOCATING;
case torrent_status::downloading_metadata:
2014-05-11 15:29:06 +04:00
m_icon = get_downloading_icon();
2013-10-22 22:34:27 +04:00
m_fgColor = QColor("green");
return STATE_DOWNLOADING_META;
case torrent_status::downloading: {
2014-05-14 02:09:45 +04:00
if (m_lastStatus.download_payload_rate > 0) {
2014-05-11 15:29:06 +04:00
m_icon = get_downloading_icon();
m_fgColor = QColor("green");
return STATE_DOWNLOADING;
} else {
2014-05-11 15:29:06 +04:00
m_icon = get_stalled_downloading_icon();
m_fgColor = QColor("grey");
return STATE_STALLED_DL;
}
}
case torrent_status::finished:
case torrent_status::seeding:
2014-05-14 02:09:45 +04:00
if (m_lastStatus.upload_payload_rate > 0) {
2014-05-11 15:29:06 +04:00
m_icon = get_uploading_icon();
m_fgColor = QColor("orange");
return STATE_SEEDING;
} else {
2014-05-11 15:29:06 +04:00
m_icon = get_stalled_uploading_icon();
m_fgColor = QColor("grey");
return STATE_STALLED_UP;
}
case torrent_status::queued_for_checking:
2014-05-11 15:29:06 +04:00
m_icon = get_checking_icon();
2013-10-22 22:34:27 +04:00
m_fgColor = QColor("grey");
return STATE_QUEUED_CHECK;
case torrent_status::checking_resume_data:
2014-05-11 15:29:06 +04:00
m_icon = get_checking_icon();
2013-10-22 22:34:27 +04:00
m_fgColor = QColor("grey");
return STATE_QUEUED_FASTCHECK;
case torrent_status::checking_files:
2014-05-11 15:29:06 +04:00
m_icon = get_checking_icon();
m_fgColor = QColor("grey");
2014-05-14 02:09:45 +04:00
return m_torrent.is_seed(m_lastStatus) ? STATE_CHECKING_UP : STATE_CHECKING_DL;
default:
2014-05-11 15:29:06 +04:00
m_icon = get_error_icon();
m_fgColor = QColor("red");
return STATE_INVALID;
}
} catch(invalid_handle&) {
2014-05-11 15:29:06 +04:00
m_icon = get_error_icon();
m_fgColor = QColor("red");
return STATE_INVALID;
}
}
bool TorrentModelItem::setData(int column, const QVariant &value, int role)
{
qDebug() << Q_FUNC_INFO << column << value;
2012-02-20 21:30:53 +04:00
if (role != Qt::DisplayRole) return false;
// Label, seed date and Name columns can be edited
switch(column) {
case TR_NAME:
m_name = value.toString();
TorrentPersistentData::saveName(m_torrent.hash(), m_name);
return true;
case TR_LABEL: {
QString new_label = value.toString();
2012-02-20 21:30:53 +04:00
if (m_label != new_label) {
QString old_label = m_label;
m_label = new_label;
TorrentPersistentData::saveLabel(m_torrent.hash(), new_label);
emit labelChanged(old_label, new_label);
}
return true;
}
case TR_SEED_DATE: {
m_seedTime = value.toDateTime();
return true;
}
default:
break;
}
return false;
}
QVariant TorrentModelItem::data(int column, int role) const
{
2012-02-20 21:30:53 +04:00
if (role == Qt::DecorationRole && column == TR_NAME) {
return m_icon;
}
2012-02-20 21:30:53 +04:00
if (role == Qt::ForegroundRole) {
return m_fgColor;
}
2012-02-20 21:30:53 +04:00
if (role != Qt::DisplayRole && role != Qt::UserRole) return QVariant();
switch(column) {
case TR_NAME:
return m_name.isEmpty() ? m_torrent.name() : m_name;
case TR_PRIORITY: {
int pos = m_torrent.queue_position();
if (pos > -1)
return pos - HiddenData::getSize();
else
return pos;
}
case TR_SIZE:
2014-05-14 02:09:45 +04:00
return m_lastStatus.has_metadata ? static_cast<qlonglong>(m_lastStatus.total_wanted) : -1;
case TR_PROGRESS:
2014-05-14 02:09:45 +04:00
return m_torrent.progress(m_lastStatus);
case TR_STATUS:
return state();
case TR_SEEDS: {
2014-05-14 02:09:45 +04:00
return (role == Qt::DisplayRole) ? m_lastStatus.num_seeds : m_lastStatus.num_complete;
}
case TR_PEERS: {
2014-05-14 02:09:45 +04:00
return (role == Qt::DisplayRole) ? (m_lastStatus.num_peers-m_lastStatus.num_seeds) : m_lastStatus.num_incomplete;
}
case TR_DLSPEED:
2014-05-14 02:09:45 +04:00
return m_lastStatus.download_payload_rate;
case TR_UPSPEED:
2014-05-14 02:09:45 +04:00
return m_lastStatus.upload_payload_rate;
case TR_ETA: {
// XXX: Is this correct?
2014-05-14 02:09:45 +04:00
if (m_torrent.is_paused(m_lastStatus) || m_torrent.is_queued(m_lastStatus)) return MAX_ETA;
return QBtSession::instance()->getETA(m_hash, m_lastStatus);
}
case TR_RATIO:
2014-05-14 02:09:45 +04:00
return QBtSession::instance()->getRealRatio(m_lastStatus);
case TR_LABEL:
return m_label;
case TR_ADD_DATE:
return m_addedTime;
case TR_SEED_DATE:
return m_seedTime;
case TR_TRACKER:
2014-05-14 02:09:45 +04:00
return misc::toQString(m_lastStatus.current_tracker);
case TR_DLLIMIT:
return m_torrent.download_limit();
case TR_UPLIMIT:
return m_torrent.upload_limit();
case TR_AMOUNT_DOWNLOADED:
2014-05-14 02:09:45 +04:00
return static_cast<qlonglong>(m_lastStatus.all_time_download);
2012-12-27 17:55:40 +04:00
case TR_AMOUNT_UPLOADED:
2014-05-14 02:09:45 +04:00
return static_cast<qlonglong>(m_lastStatus.all_time_upload);
case TR_AMOUNT_LEFT:
2014-05-14 02:09:45 +04:00
return static_cast<qlonglong>(m_lastStatus.total_wanted - m_lastStatus.total_wanted_done);
case TR_TIME_ELAPSED:
2014-05-14 02:09:45 +04:00
return (role == Qt::DisplayRole) ? m_lastStatus.active_time : m_lastStatus.seeding_time;
case TR_SAVE_PATH:
2013-11-10 23:25:16 +04:00
return fsutils::toNativePath(m_torrent.save_path_parsed());
default:
return QVariant();
}
}
// TORRENT MODEL
TorrentModel::TorrentModel(QObject *parent) :
QAbstractListModel(parent), m_refreshInterval(2000)
{
}
void TorrentModel::populate() {
// Load the torrents
std::vector<torrent_handle> torrents = QBtSession::instance()->getSession()->get_torrents();
2012-07-14 02:28:23 +04:00
std::vector<torrent_handle>::const_iterator it = torrents.begin();
std::vector<torrent_handle>::const_iterator itend = torrents.end();
for ( ; it != itend; ++it) {
addTorrent(QTorrentHandle(*it));
}
// Refresh timer
connect(&m_refreshTimer, SIGNAL(timeout()), SLOT(forceModelRefresh()));
m_refreshTimer.start(m_refreshInterval);
// Listen for torrent changes
connect(QBtSession::instance(), SIGNAL(addedTorrent(QTorrentHandle)), SLOT(addTorrent(QTorrentHandle)));
connect(QBtSession::instance(), SIGNAL(torrentAboutToBeRemoved(QTorrentHandle)), SLOT(handleTorrentAboutToBeRemoved(QTorrentHandle)));
connect(QBtSession::instance(), SIGNAL(deletedTorrent(QString)), SLOT(removeTorrent(QString)));
connect(QBtSession::instance(), SIGNAL(finishedTorrent(QTorrentHandle)), SLOT(handleFinishedTorrent(QTorrentHandle)));
2010-11-24 23:31:14 +03:00
connect(QBtSession::instance(), SIGNAL(metadataReceived(QTorrentHandle)), SLOT(handleTorrentUpdate(QTorrentHandle)));
connect(QBtSession::instance(), SIGNAL(resumedTorrent(QTorrentHandle)), SLOT(handleTorrentUpdate(QTorrentHandle)));
connect(QBtSession::instance(), SIGNAL(pausedTorrent(QTorrentHandle)), SLOT(handleTorrentUpdate(QTorrentHandle)));
connect(QBtSession::instance(), SIGNAL(torrentFinishedChecking(QTorrentHandle)), SLOT(handleTorrentUpdate(QTorrentHandle)));
}
TorrentModel::~TorrentModel() {
2011-03-14 22:18:52 +03:00
qDebug() << Q_FUNC_INFO << "ENTER";
qDeleteAll(m_torrents);
m_torrents.clear();
2011-03-14 22:18:52 +03:00
qDebug() << Q_FUNC_INFO << "EXIT";
}
QVariant TorrentModel::headerData(int section, Qt::Orientation orientation,
int role) const
{
if (orientation == Qt::Horizontal) {
2012-02-20 21:30:53 +04:00
if (role == Qt::DisplayRole) {
switch(section) {
case TorrentModelItem::TR_NAME: return tr("Name", "i.e: torrent name");
case TorrentModelItem::TR_PRIORITY: return "#";
case TorrentModelItem::TR_SIZE: return tr("Size", "i.e: torrent size");
case TorrentModelItem::TR_PROGRESS: return tr("Done", "% Done");
case TorrentModelItem::TR_STATUS: return tr("Status", "Torrent status (e.g. downloading, seeding, paused)");
case TorrentModelItem::TR_SEEDS: return tr("Seeds", "i.e. full sources (often untranslated)");
case TorrentModelItem::TR_PEERS: return tr("Peers", "i.e. partial sources (often untranslated)");
case TorrentModelItem::TR_DLSPEED: return tr("Down Speed", "i.e: Download speed");
case TorrentModelItem::TR_UPSPEED: return tr("Up Speed", "i.e: Upload speed");
case TorrentModelItem::TR_RATIO: return tr("Ratio", "Share ratio");
case TorrentModelItem::TR_ETA: return tr("ETA", "i.e: Estimated Time of Arrival / Time left");
case TorrentModelItem::TR_LABEL: return tr("Label");
case TorrentModelItem::TR_ADD_DATE: return tr("Added On", "Torrent was added to transfer list on 01/01/2010 08:00");
case TorrentModelItem::TR_SEED_DATE: return tr("Completed On", "Torrent was completed on 01/01/2010 08:00");
case TorrentModelItem::TR_TRACKER: return tr("Tracker");
case TorrentModelItem::TR_DLLIMIT: return tr("Down Limit", "i.e: Download limit");
case TorrentModelItem::TR_UPLIMIT: return tr("Up Limit", "i.e: Upload limit");
case TorrentModelItem::TR_AMOUNT_DOWNLOADED: return tr("Downloaded", "Amount of data downloaded (e.g. in MB)");
case TorrentModelItem::TR_AMOUNT_UPLOADED: return tr("Uploaded", "Amount of data uploaded (e.g. in MB)");
case TorrentModelItem::TR_AMOUNT_LEFT: return tr("Remaining", "Amount of data left to download (e.g. in MB)");
case TorrentModelItem::TR_TIME_ELAPSED: return tr("Time Active", "Time (duration) the torrent is active (not paused)");
case TorrentModelItem::TR_SAVE_PATH: return tr("Save path", "Torrent save path");
default:
return QVariant();
}
}
}
return QVariant();
}
QVariant TorrentModel::data(const QModelIndex &index, int role) const
{
2012-02-20 21:30:53 +04:00
if (!index.isValid()) return QVariant();
try {
2012-02-20 21:30:53 +04:00
if (index.row() >= 0 && index.row() < rowCount() && index.column() >= 0 && index.column() < columnCount())
return m_torrents[index.row()]->data(index.column(), role);
} catch(invalid_handle&) {}
return QVariant();
}
bool TorrentModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
qDebug() << Q_FUNC_INFO << value;
2012-02-20 21:30:53 +04:00
if (!index.isValid() || role != Qt::DisplayRole) return false;
qDebug("Index is valid and role is DisplayRole");
try {
2012-02-20 21:30:53 +04:00
if (index.row() >= 0 && index.row() < rowCount() && index.column() >= 0 && index.column() < columnCount()) {
bool change = m_torrents[index.row()]->setData(index.column(), value, role);
2012-02-20 21:30:53 +04:00
if (change)
notifyTorrentChanged(index.row());
return change;
}
} catch(invalid_handle&) {}
return false;
}
int TorrentModel::torrentRow(const QString &hash) const
{
2010-11-14 21:50:32 +03:00
int row = 0;
2012-07-14 02:28:23 +04:00
QList<TorrentModelItem*>::const_iterator it = m_torrents.constBegin();
QList<TorrentModelItem*>::const_iterator itend = m_torrents.constEnd();
for ( ; it != itend; ++it) {
2012-02-20 21:30:53 +04:00
if ((*it)->hash() == hash) return row;
++row;
}
return -1;
}
void TorrentModel::addTorrent(const QTorrentHandle &h)
{
2012-02-20 21:30:53 +04:00
if (torrentRow(h.hash()) < 0) {
beginInsertTorrent(m_torrents.size());
TorrentModelItem *item = new TorrentModelItem(h);
connect(item, SIGNAL(labelChanged(QString,QString)), SLOT(handleTorrentLabelChange(QString,QString)));
m_torrents << item;
emit torrentAdded(item);
endInsertTorrent();
}
}
void TorrentModel::removeTorrent(const QString &hash)
{
const int row = torrentRow(hash);
qDebug() << Q_FUNC_INFO << hash << row;
2012-02-20 21:30:53 +04:00
if (row >= 0) {
beginRemoveTorrent(row);
m_torrents.removeAt(row);
endRemoveTorrent();
}
}
void TorrentModel::beginInsertTorrent(int row)
{
beginInsertRows(QModelIndex(), row, row);
}
void TorrentModel::endInsertTorrent()
{
endInsertRows();
}
void TorrentModel::beginRemoveTorrent(int row)
{
beginRemoveRows(QModelIndex(), row, row);
}
void TorrentModel::endRemoveTorrent()
{
endRemoveRows();
}
2010-11-24 23:31:14 +03:00
void TorrentModel::handleTorrentUpdate(const QTorrentHandle &h)
{
const int row = torrentRow(h.hash());
2012-02-20 21:30:53 +04:00
if (row >= 0) {
notifyTorrentChanged(row);
}
}
void TorrentModel::handleFinishedTorrent(const QTorrentHandle& h)
{
const int row = torrentRow(h.hash());
if (row < 0)
return;
// Update completion date
m_torrents[row]->setData(TorrentModelItem::TR_SEED_DATE, QDateTime::currentDateTime(), Qt::DisplayRole);
notifyTorrentChanged(row);
}
void TorrentModel::notifyTorrentChanged(int row)
{
emit dataChanged(index(row, 0), index(row, columnCount()-1));
}
void TorrentModel::setRefreshInterval(int refreshInterval)
{
2012-02-20 21:30:53 +04:00
if (m_refreshInterval != refreshInterval) {
m_refreshInterval = refreshInterval;
m_refreshTimer.stop();
m_refreshTimer.start(m_refreshInterval);
}
}
void TorrentModel::forceModelRefresh()
{
2014-05-14 02:09:45 +04:00
QList<TorrentModelItem*>::const_iterator it = m_torrents.constBegin();
QList<TorrentModelItem*>::const_iterator itend = m_torrents.constEnd();
for ( ; it != itend; ++it) {
TorrentModelItem* item = *it;
item->refreshStatus();
}
emit dataChanged(index(0, 0), index(rowCount()-1, columnCount()-1));
}
TorrentStatusReport TorrentModel::getTorrentStatusReport() const
{
TorrentStatusReport report;
2012-07-14 02:28:23 +04:00
QList<TorrentModelItem*>::const_iterator it = m_torrents.constBegin();
QList<TorrentModelItem*>::const_iterator itend = m_torrents.constEnd();
for ( ; it != itend; ++it) {
switch((*it)->data(TorrentModelItem::TR_STATUS).toInt()) {
case TorrentModelItem::STATE_DOWNLOADING:
++report.nb_active;
++report.nb_downloading;
break;
case TorrentModelItem::STATE_DOWNLOADING_META:
++report.nb_downloading;
break;
case TorrentModelItem::STATE_PAUSED_DL:
++report.nb_paused;
case TorrentModelItem::STATE_STALLED_DL:
case TorrentModelItem::STATE_CHECKING_DL:
case TorrentModelItem::STATE_QUEUED_DL: {
++report.nb_inactive;
++report.nb_downloading;
break;
}
case TorrentModelItem::STATE_SEEDING:
++report.nb_active;
++report.nb_seeding;
break;
case TorrentModelItem::STATE_PAUSED_UP:
++report.nb_paused;
case TorrentModelItem::STATE_STALLED_UP:
case TorrentModelItem::STATE_CHECKING_UP:
case TorrentModelItem::STATE_QUEUED_UP: {
++report.nb_seeding;
++report.nb_inactive;
break;
}
default:
break;
}
}
return report;
}
Qt::ItemFlags TorrentModel::flags(const QModelIndex &index) const
{
if (!index.isValid())
return 0;
// Explicitely mark as editable
return QAbstractListModel::flags(index) | Qt::ItemIsEditable;
}
void TorrentModel::handleTorrentLabelChange(QString previous, QString current)
{
emit torrentChangedLabel(static_cast<TorrentModelItem*>(sender()), previous, current);
}
QString TorrentModel::torrentHash(int row) const
{
2012-02-20 21:30:53 +04:00
if (row >= 0 && row < rowCount())
return m_torrents.at(row)->hash();
return QString();
}
void TorrentModel::handleTorrentAboutToBeRemoved(const QTorrentHandle &h)
{
const int row = torrentRow(h.hash());
2012-02-20 21:30:53 +04:00
if (row >= 0) {
emit torrentAboutToBeRemoved(m_torrents.at(row));
}
}
bool TorrentModel::inhibitSystem()
{
QList<TorrentModelItem*>::const_iterator it = m_torrents.constBegin();
QList<TorrentModelItem*>::const_iterator itend = m_torrents.constEnd();
for ( ; it != itend; ++it) {
switch((*it)->data(TorrentModelItem::TR_STATUS).toInt()) {
case TorrentModelItem::STATE_DOWNLOADING:
case TorrentModelItem::STATE_DOWNLOADING_META:
case TorrentModelItem::STATE_STALLED_DL:
case TorrentModelItem::STATE_SEEDING:
case TorrentModelItem::STATE_STALLED_UP:
return true;
default:
break;
}
}
return false;
}