bool TransferListSortModel::lessThan(const QModelIndex &left, const QModelIndex &right) const { const int column = sortColumn(); if (column == TorrentModel::TR_NAME) { QVariant vL = left.data(); QVariant vR = right.data(); if (!vL.isValid() || !vR.isValid() || (vL == vR)) return lowerPositionThan(left, right); bool res = false; if (Utils::String::naturalSort(vL.toString(), vR.toString(), res)) return res; return QSortFilterProxyModel::lessThan(left, right); } else if (column == TorrentModel::TR_ADD_DATE || column == TorrentModel::TR_SEED_DATE || column == TorrentModel::TR_SEEN_COMPLETE_DATE) { QDateTime vL = left.data().toDateTime(); QDateTime vR = right.data().toDateTime(); //not valid dates should be sorted at the bottom. if (!vL.isValid()) return false; if (!vR.isValid()) return true; return vL < vR; } else if (column == TorrentModel::TR_PRIORITY) { return lowerPositionThan(left, right); } else if (column == TorrentModel::TR_PEERS || column == TorrentModel::TR_SEEDS) { int left_active = left.data().toInt(); int left_total = left.data(Qt::UserRole).toInt(); int right_active = right.data().toInt(); int right_total = right.data(Qt::UserRole).toInt(); // Active peers/seeds take precedence over total peers/seeds. if (left_active == right_active) { if (left_total == right_total) return lowerPositionThan(left, right); return (left_total < right_total); } else { return (left_active < right_active); } } else if (column == TorrentModel::TR_ETA) { TorrentModel *model = qobject_cast<TorrentModel *>(sourceModel()); const int prioL = model->data(model->index(left.row(), TorrentModel::TR_PRIORITY)).toInt(); const int prioR = model->data(model->index(right.row(), TorrentModel::TR_PRIORITY)).toInt(); const qlonglong etaL = left.data().toLongLong(); const qlonglong etaR = right.data().toLongLong(); const bool ascend = (sortOrder() == Qt::AscendingOrder); const bool invalidL = (etaL < 0 || etaL >= MAX_ETA); const bool invalidR = (etaR < 0 || etaR >= MAX_ETA); const bool seedingL = (prioL < 0); const bool seedingR = (prioR < 0); bool activeR = TorrentFilter::ActiveTorrent.match(model->torrentHandle(model->index(right.row()))); bool activeL = TorrentFilter::ActiveTorrent.match(model->torrentHandle(model->index(right.row()))); // Sorting rules prioritized. // 1. Active torrents at the top // 2. Seeding torrents at the bottom // 3. Torrents with invalid ETAs at the bottom if (activeL != activeR) return activeL; if (seedingL != seedingR) { if (seedingL) return !ascend; else return ascend; } if (invalidL && invalidR) { if (seedingL) { //Both seeding QDateTime dateL = model->data(model->index(left.row(), TorrentModel::TR_SEED_DATE)).toDateTime(); QDateTime dateR = model->data(model->index(right.row(), TorrentModel::TR_SEED_DATE)).toDateTime(); //not valid dates should be sorted at the bottom. if (!dateL.isValid()) return false; if (!dateR.isValid()) return true; return dateL < dateR; } else { return prioL < prioR; } } else if (!invalidL && !invalidR) { return etaL < etaR; } else { return !invalidL; } } else if (column == TorrentModel::TR_LAST_ACTIVITY) { const qlonglong vL = left.data().toLongLong(); const qlonglong vR = right.data().toLongLong(); if (vL == -1) return false; if (vR == -1) return true; return vL < vR; } else if (column == TorrentModel::TR_RATIO_LIMIT) { const qreal vL = left.data().toDouble(); const qreal vR = right.data().toDouble(); if (vL == -1) return false; if (vR == -1) return true; return vL < vR; } if (left.data() == right.data()) return lowerPositionThan(left, right); return QSortFilterProxyModel::lessThan(left, right); }
bool TransferListSortModel::lessThan(const QModelIndex &left, const QModelIndex &right) const { 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); 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_ADD_DATE: case TransferListModel::TR_SEED_DATE: case TransferListModel::TR_SEEN_COMPLETE_DATE: { return dateLessThan(sortColumn(), left, right, true); } case TransferListModel::TR_PRIORITY: { return lowerPositionThan(left, right); } 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(); // Active peers/seeds take precedence over total peers/seeds. if (leftActive != rightActive) return (leftActive < rightActive); if (leftTotal != rightTotal) return (leftTotal < rightTotal); return lowerPositionThan(left, right); } case TransferListModel::TR_ETA: { const TransferListModel *model = qobject_cast<TransferListModel *>(sourceModel()); // Sorting rules prioritized. // 1. Active torrents at the top // 2. Seeding torrents at the bottom // 3. Torrents with invalid ETAs at the bottom const bool isActiveL = TorrentFilter::ActiveTorrent.match(model->torrentHandle(model->index(left.row()))); const bool isActiveR = TorrentFilter::ActiveTorrent.match(model->torrentHandle(model->index(right.row()))); if (isActiveL != isActiveR) return isActiveL; const int prioL = model->data(model->index(left.row(), TransferListModel::TR_PRIORITY)).toInt(); const int prioR = model->data(model->index(right.row(), TransferListModel::TR_PRIORITY)).toInt(); const bool isSeedingL = (prioL < 0); const bool isSeedingR = (prioR < 0); if (isSeedingL != isSeedingR) { const bool isAscendingOrder = (sortOrder() == Qt::AscendingOrder); if (isSeedingL) return !isAscendingOrder; return isAscendingOrder; } const qlonglong etaL = left.data().toLongLong(); const qlonglong etaR = right.data().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 (prioL < prioR); } 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(); 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); } default: { if (left.data() != right.data()) return QSortFilterProxyModel::lessThan(left, right); return lowerPositionThan(left, right); } } }