2 * Bittorrent Client using Qt and libtorrent.
3 * Copyright (C) 2015-2024 Vladimir Golovnev <glassez@yandex.ru>
4 * Copyright (C) 2010 Christophe Dumez <chris@qbittorrent.org>
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 * In addition, as a special exception, the copyright holders give permission to
21 * link this program with the OpenSSL project's "OpenSSL" library (or with
22 * modified versions of it that use the same license as the "OpenSSL" library),
23 * and distribute the linked executables. You must obey the GNU General Public
24 * License in all respects for all of the code used other than "OpenSSL". If you
25 * modify file(s), you may extend this exception to your version of the file(s),
26 * but you are not obligated to do so. If you do not wish to do so, delete this
27 * exception statement from your version.
30 #include "transferlistmodel.h"
32 #include <QApplication>
36 #include "base/bittorrent/infohash.h"
37 #include "base/bittorrent/session.h"
38 #include "base/bittorrent/torrent.h"
39 #include "base/global.h"
40 #include "base/preferences.h"
41 #include "base/types.h"
42 #include "base/unicodestrings.h"
43 #include "base/utils/fs.h"
44 #include "base/utils/misc.h"
45 #include "base/utils/string.h"
46 #include "uithememanager.h"
50 QHash
<BitTorrent::TorrentState
, QColor
> torrentStateColorsFromUITheme()
52 struct TorrentStateColorDescriptor
54 const BitTorrent::TorrentState state
;
58 const TorrentStateColorDescriptor colorDescriptors
[] =
60 {BitTorrent::TorrentState::Downloading
, u
"TransferList.Downloading"_s
},
61 {BitTorrent::TorrentState::StalledDownloading
, u
"TransferList.StalledDownloading"_s
},
62 {BitTorrent::TorrentState::DownloadingMetadata
, u
"TransferList.DownloadingMetadata"_s
},
63 {BitTorrent::TorrentState::ForcedDownloadingMetadata
, u
"TransferList.ForcedDownloadingMetadata"_s
},
64 {BitTorrent::TorrentState::ForcedDownloading
, u
"TransferList.ForcedDownloading"_s
},
65 {BitTorrent::TorrentState::Uploading
, u
"TransferList.Uploading"_s
},
66 {BitTorrent::TorrentState::StalledUploading
, u
"TransferList.StalledUploading"_s
},
67 {BitTorrent::TorrentState::ForcedUploading
, u
"TransferList.ForcedUploading"_s
},
68 {BitTorrent::TorrentState::QueuedDownloading
, u
"TransferList.QueuedDownloading"_s
},
69 {BitTorrent::TorrentState::QueuedUploading
, u
"TransferList.QueuedUploading"_s
},
70 {BitTorrent::TorrentState::CheckingDownloading
, u
"TransferList.CheckingDownloading"_s
},
71 {BitTorrent::TorrentState::CheckingUploading
, u
"TransferList.CheckingUploading"_s
},
72 {BitTorrent::TorrentState::CheckingResumeData
, u
"TransferList.CheckingResumeData"_s
},
73 {BitTorrent::TorrentState::StoppedDownloading
, u
"TransferList.StoppedDownloading"_s
},
74 {BitTorrent::TorrentState::StoppedUploading
, u
"TransferList.StoppedUploading"_s
},
75 {BitTorrent::TorrentState::Moving
, u
"TransferList.Moving"_s
},
76 {BitTorrent::TorrentState::MissingFiles
, u
"TransferList.MissingFiles"_s
},
77 {BitTorrent::TorrentState::Error
, u
"TransferList.Error"_s
}
80 QHash
<BitTorrent::TorrentState
, QColor
> colors
;
81 for (const TorrentStateColorDescriptor
&colorDescriptor
: colorDescriptors
)
83 const QColor themeColor
= UIThemeManager::instance()->getColor(colorDescriptor
.id
);
84 colors
.insert(colorDescriptor
.state
, themeColor
);
92 TransferListModel::TransferListModel(QObject
*parent
)
93 : QAbstractListModel
{parent
}
96 {BitTorrent::TorrentState::Downloading
, tr("Downloading")},
97 {BitTorrent::TorrentState::StalledDownloading
, tr("Stalled", "Torrent is waiting for download to begin")},
98 {BitTorrent::TorrentState::DownloadingMetadata
, tr("Downloading metadata", "Used when loading a magnet link")},
99 {BitTorrent::TorrentState::ForcedDownloadingMetadata
, tr("[F] Downloading metadata", "Used when forced to load a magnet link. You probably shouldn't translate the F.")},
100 {BitTorrent::TorrentState::ForcedDownloading
, tr("[F] Downloading", "Used when the torrent is forced started. You probably shouldn't translate the F.")},
101 {BitTorrent::TorrentState::Uploading
, tr("Seeding", "Torrent is complete and in upload-only mode")},
102 {BitTorrent::TorrentState::StalledUploading
, tr("Seeding", "Torrent is complete and in upload-only mode")},
103 {BitTorrent::TorrentState::ForcedUploading
, tr("[F] Seeding", "Used when the torrent is forced started. You probably shouldn't translate the F.")},
104 {BitTorrent::TorrentState::QueuedDownloading
, tr("Queued", "Torrent is queued")},
105 {BitTorrent::TorrentState::QueuedUploading
, tr("Queued", "Torrent is queued")},
106 {BitTorrent::TorrentState::CheckingDownloading
, tr("Checking", "Torrent local data is being checked")},
107 {BitTorrent::TorrentState::CheckingUploading
, tr("Checking", "Torrent local data is being checked")},
108 {BitTorrent::TorrentState::CheckingResumeData
, tr("Checking resume data", "Used when loading the torrents from disk after qbt is launched. It checks the correctness of the .fastresume file. Normally it is completed in a fraction of a second, unless loading many many torrents.")},
109 {BitTorrent::TorrentState::StoppedDownloading
, tr("Stopped")},
110 {BitTorrent::TorrentState::StoppedUploading
, tr("Completed")},
111 {BitTorrent::TorrentState::Moving
, tr("Moving", "Torrent local data are being moved/relocated")},
112 {BitTorrent::TorrentState::MissingFiles
, tr("Missing Files")},
113 {BitTorrent::TorrentState::Error
, tr("Errored", "Torrent status, the torrent has an error")}
117 connect(Preferences::instance(), &Preferences::changed
, this, &TransferListModel::configure
);
119 loadUIThemeResources();
120 connect(UIThemeManager::instance(), &UIThemeManager::themeChanged
, this, [this]
122 loadUIThemeResources();
123 emit
dataChanged(index(0, 0), index((rowCount() - 1), (columnCount() - 1)), {Qt::DecorationRole
, Qt::ForegroundRole
});
127 using namespace BitTorrent
;
128 addTorrents(Session::instance()->torrents());
130 // Listen for torrent changes
131 connect(Session::instance(), &Session::torrentsLoaded
, this, &TransferListModel::addTorrents
);
132 connect(Session::instance(), &Session::torrentAboutToBeRemoved
, this, &TransferListModel::handleTorrentAboutToBeRemoved
);
133 connect(Session::instance(), &Session::torrentsUpdated
, this, &TransferListModel::handleTorrentsUpdated
);
135 connect(Session::instance(), &Session::torrentFinished
, this, &TransferListModel::handleTorrentStatusUpdated
);
136 connect(Session::instance(), &Session::torrentMetadataReceived
, this, &TransferListModel::handleTorrentStatusUpdated
);
137 connect(Session::instance(), &Session::torrentStarted
, this, &TransferListModel::handleTorrentStatusUpdated
);
138 connect(Session::instance(), &Session::torrentStopped
, this, &TransferListModel::handleTorrentStatusUpdated
);
139 connect(Session::instance(), &Session::torrentFinishedChecking
, this, &TransferListModel::handleTorrentStatusUpdated
);
142 int TransferListModel::rowCount(const QModelIndex
&) const
144 return m_torrentList
.size();
147 int TransferListModel::columnCount(const QModelIndex
&) const
152 QVariant
TransferListModel::headerData(const int section
, const Qt::Orientation orientation
, const int role
) const
154 if (orientation
== Qt::Horizontal
)
156 if (role
== Qt::DisplayRole
)
160 case TR_QUEUE_POSITION
: return QChar(u
'#');
161 case TR_NAME
: return tr("Name", "i.e: torrent name");
162 case TR_SIZE
: return tr("Size", "i.e: torrent size");
163 case TR_PROGRESS
: return tr("Progress", "% Done");
164 case TR_STATUS
: return tr("Status", "Torrent status (e.g. downloading, seeding, stopped)");
165 case TR_SEEDS
: return tr("Seeds", "i.e. full sources (often untranslated)");
166 case TR_PEERS
: return tr("Peers", "i.e. partial sources (often untranslated)");
167 case TR_DLSPEED
: return tr("Down Speed", "i.e: Download speed");
168 case TR_UPSPEED
: return tr("Up Speed", "i.e: Upload speed");
169 case TR_RATIO
: return tr("Ratio", "Share ratio");
170 case TR_POPULARITY
: return tr("Popularity");
171 case TR_ETA
: return tr("ETA", "i.e: Estimated Time of Arrival / Time left");
172 case TR_CATEGORY
: return tr("Category");
173 case TR_TAGS
: return tr("Tags");
174 case TR_ADD_DATE
: return tr("Added On", "Torrent was added to transfer list on 01/01/2010 08:00");
175 case TR_SEED_DATE
: return tr("Completed On", "Torrent was completed on 01/01/2010 08:00");
176 case TR_TRACKER
: return tr("Tracker");
177 case TR_DLLIMIT
: return tr("Down Limit", "i.e: Download limit");
178 case TR_UPLIMIT
: return tr("Up Limit", "i.e: Upload limit");
179 case TR_AMOUNT_DOWNLOADED
: return tr("Downloaded", "Amount of data downloaded (e.g. in MB)");
180 case TR_AMOUNT_UPLOADED
: return tr("Uploaded", "Amount of data uploaded (e.g. in MB)");
181 case TR_AMOUNT_DOWNLOADED_SESSION
: return tr("Session Download", "Amount of data downloaded since program open (e.g. in MB)");
182 case TR_AMOUNT_UPLOADED_SESSION
: return tr("Session Upload", "Amount of data uploaded since program open (e.g. in MB)");
183 case TR_AMOUNT_LEFT
: return tr("Remaining", "Amount of data left to download (e.g. in MB)");
184 case TR_TIME_ELAPSED
: return tr("Time Active", "Time (duration) the torrent is active (not stopped)");
185 case TR_SAVE_PATH
: return tr("Save Path", "Torrent save path");
186 case TR_DOWNLOAD_PATH
: return tr("Incomplete Save Path", "Torrent incomplete save path");
187 case TR_COMPLETED
: return tr("Completed", "Amount of data completed (e.g. in MB)");
188 case TR_RATIO_LIMIT
: return tr("Ratio Limit", "Upload share ratio limit");
189 case TR_SEEN_COMPLETE_DATE
: return tr("Last Seen Complete", "Indicates the time when the torrent was last seen complete/whole");
190 case TR_LAST_ACTIVITY
: return tr("Last Activity", "Time passed since a chunk was downloaded/uploaded");
191 case TR_TOTAL_SIZE
: return tr("Total Size", "i.e. Size including unwanted data");
192 case TR_AVAILABILITY
: return tr("Availability", "The number of distributed copies of the torrent");
193 case TR_INFOHASH_V1
: return tr("Info Hash v1", "i.e: torrent info hash v1");
194 case TR_INFOHASH_V2
: return tr("Info Hash v2", "i.e: torrent info hash v2");
195 case TR_REANNOUNCE
: return tr("Reannounce In", "Indicates the time until next trackers reannounce");
196 case TR_PRIVATE
: return tr("Private", "Flags private torrents");
200 else if (role
== Qt::ToolTipRole
)
204 case TR_POPULARITY
: return tr("Ratio / Time Active (in months), indicates how popular the torrent is");
208 else if (role
== Qt::TextAlignmentRole
)
212 case TR_AMOUNT_DOWNLOADED
:
213 case TR_AMOUNT_UPLOADED
:
214 case TR_AMOUNT_DOWNLOADED_SESSION
:
215 case TR_AMOUNT_UPLOADED_SESSION
:
230 case TR_QUEUE_POSITION
:
231 case TR_LAST_ACTIVITY
:
232 case TR_AVAILABILITY
:
234 return QVariant(Qt::AlignRight
| Qt::AlignVCenter
);
236 return QAbstractListModel::headerData(section
, orientation
, role
);
241 return QAbstractListModel::headerData(section
, orientation
, role
);
244 QString
TransferListModel::displayValue(const BitTorrent::Torrent
*torrent
, const int column
) const
246 bool hideValues
= false;
247 if (m_hideZeroValuesMode
== HideZeroValuesMode::Always
)
249 else if (m_hideZeroValuesMode
== HideZeroValuesMode::Stopped
)
250 hideValues
= (torrent
->state() == BitTorrent::TorrentState::StoppedDownloading
);
252 const auto availabilityString
= [hideValues
](const qreal value
) -> QString
254 if (hideValues
&& (value
== 0))
257 ? Utils::String::fromDouble(value
, 3)
261 const auto unitString
= [hideValues
](const qint64 value
, const bool isSpeedUnit
= false) -> QString
263 return (hideValues
&& (value
== 0))
264 ? QString
{} : Utils::Misc::friendlyUnit(value
, isSpeedUnit
);
267 const auto limitString
= [hideValues
](const qint64 value
) -> QString
269 if (hideValues
&& (value
<= 0))
273 ? Utils::Misc::friendlyUnit(value
, true)
277 const auto amountString
= [hideValues
](const qint64 value
, const qint64 total
) -> QString
279 if (hideValues
&& (value
== 0) && (total
== 0))
281 return u
"%1 (%2)"_s
.arg(QString::number(value
), QString::number(total
));
284 const auto etaString
= [hideValues
](const qlonglong value
) -> QString
286 if (hideValues
&& (value
>= MAX_ETA
))
288 return Utils::Misc::userFriendlyDuration(value
, MAX_ETA
);
291 const auto ratioString
= [hideValues
](const qreal value
) -> QString
293 if (hideValues
&& (value
<= 0))
296 return ((static_cast<int>(value
) == -1) || (value
> BitTorrent::Torrent::MAX_RATIO
))
297 ? C_INFINITY
: Utils::String::fromDouble(value
, 2);
300 const auto queuePositionString
= [](const qint64 value
) -> QString
302 return (value
>= 0) ? QString::number(value
+ 1) : u
"*"_s
;
305 const auto lastActivityString
= [hideValues
](qint64 value
) -> QString
307 if (hideValues
&& ((value
< 0) || (value
>= MAX_ETA
)))
310 // Show '< 1m ago' when elapsed time is 0
315 ? tr("%1 ago", "e.g.: 1h 20m ago").arg(Utils::Misc::userFriendlyDuration(value
))
316 : Utils::Misc::userFriendlyDuration(value
);
319 const auto timeElapsedString
= [hideValues
](const qint64 elapsedTime
, const qint64 seedingTime
) -> QString
321 if (seedingTime
<= 0)
323 if (hideValues
&& (elapsedTime
== 0))
325 return Utils::Misc::userFriendlyDuration(elapsedTime
);
328 return tr("%1 (seeded for %2)", "e.g. 4m39s (seeded for 3m10s)")
329 .arg(Utils::Misc::userFriendlyDuration(elapsedTime
)
330 , Utils::Misc::userFriendlyDuration(seedingTime
));
333 const auto progressString
= [](const qreal progress
) -> QString
335 return (progress
>= 1)
337 : (Utils::String::fromDouble((progress
* 100), 1) + u
'%');
340 const auto statusString
= [this](const BitTorrent::TorrentState state
, const QString
&errorMessage
) -> QString
342 return (state
== BitTorrent::TorrentState::Error
)
343 ? m_statusStrings
[state
] + u
": " + errorMessage
344 : m_statusStrings
[state
];
347 const auto hashString
= [hideValues
](const auto &hash
) -> QString
349 if (hideValues
&& !hash
.isValid())
351 return hash
.isValid() ? hash
.toString() : tr("N/A");
354 const auto reannounceString
= [hideValues
](const qint64 time
) -> QString
356 if (hideValues
&& (time
== 0))
358 return Utils::Misc::userFriendlyDuration(time
);
361 const auto privateString
= [hideValues
](const bool isPrivate
, const bool hasMetadata
) -> QString
363 if (hideValues
&& !isPrivate
)
366 return isPrivate
? tr("Yes") : tr("No");
373 return torrent
->name();
374 case TR_QUEUE_POSITION
:
375 return queuePositionString(torrent
->queuePosition());
377 return unitString(torrent
->wantedSize());
379 return progressString(torrent
->progress());
381 return statusString(torrent
->state(), torrent
->error());
383 return amountString(torrent
->seedsCount(), torrent
->totalSeedsCount());
385 return amountString(torrent
->leechsCount(), torrent
->totalLeechersCount());
387 return unitString(torrent
->downloadPayloadRate(), true);
389 return unitString(torrent
->uploadPayloadRate(), true);
391 return etaString(torrent
->eta());
393 return ratioString(torrent
->realRatio());
395 return ratioString(torrent
->maxRatio());
397 return ratioString(torrent
->popularity());
399 return torrent
->category();
401 return Utils::String::joinIntoString(torrent
->tags(), u
", "_s
);
403 return QLocale().toString(torrent
->addedTime().toLocalTime(), QLocale::ShortFormat
);
405 return QLocale().toString(torrent
->completedTime().toLocalTime(), QLocale::ShortFormat
);
407 return torrent
->currentTracker();
409 return limitString(torrent
->downloadLimit());
411 return limitString(torrent
->uploadLimit());
412 case TR_AMOUNT_DOWNLOADED
:
413 return unitString(torrent
->totalDownload());
414 case TR_AMOUNT_UPLOADED
:
415 return unitString(torrent
->totalUpload());
416 case TR_AMOUNT_DOWNLOADED_SESSION
:
417 return unitString(torrent
->totalPayloadDownload());
418 case TR_AMOUNT_UPLOADED_SESSION
:
419 return unitString(torrent
->totalPayloadUpload());
421 return unitString(torrent
->remainingSize());
422 case TR_TIME_ELAPSED
:
423 return timeElapsedString(torrent
->activeTime(), torrent
->finishedTime());
425 return torrent
->savePath().toString();
426 case TR_DOWNLOAD_PATH
:
427 return torrent
->downloadPath().toString();
429 return unitString(torrent
->completedSize());
430 case TR_SEEN_COMPLETE_DATE
:
431 return QLocale().toString(torrent
->lastSeenComplete().toLocalTime(), QLocale::ShortFormat
);
432 case TR_LAST_ACTIVITY
:
433 return lastActivityString(torrent
->timeSinceActivity());
434 case TR_AVAILABILITY
:
435 return availabilityString(torrent
->distributedCopies());
437 return unitString(torrent
->totalSize());
439 return hashString(torrent
->infoHash().v1());
441 return hashString(torrent
->infoHash().v2());
443 return reannounceString(torrent
->nextAnnounce());
445 return privateString(torrent
->isPrivate(), torrent
->hasMetadata());
451 QVariant
TransferListModel::internalValue(const BitTorrent::Torrent
*torrent
, const int column
, const bool alt
) const
456 return torrent
->name();
457 case TR_QUEUE_POSITION
:
458 return torrent
->queuePosition();
460 return torrent
->wantedSize();
462 return torrent
->progress() * 100;
464 return QVariant::fromValue(torrent
->state());
466 return !alt
? torrent
->seedsCount() : torrent
->totalSeedsCount();
468 return !alt
? torrent
->leechsCount() : torrent
->totalLeechersCount();
470 return torrent
->downloadPayloadRate();
472 return torrent
->uploadPayloadRate();
474 return torrent
->eta();
476 return torrent
->realRatio();
478 return torrent
->popularity();
480 return torrent
->category();
482 return QVariant::fromValue(torrent
->tags());
484 return torrent
->addedTime();
486 return torrent
->completedTime();
488 return torrent
->currentTracker();
490 return torrent
->downloadLimit();
492 return torrent
->uploadLimit();
493 case TR_AMOUNT_DOWNLOADED
:
494 return torrent
->totalDownload();
495 case TR_AMOUNT_UPLOADED
:
496 return torrent
->totalUpload();
497 case TR_AMOUNT_DOWNLOADED_SESSION
:
498 return torrent
->totalPayloadDownload();
499 case TR_AMOUNT_UPLOADED_SESSION
:
500 return torrent
->totalPayloadUpload();
502 return torrent
->remainingSize();
503 case TR_TIME_ELAPSED
:
504 return !alt
? torrent
->activeTime() : torrent
->finishedTime();
505 case TR_DOWNLOAD_PATH
:
506 return torrent
->downloadPath().data();
508 return torrent
->savePath().data();
510 return torrent
->completedSize();
512 return torrent
->maxRatio();
513 case TR_SEEN_COMPLETE_DATE
:
514 return torrent
->lastSeenComplete();
515 case TR_LAST_ACTIVITY
:
516 return torrent
->timeSinceActivity();
517 case TR_AVAILABILITY
:
518 return torrent
->distributedCopies();
520 return torrent
->totalSize();
522 return QVariant::fromValue(torrent
->infoHash().v1());
524 return QVariant::fromValue(torrent
->infoHash().v2());
526 return torrent
->nextAnnounce();
528 return (torrent
->hasMetadata() ? torrent
->isPrivate() : QVariant());
534 QVariant
TransferListModel::data(const QModelIndex
&index
, const int role
) const
536 if (!index
.isValid()) return {};
538 const BitTorrent::Torrent
*torrent
= m_torrentList
.value(index
.row());
539 if (!torrent
) return {};
543 case Qt::ForegroundRole
:
544 return m_stateThemeColors
.value(torrent
->state());
545 case Qt::DisplayRole
:
546 return displayValue(torrent
, index
.column());
547 case UnderlyingDataRole
:
548 return internalValue(torrent
, index
.column(), false);
549 case AdditionalUnderlyingDataRole
:
550 return internalValue(torrent
, index
.column(), true);
551 case Qt::DecorationRole
:
552 if (index
.column() == TR_NAME
)
553 return getIconByState(torrent
->state());
555 case Qt::ToolTipRole
:
556 switch (index
.column())
564 case TR_DOWNLOAD_PATH
:
567 return displayValue(torrent
, index
.column());
570 case Qt::TextAlignmentRole
:
571 switch (index
.column())
573 case TR_AMOUNT_DOWNLOADED
:
574 case TR_AMOUNT_UPLOADED
:
575 case TR_AMOUNT_DOWNLOADED_SESSION
:
576 case TR_AMOUNT_UPLOADED_SESSION
:
591 case TR_QUEUE_POSITION
:
592 case TR_LAST_ACTIVITY
:
593 case TR_AVAILABILITY
:
595 return QVariant(Qt::AlignRight
| Qt::AlignVCenter
);
605 bool TransferListModel::setData(const QModelIndex
&index
, const QVariant
&value
, int role
)
607 if (!index
.isValid() || (role
!= Qt::DisplayRole
)) return false;
609 BitTorrent::Torrent
*const torrent
= m_torrentList
.value(index
.row());
610 if (!torrent
) return false;
612 // Category and Name columns can be edited
613 switch (index
.column())
616 torrent
->setName(value
.toString());
619 torrent
->setCategory(value
.toString());
628 void TransferListModel::addTorrents(const QList
<BitTorrent::Torrent
*> &torrents
)
630 qsizetype row
= m_torrentList
.size();
631 const qsizetype total
= row
+ torrents
.size();
633 beginInsertRows({}, row
, total
);
635 m_torrentList
.reserve(total
);
636 for (BitTorrent::Torrent
*torrent
: torrents
)
638 Q_ASSERT(!m_torrentMap
.contains(torrent
));
640 m_torrentList
.append(torrent
);
641 m_torrentMap
[torrent
] = row
++;
647 Qt::ItemFlags
TransferListModel::flags(const QModelIndex
&index
) const
649 if (!index
.isValid()) return Qt::NoItemFlags
;
651 // Explicitly mark as editable
652 return QAbstractListModel::flags(index
) | Qt::ItemIsEditable
;
655 BitTorrent::Torrent
*TransferListModel::torrentHandle(const QModelIndex
&index
) const
657 if (!index
.isValid()) return nullptr;
659 return m_torrentList
.value(index
.row());
662 void TransferListModel::handleTorrentAboutToBeRemoved(BitTorrent::Torrent
*const torrent
)
664 const int row
= m_torrentMap
.value(torrent
, -1);
667 beginRemoveRows({}, row
, row
);
668 m_torrentList
.removeAt(row
);
669 m_torrentMap
.remove(torrent
);
670 for (int &value
: m_torrentMap
)
678 void TransferListModel::handleTorrentStatusUpdated(BitTorrent::Torrent
*const torrent
)
680 const int row
= m_torrentMap
.value(torrent
, -1);
683 emit
dataChanged(index(row
, 0), index(row
, columnCount() - 1));
686 void TransferListModel::handleTorrentsUpdated(const QList
<BitTorrent::Torrent
*> &torrents
)
688 const int columns
= (columnCount() - 1);
690 if (torrents
.size() <= (m_torrentList
.size() * 0.5))
692 for (BitTorrent::Torrent
*const torrent
: torrents
)
694 const int row
= m_torrentMap
.value(torrent
, -1);
697 emit
dataChanged(index(row
, 0), index(row
, columns
));
702 // save the overhead when more than half of the torrent list needs update
703 emit
dataChanged(index(0, 0), index((rowCount() - 1), columns
));
707 void TransferListModel::configure()
709 const Preferences
*pref
= Preferences::instance();
711 HideZeroValuesMode hideZeroValuesMode
= HideZeroValuesMode::Never
;
712 if (pref
->getHideZeroValues())
714 if (pref
->getHideZeroComboValues() == 1)
715 hideZeroValuesMode
= HideZeroValuesMode::Stopped
;
717 hideZeroValuesMode
= HideZeroValuesMode::Always
;
720 if (m_hideZeroValuesMode
!= hideZeroValuesMode
)
722 m_hideZeroValuesMode
= hideZeroValuesMode
;
723 emit
dataChanged(index(0, 0), index((rowCount() - 1), (columnCount() - 1)));
727 void TransferListModel::loadUIThemeResources()
729 m_stateThemeColors
= torrentStateColorsFromUITheme();
731 const auto *themeManager
= UIThemeManager::instance();
732 m_checkingIcon
= themeManager
->getIcon(u
"force-recheck"_s
, u
"checking"_s
);
733 m_completedIcon
= themeManager
->getIcon(u
"checked-completed"_s
, u
"completed"_s
);
734 m_downloadingIcon
= themeManager
->getIcon(u
"downloading"_s
);
735 m_errorIcon
= themeManager
->getIcon(u
"error"_s
);
736 m_movingIcon
= themeManager
->getIcon(u
"set-location"_s
);
737 m_stoppedIcon
= themeManager
->getIcon(u
"stopped"_s
, u
"media-playback-pause"_s
);
738 m_queuedIcon
= themeManager
->getIcon(u
"queued"_s
);
739 m_stalledDLIcon
= themeManager
->getIcon(u
"stalledDL"_s
);
740 m_stalledUPIcon
= themeManager
->getIcon(u
"stalledUP"_s
);
741 m_uploadingIcon
= themeManager
->getIcon(u
"upload"_s
, u
"uploading"_s
);
744 QIcon
TransferListModel::getIconByState(const BitTorrent::TorrentState state
) const
748 case BitTorrent::TorrentState::Downloading
:
749 case BitTorrent::TorrentState::ForcedDownloading
:
750 case BitTorrent::TorrentState::DownloadingMetadata
:
751 case BitTorrent::TorrentState::ForcedDownloadingMetadata
:
752 return m_downloadingIcon
;
753 case BitTorrent::TorrentState::StalledDownloading
:
754 return m_stalledDLIcon
;
755 case BitTorrent::TorrentState::StalledUploading
:
756 return m_stalledUPIcon
;
757 case BitTorrent::TorrentState::Uploading
:
758 case BitTorrent::TorrentState::ForcedUploading
:
759 return m_uploadingIcon
;
760 case BitTorrent::TorrentState::StoppedDownloading
:
761 return m_stoppedIcon
;
762 case BitTorrent::TorrentState::StoppedUploading
:
763 return m_completedIcon
;
764 case BitTorrent::TorrentState::QueuedDownloading
:
765 case BitTorrent::TorrentState::QueuedUploading
:
767 case BitTorrent::TorrentState::CheckingDownloading
:
768 case BitTorrent::TorrentState::CheckingUploading
:
769 case BitTorrent::TorrentState::CheckingResumeData
:
770 return m_checkingIcon
;
771 case BitTorrent::TorrentState::Moving
:
773 case BitTorrent::TorrentState::Unknown
:
774 case BitTorrent::TorrentState::MissingFiles
:
775 case BitTorrent::TorrentState::Error
: