qBittorrent/src/gui/transferlistmodel.cpp

492 lines
17 KiB
C++
Raw Normal View History

2010-11-14 22:32:29 +03:00
/*
2015-06-07 15:03:30 +03:00
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2015 Vladimir Golovnev <glassez@yandex.ru>
* Copyright (C) 2010 Christophe Dumez <chris@qbittorrent.org>
2010-11-14 22:32:29 +03:00
*
* 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.
*/
#include "transferlistmodel.h"
2018-06-06 16:48:17 +03:00
#include <QApplication>
#include <QDateTime>
2018-06-06 16:48:17 +03:00
#include <QDebug>
2015-04-19 18:17:47 +03:00
#include <QIcon>
2018-06-06 16:48:17 +03:00
#include <QPalette>
2015-09-25 11:10:05 +03:00
#include "base/bittorrent/session.h"
#include "base/bittorrent/torrenthandle.h"
#include "base/global.h"
2015-09-25 11:10:05 +03:00
#include "base/utils/fs.h"
2015-06-07 15:03:30 +03:00
static QIcon getIconByState(BitTorrent::TorrentState state);
static QColor getColorByState(BitTorrent::TorrentState state);
2014-05-11 15:29:06 +04:00
2015-06-07 15:03:30 +03:00
static QIcon getPausedIcon();
static QIcon getQueuedIcon();
static QIcon getDownloadingIcon();
static QIcon getStalledDownloadingIcon();
static QIcon getUploadingIcon();
static QIcon getStalledUploadingIcon();
static QIcon getCompletedIcon();
static QIcon getCheckingIcon();
static QIcon getErrorIcon();
2014-05-11 15:29:06 +04:00
2015-06-07 15:03:30 +03:00
static bool isDarkTheme();
2014-05-11 15:29:06 +04:00
// TransferListModel
2014-05-11 15:29:06 +04:00
TransferListModel::TransferListModel(QObject *parent)
2015-06-07 15:03:30 +03:00
: QAbstractListModel(parent)
{
// Load the torrents
2018-04-18 16:59:41 +03:00
using namespace BitTorrent;
for (TorrentHandle *const torrent : asConst(Session::instance()->torrents()))
2015-06-07 15:03:30 +03:00
addTorrent(torrent);
2014-05-11 15:29:06 +04:00
2015-06-07 15:03:30 +03:00
// Listen for torrent changes
connect(Session::instance(), &Session::torrentAdded, this, &TransferListModel::addTorrent);
connect(Session::instance(), &Session::torrentAboutToBeRemoved, this, &TransferListModel::handleTorrentAboutToBeRemoved);
connect(Session::instance(), &Session::torrentsUpdated, this, &TransferListModel::handleTorrentsUpdated);
connect(Session::instance(), &Session::torrentFinished, this, &TransferListModel::handleTorrentStatusUpdated);
connect(Session::instance(), &Session::torrentMetadataLoaded, this, &TransferListModel::handleTorrentStatusUpdated);
connect(Session::instance(), &Session::torrentResumed, this, &TransferListModel::handleTorrentStatusUpdated);
connect(Session::instance(), &Session::torrentPaused, this, &TransferListModel::handleTorrentStatusUpdated);
connect(Session::instance(), &Session::torrentFinishedChecking, this, &TransferListModel::handleTorrentStatusUpdated);
2014-05-11 15:29:06 +04:00
}
int TransferListModel::rowCount(const QModelIndex &) const
{
return m_torrentList.size();
}
int TransferListModel::columnCount(const QModelIndex &) const
{
2015-06-07 15:03:30 +03:00
return NB_COLUMNS;
}
QVariant TransferListModel::headerData(int section, Qt::Orientation orientation, int role) const
2015-04-19 18:17:47 +03:00
{
2015-06-07 15:03:30 +03:00
if (orientation == Qt::Horizontal) {
if (role == Qt::DisplayRole) {
switch (section) {
case TR_QUEUE_POSITION: return QChar('#');
2015-06-07 15:03:30 +03:00
case TR_NAME: return tr("Name", "i.e: torrent name");
case TR_SIZE: return tr("Size", "i.e: torrent size");
case TR_PROGRESS: return tr("Done", "% Done");
case TR_STATUS: return tr("Status", "Torrent status (e.g. downloading, seeding, paused)");
case TR_SEEDS: return tr("Seeds", "i.e. full sources (often untranslated)");
case TR_PEERS: return tr("Peers", "i.e. partial sources (often untranslated)");
case TR_DLSPEED: return tr("Down Speed", "i.e: Download speed");
case TR_UPSPEED: return tr("Up Speed", "i.e: Upload speed");
case TR_RATIO: return tr("Ratio", "Share ratio");
case TR_ETA: return tr("ETA", "i.e: Estimated Time of Arrival / Time left");
case TR_CATEGORY: return tr("Category");
case TR_TAGS: return tr("Tags");
2015-06-07 15:03:30 +03:00
case TR_ADD_DATE: return tr("Added On", "Torrent was added to transfer list on 01/01/2010 08:00");
case TR_SEED_DATE: return tr("Completed On", "Torrent was completed on 01/01/2010 08:00");
case TR_TRACKER: return tr("Tracker");
case TR_DLLIMIT: return tr("Down Limit", "i.e: Download limit");
case TR_UPLIMIT: return tr("Up Limit", "i.e: Upload limit");
case TR_AMOUNT_DOWNLOADED: return tr("Downloaded", "Amount of data downloaded (e.g. in MB)");
case TR_AMOUNT_UPLOADED: return tr("Uploaded", "Amount of data uploaded (e.g. in MB)");
case TR_AMOUNT_DOWNLOADED_SESSION: return tr("Session Download", "Amount of data downloaded since program open (e.g. in MB)");
case TR_AMOUNT_UPLOADED_SESSION: return tr("Session Upload", "Amount of data uploaded since program open (e.g. in MB)");
case TR_AMOUNT_LEFT: return tr("Remaining", "Amount of data left to download (e.g. in MB)");
case TR_TIME_ELAPSED: return tr("Time Active", "Time (duration) the torrent is active (not paused)");
case TR_SAVE_PATH: return tr("Save path", "Torrent save path");
case TR_COMPLETED: return tr("Completed", "Amount of data completed (e.g. in MB)");
case TR_RATIO_LIMIT: return tr("Ratio Limit", "Upload share ratio limit");
case TR_SEEN_COMPLETE_DATE: return tr("Last Seen Complete", "Indicates the time when the torrent was last seen complete/whole");
case TR_LAST_ACTIVITY: return tr("Last Activity", "Time passed since a chunk was downloaded/uploaded");
case TR_TOTAL_SIZE: return tr("Total Size", "i.e. Size including unwanted data");
2019-07-21 10:20:54 +03:00
case TR_AVAILABILITY: return tr("Availability", "The number of distributed copies of the torrent");
default: return {};
2015-06-07 15:03:30 +03:00
}
}
else if (role == Qt::TextAlignmentRole) {
switch (section) {
2015-06-07 15:03:30 +03:00
case TR_AMOUNT_DOWNLOADED:
case TR_AMOUNT_UPLOADED:
case TR_AMOUNT_DOWNLOADED_SESSION:
case TR_AMOUNT_UPLOADED_SESSION:
case TR_AMOUNT_LEFT:
case TR_COMPLETED:
case TR_SIZE:
case TR_TOTAL_SIZE:
case TR_ETA:
case TR_SEEDS:
case TR_PEERS:
case TR_UPSPEED:
case TR_DLSPEED:
case TR_UPLIMIT:
case TR_DLLIMIT:
case TR_RATIO_LIMIT:
case TR_RATIO:
case TR_QUEUE_POSITION:
2015-06-07 15:03:30 +03:00
case TR_LAST_ACTIVITY:
2019-07-21 10:20:54 +03:00
case TR_AVAILABILITY:
2019-02-22 04:59:31 +03:00
return QVariant(Qt::AlignRight | Qt::AlignVCenter);
2015-06-07 15:03:30 +03:00
default:
return QAbstractListModel::headerData(section, orientation, role);
}
}
}
2019-02-14 20:16:42 +03:00
return {};
}
2019-08-07 09:04:03 +03:00
QVariant TransferListModel::data(const QModelIndex &index, const int role) const
{
2019-02-14 20:16:42 +03:00
if (!index.isValid()) return {};
2015-04-19 18:17:47 +03:00
const BitTorrent::TorrentHandle *torrent = m_torrentList.value(index.row());
2019-02-14 20:16:42 +03:00
if (!torrent) return {};
2015-06-07 15:03:30 +03:00
if ((role == Qt::DecorationRole) && (index.column() == TR_NAME))
return getIconByState(torrent->state());
2015-04-19 18:17:47 +03:00
if (role == Qt::ForegroundRole)
2015-06-07 15:03:30 +03:00
return getColorByState(torrent->state());
2015-04-19 18:17:47 +03:00
if ((role != Qt::DisplayRole) && (role != Qt::UserRole))
2019-02-14 20:16:42 +03:00
return {};
2015-04-19 18:17:47 +03:00
switch (index.column()) {
case TR_NAME:
2015-06-07 15:03:30 +03:00
return torrent->name();
case TR_QUEUE_POSITION:
2015-06-07 15:03:30 +03:00
return torrent->queuePosition();
case TR_SIZE:
2015-06-07 15:03:30 +03:00
return torrent->wantedSize();
case TR_PROGRESS:
2015-06-07 15:03:30 +03:00
return torrent->progress();
case TR_STATUS:
return QVariant::fromValue(torrent->state());
2015-04-19 18:17:47 +03:00
case TR_SEEDS:
2016-02-01 15:07:18 +03:00
return (role == Qt::DisplayRole) ? torrent->seedsCount() : torrent->totalSeedsCount();
2015-04-19 18:17:47 +03:00
case TR_PEERS:
2016-02-01 15:07:18 +03:00
return (role == Qt::DisplayRole) ? torrent->leechsCount() : torrent->totalLeechersCount();
case TR_DLSPEED:
2015-06-07 15:03:30 +03:00
return torrent->downloadPayloadRate();
case TR_UPSPEED:
2015-06-07 15:03:30 +03:00
return torrent->uploadPayloadRate();
2015-04-19 18:17:47 +03:00
case TR_ETA:
2015-06-07 15:03:30 +03:00
return torrent->eta();
case TR_RATIO:
2015-06-07 15:03:30 +03:00
return torrent->realRatio();
case TR_CATEGORY:
return torrent->category();
case TR_TAGS: {
QStringList tagsList = torrent->tags().toList();
tagsList.sort();
return tagsList.join(", ");
}
case TR_ADD_DATE:
2015-06-07 15:03:30 +03:00
return torrent->addedTime();
case TR_SEED_DATE:
2015-06-07 15:03:30 +03:00
return torrent->completedTime();
case TR_TRACKER:
2015-06-07 15:03:30 +03:00
return torrent->currentTracker();
case TR_DLLIMIT:
2015-06-07 15:03:30 +03:00
return torrent->downloadLimit();
case TR_UPLIMIT:
2015-06-07 15:03:30 +03:00
return torrent->uploadLimit();
case TR_AMOUNT_DOWNLOADED:
2015-06-07 15:03:30 +03:00
return torrent->totalDownload();
case TR_AMOUNT_UPLOADED:
2015-06-07 15:03:30 +03:00
return torrent->totalUpload();
case TR_AMOUNT_DOWNLOADED_SESSION:
2015-06-07 15:03:30 +03:00
return torrent->totalPayloadDownload();
case TR_AMOUNT_UPLOADED_SESSION:
2015-06-07 15:03:30 +03:00
return torrent->totalPayloadUpload();
case TR_AMOUNT_LEFT:
2015-06-07 15:03:30 +03:00
return torrent->incompletedSize();
case TR_TIME_ELAPSED:
2015-06-07 15:03:30 +03:00
return (role == Qt::DisplayRole) ? torrent->activeTime() : torrent->seedingTime();
case TR_SAVE_PATH:
return Utils::Fs::toNativePath(torrent->savePath());
case TR_COMPLETED:
2015-06-07 15:03:30 +03:00
return torrent->completedSize();
2015-04-19 18:17:47 +03:00
case TR_RATIO_LIMIT:
2015-06-07 15:03:30 +03:00
return torrent->maxRatio();
case TR_SEEN_COMPLETE_DATE:
2015-06-07 15:03:30 +03:00
return torrent->lastSeenComplete();
case TR_LAST_ACTIVITY:
2015-06-07 15:03:30 +03:00
if (torrent->isPaused() || torrent->isChecking())
return -1;
2015-06-07 15:03:30 +03:00
return torrent->timeSinceActivity();
2019-07-21 10:20:54 +03:00
case TR_AVAILABILITY:
return torrent->distributedCopies();
case TR_TOTAL_SIZE:
return torrent->totalSize();
}
2019-02-14 20:16:42 +03:00
return {};
}
bool TransferListModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
2015-06-07 15:03:30 +03:00
if (!index.isValid() || (role != Qt::DisplayRole)) return false;
BitTorrent::TorrentHandle *const torrent = m_torrentList.value(index.row());
2015-06-07 15:03:30 +03:00
if (!torrent) return false;
2015-04-19 18:17:47 +03:00
2019-08-07 09:04:03 +03:00
// Category and Name columns can be edited
switch (index.column()) {
2015-06-07 15:03:30 +03:00
case TR_NAME:
torrent->setName(value.toString());
break;
case TR_CATEGORY:
torrent->setCategory(value.toString());
2015-06-07 15:03:30 +03:00
break;
default:
return false;
}
2015-04-19 18:17:47 +03:00
2015-06-07 15:03:30 +03:00
return true;
}
void TransferListModel::addTorrent(BitTorrent::TorrentHandle *const torrent)
2015-06-07 15:03:30 +03:00
{
if (m_torrentMap.contains(torrent))
return;
const int row = m_torrentList.size();
beginInsertRows({}, row, row);
m_torrentList << torrent;
m_torrentMap[torrent] = row;
endInsertRows();
}
Qt::ItemFlags TransferListModel::flags(const QModelIndex &index) const
{
2018-09-07 14:12:38 +03:00
if (!index.isValid()) return Qt::NoItemFlags;
2015-06-07 15:03:30 +03:00
// Explicitly mark as editable
return QAbstractListModel::flags(index) | Qt::ItemIsEditable;
}
BitTorrent::TorrentHandle *TransferListModel::torrentHandle(const QModelIndex &index) const
{
2018-09-07 14:12:38 +03:00
if (!index.isValid()) return nullptr;
2015-04-19 18:17:47 +03:00
return m_torrentList.value(index.row());
}
void TransferListModel::handleTorrentAboutToBeRemoved(BitTorrent::TorrentHandle *const torrent)
{
const int row = m_torrentMap.value(torrent, -1);
if (row < 0)
return;
beginRemoveRows({}, row, row);
m_torrentList.removeAt(row);
m_torrentMap.remove(torrent);
for (int &value : m_torrentMap) {
if (value > row)
--value;
2015-04-19 18:17:47 +03:00
}
endRemoveRows();
}
void TransferListModel::handleTorrentStatusUpdated(BitTorrent::TorrentHandle *const torrent)
{
const int row = m_torrentMap.value(torrent, -1);
if (row < 0)
return;
emit dataChanged(index(row, 0), index(row, columnCount() - 1));
}
void TransferListModel::handleTorrentsUpdated(const QVector<BitTorrent::TorrentHandle *> &torrents)
{
const int columns = (columnCount() - 1);
for (BitTorrent::TorrentHandle *const torrent : torrents) {
const int row = m_torrentMap.value(torrent, -1);
if (row < 0)
continue;
emit dataChanged(index(row, 0), index(row, columns));
}
}
2015-06-07 15:03:30 +03:00
// Static functions
2019-08-07 09:04:03 +03:00
QIcon getIconByState(const BitTorrent::TorrentState state)
{
2015-06-07 15:03:30 +03:00
switch (state) {
case BitTorrent::TorrentState::Downloading:
case BitTorrent::TorrentState::ForcedDownloading:
case BitTorrent::TorrentState::DownloadingMetadata:
return getDownloadingIcon();
case BitTorrent::TorrentState::Allocating:
case BitTorrent::TorrentState::StalledDownloading:
return getStalledDownloadingIcon();
case BitTorrent::TorrentState::StalledUploading:
return getStalledUploadingIcon();
case BitTorrent::TorrentState::Uploading:
case BitTorrent::TorrentState::ForcedUploading:
return getUploadingIcon();
case BitTorrent::TorrentState::PausedDownloading:
return getPausedIcon();
case BitTorrent::TorrentState::PausedUploading:
return getCompletedIcon();
case BitTorrent::TorrentState::QueuedDownloading:
case BitTorrent::TorrentState::QueuedUploading:
return getQueuedIcon();
case BitTorrent::TorrentState::CheckingDownloading:
case BitTorrent::TorrentState::CheckingUploading:
2015-06-30 11:03:46 +03:00
case BitTorrent::TorrentState::CheckingResumeData:
case BitTorrent::TorrentState::Moving:
2015-06-07 15:03:30 +03:00
return getCheckingIcon();
case BitTorrent::TorrentState::Unknown:
case BitTorrent::TorrentState::MissingFiles:
2015-06-07 15:03:30 +03:00
case BitTorrent::TorrentState::Error:
return getErrorIcon();
default:
Q_ASSERT(false);
return getErrorIcon();
}
}
2019-08-07 09:04:03 +03:00
QColor getColorByState(const BitTorrent::TorrentState state)
{
// Color names taken from http://cloford.com/resources/colours/500col.htm
2015-06-07 15:03:30 +03:00
bool dark = isDarkTheme();
switch (state) {
case BitTorrent::TorrentState::Downloading:
case BitTorrent::TorrentState::ForcedDownloading:
case BitTorrent::TorrentState::DownloadingMetadata:
if (!dark)
2019-02-14 20:16:42 +03:00
return {34, 139, 34}; // Forest Green
else
2019-02-14 20:16:42 +03:00
return {50, 205, 50}; // Lime Green
2015-06-07 15:03:30 +03:00
case BitTorrent::TorrentState::Allocating:
case BitTorrent::TorrentState::StalledDownloading:
case BitTorrent::TorrentState::StalledUploading:
if (!dark)
2019-02-14 20:16:42 +03:00
return {0, 0, 0}; // Black
2015-06-07 15:03:30 +03:00
else
2019-02-14 20:16:42 +03:00
return {204, 204, 204}; // Gray 80
2015-06-07 15:03:30 +03:00
case BitTorrent::TorrentState::Uploading:
case BitTorrent::TorrentState::ForcedUploading:
if (!dark)
2019-02-14 20:16:42 +03:00
return {65, 105, 225}; // Royal Blue
2015-06-07 15:03:30 +03:00
else
2019-02-14 20:16:42 +03:00
return {99, 184, 255}; // Steel Blue 1
2015-06-07 15:03:30 +03:00
case BitTorrent::TorrentState::PausedDownloading:
2019-02-14 20:16:42 +03:00
return {250, 128, 114}; // Salmon
2015-06-07 15:03:30 +03:00
case BitTorrent::TorrentState::PausedUploading:
if (!dark)
2019-02-14 20:16:42 +03:00
return {0, 0, 139}; // Dark Blue
2015-06-07 15:03:30 +03:00
else
2019-02-14 20:16:42 +03:00
return {79, 148, 205}; // Steel Blue 3
2015-06-07 15:03:30 +03:00
case BitTorrent::TorrentState::Error:
case BitTorrent::TorrentState::MissingFiles:
2019-02-14 20:16:42 +03:00
return {255, 0, 0}; // red
2015-06-07 15:03:30 +03:00
case BitTorrent::TorrentState::QueuedDownloading:
case BitTorrent::TorrentState::QueuedUploading:
case BitTorrent::TorrentState::CheckingDownloading:
case BitTorrent::TorrentState::CheckingUploading:
2015-06-30 11:03:46 +03:00
case BitTorrent::TorrentState::CheckingResumeData:
case BitTorrent::TorrentState::Moving:
if (!dark)
2019-02-14 20:16:42 +03:00
return {0, 128, 128}; // Teal
else
2019-02-14 20:16:42 +03:00
return {0, 205, 205}; // Cyan 3
2015-06-07 15:03:30 +03:00
case BitTorrent::TorrentState::Unknown:
2019-02-14 20:16:42 +03:00
return {255, 0, 0}; // red
2015-06-07 15:03:30 +03:00
default:
Q_ASSERT(false);
2019-02-14 20:16:42 +03:00
return {255, 0, 0}; // red
2015-06-07 15:03:30 +03:00
}
}
2015-06-07 15:03:30 +03:00
QIcon getPausedIcon()
{
2018-07-23 07:28:14 +03:00
static QIcon cached = QIcon(":/icons/skin/paused.svg");
2015-06-07 15:03:30 +03:00
return cached;
}
2015-06-07 15:03:30 +03:00
QIcon getQueuedIcon()
{
2018-07-23 07:28:14 +03:00
static QIcon cached = QIcon(":/icons/skin/queued.svg");
2015-06-07 15:03:30 +03:00
return cached;
}
2015-06-07 15:03:30 +03:00
QIcon getDownloadingIcon()
{
2018-07-23 07:28:14 +03:00
static QIcon cached = QIcon(":/icons/skin/downloading.svg");
2015-06-07 15:03:30 +03:00
return cached;
}
2015-06-07 15:03:30 +03:00
QIcon getStalledDownloadingIcon()
{
2018-07-23 07:28:14 +03:00
static QIcon cached = QIcon(":/icons/skin/stalledDL.svg");
2015-06-07 15:03:30 +03:00
return cached;
}
2015-06-07 15:03:30 +03:00
QIcon getUploadingIcon()
{
2018-07-23 07:28:14 +03:00
static QIcon cached = QIcon(":/icons/skin/uploading.svg");
2015-06-07 15:03:30 +03:00
return cached;
}
2015-06-07 15:03:30 +03:00
QIcon getStalledUploadingIcon()
{
2018-07-23 07:28:14 +03:00
static QIcon cached = QIcon(":/icons/skin/stalledUP.svg");
2015-06-07 15:03:30 +03:00
return cached;
}
2015-06-07 15:03:30 +03:00
QIcon getCompletedIcon()
{
2018-07-23 07:28:14 +03:00
static QIcon cached = QIcon(":/icons/skin/completed.svg");
2015-06-07 15:03:30 +03:00
return cached;
}
2015-06-07 15:03:30 +03:00
QIcon getCheckingIcon()
2015-04-19 18:17:47 +03:00
{
2018-07-23 07:28:14 +03:00
static QIcon cached = QIcon(":/icons/skin/checking.svg");
2015-06-07 15:03:30 +03:00
return cached;
2015-04-19 18:17:47 +03:00
}
2015-06-07 15:03:30 +03:00
QIcon getErrorIcon()
{
2018-07-23 07:28:14 +03:00
static QIcon cached = QIcon(":/icons/skin/error.svg");
2015-06-07 15:03:30 +03:00
return cached;
}
2015-06-07 15:03:30 +03:00
bool isDarkTheme()
{
const QPalette pal = QApplication::palette();
2015-06-07 15:03:30 +03:00
// QPalette::Base is used for the background of the Treeview
const QColor &color = pal.color(QPalette::Active, QPalette::Base);
2015-06-07 15:03:30 +03:00
return (color.lightness() < 127);
}