diff --git a/src/gui/transferlistdelegate.cpp b/src/gui/transferlistdelegate.cpp index 59c9015cd..7cfbef768 100644 --- a/src/gui/transferlistdelegate.cpp +++ b/src/gui/transferlistdelegate.cpp @@ -192,6 +192,18 @@ void TransferListDelegate::paint(QPainter *painter, const QStyleOptionViewItem & QItemDelegate::drawDisplay(painter, opt, option.rect, elapsedString); } break; + + case TransferListModel::TR_AVAILABILITY: { + const qreal availability = index.data().toReal(); + if (hideValues && (availability <= 0)) + break; + + const QString availabilityStr = Utils::String::fromDouble(availability, 3); + opt.displayAlignment = (Qt::AlignRight | Qt::AlignVCenter); + QItemDelegate::drawDisplay(painter, opt, option.rect, availabilityStr); + } + break; + default: QItemDelegate::paint(painter, option, index); } diff --git a/src/gui/transferlistmodel.cpp b/src/gui/transferlistmodel.cpp index 8b571c895..7c79e8528 100644 --- a/src/gui/transferlistmodel.cpp +++ b/src/gui/transferlistmodel.cpp @@ -124,8 +124,8 @@ QVariant TransferListModel::headerData(int section, Qt::Orientation orientation, 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"); - default: - return {}; + case TR_AVAILABILITY: return tr("Availability", "The number of distributed copies of the torrent"); + default: return {}; } } else if (role == Qt::TextAlignmentRole) { @@ -149,6 +149,7 @@ QVariant TransferListModel::headerData(int section, Qt::Orientation orientation, case TR_RATIO: case TR_QUEUE_POSITION: case TR_LAST_ACTIVITY: + case TR_AVAILABILITY: return QVariant(Qt::AlignRight | Qt::AlignVCenter); default: return QAbstractListModel::headerData(section, orientation, role); @@ -239,6 +240,8 @@ QVariant TransferListModel::data(const QModelIndex &index, int role) const if (torrent->isPaused() || torrent->isChecking()) return -1; return torrent->timeSinceActivity(); + case TR_AVAILABILITY: + return torrent->distributedCopies(); case TR_TOTAL_SIZE: return torrent->totalSize(); } diff --git a/src/gui/transferlistmodel.h b/src/gui/transferlistmodel.h index 6fee78a69..38ee85427 100644 --- a/src/gui/transferlistmodel.h +++ b/src/gui/transferlistmodel.h @@ -77,6 +77,7 @@ public: TR_RATIO_LIMIT, TR_SEEN_COMPLETE_DATE, TR_LAST_ACTIVITY, + TR_AVAILABILITY, NB_COLUMNS }; diff --git a/src/webui/api/serialize/serialize_torrent.cpp b/src/webui/api/serialize/serialize_torrent.cpp index ce0f7127b..655902607 100644 --- a/src/webui/api/serialize/serialize_torrent.cpp +++ b/src/webui/api/serialize/serialize_torrent.cpp @@ -125,6 +125,7 @@ QVariantMap serialize(const BitTorrent::TorrentHandle &torrent) ret[KEY_TORRENT_LAST_SEEN_COMPLETE_TIME] = torrent.lastSeenComplete().toTime_t(); ret[KEY_TORRENT_AUTO_TORRENT_MANAGEMENT] = torrent.isAutoTMMEnabled(); ret[KEY_TORRENT_TIME_ACTIVE] = torrent.activeTime(); + ret[KEY_TORRENT_AVAILABILITY] = torrent.distributedCopies(); if (torrent.isPaused() || torrent.isChecking()) { ret[KEY_TORRENT_LAST_ACTIVITY_TIME] = 0; diff --git a/src/webui/api/serialize/serialize_torrent.h b/src/webui/api/serialize/serialize_torrent.h index cb446ec76..b02c6574c 100644 --- a/src/webui/api/serialize/serialize_torrent.h +++ b/src/webui/api/serialize/serialize_torrent.h @@ -78,5 +78,6 @@ const char KEY_TORRENT_LAST_ACTIVITY_TIME[] = "last_activity"; const char KEY_TORRENT_TOTAL_SIZE[] = "total_size"; const char KEY_TORRENT_AUTO_TORRENT_MANAGEMENT[] = "auto_tmm"; const char KEY_TORRENT_TIME_ACTIVE[] = "time_active"; +const char KEY_TORRENT_AVAILABILITY[] = "availability"; QVariantMap serialize(const BitTorrent::TorrentHandle &torrent); diff --git a/src/webui/www/private/scripts/dynamicTable.js b/src/webui/www/private/scripts/dynamicTable.js index a3fc00421..e4faa17c2 100644 --- a/src/webui/www/private/scripts/dynamicTable.js +++ b/src/webui/www/private/scripts/dynamicTable.js @@ -828,12 +828,12 @@ const TorrentsTable = new Class({ this.newColumn('max_ratio', '', 'QBT_TR(Ratio Limit)QBT_TR[CONTEXT=TransferListModel]', 100, false); this.newColumn('seen_complete', '', 'QBT_TR(Last Seen Complete)QBT_TR[CONTEXT=TransferListModel]', 100, false); this.newColumn('last_activity', '', 'QBT_TR(Last Activity)QBT_TR[CONTEXT=TransferListModel]', 100, false); + this.newColumn('availability', '', 'QBT_TR(Availability)QBT_TR[CONTEXT=TransferListModel]', 100, false); this.columns['state_icon'].onclick = ''; this.columns['state_icon'].dataProperties[0] = 'state'; this.columns['num_seeds'].dataProperties.push('num_complete'); - this.columns['num_leechs'].dataProperties.push('num_incomplete'); this.initColumnsFunctions(); @@ -1092,13 +1092,9 @@ const TorrentsTable = new Class({ // ratio this.columns['ratio'].updateTd = function(td, row) { const ratio = this.getRowValue(row); - let html = null; - if (ratio == -1) - html = '∞'; - else - html = (Math.floor(100 * ratio) / 100).toFixed(2); //Don't round up - td.set('html', html); - td.set('title', html); + const string = (ratio === -1) ? '∞' : toFixedPointString(ratio, 2); + td.set('html', string); + td.set('title', string); }; // tags @@ -1181,6 +1177,13 @@ const TorrentsTable = new Class({ td.set('html', time); td.set('title', time); }; + + // availability + this.columns['availability'].updateTd = function(td, row) { + const value = toFixedPointString(this.getRowValue(row), 3); + td.set('html', value); + td.set('title', value); + }; }, applyFilter: function(row, filterName, categoryHash, tagHash, filterTerms) { diff --git a/src/webui/www/private/scripts/misc.js b/src/webui/www/private/scripts/misc.js index 537b7c908..589bb8548 100644 --- a/src/webui/www/private/scripts/misc.js +++ b/src/webui/www/private/scripts/misc.js @@ -161,3 +161,9 @@ function safeTrim(value) { throw e; } } + +function toFixedPointString(number, digits) { + // Do not round up number + const power = Math.pow(10, digits); + return (Math.floor(power * number) / power).toFixed(digits); +}