qBittorrent
transferlistmodel.cpp
Go to the documentation of this file.
1 /*
2  * Bittorrent Client using Qt and libtorrent.
3  * Copyright (C) 2015 Vladimir Golovnev <glassez@yandex.ru>
4  * Copyright (C) 2010 Christophe Dumez <chris@qbittorrent.org>
5  *
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.
10  *
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.
15  *
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.
19  *
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.
28  */
29 
30 #include "transferlistmodel.h"
31 
32 #include <QApplication>
33 #include <QDateTime>
34 #include <QDebug>
35 #include <QIcon>
36 #include <QPalette>
37 
40 #include "base/global.h"
41 #include "base/preferences.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"
47 
48 static QIcon getIconByState(BitTorrent::TorrentState state);
50 
51 static QIcon getPausedIcon();
52 static QIcon getQueuedIcon();
53 static QIcon getDownloadingIcon();
54 static QIcon getStalledDownloadingIcon();
55 static QIcon getUploadingIcon();
56 static QIcon getStalledUploadingIcon();
57 static QIcon getCompletedIcon();
58 static QIcon getCheckingIcon();
59 static QIcon getErrorIcon();
60 
61 static bool isDarkTheme();
62 
63 namespace
64 {
65  QHash<BitTorrent::TorrentState, QColor> torrentStateColorsFromUITheme()
66  {
67  struct TorrentStateColorDescriptor
68  {
69  const BitTorrent::TorrentState state;
70  const QString id;
71  };
72 
73  const TorrentStateColorDescriptor colorDescriptors[] =
74  {
75  {BitTorrent::TorrentState::Downloading, QLatin1String("TransferList.Downloading")},
76  {BitTorrent::TorrentState::StalledDownloading, QLatin1String("TransferList.StalledDownloading")},
77  {BitTorrent::TorrentState::DownloadingMetadata, QLatin1String("TransferList.DownloadingMetadata")},
78  {BitTorrent::TorrentState::ForcedDownloadingMetadata, QLatin1String("TransferList.ForcedDownloadingMetadata")},
79  {BitTorrent::TorrentState::ForcedDownloading, QLatin1String("TransferList.ForcedDownloading")},
80  {BitTorrent::TorrentState::Uploading, QLatin1String("TransferList.Uploading")},
81  {BitTorrent::TorrentState::StalledUploading, QLatin1String("TransferList.StalledUploading")},
82  {BitTorrent::TorrentState::ForcedUploading, QLatin1String("TransferList.ForcedUploading")},
83  {BitTorrent::TorrentState::QueuedDownloading, QLatin1String("TransferList.QueuedDownloading")},
84  {BitTorrent::TorrentState::QueuedUploading, QLatin1String("TransferList.QueuedUploading")},
85  {BitTorrent::TorrentState::CheckingDownloading, QLatin1String("TransferList.CheckingDownloading")},
86  {BitTorrent::TorrentState::CheckingUploading, QLatin1String("TransferList.CheckingUploading")},
87  {BitTorrent::TorrentState::CheckingResumeData, QLatin1String("TransferList.CheckingResumeData")},
88  {BitTorrent::TorrentState::PausedDownloading, QLatin1String("TransferList.PausedDownloading")},
89  {BitTorrent::TorrentState::PausedUploading, QLatin1String("TransferList.PausedUploading")},
90  {BitTorrent::TorrentState::Moving, QLatin1String("TransferList.Moving")},
91  {BitTorrent::TorrentState::MissingFiles, QLatin1String("TransferList.MissingFiles")},
92  {BitTorrent::TorrentState::Error, QLatin1String("TransferList.Error")}
93  };
94 
95  QHash<BitTorrent::TorrentState, QColor> colors;
96  for (const TorrentStateColorDescriptor &colorDescriptor : colorDescriptors)
97  {
98  const QColor themeColor = UIThemeManager::instance()->getColor(colorDescriptor.id, QColor());
99  if (themeColor.isValid())
100  colors.insert(colorDescriptor.state, themeColor);
101  }
102  return colors;
103  }
104 }
105 
106 // TransferListModel
107 
109  : QAbstractListModel {parent}
110  , m_statusStrings
111  {
112  {BitTorrent::TorrentState::Downloading, tr("Downloading")},
113  {BitTorrent::TorrentState::StalledDownloading, tr("Stalled", "Torrent is waiting for download to begin")},
114  {BitTorrent::TorrentState::DownloadingMetadata, tr("Downloading metadata", "Used when loading a magnet link")},
115  {BitTorrent::TorrentState::ForcedDownloadingMetadata, tr("[F] Downloading metadata", "Used when forced to load a magnet link. You probably shouldn't translate the F.")},
116  {BitTorrent::TorrentState::ForcedDownloading, tr("[F] Downloading", "Used when the torrent is forced started. You probably shouldn't translate the F.")},
117  {BitTorrent::TorrentState::Uploading, tr("Seeding", "Torrent is complete and in upload-only mode")},
118  {BitTorrent::TorrentState::StalledUploading, tr("Seeding", "Torrent is complete and in upload-only mode")},
119  {BitTorrent::TorrentState::ForcedUploading, tr("[F] Seeding", "Used when the torrent is forced started. You probably shouldn't translate the F.")},
120  {BitTorrent::TorrentState::QueuedDownloading, tr("Queued", "Torrent is queued")},
121  {BitTorrent::TorrentState::QueuedUploading, tr("Queued", "Torrent is queued")},
122  {BitTorrent::TorrentState::CheckingDownloading, tr("Checking", "Torrent local data is being checked")},
123  {BitTorrent::TorrentState::CheckingUploading, tr("Checking", "Torrent local data is being checked")},
124  {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.")},
127  {BitTorrent::TorrentState::Moving, tr("Moving", "Torrent local data are being moved/relocated")},
128  {BitTorrent::TorrentState::MissingFiles, tr("Missing Files")},
129  {BitTorrent::TorrentState::Error, tr("Errored", "Torrent status, the torrent has an error")}
130  }
131  , m_stateThemeColors {torrentStateColorsFromUITheme()}
132 {
133  configure();
135 
136  // Load the torrents
137  using namespace BitTorrent;
138  for (Torrent *const torrent : asConst(Session::instance()->torrents()))
139  addTorrent(torrent);
140 
141  // Listen for torrent changes
145 
151 }
152 
153 int TransferListModel::rowCount(const QModelIndex &) const
154 {
155  return m_torrentList.size();
156 }
157 
158 int TransferListModel::columnCount(const QModelIndex &) const
159 {
160  return NB_COLUMNS;
161 }
162 
163 QVariant TransferListModel::headerData(int section, Qt::Orientation orientation, int role) const
164 {
165  if (orientation == Qt::Horizontal)
166  {
167  if (role == Qt::DisplayRole)
168  {
169  switch (section)
170  {
171  case TR_QUEUE_POSITION: return QChar('#');
172  case TR_NAME: return tr("Name", "i.e: torrent name");
173  case TR_SIZE: return tr("Size", "i.e: torrent size");
174  case TR_PROGRESS: return tr("Progress", "% Done");
175  case TR_STATUS: return tr("Status", "Torrent status (e.g. downloading, seeding, paused)");
176  case TR_SEEDS: return tr("Seeds", "i.e. full sources (often untranslated)");
177  case TR_PEERS: return tr("Peers", "i.e. partial sources (often untranslated)");
178  case TR_DLSPEED: return tr("Down Speed", "i.e: Download speed");
179  case TR_UPSPEED: return tr("Up Speed", "i.e: Upload speed");
180  case TR_RATIO: return tr("Ratio", "Share ratio");
181  case TR_ETA: return tr("ETA", "i.e: Estimated Time of Arrival / Time left");
182  case TR_CATEGORY: return tr("Category");
183  case TR_TAGS: return tr("Tags");
184  case TR_ADD_DATE: return tr("Added On", "Torrent was added to transfer list on 01/01/2010 08:00");
185  case TR_SEED_DATE: return tr("Completed On", "Torrent was completed on 01/01/2010 08:00");
186  case TR_TRACKER: return tr("Tracker");
187  case TR_DLLIMIT: return tr("Down Limit", "i.e: Download limit");
188  case TR_UPLIMIT: return tr("Up Limit", "i.e: Upload limit");
189  case TR_AMOUNT_DOWNLOADED: return tr("Downloaded", "Amount of data downloaded (e.g. in MB)");
190  case TR_AMOUNT_UPLOADED: return tr("Uploaded", "Amount of data uploaded (e.g. in MB)");
191  case TR_AMOUNT_DOWNLOADED_SESSION: return tr("Session Download", "Amount of data downloaded since program open (e.g. in MB)");
192  case TR_AMOUNT_UPLOADED_SESSION: return tr("Session Upload", "Amount of data uploaded since program open (e.g. in MB)");
193  case TR_AMOUNT_LEFT: return tr("Remaining", "Amount of data left to download (e.g. in MB)");
194  case TR_TIME_ELAPSED: return tr("Time Active", "Time (duration) the torrent is active (not paused)");
195  case TR_SAVE_PATH: return tr("Save path", "Torrent save path");
196  case TR_COMPLETED: return tr("Completed", "Amount of data completed (e.g. in MB)");
197  case TR_RATIO_LIMIT: return tr("Ratio Limit", "Upload share ratio limit");
198  case TR_SEEN_COMPLETE_DATE: return tr("Last Seen Complete", "Indicates the time when the torrent was last seen complete/whole");
199  case TR_LAST_ACTIVITY: return tr("Last Activity", "Time passed since a chunk was downloaded/uploaded");
200  case TR_TOTAL_SIZE: return tr("Total Size", "i.e. Size including unwanted data");
201  case TR_AVAILABILITY: return tr("Availability", "The number of distributed copies of the torrent");
202  default: return {};
203  }
204  }
205  else if (role == Qt::TextAlignmentRole)
206  {
207  switch (section)
208  {
210  case TR_AMOUNT_UPLOADED:
213  case TR_AMOUNT_LEFT:
214  case TR_COMPLETED:
215  case TR_SIZE:
216  case TR_TOTAL_SIZE:
217  case TR_ETA:
218  case TR_SEEDS:
219  case TR_PEERS:
220  case TR_UPSPEED:
221  case TR_DLSPEED:
222  case TR_UPLIMIT:
223  case TR_DLLIMIT:
224  case TR_RATIO_LIMIT:
225  case TR_RATIO:
226  case TR_QUEUE_POSITION:
227  case TR_LAST_ACTIVITY:
228  case TR_AVAILABILITY:
229  return QVariant(Qt::AlignRight | Qt::AlignVCenter);
230  default:
231  return QAbstractListModel::headerData(section, orientation, role);
232  }
233  }
234  }
235 
236  return {};
237 }
238 
239 QString TransferListModel::displayValue(const BitTorrent::Torrent *torrent, const int column) const
240 {
241  bool hideValues = false;
243  hideValues = true;
245  hideValues = (torrent->state() == BitTorrent::TorrentState::PausedDownloading);
246 
247  const auto availabilityString = [hideValues](const qreal value) -> QString
248  {
249  if (hideValues && (value == 0))
250  return {};
251  return (value >= 0)
253  : tr("N/A");
254  };
255 
256  const auto unitString = [hideValues](const qint64 value, const bool isSpeedUnit = false) -> QString
257  {
258  return (hideValues && (value == 0))
259  ? QString {} : Utils::Misc::friendlyUnit(value, isSpeedUnit);
260  };
261 
262  const auto limitString = [hideValues](const qint64 value) -> QString
263  {
264  if (hideValues && (value <= 0))
265  return {};
266 
267  return (value > 0)
269  : QString::fromUtf8(C_INFINITY);
270  };
271 
272  const auto amountString = [hideValues](const qint64 value, const qint64 total) -> QString
273  {
274  if (hideValues && (value == 0) && (total == 0))
275  return {};
276  return QString::fromLatin1("%1 (%2)").arg(QString::number(value), QString::number(total));
277  };
278 
279  const auto etaString = [hideValues](const qlonglong value) -> QString
280  {
281  if (hideValues && (value >= MAX_ETA))
282  return {};
284  };
285 
286  const auto ratioString = [hideValues](const qreal value) -> QString
287  {
288  if (hideValues && (value <= 0))
289  return {};
290 
291  return ((static_cast<int>(value) == -1) || (value > BitTorrent::Torrent::MAX_RATIO))
292  ? QString::fromUtf8(C_INFINITY) : Utils::String::fromDouble(value, 2);
293  };
294 
295  const auto queuePositionString = [](const qint64 value) -> QString
296  {
297  return (value >= 0) ? QString::number(value + 1) : QLatin1String("*");
298  };
299 
300  const auto lastActivityString = [hideValues](qint64 value) -> QString
301  {
302  if (hideValues && ((value < 0) || (value >= MAX_ETA)))
303  return {};
304 
305  // Show '< 1m ago' when elapsed time is 0
306  if (value == 0)
307  value = 1;
308 
309  return (value >= 0)
310  ? tr("%1 ago", "e.g.: 1h 20m ago").arg(Utils::Misc::userFriendlyDuration(value))
312  };
313 
314  const auto timeElapsedString = [hideValues](const qint64 elapsedTime, const qint64 seedingTime) -> QString
315  {
316  if (seedingTime <= 0)
317  {
318  if (hideValues && (elapsedTime == 0))
319  return {};
320  return Utils::Misc::userFriendlyDuration(elapsedTime);
321  }
322 
323  return tr("%1 (seeded for %2)", "e.g. 4m39s (seeded for 3m10s)")
324  .arg(Utils::Misc::userFriendlyDuration(elapsedTime)
325  , Utils::Misc::userFriendlyDuration(seedingTime));
326  };
327 
328  const auto progressString = [](const qreal progress) -> QString
329  {
330  return (progress >= 1)
331  ? QString::fromLatin1("100%")
332  : Utils::String::fromDouble((progress * 100), 1) + QLatin1Char('%');
333  };
334 
335  const auto statusString = [this](const BitTorrent::TorrentState state, const QString &errorMessage) -> QString
336  {
337  return (state == BitTorrent::TorrentState::Error)
338  ? m_statusStrings[state] + ": " + errorMessage
339  : m_statusStrings[state];
340  };
341 
342  switch (column)
343  {
344  case TR_NAME:
345  return torrent->name();
346  case TR_QUEUE_POSITION:
347  return queuePositionString(torrent->queuePosition());
348  case TR_SIZE:
349  return unitString(torrent->wantedSize());
350  case TR_PROGRESS:
351  return progressString(torrent->progress());
352  case TR_STATUS:
353  return statusString(torrent->state(), torrent->error());
354  case TR_SEEDS:
355  return amountString(torrent->seedsCount(), torrent->totalSeedsCount());
356  case TR_PEERS:
357  return amountString(torrent->leechsCount(), torrent->totalLeechersCount());
358  case TR_DLSPEED:
359  return unitString(torrent->downloadPayloadRate(), true);
360  case TR_UPSPEED:
361  return unitString(torrent->uploadPayloadRate(), true);
362  case TR_ETA:
363  return etaString(torrent->eta());
364  case TR_RATIO:
365  return ratioString(torrent->realRatio());
366  case TR_RATIO_LIMIT:
367  return ratioString(torrent->maxRatio());
368  case TR_CATEGORY:
369  return torrent->category();
370  case TR_TAGS:
371  return torrent->tags().join(QLatin1String(", "));
372  case TR_ADD_DATE:
373  return QLocale().toString(torrent->addedTime().toLocalTime(), QLocale::ShortFormat);
374  case TR_SEED_DATE:
375  return QLocale().toString(torrent->completedTime().toLocalTime(), QLocale::ShortFormat);
376  case TR_TRACKER:
377  return torrent->currentTracker();
378  case TR_DLLIMIT:
379  return limitString(torrent->downloadLimit());
380  case TR_UPLIMIT:
381  return limitString(torrent->uploadLimit());
383  return unitString(torrent->totalDownload());
384  case TR_AMOUNT_UPLOADED:
385  return unitString(torrent->totalUpload());
387  return unitString(torrent->totalPayloadDownload());
389  return unitString(torrent->totalPayloadUpload());
390  case TR_AMOUNT_LEFT:
391  return unitString(torrent->remainingSize());
392  case TR_TIME_ELAPSED:
393  return timeElapsedString(torrent->activeTime(), torrent->finishedTime());
394  case TR_SAVE_PATH:
395  return Utils::Fs::toNativePath(torrent->savePath());
396  case TR_COMPLETED:
397  return unitString(torrent->completedSize());
399  return QLocale().toString(torrent->lastSeenComplete().toLocalTime(), QLocale::ShortFormat);
400  case TR_LAST_ACTIVITY:
401  return lastActivityString(torrent->timeSinceActivity());
402  case TR_AVAILABILITY:
403  return availabilityString(torrent->distributedCopies());
404  case TR_TOTAL_SIZE:
405  return unitString(torrent->totalSize());
406  }
407 
408  return {};
409 }
410 
411 QVariant TransferListModel::internalValue(const BitTorrent::Torrent *torrent, const int column, const bool alt) const
412 {
413  switch (column)
414  {
415  case TR_NAME:
416  return torrent->name();
417  case TR_QUEUE_POSITION:
418  return torrent->queuePosition();
419  case TR_SIZE:
420  return torrent->wantedSize();
421  case TR_PROGRESS:
422  return torrent->progress() * 100;
423  case TR_STATUS:
424  return QVariant::fromValue(torrent->state());
425  case TR_SEEDS:
426  return !alt ? torrent->seedsCount() : torrent->totalSeedsCount();
427  case TR_PEERS:
428  return !alt ? torrent->leechsCount() : torrent->totalLeechersCount();
429  case TR_DLSPEED:
430  return torrent->downloadPayloadRate();
431  case TR_UPSPEED:
432  return torrent->uploadPayloadRate();
433  case TR_ETA:
434  return torrent->eta();
435  case TR_RATIO:
436  return torrent->realRatio();
437  case TR_CATEGORY:
438  return torrent->category();
439  case TR_TAGS:
440  return QVariant::fromValue(torrent->tags());
441  case TR_ADD_DATE:
442  return torrent->addedTime();
443  case TR_SEED_DATE:
444  return torrent->completedTime();
445  case TR_TRACKER:
446  return torrent->currentTracker();
447  case TR_DLLIMIT:
448  return torrent->downloadLimit();
449  case TR_UPLIMIT:
450  return torrent->uploadLimit();
452  return torrent->totalDownload();
453  case TR_AMOUNT_UPLOADED:
454  return torrent->totalUpload();
456  return torrent->totalPayloadDownload();
458  return torrent->totalPayloadUpload();
459  case TR_AMOUNT_LEFT:
460  return torrent->remainingSize();
461  case TR_TIME_ELAPSED:
462  return !alt ? torrent->activeTime() : torrent->finishedTime();
463  case TR_SAVE_PATH:
464  return Utils::Fs::toNativePath(torrent->savePath());
465  case TR_COMPLETED:
466  return torrent->completedSize();
467  case TR_RATIO_LIMIT:
468  return torrent->maxRatio();
470  return torrent->lastSeenComplete();
471  case TR_LAST_ACTIVITY:
472  return torrent->timeSinceActivity();
473  case TR_AVAILABILITY:
474  return torrent->distributedCopies();
475  case TR_TOTAL_SIZE:
476  return torrent->totalSize();
477  }
478 
479  return {};
480 }
481 
482 QVariant TransferListModel::data(const QModelIndex &index, const int role) const
483 {
484  if (!index.isValid()) return {};
485 
486  const BitTorrent::Torrent *torrent = m_torrentList.value(index.row());
487  if (!torrent) return {};
488 
489  switch (role)
490  {
491  case Qt::ForegroundRole:
492  return m_stateThemeColors.value(torrent->state(), getDefaultColorByState(torrent->state()));
493  case Qt::DisplayRole:
494  return displayValue(torrent, index.column());
495  case UnderlyingDataRole:
496  return internalValue(torrent, index.column(), false);
498  return internalValue(torrent, index.column(), true);
499  case Qt::DecorationRole:
500  if (index.column() == TR_NAME)
501  return getIconByState(torrent->state());
502  break;
503  case Qt::ToolTipRole:
504  switch (index.column())
505  {
506  case TR_NAME:
507  case TR_STATUS:
508  case TR_CATEGORY:
509  case TR_TAGS:
510  case TR_TRACKER:
511  case TR_SAVE_PATH:
512  return displayValue(torrent, index.column());
513  }
514  break;
515  case Qt::TextAlignmentRole:
516  switch (index.column())
517  {
519  case TR_AMOUNT_UPLOADED:
522  case TR_AMOUNT_LEFT:
523  case TR_COMPLETED:
524  case TR_SIZE:
525  case TR_TOTAL_SIZE:
526  case TR_ETA:
527  case TR_SEEDS:
528  case TR_PEERS:
529  case TR_UPSPEED:
530  case TR_DLSPEED:
531  case TR_UPLIMIT:
532  case TR_DLLIMIT:
533  case TR_RATIO_LIMIT:
534  case TR_RATIO:
535  case TR_QUEUE_POSITION:
536  case TR_LAST_ACTIVITY:
537  case TR_AVAILABILITY:
538  return QVariant {Qt::AlignRight | Qt::AlignVCenter};
539  }
540  }
541 
542  return {};
543 }
544 
545 bool TransferListModel::setData(const QModelIndex &index, const QVariant &value, int role)
546 {
547  if (!index.isValid() || (role != Qt::DisplayRole)) return false;
548 
549  BitTorrent::Torrent *const torrent = m_torrentList.value(index.row());
550  if (!torrent) return false;
551 
552  // Category and Name columns can be edited
553  switch (index.column())
554  {
555  case TR_NAME:
556  torrent->setName(value.toString());
557  break;
558  case TR_CATEGORY:
559  torrent->setCategory(value.toString());
560  break;
561  default:
562  return false;
563  }
564 
565  return true;
566 }
567 
569 {
570  Q_ASSERT(!m_torrentMap.contains(torrent));
571 
572  const int row = m_torrentList.size();
573 
574  beginInsertRows({}, row, row);
575  m_torrentList << torrent;
576  m_torrentMap[torrent] = row;
577  endInsertRows();
578 }
579 
580 Qt::ItemFlags TransferListModel::flags(const QModelIndex &index) const
581 {
582  if (!index.isValid()) return Qt::NoItemFlags;
583 
584  // Explicitly mark as editable
585  return QAbstractListModel::flags(index) | Qt::ItemIsEditable;
586 }
587 
589 {
590  if (!index.isValid()) return nullptr;
591 
592  return m_torrentList.value(index.row());
593 }
594 
596 {
597  const int row = m_torrentMap.value(torrent, -1);
598  Q_ASSERT(row >= 0);
599 
600  beginRemoveRows({}, row, row);
601  m_torrentList.removeAt(row);
602  m_torrentMap.remove(torrent);
603  for (int &value : m_torrentMap)
604  {
605  if (value > row)
606  --value;
607  }
608  endRemoveRows();
609 }
610 
612 {
613  const int row = m_torrentMap.value(torrent, -1);
614  Q_ASSERT(row >= 0);
615 
616  emit dataChanged(index(row, 0), index(row, columnCount() - 1));
617 }
618 
619 void TransferListModel::handleTorrentsUpdated(const QVector<BitTorrent::Torrent *> &torrents)
620 {
621  const int columns = (columnCount() - 1);
622 
623  if (torrents.size() <= (m_torrentList.size() * 0.5))
624  {
625  for (BitTorrent::Torrent *const torrent : torrents)
626  {
627  const int row = m_torrentMap.value(torrent, -1);
628  Q_ASSERT(row >= 0);
629 
630  emit dataChanged(index(row, 0), index(row, columns));
631  }
632  }
633  else
634  {
635  // save the overhead when more than half of the torrent list needs update
636  emit dataChanged(index(0, 0), index((rowCount() - 1), columns));
637  }
638 }
639 
641 {
642  const Preferences *pref = Preferences::instance();
643 
644  HideZeroValuesMode hideZeroValuesMode = HideZeroValuesMode::Never;
645  if (pref->getHideZeroValues())
646  {
647  if (pref->getHideZeroComboValues() == 1)
648  hideZeroValuesMode = HideZeroValuesMode::Paused;
649  else
650  hideZeroValuesMode = HideZeroValuesMode::Always;
651  }
652 
653  if (m_hideZeroValuesMode != hideZeroValuesMode)
654  {
655  m_hideZeroValuesMode = hideZeroValuesMode;
656  emit dataChanged(index(0, 0), index((rowCount() - 1), (columnCount() - 1)));
657  }
658 }
659 
660 // Static functions
661 
663 {
664  switch (state)
665  {
670  return getDownloadingIcon();
672  return getStalledDownloadingIcon();
674  return getStalledUploadingIcon();
677  return getUploadingIcon();
679  return getPausedIcon();
681  return getCompletedIcon();
684  return getQueuedIcon();
689  return getCheckingIcon();
693  return getErrorIcon();
694  default:
695  Q_ASSERT(false);
696  return getErrorIcon();
697  }
698 }
699 
701 {
702  // Color names taken from http://cloford.com/resources/colours/500col.htm
703  bool dark = isDarkTheme();
704 
705  switch (state)
706  {
711  if (!dark)
712  return {34, 139, 34}; // Forest Green
713  else
714  return {50, 205, 50}; // Lime Green
717  if (!dark)
718  return {0, 0, 0}; // Black
719  else
720  return {204, 204, 204}; // Gray 80
723  if (!dark)
724  return {65, 105, 225}; // Royal Blue
725  else
726  return {99, 184, 255}; // Steel Blue 1
728  return {250, 128, 114}; // Salmon
730  if (!dark)
731  return {0, 0, 139}; // Dark Blue
732  else
733  return {79, 148, 205}; // Steel Blue 3
736  return {255, 0, 0}; // red
743  if (!dark)
744  return {0, 128, 128}; // Teal
745  else
746  return {0, 205, 205}; // Cyan 3
748  return {255, 0, 0}; // red
749  default:
750  Q_ASSERT(false);
751  return {255, 0, 0}; // red
752  }
753 }
754 
756 {
757  static QIcon cached = UIThemeManager::instance()->getIcon(QLatin1String("paused"));
758  return cached;
759 }
760 
762 {
763  static QIcon cached = UIThemeManager::instance()->getIcon(QLatin1String("queued"));
764  return cached;
765 }
766 
768 {
769  static QIcon cached = UIThemeManager::instance()->getIcon(QLatin1String("downloading"));
770  return cached;
771 }
772 
774 {
775  static QIcon cached = UIThemeManager::instance()->getIcon(QLatin1String("stalledDL"));
776  return cached;
777 }
778 
780 {
781  static QIcon cached = UIThemeManager::instance()->getIcon(QLatin1String("uploading"));
782  return cached;
783 }
784 
786 {
787  static QIcon cached = UIThemeManager::instance()->getIcon(QLatin1String("stalledUP"));
788  return cached;
789 }
790 
792 {
793  static QIcon cached = UIThemeManager::instance()->getIcon(QLatin1String("completed"));
794  return cached;
795 }
796 
798 {
799  static QIcon cached = UIThemeManager::instance()->getIcon(QLatin1String("checking"));
800  return cached;
801 }
802 
804 {
805  static QIcon cached = UIThemeManager::instance()->getIcon(QLatin1String("error"));
806  return cached;
807 }
808 
810 {
811  const QPalette pal = QApplication::palette();
812  // QPalette::Base is used for the background of the Treeview
813  const QColor &color = pal.color(QPalette::Active, QPalette::Base);
814  return (color.lightness() < 127);
815 }
void torrentPaused(Torrent *torrent)
static Session * instance()
Definition: session.cpp:997
void torrentAboutToBeRemoved(Torrent *torrent)
void torrentFinishedChecking(Torrent *torrent)
void torrentFinished(Torrent *torrent)
void torrentResumed(Torrent *torrent)
void torrentsUpdated(const QVector< Torrent * > &torrents)
void torrentLoaded(Torrent *torrent)
void torrentMetadataReceived(Torrent *torrent)
virtual qlonglong totalSize() const =0
virtual qreal distributedCopies() const =0
virtual int uploadPayloadRate() const =0
virtual int seedsCount() const =0
virtual int downloadLimit() const =0
virtual TagSet tags() const =0
virtual qlonglong totalUpload() const =0
virtual QString currentTracker() const =0
virtual QString category() const =0
virtual qlonglong activeTime() const =0
virtual qlonglong totalPayloadDownload() const =0
virtual QString error() const =0
virtual qlonglong finishedTime() const =0
virtual int uploadLimit() const =0
qlonglong remainingSize() const
Definition: torrent.cpp:64
virtual qlonglong completedSize() const =0
virtual int totalSeedsCount() const =0
virtual TorrentState state() const =0
virtual qlonglong totalDownload() const =0
virtual QString savePath() const =0
virtual qreal realRatio() const =0
virtual qreal maxRatio() const =0
virtual int queuePosition() const =0
virtual bool setCategory(const QString &category)=0
virtual QDateTime addedTime() const =0
virtual void setName(const QString &name)=0
virtual qlonglong totalPayloadUpload() const =0
virtual QDateTime completedTime() const =0
virtual QDateTime lastSeenComplete() const =0
virtual int leechsCount() const =0
virtual qreal progress() const =0
virtual qlonglong timeSinceActivity() const =0
virtual qlonglong eta() const =0
static const qreal MAX_RATIO
Definition: torrent.h:110
virtual qlonglong wantedSize() const =0
virtual int downloadPayloadRate() const =0
virtual int totalLeechersCount() const =0
virtual QString name() const =0
QString join(const QString &separator) const
Definition: orderedset.h:80
static Preferences * instance()
void changed()
int getHideZeroComboValues() const
bool getHideZeroValues() const
void handleTorrentStatusUpdated(BitTorrent::Torrent *const torrent)
bool setData(const QModelIndex &index, const QVariant &value, int role) override
int rowCount(const QModelIndex &parent={}) const override
QHash< BitTorrent::Torrent *, int > m_torrentMap
const QHash< BitTorrent::TorrentState, QColor > m_stateThemeColors
HideZeroValuesMode m_hideZeroValuesMode
void addTorrent(BitTorrent::Torrent *const torrent)
QList< BitTorrent::Torrent * > m_torrentList
QString displayValue(const BitTorrent::Torrent *torrent, int column) const
const QHash< BitTorrent::TorrentState, QString > m_statusStrings
QVariant internalValue(const BitTorrent::Torrent *torrent, int column, bool alt) const
QVariant headerData(int section, Qt::Orientation orientation, int role) const override
QVariant data(const QModelIndex &index, int role=Qt::DisplayRole) const override
Qt::ItemFlags flags(const QModelIndex &index) const override
void handleTorrentsUpdated(const QVector< BitTorrent::Torrent * > &torrents)
BitTorrent::Torrent * torrentHandle(const QModelIndex &index) const
TransferListModel(QObject *parent=nullptr)
int columnCount(const QModelIndex &parent={}) const override
void handleTorrentAboutToBeRemoved(BitTorrent::Torrent *const torrent)
static UIThemeManager * instance()
QColor getColor(const QString &id, const QColor &defaultColor) const
QIcon getIcon(const QString &iconId, const QString &fallback={}) const
constexpr std::add_const_t< T > & asConst(T &t) noexcept
Definition: global.h:42
TorrentState
Definition: torrent.h:70
QString toNativePath(const QString &path)
Definition: fs.cpp:64
QString unitString(SizeUnit unit, bool isSpeed=false)
Definition: misc.cpp:252
QString userFriendlyDuration(qlonglong seconds, qlonglong maxCap=-1)
Definition: misc.cpp:353
QString friendlyUnit(qint64 bytes, bool isSpeed=false)
Definition: misc.cpp:261
QString fromDouble(double n, int precision)
Definition: string.cpp:44
T value(const QString &key, const T &defaultValue={})
Definition: preferences.cpp:64
QHash< BitTorrent::TorrentState, QColor > torrentStateColorsFromUITheme()
static QIcon getStalledDownloadingIcon()
static bool isDarkTheme()
static QIcon getCheckingIcon()
static QIcon getCompletedIcon()
static QIcon getIconByState(BitTorrent::TorrentState state)
static QColor getDefaultColorByState(BitTorrent::TorrentState state)
static QIcon getDownloadingIcon()
static QIcon getStalledUploadingIcon()
static QIcon getPausedIcon()
static QIcon getErrorIcon()
static QIcon getUploadingIcon()
static QIcon getQueuedIcon()
const qlonglong MAX_ETA
Definition: types.h:33
const char C_INFINITY[]