mirror of
https://github.com/qbittorrent/qBittorrent.git
synced 2025-12-19 06:57:24 -06:00
Fix transfer list architecture
Model returns string for DisplayRole. Text alignment is set by Model (using TextAlignmentRole). Delegate performs custom painting only where necessary (i.e. for Progress bar).
This commit is contained in:
@@ -37,8 +37,9 @@
|
||||
#include "transferlistmodel.h"
|
||||
|
||||
TransferListSortModel::TransferListSortModel(QObject *parent)
|
||||
: QSortFilterProxyModel(parent)
|
||||
: QSortFilterProxyModel {parent}
|
||||
{
|
||||
QMetaType::registerComparators<BitTorrent::TorrentState>();
|
||||
}
|
||||
|
||||
void TransferListSortModel::setStatusFilter(TorrentFilter::Type filter)
|
||||
@@ -85,56 +86,100 @@ void TransferListSortModel::disableTrackerFilter()
|
||||
|
||||
bool TransferListSortModel::lessThan(const QModelIndex &left, const QModelIndex &right) const
|
||||
{
|
||||
switch (sortColumn()) {
|
||||
return lessThan_impl(left, right);
|
||||
}
|
||||
|
||||
bool TransferListSortModel::lessThan_impl(const QModelIndex &left, const QModelIndex &right) const
|
||||
{
|
||||
Q_ASSERT(left.column() == right.column());
|
||||
|
||||
const auto invokeLessThanForColumn = [this, &left, &right](const int column) -> bool
|
||||
{
|
||||
return lessThan_impl(left.sibling(left.row(), column), right.sibling(left.row(), column));
|
||||
};
|
||||
|
||||
const int sortColumn = left.column();
|
||||
const QVariant leftValue = left.data(TransferListModel::UnderlyingDataRole);
|
||||
const QVariant rightValue = right.data(TransferListModel::UnderlyingDataRole);
|
||||
|
||||
switch (sortColumn) {
|
||||
case TransferListModel::TR_CATEGORY:
|
||||
case TransferListModel::TR_TAGS:
|
||||
case TransferListModel::TR_NAME: {
|
||||
const QVariant vL = left.data();
|
||||
const QVariant vR = right.data();
|
||||
if (!vL.isValid() || !vR.isValid() || (vL == vR))
|
||||
return lowerPositionThan(left, right);
|
||||
case TransferListModel::TR_NAME:
|
||||
if (!leftValue.isValid() || !rightValue.isValid() || (leftValue == rightValue))
|
||||
return invokeLessThanForColumn(TransferListModel::TR_QUEUE_POSITION);
|
||||
return (Utils::String::naturalCompare(leftValue.toString(), rightValue.toString(), Qt::CaseInsensitive) < 0);
|
||||
|
||||
const int result = Utils::String::naturalCompare(vL.toString(), vR.toString(), Qt::CaseInsensitive);
|
||||
return (result < 0);
|
||||
}
|
||||
|
||||
case TransferListModel::TR_STATUS: {
|
||||
// QSortFilterProxyModel::lessThan() uses the < operator only for specific QVariant types
|
||||
// so our custom type is outside that list.
|
||||
// In this case QSortFilterProxyModel::lessThan() converts other types to QString and
|
||||
// sorts them.
|
||||
// Thus we can't use the code in the default label.
|
||||
const auto leftValue = left.data().value<BitTorrent::TorrentState>();
|
||||
const auto rightValue = right.data().value<BitTorrent::TorrentState>();
|
||||
if (leftValue != rightValue)
|
||||
return leftValue < rightValue;
|
||||
|
||||
return lowerPositionThan(left, right);
|
||||
}
|
||||
case TransferListModel::TR_STATUS:
|
||||
// QSortFilterProxyModel::lessThan() uses the < operator only for specific QVariant types
|
||||
// so our custom type is outside that list.
|
||||
// In this case QSortFilterProxyModel::lessThan() converts other types to QString and
|
||||
// sorts them.
|
||||
// Thus we can't use the code in the default label.
|
||||
if (leftValue != rightValue)
|
||||
return leftValue < rightValue;
|
||||
return invokeLessThanForColumn(TransferListModel::TR_QUEUE_POSITION);
|
||||
|
||||
case TransferListModel::TR_ADD_DATE:
|
||||
case TransferListModel::TR_SEED_DATE:
|
||||
case TransferListModel::TR_SEEN_COMPLETE_DATE:
|
||||
return dateLessThan(sortColumn(), left, right, true);
|
||||
case TransferListModel::TR_SEEN_COMPLETE_DATE: {
|
||||
const QDateTime dateL = leftValue.toDateTime();
|
||||
const QDateTime dateR = rightValue.toDateTime();
|
||||
|
||||
case TransferListModel::TR_QUEUE_POSITION:
|
||||
return lowerPositionThan(left, right);
|
||||
if (dateL.isValid() && dateR.isValid()) {
|
||||
if (dateL != dateR)
|
||||
return dateL < dateR;
|
||||
}
|
||||
else if (dateL.isValid()) {
|
||||
return true;
|
||||
}
|
||||
else if (dateR.isValid()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case TransferListModel::TR_QUEUE_POSITION: {
|
||||
// QVariant has comparators for all basic types
|
||||
if ((leftValue > 0) || (rightValue > 0)) {
|
||||
if ((leftValue > 0) && (rightValue > 0))
|
||||
return leftValue < rightValue;
|
||||
|
||||
return leftValue != 0;
|
||||
}
|
||||
|
||||
// Sort according to TR_SEED_DATE
|
||||
const QDateTime dateL = left.sibling(left.row(), TransferListModel::TR_SEED_DATE)
|
||||
.data(TransferListModel::UnderlyingDataRole).toDateTime();
|
||||
const QDateTime dateR = right.sibling(right.row(), TransferListModel::TR_SEED_DATE)
|
||||
.data(TransferListModel::UnderlyingDataRole).toDateTime();
|
||||
|
||||
if (dateL.isValid() && dateR.isValid()) {
|
||||
if (dateL != dateR)
|
||||
return dateL < dateR;
|
||||
}
|
||||
else if (dateL.isValid()) {
|
||||
return false;
|
||||
}
|
||||
else if (dateR.isValid()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case TransferListModel::TR_SEEDS:
|
||||
case TransferListModel::TR_PEERS: {
|
||||
const int leftActive = left.data().toInt();
|
||||
const int leftTotal = left.data(Qt::UserRole).toInt();
|
||||
const int rightActive = right.data().toInt();
|
||||
const int rightTotal = right.data(Qt::UserRole).toInt();
|
||||
|
||||
// QVariant has comparators for all basic types
|
||||
// Active peers/seeds take precedence over total peers/seeds.
|
||||
if (leftActive != rightActive)
|
||||
return (leftActive < rightActive);
|
||||
if (leftValue != rightValue)
|
||||
return (leftValue < rightValue);
|
||||
|
||||
if (leftTotal != rightTotal)
|
||||
return (leftTotal < rightTotal);
|
||||
const QVariant leftValueTotal = left.data(TransferListModel::AdditionalUnderlyingDataRole);
|
||||
const QVariant rightValueTotal = right.data(TransferListModel::AdditionalUnderlyingDataRole);
|
||||
if (leftValueTotal != rightValueTotal)
|
||||
return (leftValueTotal < rightValueTotal);
|
||||
|
||||
return lowerPositionThan(left, right);
|
||||
return invokeLessThanForColumn(TransferListModel::TR_QUEUE_POSITION);
|
||||
}
|
||||
|
||||
case TransferListModel::TR_ETA: {
|
||||
@@ -152,8 +197,10 @@ bool TransferListSortModel::lessThan(const QModelIndex &left, const QModelIndex
|
||||
if (isActiveL != isActiveR)
|
||||
return isActiveL;
|
||||
|
||||
const int queuePosL = left.sibling(left.row(), TransferListModel::TR_QUEUE_POSITION).data().toInt();
|
||||
const int queuePosR = right.sibling(right.row(), TransferListModel::TR_QUEUE_POSITION).data().toInt();
|
||||
const int queuePosL = left.sibling(left.row(), TransferListModel::TR_QUEUE_POSITION)
|
||||
.data(TransferListModel::UnderlyingDataRole).toInt();
|
||||
const int queuePosR = right.sibling(right.row(), TransferListModel::TR_QUEUE_POSITION)
|
||||
.data(TransferListModel::UnderlyingDataRole).toInt();
|
||||
const bool isSeedingL = (queuePosL < 0);
|
||||
const bool isSeedingR = (queuePosR < 0);
|
||||
if (isSeedingL != isSeedingR) {
|
||||
@@ -164,85 +211,36 @@ bool TransferListSortModel::lessThan(const QModelIndex &left, const QModelIndex
|
||||
return isAscendingOrder;
|
||||
}
|
||||
|
||||
const qlonglong etaL = left.data().toLongLong();
|
||||
const qlonglong etaR = right.data().toLongLong();
|
||||
const qlonglong etaL = leftValue.toLongLong();
|
||||
const qlonglong etaR = rightValue.toLongLong();
|
||||
const bool isInvalidL = ((etaL < 0) || (etaL >= MAX_ETA));
|
||||
const bool isInvalidR = ((etaR < 0) || (etaR >= MAX_ETA));
|
||||
if (isInvalidL && isInvalidR) {
|
||||
if (isSeedingL) // Both seeding
|
||||
return dateLessThan(TransferListModel::TR_SEED_DATE, left, right, true);
|
||||
return invokeLessThanForColumn(TransferListModel::TR_SEED_DATE);
|
||||
|
||||
return (queuePosL < queuePosR);
|
||||
}
|
||||
if (!isInvalidL && !isInvalidR) {
|
||||
|
||||
if (!isInvalidL && !isInvalidR)
|
||||
return (etaL < etaR);
|
||||
}
|
||||
|
||||
return !isInvalidL;
|
||||
}
|
||||
|
||||
case TransferListModel::TR_LAST_ACTIVITY: {
|
||||
const int vL = left.data().toInt();
|
||||
const int vR = right.data().toInt();
|
||||
case TransferListModel::TR_LAST_ACTIVITY:
|
||||
case TransferListModel::TR_RATIO_LIMIT:
|
||||
// QVariant has comparators for all basic types
|
||||
if (leftValue < 0) return false;
|
||||
if (rightValue < 0) return true;
|
||||
|
||||
if (vL < 0) return false;
|
||||
if (vR < 0) return true;
|
||||
|
||||
return (vL < vR);
|
||||
}
|
||||
|
||||
case TransferListModel::TR_RATIO_LIMIT: {
|
||||
const qreal vL = left.data().toReal();
|
||||
const qreal vR = right.data().toReal();
|
||||
|
||||
if (vL < 0) return false;
|
||||
if (vR < 0) return true;
|
||||
|
||||
return (vL < vR);
|
||||
}
|
||||
return (leftValue < rightValue);
|
||||
|
||||
default:
|
||||
if (left.data() != right.data())
|
||||
if (leftValue != rightValue)
|
||||
return QSortFilterProxyModel::lessThan(left, right);
|
||||
|
||||
return lowerPositionThan(left, right);
|
||||
}
|
||||
}
|
||||
|
||||
bool TransferListSortModel::lowerPositionThan(const QModelIndex &left, const QModelIndex &right) const
|
||||
{
|
||||
// Sort according to TR_QUEUE_POSITION
|
||||
const int queueL = left.sibling(left.row(), TransferListModel::TR_QUEUE_POSITION).data().toInt();
|
||||
const int queueR = right.sibling(right.row(), TransferListModel::TR_QUEUE_POSITION).data().toInt();
|
||||
|
||||
if ((queueL > 0) || (queueR > 0)) {
|
||||
if ((queueL > 0) && (queueR > 0))
|
||||
return queueL < queueR;
|
||||
|
||||
return queueL != 0;
|
||||
}
|
||||
|
||||
// Sort according to TR_SEED_DATE
|
||||
return dateLessThan(TransferListModel::TR_SEED_DATE, left, right, false);
|
||||
}
|
||||
|
||||
// Every time we compare QDateTimes we need a fallback comparison in case both
|
||||
// values are empty. This is a workaround for unstable sort in QSortFilterProxyModel
|
||||
// (detailed discussion in #2526 and #2158).
|
||||
bool TransferListSortModel::dateLessThan(const int dateColumn, const QModelIndex &left, const QModelIndex &right, bool sortInvalidInBottom) const
|
||||
{
|
||||
const QDateTime dateL = left.sibling(left.row(), dateColumn).data().toDateTime();
|
||||
const QDateTime dateR = right.sibling(right.row(), dateColumn).data().toDateTime();
|
||||
|
||||
if (dateL.isValid() && dateR.isValid()) {
|
||||
if (dateL != dateR)
|
||||
return dateL < dateR;
|
||||
}
|
||||
else if (dateL.isValid()) {
|
||||
return sortInvalidInBottom;
|
||||
}
|
||||
else if (dateR.isValid()) {
|
||||
return !sortInvalidInBottom;
|
||||
return invokeLessThanForColumn(TransferListModel::TR_QUEUE_POSITION);
|
||||
}
|
||||
|
||||
// Finally, sort by hash
|
||||
|
||||
Reference in New Issue
Block a user