From c62cb35fd44f76e389d47f9371a6d935cd36fa07 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Mon, 11 Nov 2019 19:37:05 -0600 Subject: [PATCH] qt client speedups * faster updating of trackers combobox. * generate trackerDisplayNames just once per torrent * refactor: cache torrent delegate's warning emblem * refactor: change mainwin refresh debounce to 200ms * refactor: do not store trackers, hosts in QVariant * refactor: don't use `virtual` when it's not needed * refactor: faster counting torrents-matching-filter * refactor: faster tracker handling in filterbar * refactor: improve json parser's prealloc heuristic * refactor: make Torrent::hasError() faster * refactor: remove redundant speed stats collection * refactor: remove unnecessary tor->isQueued() calls * refactor: use unordered containers where possible * scale favicons only once, when adding to the cache --- libtransmission/variant-json.c | 42 +++++--- qt/AboutDialog.h | 1 - qt/Application.cc | 10 +- qt/Application.h | 10 +- qt/CMakeLists.txt | 1 + qt/ColumnResizer.h | 2 +- qt/DetailsDialog.cc | 28 ++--- qt/DetailsDialog.h | 9 +- qt/FaviconCache.cc | 78 +++++++++----- qt/FaviconCache.h | 25 +++-- qt/FileTreeDelegate.h | 8 +- qt/FileTreeModel.cc | 2 +- qt/FileTreeModel.h | 18 ++-- qt/FileTreeView.h | 10 +- qt/FilterBar.cc | 190 +++++++++++---------------------- qt/FilterBar.h | 4 +- qt/FilterBarComboBox.cc | 2 +- qt/FilterBarComboBox.h | 6 +- qt/FilterBarComboBoxDelegate.h | 4 +- qt/IconToolButton.h | 4 +- qt/MainWindow.cc | 109 ++++++++++--------- qt/MainWindow.h | 29 +++-- qt/MakeDialog.h | 4 +- qt/PathButton.h | 4 +- qt/PrefsDialog.cc | 2 +- qt/PrefsDialog.h | 1 - qt/RelocateDialog.cc | 2 +- qt/RelocateDialog.h | 7 +- qt/RpcClient.cc | 2 +- qt/RpcClient.h | 2 +- qt/Session.cc | 69 ++++++------ qt/Session.h | 48 ++++----- qt/SessionDialog.h | 6 +- qt/SqueezeLabel.h | 2 +- qt/StatsDialog.h | 4 +- qt/Torrent.cc | 24 ++--- qt/Torrent.h | 17 ++- qt/TorrentDelegate.cc | 22 +++- qt/TorrentDelegate.h | 6 +- qt/TorrentDelegateMin.cc | 3 +- qt/TorrentDelegateMin.h | 8 +- qt/TorrentFilter.cc | 13 +-- qt/TorrentFilter.h | 4 +- qt/TorrentModel.cc | 90 +++++++--------- qt/TorrentModel.h | 28 ++--- qt/TorrentView.h | 2 +- qt/TrackerDelegate.cc | 2 +- qt/TrackerDelegate.h | 8 +- qt/TrackerModel.cc | 4 +- qt/TrackerModel.h | 16 +-- qt/Typedefs.h | 5 + qt/Utils.cc | 8 +- qt/Utils.h | 23 +++- qt/WatchDir.cc | 4 - qt/WatchDir.h | 1 - qt/qtr.pro | 2 +- 56 files changed, 504 insertions(+), 531 deletions(-) create mode 100644 qt/Typedefs.h diff --git a/libtransmission/variant-json.c b/libtransmission/variant-json.c index 2b8a9368c..0dc8a50c9 100644 --- a/libtransmission/variant-json.c +++ b/libtransmission/variant-json.c @@ -45,6 +45,12 @@ struct json_wrapper_data struct evbuffer* strbuf; char const* source; tr_ptrArray stack; + + /* A very common pattern is for a container's children to be similar, + * e.g. they may all be objects with the same set of keys. So when + * a container is popped off the stack, remember its size to use as + * a preallocation heuristic for the next container at that depth. */ + size_t preallocGuess[MAX_DEPTH]; }; static tr_variant* get_node(struct jsonsl_st* jsn) @@ -103,25 +109,22 @@ static void action_callback_PUSH(jsonsl_t jsn, jsonsl_action_t action UNUSED, st tr_variant* node; struct json_wrapper_data* data = jsn->data; - switch (state->type) + if ((state->type == JSONSL_T_LIST) || (state->type == JSONSL_T_OBJECT)) { - case JSONSL_T_LIST: data->has_content = true; node = get_node(jsn); - tr_variantInitList(node, 0); tr_ptrArrayAppend(&data->stack, node); - break; - case JSONSL_T_OBJECT: - data->has_content = true; - node = get_node(jsn); - tr_variantInitDict(node, 0); - tr_ptrArrayAppend(&data->stack, node); - break; - - default: - /* nothing else interesting on push */ - break; + int const depth = tr_ptrArraySize(&data->stack); + size_t const n = depth < MAX_DEPTH ? data->preallocGuess[depth] : 0; + if (state->type == JSONSL_T_LIST) + { + tr_variantInitList(node, n); + } + else + { + tr_variantInitDict(node, n); + } } } @@ -318,7 +321,12 @@ static void action_callback_POP(jsonsl_t jsn, jsonsl_action_t action UNUSED, str } else if (state->type == JSONSL_T_LIST || state->type == JSONSL_T_OBJECT) { - tr_ptrArrayPop(&data->stack); + int const depth = tr_ptrArraySize(&data->stack); + tr_variant const* v = tr_ptrArrayPop(&data->stack); + if (depth < MAX_DEPTH) + { + data->preallocGuess[depth] = v->val.l.count; + } } else if (state->type == JSONSL_T_SPECIAL) { @@ -369,6 +377,10 @@ int tr_jsonParse(char const* source, void const* vbuf, size_t len, tr_variant* s data.source = source; data.keybuf = evbuffer_new(); data.strbuf = evbuffer_new(); + for (int i = 0; i < MAX_DEPTH; ++i) + { + data.preallocGuess[i] = 0; + } /* parse it */ jsonsl_feed(jsn, vbuf, len); diff --git a/qt/AboutDialog.h b/qt/AboutDialog.h index 14c83bbab..ce479490e 100644 --- a/qt/AboutDialog.h +++ b/qt/AboutDialog.h @@ -22,7 +22,6 @@ class AboutDialog : public BaseDialog public: AboutDialog(QWidget* parent = nullptr); - virtual ~AboutDialog() = default; private slots: void showCredits(); diff --git a/qt/Application.cc b/qt/Application.cc index b4c0e4c9d..6e2867239 100644 --- a/qt/Application.cc +++ b/qt/Application.cc @@ -399,7 +399,7 @@ void Application::quitLater() QTimer::singleShot(0, this, SLOT(quit())); } -QStringList Application::getNames(QSet const& ids) const +QStringList Application::getNames(torrent_ids_t const& ids) const { QStringList names; for (auto const& id : ids) @@ -411,7 +411,7 @@ QStringList Application::getNames(QSet const& ids) const return names; } -void Application::onTorrentsAdded(QSet const& ids) +void Application::onTorrentsAdded(torrent_ids_t const& ids) { if (myPrefs->getBool(Prefs::SHOW_NOTIFICATION_ON_ADD)) { @@ -421,7 +421,7 @@ void Application::onTorrentsAdded(QSet const& ids) } } -void Application::onTorrentsCompleted(QSet const& ids) +void Application::onTorrentsCompleted(torrent_ids_t const& ids) { if (myPrefs->getBool(Prefs::SHOW_NOTIFICATION_ON_COMPLETE)) { @@ -440,9 +440,9 @@ void Application::onTorrentsCompleted(QSet const& ids) } } -void Application::onTorrentsNeedInfo(QSet const& ids) +void Application::onTorrentsNeedInfo(torrent_ids_t const& ids) { - if (!ids.isEmpty()) + if (!ids.empty()) { mySession->initTorrents(ids); } diff --git a/qt/Application.h b/qt/Application.h index ac86addec..dec2d7386 100644 --- a/qt/Application.h +++ b/qt/Application.h @@ -9,11 +9,11 @@ #pragma once #include -#include #include #include #include "FaviconCache.h" +#include "Typedefs.h" class AddData; class Prefs; @@ -43,14 +43,14 @@ private slots: void onSessionSourceChanged(); void refreshPref(int key); void refreshTorrents(); - void onTorrentsAdded(QSet const& torrents); - void onTorrentsCompleted(QSet const& torrents); - void onTorrentsNeedInfo(QSet const& torrents); + void onTorrentsAdded(torrent_ids_t const& torrents); + void onTorrentsCompleted(torrent_ids_t const& torrents); + void onTorrentsNeedInfo(torrent_ids_t const& torrents); private: void maybeUpdateBlocklist(); void loadTranslations(); - QStringList getNames(QSet const& ids) const; + QStringList getNames(torrent_ids_t const& ids) const; void quitLater(); private: diff --git a/qt/CMakeLists.txt b/qt/CMakeLists.txt index 4c364d807..a6dd8c0be 100644 --- a/qt/CMakeLists.txt +++ b/qt/CMakeLists.txt @@ -106,6 +106,7 @@ set(${PROJECT_NAME}_HEADERS TrackerDelegate.h TrackerModel.h TrackerModelFilter.h + Typedefs.h Utils.h WatchDir.h ) diff --git a/qt/ColumnResizer.h b/qt/ColumnResizer.h index dc9ad7fc5..fe4f6a66e 100644 --- a/qt/ColumnResizer.h +++ b/qt/ColumnResizer.h @@ -24,7 +24,7 @@ public: void addLayout(QGridLayout* layout); // QObject - virtual bool eventFilter(QObject* object, QEvent* event); + bool eventFilter(QObject* object, QEvent* event) override; public slots: void update(); diff --git a/qt/DetailsDialog.cc b/qt/DetailsDialog.cc index 1db99dece..4afde07ff 100644 --- a/qt/DetailsDialog.cc +++ b/qt/DetailsDialog.cc @@ -6,6 +6,7 @@ * */ +#include // std::any_of #include #include /* INT_MAX */ #include @@ -249,7 +250,7 @@ DetailsDialog::~DetailsDialog() myTrackerModel->deleteLater(); } -void DetailsDialog::setIds(QSet const& ids) +void DetailsDialog::setIds(torrent_ids_t const& ids) { if (ids != myIds) { @@ -309,15 +310,20 @@ void DetailsDialog::getNewData() } } -void DetailsDialog::onTorrentsChanged(QSet const& ids) +void DetailsDialog::onTorrentsChanged(torrent_ids_t const& ids) { - auto const interesting = ids & myIds; - - if (!interesting.isEmpty() && !myHavePendingRefresh) + if (myHavePendingRefresh) { - myHavePendingRefresh = true; - QTimer::singleShot(100, this, SLOT(refresh())); + return; } + + if (!std::any_of(ids.begin(), ids.end(), [this](auto const& id) { return myIds.count(id) != 0; })) + { + return; + } + + myHavePendingRefresh = true; + QTimer::singleShot(100, this, SLOT(refresh())); } namespace @@ -1290,7 +1296,7 @@ void DetailsDialog::onAddTrackerClicked() } else { - QSet ids; + torrent_ids_t ids; for (int const id : myIds) { @@ -1336,8 +1342,7 @@ void DetailsDialog::onEditTrackerClicked() } else { - QSet ids; - ids << trackerInfo.torrentId; + torrent_ids_t ids{ trackerInfo.torrentId }; QPair const idUrl = qMakePair(trackerInfo.st.id, newval); @@ -1362,8 +1367,7 @@ void DetailsDialog::onRemoveTrackerClicked() // batch all of a tracker's torrents into one command for (int const id : torrentId_to_trackerIds.uniqueKeys()) { - QSet ids; - ids << id; + torrent_ids_t const ids{ id }; mySession.torrentSet(ids, TR_KEY_trackerRemove, torrentId_to_trackerIds.values(id)); } diff --git a/qt/DetailsDialog.h b/qt/DetailsDialog.h index 7a17c03b6..ebb117851 100644 --- a/qt/DetailsDialog.h +++ b/qt/DetailsDialog.h @@ -14,6 +14,7 @@ #include #include "BaseDialog.h" +#include "Typedefs.h" #include "ui_DetailsDialog.h" @@ -35,10 +36,10 @@ public: DetailsDialog(Session&, Prefs&, TorrentModel const&, QWidget* parent = nullptr); virtual ~DetailsDialog(); - void setIds(QSet const& ids); + void setIds(torrent_ids_t const& ids); // QWidget - virtual QSize sizeHint() const + QSize sizeHint() const override { return QSize(440, 460); } @@ -59,7 +60,7 @@ private slots: void refresh(); void refreshPref(int key); - void onTorrentsChanged(QSet const& ids); + void onTorrentsChanged(torrent_ids_t const& ids); void onTimer(); // Tracker tab @@ -93,7 +94,7 @@ private: Ui::DetailsDialog ui; - QSet myIds; + torrent_ids_t myIds; QTimer myTimer; bool myChangedTorrents; bool myHavePendingRefresh; diff --git a/qt/FaviconCache.cc b/qt/FaviconCache.cc index ca9220405..42d7e514c 100644 --- a/qt/FaviconCache.cc +++ b/qt/FaviconCache.cc @@ -40,6 +40,15 @@ QString FaviconCache::getCacheDir() return QDir(base).absoluteFilePath(QLatin1String("favicons")); } +namespace +{ + +QPixmap scale(QPixmap pixmap) +{ + return pixmap.scaled(FaviconCache::getIconSize(), Qt::KeepAspectRatio, Qt::SmoothTransformation); +} + +} void FaviconCache::ensureCacheDirHasBeenScanned() { static bool hasBeenScanned = false; @@ -60,24 +69,44 @@ void FaviconCache::ensureCacheDirHasBeenScanned() if (!pixmap.isNull()) { - myPixmaps.insert(file, pixmap); + myPixmaps[file] = scale(pixmap); } } } } -QString FaviconCache::getHost(QUrl const& url) +/*** +**** +***/ + +QString FaviconCache::getDisplayName(QUrl const& url) { - QString host = url.host(); - int const first_dot = host.indexOf(QLatin1Char('.')); - int const last_dot = host.lastIndexOf(QLatin1Char('.')); + return getDisplayName(getKey(url)); +} - if (first_dot != -1 && last_dot != -1 && first_dot != last_dot) - { - host.remove(0, first_dot + 1); - } +QString FaviconCache::getDisplayName(QString const& key) +{ + auto name = key; + name[0] = name.at(0).toTitleCase(); + return name; +} - return host; +QString FaviconCache::getKey(QUrl const& url) +{ + auto host = url.host(); + + // remove tld + auto const suffix = url.topLevelDomain(); + host.truncate(host.size() - suffix.size()); + + // remove subdomain + auto const pos = host.indexOf(QLatin1Char('.')); + return pos < 0 ? host : host.remove(0, pos + 1); +} + +QString FaviconCache::getKey(QString const& displayName) +{ + return displayName.toLower(); } QSize FaviconCache::getIconSize() @@ -85,31 +114,26 @@ QSize FaviconCache::getIconSize() return QSize(16, 16); } -QPixmap FaviconCache::find(QUrl const& url) -{ - return findFromHost(getHost(url)); -} - -QPixmap FaviconCache::findFromHost(QString const& host) +QPixmap FaviconCache::find(QString const& key) { ensureCacheDirHasBeenScanned(); - return myPixmaps[host]; + return myPixmaps[key]; } -void FaviconCache::add(QUrl const& url) +QString FaviconCache::add(QUrl const& url) { ensureCacheDirHasBeenScanned(); - QString const host = getHost(url); + QString const key = getKey(url); - if (!myPixmaps.contains(host)) + if (myPixmaps.count(key) == 0) { // add a placholder s.t. we only ping the server once per session - myPixmaps.insert(host, QPixmap()); + myPixmaps[key] = QPixmap(); // try to download the favicon - QString const path = QLatin1String("http://") + host + QLatin1String("/favicon."); + QString const path = QLatin1String("http://") + url.host() + QLatin1String("/favicon."); QStringList suffixes; suffixes << QLatin1String("ico") << QLatin1String("png") << QLatin1String("gif") << QLatin1String("jpg"); @@ -118,11 +142,13 @@ void FaviconCache::add(QUrl const& url) myNAM->get(QNetworkRequest(path + suffix)); } } + + return key; } void FaviconCache::onRequestFinished(QNetworkReply* reply) { - QString const host = reply->url().host(); + auto const key = getKey(reply->url()); QPixmap pixmap; @@ -136,17 +162,17 @@ void FaviconCache::onRequestFinished(QNetworkReply* reply) if (!pixmap.isNull()) { // save it in memory... - myPixmaps.insert(host, pixmap); + myPixmaps[key] = scale(pixmap); // save it on disk... QDir cacheDir(getCacheDir()); cacheDir.mkpath(cacheDir.absolutePath()); - QFile file(cacheDir.absoluteFilePath(host)); + QFile file(cacheDir.absoluteFilePath(key)); file.open(QIODevice::WriteOnly); file.write(content); file.close(); // notify listeners - emit pixmapReady(host); + emit pixmapReady(key); } } diff --git a/qt/FaviconCache.h b/qt/FaviconCache.h index 3b81970d9..3ce0618b5 100644 --- a/qt/FaviconCache.h +++ b/qt/FaviconCache.h @@ -8,11 +8,14 @@ #pragma once -#include +#include + #include #include #include +#include // std::hash + class QNetworkAccessManager; class QNetworkReply; class QUrl; @@ -26,19 +29,21 @@ public: virtual ~FaviconCache(); // returns a cached pixmap, or a NULL pixmap if there's no match in the cache - QPixmap find(QUrl const& url); + QPixmap find(QString const& key); + QPixmap find(QUrl const& url) { return find(getKey(url)); } - // returns a cached pixmap, or a NULL pixmap if there's no match in the cache - QPixmap findFromHost(QString const& host); + // This will emit a signal when (if) the icon becomes ready. + // Returns the key. + QString add(QUrl const& url); - // this will emit a signal when (if) the icon becomes ready - void add(QUrl const& url); - - static QString getHost(QUrl const& url); + static QString getDisplayName(QUrl const& url); + static QString getDisplayName(QString const& key); + static QString getKey(QUrl const& url); + static QString getKey(QString const& displayName); static QSize getIconSize(); signals: - void pixmapReady(QString const& host); + void pixmapReady(QString const& key); private: QString getCacheDir(); @@ -49,5 +54,5 @@ private slots: private: QNetworkAccessManager* myNAM; - QMap myPixmaps; + std::unordered_map myPixmaps; }; diff --git a/qt/FileTreeDelegate.h b/qt/FileTreeDelegate.h index 97375fa4c..1b121f17b 100644 --- a/qt/FileTreeDelegate.h +++ b/qt/FileTreeDelegate.h @@ -20,12 +20,8 @@ public: { } - virtual ~FileTreeDelegate() - { - } - public: // QAbstractItemDelegate - virtual QSize sizeHint(QStyleOptionViewItem const&, QModelIndex const&) const; - virtual void paint(QPainter*, QStyleOptionViewItem const&, QModelIndex const&) const; + QSize sizeHint(QStyleOptionViewItem const&, QModelIndex const&) const override; + void paint(QPainter*, QStyleOptionViewItem const&, QModelIndex const&) const override; }; diff --git a/qt/FileTreeModel.cc b/qt/FileTreeModel.cc index a1ed8cd6e..fb484e7ba 100644 --- a/qt/FileTreeModel.cc +++ b/qt/FileTreeModel.cc @@ -292,7 +292,7 @@ int FileTreeModel::rowCount(QModelIndex const& parent) const int FileTreeModel::columnCount(QModelIndex const& parent) const { - Q_UNUSED(parent); + Q_UNUSED(parent) return NUM_COLUMNS; } diff --git a/qt/FileTreeModel.h b/qt/FileTreeModel.h index 15ef0f2aa..fe966ca8f 100644 --- a/qt/FileTreeModel.h +++ b/qt/FileTreeModel.h @@ -43,7 +43,7 @@ public: public: FileTreeModel(QObject* parent = nullptr, bool isEditable = true); - virtual ~FileTreeModel(); + ~FileTreeModel(); void setEditable(bool editable); @@ -62,14 +62,14 @@ public: QModelIndex parent(QModelIndex const& child, int column) const; // QAbstractItemModel - virtual QVariant data(QModelIndex const& index, int role = Qt::DisplayRole) const; - virtual Qt::ItemFlags flags(QModelIndex const& index) const; - virtual QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; - virtual QModelIndex index(int row, int column, QModelIndex const& parent = QModelIndex()) const; - virtual QModelIndex parent(QModelIndex const& child) const; - virtual int rowCount(QModelIndex const& parent = QModelIndex()) const; - virtual int columnCount(QModelIndex const& parent = QModelIndex()) const; - virtual bool setData(QModelIndex const& index, QVariant const& value, int role = Qt::EditRole); + QVariant data(QModelIndex const& index, int role = Qt::DisplayRole) const override; + Qt::ItemFlags flags(QModelIndex const& index) const override; + QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; + QModelIndex index(int row, int column, QModelIndex const& parent = QModelIndex()) const override; + QModelIndex parent(QModelIndex const& child) const override; + int rowCount(QModelIndex const& parent = QModelIndex()) const override; + int columnCount(QModelIndex const& parent = QModelIndex()) const override; + bool setData(QModelIndex const& index, QVariant const& value, int role = Qt::EditRole) override; signals: void priorityChanged(QSet const& fileIndices, int); diff --git a/qt/FileTreeView.h b/qt/FileTreeView.h index e66fc1486..ccaf482b2 100644 --- a/qt/FileTreeView.h +++ b/qt/FileTreeView.h @@ -40,13 +40,13 @@ signals: protected: // QWidget - virtual void resizeEvent(QResizeEvent* event); - virtual void keyPressEvent(QKeyEvent* event); - virtual void mouseDoubleClickEvent(QMouseEvent* event); - virtual void contextMenuEvent(QContextMenuEvent* event); + void resizeEvent(QResizeEvent* event) override; + void keyPressEvent(QKeyEvent* event) override; + void mouseDoubleClickEvent(QMouseEvent* event) override; + void contextMenuEvent(QContextMenuEvent* event) override; // QAbstractItemView - virtual bool edit(QModelIndex const& index, EditTrigger trigger, QEvent* event); + bool edit(QModelIndex const& index, EditTrigger trigger, QEvent* event) override; private slots: void onClicked(QModelIndex const& index); diff --git a/qt/FilterBar.cc b/qt/FilterBar.cc index 1a6567600..d84258642 100644 --- a/qt/FilterBar.cc +++ b/qt/FilterBar.cc @@ -6,6 +6,9 @@ * */ +#include +#include + #include #include #include @@ -21,6 +24,7 @@ #include "Torrent.h" #include "TorrentFilter.h" #include "TorrentModel.h" +#include "Utils.h" enum { @@ -28,30 +32,6 @@ enum TrackerRole }; -namespace -{ - -QString readableHostName(QString const& host) -{ - // get the readable name... - QString name = host; - int const pos = name.lastIndexOf(QLatin1Char('.')); - - if (pos >= 0) - { - name.truncate(pos); - } - - if (!name.isEmpty()) - { - name[0] = name[0].toUpper(); - } - - return name; -} - -} // namespace - /*** **** ***/ @@ -107,122 +87,88 @@ FilterBarComboBox* FilterBar::createActivityCombo() **** ***/ +namespace +{ + +QString getCountString(int n) +{ + return QString::fromLatin1("%L1").arg(n); +} + +} // namespace + void FilterBar::refreshTrackers() { - FaviconCache& favicons = qApp->faviconCache(); - int const firstTrackerRow = 2; // skip over the "All" and separator... - - // pull info from the tracker model... - QSet oldHosts; - - for (int row = firstTrackerRow;; ++row) + enum { - QModelIndex index = myTrackerModel->index(row, 0); + ROW_TOTALS = 0, ROW_SEPARATOR, ROW_FIRST_TRACKER + }; - if (!index.isValid()) - { - break; - } - - oldHosts << index.data(TrackerRole).toString(); - } - - // pull the new stats from the torrent model... - QSet newHosts; - QMap torrentsPerHost; - - for (int row = 0;; ++row) + auto torrentsPerHost = std::unordered_map{}; + for (auto const& tor : myTorrents.torrents()) { - QModelIndex index = myTorrents.index(row, 0); - - if (!index.isValid()) + for (auto const& displayName : tor->trackerDisplayNames()) { - break; - } - - Torrent const* tor = index.data(TorrentModel::TorrentRole).value(); - QSet torrentNames; - - for (QString const& host : tor->hosts()) - { - newHosts.insert(host); - torrentNames.insert(readableHostName(host)); - } - - for (QString const& name : torrentNames) - { - ++torrentsPerHost[name]; + ++torrentsPerHost[displayName]; } } // update the "All" row - myTrackerModel->setData(myTrackerModel->index(0, 0), myTorrents.rowCount(), FilterBarComboBox::CountRole); - myTrackerModel->setData(myTrackerModel->index(0, 0), getCountString(myTorrents.rowCount()), - FilterBarComboBox::CountStringRole); + auto const num_trackers = torrentsPerHost.size(); + auto item = myTrackerModel->item(ROW_TOTALS); + item->setData(int(num_trackers), FilterBarComboBox::CountRole); + item->setData(getCountString(num_trackers), FilterBarComboBox::CountStringRole); - // rows to update - for (QString const& host : oldHosts & newHosts) - { - QString const name = readableHostName(host); - QStandardItem* row = myTrackerModel->findItems(name).front(); - int const count = torrentsPerHost[name]; - row->setData(count, FilterBarComboBox::CountRole); - row->setData(getCountString(count), FilterBarComboBox::CountStringRole); - row->setData(QIcon(favicons.findFromHost(host)), Qt::DecorationRole); - } - - // rows to remove - for (QString const& host : oldHosts - newHosts) - { - QString const name = readableHostName(host); - QStandardItem* item = myTrackerModel->findItems(name).front(); - - if (!item->data(TrackerRole).toString().isEmpty()) // don't remove "All" + auto updateTrackerItem = [](QStandardItem* i, auto const& it) { - myTrackerModel->removeRows(item->row(), 1); - } - } + auto const& displayName = it->first; + auto const& count = it->second; + auto const icon = qApp->faviconCache().find(FaviconCache::getKey(displayName)); + i->setData(displayName, Qt::DisplayRole); + i->setData(displayName, TrackerRole); + i->setData(getCountString(count), FilterBarComboBox::CountStringRole); + i->setData(icon, Qt::DecorationRole); + i->setData(int(count), FilterBarComboBox::CountRole); + return i; + }; - // rows to add + auto newTrackers = std::map(torrentsPerHost.begin(), torrentsPerHost.end()); + auto old_it = myTrackerCounts.cbegin(); + auto new_it = newTrackers.cbegin(); + auto const old_end = myTrackerCounts.cend(); + auto const new_end = newTrackers.cend(); bool anyAdded = false; + int row = ROW_FIRST_TRACKER; - for (QString const& host : newHosts - oldHosts) + while ((old_it != old_end) || (new_it != new_end)) { - QString const name = readableHostName(host); - - if (!myTrackerModel->findItems(name).isEmpty()) + if ((old_it == old_end) || ((new_it != new_end) && (old_it->first > new_it->first))) { - continue; + myTrackerModel->insertRow(row, updateTrackerItem(new QStandardItem(1), new_it)); + anyAdded = true; + ++new_it; + ++row; } - - // find the sorted position to add this row - int i = firstTrackerRow; - - for (int n = myTrackerModel->rowCount(); i < n; ++i) + else if ((new_it == new_end) || ((old_it != old_end) && (old_it->first < new_it->first))) { - QString const rowName = myTrackerModel->index(i, 0).data(Qt::DisplayRole).toString(); - - if (rowName >= name) - { - break; - } + myTrackerModel->removeRow(row); + ++old_it; + } + else // update + { + updateTrackerItem(myTrackerModel->item(row), new_it); + ++old_it; + ++new_it; + ++row; } - - // add the row - QStandardItem* row = new QStandardItem(favicons.findFromHost(host), name); - int const count = torrentsPerHost[host]; - row->setData(count, FilterBarComboBox::CountRole); - row->setData(getCountString(count), FilterBarComboBox::CountStringRole); - row->setData(QIcon(favicons.findFromHost(host)), Qt::DecorationRole); - row->setData(host, TrackerRole); - myTrackerModel->insertRow(i, row); - anyAdded = true; } if (anyAdded) // the one added might match our filter... { refreshPref(Prefs::FILTER_TRACKERS); } + + myTrackerCounts.swap(newTrackers); } FilterBarComboBox* FilterBar::createTrackerCombo(QStandardItemModel* model) @@ -294,10 +240,7 @@ FilterBar::FilterBar(Prefs& prefs, TorrentModel const& torrents, TorrentFilter c myIsBootstrapping = false; // initialize our state - QList initKeys; - initKeys << Prefs::FILTER_MODE << Prefs::FILTER_TRACKERS; - - for (int const key : initKeys) + for (int const key : { Prefs::FILTER_MODE, Prefs::FILTER_TRACKERS }) { refreshPref(key); } @@ -338,10 +281,8 @@ void FilterBar::refreshPref(int key) case Prefs::FILTER_TRACKERS: { - QString const tracker = myPrefs.getString(key); - QString const name = readableHostName(tracker); - QList rows = myTrackerModel->findItems(name); - + auto const displayName = myPrefs.getString(key); + auto rows = myTrackerModel->findItems(displayName); if (!rows.isEmpty()) { myTrackerCombo->setCurrentIndex(rows.front()->row()); @@ -435,8 +376,3 @@ void FilterBar::recount() refreshTrackers(); } - -QString FilterBar::getCountString(int n) const -{ - return QString::fromLatin1("%L1").arg(n); -} diff --git a/qt/FilterBar.h b/qt/FilterBar.h index 24d8a17ae..e1333526c 100644 --- a/qt/FilterBar.h +++ b/qt/FilterBar.h @@ -8,6 +8,8 @@ #pragma once +#include + #include class QLabel; @@ -35,7 +37,6 @@ private: FilterBarComboBox* createTrackerCombo(QStandardItemModel*); FilterBarComboBox* createActivityCombo(); void refreshTrackers(); - QString getCountString(int n) const; private slots: void recountSoon(); @@ -57,4 +58,5 @@ private: QTimer* myRecountTimer; bool myIsBootstrapping; QLineEdit* myLineEdit; + std::map myTrackerCounts; }; diff --git a/qt/FilterBarComboBox.cc b/qt/FilterBarComboBox.cc index 7aac7e3c8..c07fa71a3 100644 --- a/qt/FilterBarComboBox.cc +++ b/qt/FilterBarComboBox.cc @@ -89,7 +89,7 @@ QSize FilterBarComboBox::calculateSize(QSize const& textSize, QSize const& count void FilterBarComboBox::paintEvent(QPaintEvent* e) { - Q_UNUSED(e); + Q_UNUSED(e) QStylePainter painter(this); painter.setPen(palette().color(QPalette::Text)); diff --git a/qt/FilterBarComboBox.h b/qt/FilterBarComboBox.h index 7680894b7..514c2df49 100644 --- a/qt/FilterBarComboBox.h +++ b/qt/FilterBarComboBox.h @@ -28,12 +28,12 @@ public: int currentCount() const; // QWidget - virtual QSize minimumSizeHint() const; - virtual QSize sizeHint() const; + QSize minimumSizeHint() const override; + QSize sizeHint() const override; protected: // QWidget - virtual void paintEvent(QPaintEvent* e); + void paintEvent(QPaintEvent* e) override; private: QSize calculateSize(QSize const& textSize, QSize const& countSize) const; diff --git a/qt/FilterBarComboBoxDelegate.h b/qt/FilterBarComboBoxDelegate.h index b19d5b6aa..3f0a60bf4 100644 --- a/qt/FilterBarComboBoxDelegate.h +++ b/qt/FilterBarComboBoxDelegate.h @@ -25,8 +25,8 @@ public: protected: // QAbstractItemDelegate - virtual void paint(QPainter*, QStyleOptionViewItem const&, QModelIndex const&) const; - virtual QSize sizeHint(QStyleOptionViewItem const&, QModelIndex const&) const; + void paint(QPainter*, QStyleOptionViewItem const&, QModelIndex const&) const override; + QSize sizeHint(QStyleOptionViewItem const&, QModelIndex const&) const override; private: QComboBox* const myCombo; diff --git a/qt/IconToolButton.h b/qt/IconToolButton.h index 9dd13aae0..8029d2aa5 100644 --- a/qt/IconToolButton.h +++ b/qt/IconToolButton.h @@ -18,9 +18,9 @@ public: IconToolButton(QWidget* parent = nullptr); // QWidget - virtual QSize sizeHint() const; + QSize sizeHint() const override; protected: // QWidget - virtual void paintEvent(QPaintEvent* event); + void paintEvent(QPaintEvent* event) override; }; diff --git a/qt/MainWindow.cc b/qt/MainWindow.cc index b49ef4334..467c30417 100644 --- a/qt/MainWindow.cc +++ b/qt/MainWindow.cc @@ -8,14 +8,14 @@ #include -#include #include +#include #include +#include +#include #include #include -#include -#include -#include +#include #include #include @@ -330,8 +330,9 @@ MainWindow::MainWindow(Session& session, Prefs& prefs, TorrentModel& model, bool refreshPref(key); } + auto refreshStatusSoon = [this]() { refreshSoon(REFRESH_STATUS_BAR); }; connect(&mySession, SIGNAL(sourceChanged()), this, SLOT(onSessionSourceChanged())); - connect(&mySession, SIGNAL(statsUpdated()), this, SLOT(refreshStatusBar())); + connect(&mySession, &Session::statsUpdated, refreshStatusSoon); connect(&mySession, SIGNAL(dataReadProgress()), this, SLOT(dataReadProgress())); connect(&mySession, SIGNAL(dataSendProgress()), this, SLOT(dataSendProgress())); connect(&mySession, SIGNAL(httpAuthenticationRequired()), this, SLOT(wrongAuthentication())); @@ -518,7 +519,7 @@ void MainWindow::setSortAscendingPref(bool b) void MainWindow::showEvent(QShowEvent* event) { - Q_UNUSED(event); + Q_UNUSED(event) ui.action_ShowMainWindow->setChecked(true); } @@ -529,7 +530,7 @@ void MainWindow::showEvent(QShowEvent* event) void MainWindow::hideEvent(QHideEvent* event) { - Q_UNUSED(event); + Q_UNUSED(event) if (!isVisible()) { @@ -610,7 +611,7 @@ static void openSelect(QString const& path) void MainWindow::openFolder() { - QSet const selectedTorrents = getSelectedTorrents(); + auto const selectedTorrents = getSelectedTorrents(); if (selectedTorrents.size() != 1) { @@ -692,10 +693,26 @@ void MainWindow::refreshSoon(int fields) if (!myRefreshTimer.isActive()) { myRefreshTimer.setSingleShot(true); - myRefreshTimer.start(100); + myRefreshTimer.start(200); } } +MainWindow::TransferStats MainWindow::getTransferStats() const +{ + TransferStats stats; + + for (auto const& tor : myModel.torrents()) + { + stats.speedUp += tor->uploadSpeed(); + stats.speedDown += tor->downloadSpeed(); + stats.peersSending += tor->webseedsWeAreDownloadingFrom(); + stats.peersSending += tor->peersWeAreDownloadingFrom(); + stats.peersReceiving += tor->peersWeAreUploadingTo(); + } + + return stats; +} + void MainWindow::onRefreshTimer() { int fields = 0; @@ -706,14 +723,19 @@ void MainWindow::onRefreshTimer() refreshTitle(); } - if (fields & REFRESH_STATUS_BAR) + if (fields & (REFRESH_TRAY_ICON | REFRESH_STATUS_BAR)) { - refreshStatusBar(); - } + auto const stats = getTransferStats(); - if (fields & REFRESH_TRAY_ICON) - { - refreshTrayIcon(); + if (fields & REFRESH_TRAY_ICON) + { + refreshTrayIcon(stats); + } + + if (fields & REFRESH_STATUS_BAR) + { + refreshStatusBar(stats); + } } if (fields & REFRESH_TORRENT_VIEW_HEADER) @@ -742,48 +764,37 @@ void MainWindow::refreshTitle() setWindowTitle(title); } -void MainWindow::refreshTrayIcon() +void MainWindow::refreshTrayIcon(TransferStats const& stats) { - Speed upSpeed; - Speed downSpeed; - size_t upCount; - size_t downCount; QString tip; - myModel.getTransferSpeed(upSpeed, upCount, downSpeed, downCount); - if (myNetworkError) { tip = tr("Network Error"); } - else if (upCount == 0 && downCount == 0) + else if (stats.peersSending == 0 && stats.peersReceiving == 0) { tip = tr("Idle"); } - else if (downCount != 0) + else if (stats.peersSending != 0) { - tip = Formatter::downloadSpeedToString(downSpeed) + QLatin1String(" ") + Formatter::uploadSpeedToString(upSpeed); + tip = Formatter::downloadSpeedToString(stats.speedDown) + QLatin1String(" ") + Formatter::uploadSpeedToString( + stats.speedUp); } - else if (upCount != 0) + else if (stats.peersReceiving != 0) { - tip = Formatter::uploadSpeedToString(upSpeed); + tip = Formatter::uploadSpeedToString(stats.speedUp); } myTrayIcon.setToolTip(tip); } -void MainWindow::refreshStatusBar() +void MainWindow::refreshStatusBar(TransferStats const& stats) { - Speed upSpeed; - Speed downSpeed; - size_t upCount; - size_t downCount; - myModel.getTransferSpeed(upSpeed, upCount, downSpeed, downCount); - - ui.uploadSpeedLabel->setText(Formatter::uploadSpeedToString(upSpeed)); - ui.uploadSpeedLabel->setVisible(downCount || upCount); - ui.downloadSpeedLabel->setText(Formatter::downloadSpeedToString(downSpeed)); - ui.downloadSpeedLabel->setVisible(downCount); + ui.uploadSpeedLabel->setText(Formatter::uploadSpeedToString(stats.speedUp)); + ui.uploadSpeedLabel->setVisible(stats.peersSending || stats.peersReceiving); + ui.downloadSpeedLabel->setText(Formatter::downloadSpeedToString(stats.speedDown)); + ui.downloadSpeedLabel->setVisible(stats.peersSending); ui.networkLabel->setVisible(!mySession.isServer()); @@ -833,7 +844,6 @@ void MainWindow::refreshTorrentViewHeader() void MainWindow::refreshActionSensitivity() { int paused(0); - int queued(0); int selected(0); int selectedAndCanAnnounce(0); int selectedAndPaused(0); @@ -848,19 +858,12 @@ void MainWindow::refreshActionSensitivity() for (int row = 0; row < rowCount; ++row) { QModelIndex const modelIndex(model->index(row, 0)); - assert(model == modelIndex.model()); - Torrent const* tor(model->data(modelIndex, TorrentModel::TorrentRole).value()); + auto const& tor = model->data(modelIndex, TorrentModel::TorrentRole).value(); if (tor != nullptr) { bool const isSelected(selectionModel->isSelected(modelIndex)); bool const isPaused(tor->isPaused()); - bool const isQueued(tor->isQueued()); - - if (isQueued) - { - ++queued; - } if (isPaused) { @@ -876,7 +879,7 @@ void MainWindow::refreshActionSensitivity() ++selectedAndPaused; } - if (isQueued) + if (tor->isQueued()) { ++selectedAndQueued; } @@ -936,9 +939,9 @@ void MainWindow::clearSelection() ui.action_DeselectAll->trigger(); } -QSet MainWindow::getSelectedTorrents(bool withMetadataOnly) const +torrent_ids_t MainWindow::getSelectedTorrents(bool withMetadataOnly) const { - QSet ids; + torrent_ids_t ids; for (QModelIndex const& index : ui.listView->selectionModel()->selectedRows()) { @@ -1120,7 +1123,7 @@ void MainWindow::refreshPref(int key) action->setChecked(str == action->property(STATS_MODE_KEY).toString()); } - refreshStatusBar(); + refreshSoon(REFRESH_STATUS_BAR); break; case Prefs::SORT_REVERSED: @@ -1340,7 +1343,7 @@ void MainWindow::addTorrent(AddData const& addMe, bool showOptions) void MainWindow::removeTorrents(bool const deleteFiles) { - QSet ids; + torrent_ids_t ids; QMessageBox msgBox(this); QString primary_text; QString secondary_text; @@ -1364,7 +1367,7 @@ void MainWindow::removeTorrents(bool const deleteFiles) } } - if (ids.isEmpty()) + if (ids.empty()) { return; } diff --git a/qt/MainWindow.h b/qt/MainWindow.h index 6062c4783..97ecbca1a 100644 --- a/qt/MainWindow.h +++ b/qt/MainWindow.h @@ -13,13 +13,15 @@ #include #include #include -#include #include #include #include #include "Filters.h" +#include "Speed.h" #include "TorrentFilter.h" +#include "Typedefs.h" + #include "ui_MainWindow.h" class QAction; @@ -84,15 +86,15 @@ public slots: protected: // QWidget - virtual void contextMenuEvent(QContextMenuEvent*); - virtual void dragEnterEvent(QDragEnterEvent*); - virtual void dropEvent(QDropEvent*); + void contextMenuEvent(QContextMenuEvent*) override; + void dragEnterEvent(QDragEnterEvent*) override; + void dropEvent(QDropEvent*) override; private: QIcon getStockIcon(QString const&, int fallback = -1); QIcon addEmblem(QIcon icon, QStringList const& emblemNames); - QSet getSelectedTorrents(bool withMetadataOnly = false) const; + torrent_ids_t getSelectedTorrents(bool withMetadataOnly = false) const; void updateNetworkIcon(); QMenu* createOptionsMenu(); @@ -103,8 +105,8 @@ private: void addTorrent(AddData const& addMe, bool showOptions); // QWidget - virtual void hideEvent(QHideEvent* event); - virtual void showEvent(QShowEvent* event); + void hideEvent(QHideEvent* event) override; + void showEvent(QShowEvent* event) override; private slots: void addTorrents(QStringList const& filenames); @@ -130,7 +132,6 @@ private slots: void openURL(); void refreshPref(int key); void refreshSoon(int fields = ~0); - void refreshStatusBar(); void removeTorrents(bool const deleteFiles); void setLocation(); void setSortAscendingPref(bool); @@ -176,6 +177,15 @@ private: QAction* myAltSpeedAction; QString myErrorMessage; + struct TransferStats + { + Speed speedUp; + Speed speedDown; + size_t peersSending = 0; + size_t peersReceiving = 0; + }; + TransferStats getTransferStats() const; + enum { REFRESH_TITLE = (1 << 0), @@ -187,6 +197,7 @@ private: int myRefreshFields = 0; QTimer myRefreshTimer; void refreshTitle(); - void refreshTrayIcon(); + void refreshTrayIcon(TransferStats const&); + void refreshStatusBar(TransferStats const&); void refreshTorrentViewHeader(); }; diff --git a/qt/MakeDialog.h b/qt/MakeDialog.h index efa7e3087..f6c9f812a 100644 --- a/qt/MakeDialog.h +++ b/qt/MakeDialog.h @@ -33,8 +33,8 @@ public: protected: // QWidget - virtual void dragEnterEvent(QDragEnterEvent*); - virtual void dropEvent(QDropEvent*); + void dragEnterEvent(QDragEnterEvent*) override; + void dropEvent(QDropEvent*) override; private: QString getSource() const; diff --git a/qt/PathButton.h b/qt/PathButton.h index 81003513b..c310df81c 100644 --- a/qt/PathButton.h +++ b/qt/PathButton.h @@ -32,14 +32,14 @@ public: QString const& path() const; // QWidget - virtual QSize sizeHint() const; + QSize sizeHint() const override; signals: void pathChanged(QString const& path); protected: // QWidget - virtual void paintEvent(QPaintEvent* event); + void paintEvent(QPaintEvent* event) override; private: void updateAppearance(); diff --git a/qt/PrefsDialog.cc b/qt/PrefsDialog.cc index fe538d2a8..4094965dd 100644 --- a/qt/PrefsDialog.cc +++ b/qt/PrefsDialog.cc @@ -433,7 +433,7 @@ void PrefsDialog::initNetworkTab() void PrefsDialog::onBlocklistDialogDestroyed(QObject* o) { - Q_UNUSED(o); + Q_UNUSED(o) myBlocklistDialog = nullptr; } diff --git a/qt/PrefsDialog.h b/qt/PrefsDialog.h index 30dde12bf..9d1095983 100644 --- a/qt/PrefsDialog.h +++ b/qt/PrefsDialog.h @@ -9,7 +9,6 @@ #pragma once #include -#include #include "BaseDialog.h" #include "Prefs.h" diff --git a/qt/RelocateDialog.cc b/qt/RelocateDialog.cc index acb501fcd..bb6836135 100644 --- a/qt/RelocateDialog.cc +++ b/qt/RelocateDialog.cc @@ -26,7 +26,7 @@ void RelocateDialog::onMoveToggled(bool b) myMoveFlag = b; } -RelocateDialog::RelocateDialog(Session& session, TorrentModel const& model, QSet const& ids, QWidget* parent) : +RelocateDialog::RelocateDialog(Session& session, TorrentModel const& model, torrent_ids_t const& ids, QWidget* parent) : BaseDialog(parent), mySession(session), myIds(ids) diff --git a/qt/RelocateDialog.h b/qt/RelocateDialog.h index d554f3506..276c3fa81 100644 --- a/qt/RelocateDialog.h +++ b/qt/RelocateDialog.h @@ -8,9 +8,8 @@ #pragma once -#include - #include "BaseDialog.h" +#include "Typedefs.h" #include "ui_RelocateDialog.h" @@ -22,7 +21,7 @@ class RelocateDialog : public BaseDialog Q_OBJECT public: - RelocateDialog(Session&, TorrentModel const&, QSet const& ids, QWidget* parent = nullptr); + RelocateDialog(Session&, TorrentModel const&, torrent_ids_t const& ids, QWidget* parent = nullptr); virtual ~RelocateDialog() { @@ -37,7 +36,7 @@ private slots: private: Session& mySession; - QSet const myIds; + torrent_ids_t const myIds; Ui::RelocateDialog ui; diff --git a/qt/RpcClient.cc b/qt/RpcClient.cc index c282aa92a..1d68a2d3e 100644 --- a/qt/RpcClient.cc +++ b/qt/RpcClient.cc @@ -204,7 +204,7 @@ QNetworkAccessManager* RpcClient::networkAccessManager() void RpcClient::localSessionCallback(tr_session* s, tr_variant* response, void* vself) { - Q_UNUSED(s); + Q_UNUSED(s) RpcClient* self = static_cast(vself); diff --git a/qt/RpcClient.h b/qt/RpcClient.h index abee24e48..7e22e7ff5 100644 --- a/qt/RpcClient.h +++ b/qt/RpcClient.h @@ -42,7 +42,7 @@ struct RpcResponse QNetworkReply::NetworkError networkError = QNetworkReply::NoError; }; -Q_DECLARE_METATYPE(QFutureInterface); +Q_DECLARE_METATYPE(QFutureInterface) // The response future -- the RPC engine returns one for each request made. typedef QFuture RpcResponseFuture; diff --git a/qt/Session.cc b/qt/Session.cc index b9f64885d..59a18e6c4 100644 --- a/qt/Session.cc +++ b/qt/Session.cc @@ -54,10 +54,10 @@ void addList(tr_variant* list, KeyList const& keys) } // If this object is passed as "ids" (compared by address), then recently active torrents are queried. -QSet const recentlyActiveIds = QSet() << -1; +auto const recentlyActiveIds = torrent_ids_t{ -1 }; // If this object is passed as "ids" (compared by being empty), then all torrents are queried. -QSet const allIds; +auto const allIds = torrent_ids_t{}; } // namespace @@ -405,13 +405,13 @@ bool Session::isLocal() const namespace { -void addOptionalIds(tr_variant* args, QSet const& ids) +void addOptionalIds(tr_variant* args, torrent_ids_t const& ids) { if (&ids == &recentlyActiveIds) { tr_variantDictAddStr(args, TR_KEY_ids, "recently-active"); } - else if (!ids.isEmpty()) + else if (!ids.empty()) { tr_variant* idList(tr_variantDictAddList(args, TR_KEY_ids, ids.size())); @@ -424,7 +424,7 @@ void addOptionalIds(tr_variant* args, QSet const& ids) } // namespace -void Session::torrentSet(QSet const& ids, tr_quark const key, double value) +void Session::torrentSet(torrent_ids_t const& ids, tr_quark const key, double value) { tr_variant args; tr_variantInitDict(&args, 2); @@ -434,7 +434,7 @@ void Session::torrentSet(QSet const& ids, tr_quark const key, double value) exec(TR_KEY_torrent_set, &args); } -void Session::torrentSet(QSet const& ids, tr_quark const key, int value) +void Session::torrentSet(torrent_ids_t const& ids, tr_quark const key, int value) { tr_variant args; tr_variantInitDict(&args, 2); @@ -444,7 +444,7 @@ void Session::torrentSet(QSet const& ids, tr_quark const key, int value) exec(TR_KEY_torrent_set, &args); } -void Session::torrentSet(QSet const& ids, tr_quark const key, bool value) +void Session::torrentSet(torrent_ids_t const& ids, tr_quark const key, bool value) { tr_variant args; tr_variantInitDict(&args, 2); @@ -454,7 +454,7 @@ void Session::torrentSet(QSet const& ids, tr_quark const key, bool value) exec(TR_KEY_torrent_set, &args); } -void Session::torrentSet(QSet const& ids, tr_quark const key, QStringList const& value) +void Session::torrentSet(torrent_ids_t const& ids, tr_quark const key, QStringList const& value) { tr_variant args; tr_variantInitDict(&args, 2); @@ -469,7 +469,7 @@ void Session::torrentSet(QSet const& ids, tr_quark const key, QStringList c exec(TR_KEY_torrent_set, &args); } -void Session::torrentSet(QSet const& ids, tr_quark const key, QList const& value) +void Session::torrentSet(torrent_ids_t const& ids, tr_quark const key, QList const& value) { tr_variant args; tr_variantInitDict(&args, 2); @@ -484,7 +484,7 @@ void Session::torrentSet(QSet const& ids, tr_quark const key, QList co exec(TR_KEY_torrent_set, &args); } -void Session::torrentSet(QSet const& ids, tr_quark const key, QPair const& value) +void Session::torrentSet(torrent_ids_t const& ids, tr_quark const key, QPair const& value) { tr_variant args; tr_variantInitDict(&args, 2); @@ -496,7 +496,7 @@ void Session::torrentSet(QSet const& ids, tr_quark const key, QPair const& ids, QString const& location, bool doMove) +void Session::torrentSetLocation(torrent_ids_t const& ids, QString const& location, bool doMove) { tr_variant args; tr_variantInitDict(&args, 3); @@ -507,7 +507,7 @@ void Session::torrentSetLocation(QSet const& ids, QString const& location, exec(TR_KEY_torrent_set_location, &args); } -void Session::torrentRenamePath(QSet const& ids, QString const& oldpath, QString const& newname) +void Session::torrentRenamePath(torrent_ids_t const& ids, QString const& oldpath, QString const& newname) { tr_variant args; tr_variantInitDict(&args, 2); @@ -536,20 +536,15 @@ void Session::torrentRenamePath(QSet const& ids, QString const& oldpath, QS d->show(); }); - q->add([this](RpcResponse const& r) + q->add([this, ids](RpcResponse const& /*r*/) { - int64_t id = 0; - - if (tr_variantDictFindInt(r.args.get(), TR_KEY_id, &id) && id != 0) - { - refreshTorrents(QSet() << id, KeyList() << TR_KEY_fileStats << TR_KEY_files << TR_KEY_id << TR_KEY_name); - } + refreshTorrents(ids, { TR_KEY_fileStats, TR_KEY_files, TR_KEY_id, TR_KEY_name }); }); q->run(); } -void Session::refreshTorrents(QSet const& ids, KeyList const& keys) +void Session::refreshTorrents(torrent_ids_t const& ids, KeyList const& keys) { tr_variant args; tr_variantInitDict(&args, 3); @@ -584,17 +579,17 @@ void Session::refreshTorrents(QSet const& ids, KeyList const& keys) q->run(); } -void Session::refreshDetailInfo(QSet const& ids) +void Session::refreshDetailInfo(torrent_ids_t const& ids) { refreshTorrents(ids, Torrent::detailInfoKeys); } -void Session::refreshExtraStats(QSet const& ids) +void Session::refreshExtraStats(torrent_ids_t const& ids) { refreshTorrents(ids, Torrent::mainStatKeys + Torrent::detailStatKeys); } -void Session::sendTorrentRequest(char const* request, QSet const& ids) +void Session::sendTorrentRequest(char const* request, torrent_ids_t const& ids) { tr_variant args; tr_variantInitDict(&args, 1); @@ -615,37 +610,37 @@ void Session::sendTorrentRequest(char const* request, QSet const& ids) q->run(); } -void Session::pauseTorrents(QSet const& ids) +void Session::pauseTorrents(torrent_ids_t const& ids) { sendTorrentRequest("torrent-stop", ids); } -void Session::startTorrents(QSet const& ids) +void Session::startTorrents(torrent_ids_t const& ids) { sendTorrentRequest("torrent-start", ids); } -void Session::startTorrentsNow(QSet const& ids) +void Session::startTorrentsNow(torrent_ids_t const& ids) { sendTorrentRequest("torrent-start-now", ids); } -void Session::queueMoveTop(QSet const& ids) +void Session::queueMoveTop(torrent_ids_t const& ids) { sendTorrentRequest("queue-move-top", ids); } -void Session::queueMoveUp(QSet const& ids) +void Session::queueMoveUp(torrent_ids_t const& ids) { sendTorrentRequest("queue-move-up", ids); } -void Session::queueMoveDown(QSet const& ids) +void Session::queueMoveDown(torrent_ids_t const& ids) { sendTorrentRequest("queue-move-down", ids); } -void Session::queueMoveBottom(QSet const& ids) +void Session::queueMoveBottom(torrent_ids_t const& ids) { sendTorrentRequest("queue-move-bottom", ids); } @@ -660,7 +655,7 @@ void Session::refreshAllTorrents() refreshTorrents(allIds, Torrent::mainStatKeys); } -void Session::initTorrents(QSet const& ids) +void Session::initTorrents(torrent_ids_t const& ids) { refreshTorrents(ids, Torrent::allMainKeys); } @@ -1047,9 +1042,9 @@ void Session::addNewlyCreatedTorrent(QString const& filename, QString const& loc exec("torrent-add", &args); } -void Session::removeTorrents(QSet const& ids, bool deleteFiles) +void Session::removeTorrents(torrent_ids_t const& ids, bool deleteFiles) { - if (!ids.isEmpty()) + if (!ids.empty()) { tr_variant args; tr_variantInitDict(&args, 2); @@ -1060,9 +1055,9 @@ void Session::removeTorrents(QSet const& ids, bool deleteFiles) } } -void Session::verifyTorrents(QSet const& ids) +void Session::verifyTorrents(torrent_ids_t const& ids) { - if (!ids.isEmpty()) + if (!ids.empty()) { tr_variant args; tr_variantInitDict(&args, 1); @@ -1072,9 +1067,9 @@ void Session::verifyTorrents(QSet const& ids) } } -void Session::reannounceTorrents(QSet const& ids) +void Session::reannounceTorrents(torrent_ids_t const& ids) { - if (!ids.isEmpty()) + if (!ids.empty()) { tr_variant args; tr_variantInitDict(&args, 1); diff --git a/qt/Session.h b/qt/Session.h index 6ddfa9754..599b7b825 100644 --- a/qt/Session.h +++ b/qt/Session.h @@ -9,7 +9,6 @@ #pragma once #include -#include #include #include @@ -18,6 +17,7 @@ #include "RpcClient.h" #include "Torrent.h" +#include "Typedefs.h" class AddData; class Prefs; @@ -77,37 +77,37 @@ public: RpcResponseFuture exec(tr_quark method, tr_variant* args); RpcResponseFuture exec(char const* method, tr_variant* args); - void torrentSet(QSet const& ids, tr_quark const key, bool val); - void torrentSet(QSet const& ids, tr_quark const key, int val); - void torrentSet(QSet const& ids, tr_quark const key, double val); - void torrentSet(QSet const& ids, tr_quark const key, QList const& val); - void torrentSet(QSet const& ids, tr_quark const key, QStringList const& val); - void torrentSet(QSet const& ids, tr_quark const key, QPair const& val); - void torrentSetLocation(QSet const& ids, QString const& path, bool doMove); - void torrentRenamePath(QSet const& ids, QString const& oldpath, QString const& newname); + void torrentSet(torrent_ids_t const& ids, tr_quark const key, bool val); + void torrentSet(torrent_ids_t const& ids, tr_quark const key, int val); + void torrentSet(torrent_ids_t const& ids, tr_quark const key, double val); + void torrentSet(torrent_ids_t const& ids, tr_quark const key, QList const& val); + void torrentSet(torrent_ids_t const& ids, tr_quark const key, QStringList const& val); + void torrentSet(torrent_ids_t const& ids, tr_quark const key, QPair const& val); + void torrentSetLocation(torrent_ids_t const& ids, QString const& path, bool doMove); + void torrentRenamePath(torrent_ids_t const& ids, QString const& oldpath, QString const& newname); void addTorrent(AddData const& addme, tr_variant* top, bool trashOriginal); - void initTorrents(QSet const& ids = QSet()); - void pauseTorrents(QSet const& torrentIds = QSet()); - void startTorrents(QSet const& torrentIds = QSet()); - void startTorrentsNow(QSet const& torrentIds = QSet()); - void refreshDetailInfo(QSet const& torrentIds); + void initTorrents(torrent_ids_t const& ids = {}); + void pauseTorrents(torrent_ids_t const& torrentIds = {}); + void startTorrents(torrent_ids_t const& torrentIds = {}); + void startTorrentsNow(torrent_ids_t const& torrentIds = {}); + void refreshDetailInfo(torrent_ids_t const& torrentIds); void refreshActiveTorrents(); void refreshAllTorrents(); void addNewlyCreatedTorrent(QString const& filename, QString const& localPath); - void verifyTorrents(QSet const& torrentIds); - void reannounceTorrents(QSet const& torrentIds); - void refreshExtraStats(QSet const& ids); + void verifyTorrents(torrent_ids_t const& torrentIds); + void reannounceTorrents(torrent_ids_t const& torrentIds); + void refreshExtraStats(torrent_ids_t const& ids); public slots: void addTorrent(AddData const& addme); void launchWebInterface(); - void queueMoveBottom(QSet const& torrentIds = QSet()); - void queueMoveDown(QSet const& torrentIds = QSet()); - void queueMoveTop(QSet const& torrentIds = QSet()); - void queueMoveUp(QSet const& torrentIds = QSet()); + void queueMoveBottom(torrent_ids_t const& torrentIds = {}); + void queueMoveDown(torrent_ids_t const& torrentIds = {}); + void queueMoveTop(torrent_ids_t const& torrentIds = {}); + void queueMoveUp(torrent_ids_t const& torrentIds = {}); void refreshSessionInfo(); void refreshSessionStats(); - void removeTorrents(QSet const& torrentIds, bool deleteFiles = false); + void removeTorrents(torrent_ids_t const& torrentIds, bool deleteFiles = false); void updatePref(int key); signals: @@ -131,8 +131,8 @@ private: void sessionSet(tr_quark const key, QVariant const& variant); void pumpRequests(); - void sendTorrentRequest(char const* request, QSet const& torrentIds); - void refreshTorrents(QSet const& torrentIds, Torrent::KeyList const& keys); + void sendTorrentRequest(char const* request, torrent_ids_t const& torrentIds); + void refreshTorrents(torrent_ids_t const& torrentIds, Torrent::KeyList const& keys); static void updateStats(tr_variant* d, tr_session_stats* stats); diff --git a/qt/SessionDialog.h b/qt/SessionDialog.h index 8052b9f4e..64501cfc5 100644 --- a/qt/SessionDialog.h +++ b/qt/SessionDialog.h @@ -24,13 +24,9 @@ class SessionDialog : public BaseDialog public: SessionDialog(Session& session, Prefs& prefs, QWidget* parent = nullptr); - virtual ~SessionDialog() - { - } - public slots: // QDialog - virtual void accept(); + void accept() override; private slots: void resensitize(); diff --git a/qt/SqueezeLabel.h b/qt/SqueezeLabel.h index 9933af3d7..71ade466e 100644 --- a/qt/SqueezeLabel.h +++ b/qt/SqueezeLabel.h @@ -53,5 +53,5 @@ public: protected: // QWidget - virtual void paintEvent(QPaintEvent* paintEvent); + void paintEvent(QPaintEvent* paintEvent) override; }; diff --git a/qt/StatsDialog.h b/qt/StatsDialog.h index 7b8472918..c113fcf83 100644 --- a/qt/StatsDialog.h +++ b/qt/StatsDialog.h @@ -22,10 +22,10 @@ class StatsDialog : public BaseDialog public: StatsDialog(Session&, QWidget* parent = nullptr); - ~StatsDialog(); + ~StatsDialog() override; // QWidget - virtual void setVisible(bool visible); + void setVisible(bool visible) override; private slots: void updateStats(); diff --git a/qt/Torrent.cc b/qt/Torrent.cc index 04b8a044b..5c0479c01 100644 --- a/qt/Torrent.cc +++ b/qt/Torrent.cc @@ -75,8 +75,6 @@ Torrent::Property Torrent::myProperties[] = { DOWNLOADED_EVER, TR_KEY_downloadedEver, QVariant::ULongLong }, { UPLOADED_EVER, TR_KEY_uploadedEver, QVariant::ULongLong }, { FAILED_EVER, TR_KEY_corruptEver, QVariant::ULongLong }, - { TRACKERS, TR_KEY_trackers, QVariant::StringList }, - { HOSTS, TR_KEY_NONE, QVariant::StringList }, { TRACKERSTATS, TR_KEY_trackerStats, CustomVariantType::TrackerStatsList }, { MIME_ICON, TR_KEY_NONE, QVariant::Icon }, { SEED_RATIO_LIMIT, TR_KEY_seedRatioLimit, QVariant::Double }, @@ -406,7 +404,7 @@ bool Torrent::hasFileSubstring(QString const& substr) const bool Torrent::hasTrackerSubstring(QString const& substr) const { - for (QString const& s : myValues[TRACKERS].toStringList()) + for (auto const& s : trackers()) { if (s.contains(substr, Qt::CaseInsensitive)) { @@ -505,7 +503,7 @@ int Torrent::compareETA(Torrent const& that) const int Torrent::compareTracker(Torrent const& that) const { - Q_UNUSED(that); + Q_UNUSED(that) // FIXME return 0; @@ -657,7 +655,6 @@ bool Torrent::update(tr_quark const* keys, tr_variant** values, size_t n) break; } - case QVariant::StringList: case CustomVariantType::PeerList: // handled below break; @@ -756,21 +753,21 @@ bool Torrent::update(tr_quark const* keys, tr_variant** values, size_t n) } // update the trackers - if (myValues[TRACKERS] != trackers) + if (trackers_ != trackers) { - QStringList hosts; + QStringList displayNames; + displayNames.reserve(trackers.size()); for (auto const& tracker : trackers) { auto const url = QUrl(tracker); - qApp->faviconCache().add(url); - hosts.append(FaviconCache::getHost(url)); + auto const key = qApp->faviconCache().add(url); + displayNames.append(FaviconCache::getDisplayName(key)); } - hosts.removeDuplicates(); - hosts.removeOne(QString()); + displayNames.removeDuplicates(); - myValues[TRACKERS].setValue(trackers); - myValues[HOSTS].setValue(hosts); + trackers_.swap(trackers); + trackerDisplayNames_.swap(displayNames); changed = true; } } @@ -794,7 +791,6 @@ bool Torrent::update(tr_quark const* keys, tr_variant** values, size_t n) if (tr_variantDictFindStr(child, TR_KEY_announce, &str, &len)) { trackerStat.announce = QString::fromUtf8(str, len); - qApp->faviconCache().add(QUrl(trackerStat.announce)); } if (tr_variantDictFindInt(child, TR_KEY_announceState, &i)) diff --git a/qt/Torrent.h b/qt/Torrent.h index 9b127efdc..77bee9db5 100644 --- a/qt/Torrent.h +++ b/qt/Torrent.h @@ -158,8 +158,6 @@ public: DOWNLOADED_EVER, UPLOADED_EVER, FAILED_EVER, - TRACKERS, - HOSTS, TRACKERSTATS, MIME_ICON, SEED_RATIO_LIMIT, @@ -188,8 +186,6 @@ public: public: Torrent(Prefs const&, int id); - virtual ~Torrent() = default; - ; int getBandwidthPriority() const { @@ -235,7 +231,7 @@ public: bool hasError() const { - return !getError().isEmpty(); + return getInt(ERROR) != TR_STAT_OK; } bool isDone() const @@ -493,14 +489,14 @@ public: return myValues[TRACKERSTATS].value(); } - QStringList trackers() const + QStringList const& trackers() const { - return myValues[TRACKERS].value(); + return trackers_; } - QStringList hosts() const + QStringList const& trackerDisplayNames() const { - return myValues[HOSTS].value(); + return trackerDisplayNames_; } PeerList peers() const @@ -619,6 +615,9 @@ private: bool setSize(int key, qulonglong); bool setTime(int key, time_t); + QStringList trackers_; + QStringList trackerDisplayNames_; + char const* getMimeTypeString() const; void updateMimeIcon(); diff --git a/qt/TorrentDelegate.cc b/qt/TorrentDelegate.cc index fe4df5750..548813cc7 100644 --- a/qt/TorrentDelegate.cc +++ b/qt/TorrentDelegate.cc @@ -155,7 +155,7 @@ TorrentDelegate::~TorrentDelegate() QSize TorrentDelegate::margin(QStyle const& style) const { - Q_UNUSED(style); + Q_UNUSED(style) return QSize(4, 4); } @@ -420,6 +420,23 @@ QSize TorrentDelegate::sizeHint(QStyleOptionViewItem const& option, QModelIndex return QSize(option.rect.width(), *myHeightHint); } +QIcon& TorrentDelegate::getWarningEmblem() const +{ + auto& icon = myWarningEmblem; + + if (icon.isNull()) + { + icon = QIcon::fromTheme(QLatin1String("emblem-important")); + } + + if (icon.isNull()) + { + icon = qApp->style()->standardIcon(QStyle::SP_MessageBoxWarning); + } + + return icon; +} + void TorrentDelegate::paint(QPainter* painter, QStyleOptionViewItem const& option, QModelIndex const& index) const { Torrent const* tor(index.data(TorrentModel::TorrentRole).value()); @@ -531,8 +548,7 @@ void TorrentDelegate::drawTorrent(QPainter* painter, QStyleOptionViewItem const& progressBarState |= QStyle::State_Small; QIcon::Mode const emblemIm = isItemSelected ? QIcon::Selected : QIcon::Normal; - QIcon const emblemIcon = tor.hasError() ? QIcon::fromTheme(QLatin1String("emblem-important"), - style->standardIcon(QStyle::SP_MessageBoxWarning)) : QIcon(); + QIcon const emblemIcon = tor.hasError() ? getWarningEmblem() : QIcon(); // layout QSize const m(margin(*style)); diff --git a/qt/TorrentDelegate.h b/qt/TorrentDelegate.h index b598ceee0..7388b7f3f 100644 --- a/qt/TorrentDelegate.h +++ b/qt/TorrentDelegate.h @@ -26,12 +26,13 @@ public: virtual ~TorrentDelegate(); // QAbstractItemDelegate - virtual QSize sizeHint(QStyleOptionViewItem const& option, QModelIndex const& index) const; - virtual void paint(QPainter* painter, QStyleOptionViewItem const& option, QModelIndex const& index) const; + QSize sizeHint(QStyleOptionViewItem const& option, QModelIndex const& index) const override; + void paint(QPainter* painter, QStyleOptionViewItem const& option, QModelIndex const& index) const override; protected: QSize margin(QStyle const& style) const; void setProgressBarPercentDone(QStyleOptionViewItem const& option, Torrent const&) const; + QIcon& getWarningEmblem() const; // Our own overridables virtual QSize sizeHint(QStyleOptionViewItem const&, Torrent const&) const; @@ -55,4 +56,5 @@ protected: private: mutable std::optional myHeightHint; mutable QFont myHeightFont; + mutable QIcon myWarningEmblem; }; diff --git a/qt/TorrentDelegateMin.cc b/qt/TorrentDelegateMin.cc index 68de76981..2f9bdeffc 100644 --- a/qt/TorrentDelegateMin.cc +++ b/qt/TorrentDelegateMin.cc @@ -221,8 +221,7 @@ void TorrentDelegateMin::drawTorrent(QPainter* painter, QStyleOptionViewItem con progressBarState |= QStyle::State_Small; QIcon::Mode const emblemIm = isItemSelected ? QIcon::Selected : QIcon::Normal; - QIcon const emblemIcon = tor.hasError() ? QIcon::fromTheme(QLatin1String("emblem-important"), - style->standardIcon(QStyle::SP_MessageBoxWarning)) : QIcon(); + QIcon const emblemIcon = tor.hasError() ? getWarningEmblem() : QIcon(); // layout QSize const m(margin(*style)); diff --git a/qt/TorrentDelegateMin.h b/qt/TorrentDelegateMin.h index 37811e428..9bf0d49b6 100644 --- a/qt/TorrentDelegateMin.h +++ b/qt/TorrentDelegateMin.h @@ -20,12 +20,8 @@ public: { } - virtual ~TorrentDelegateMin() - { - } - protected: // TorrentDelegate - virtual QSize sizeHint(QStyleOptionViewItem const&, Torrent const&) const; - virtual void drawTorrent(QPainter* painter, QStyleOptionViewItem const& option, Torrent const&) const; + QSize sizeHint(QStyleOptionViewItem const&, Torrent const&) const override; + void drawTorrent(QPainter* painter, QStyleOptionViewItem const& option, Torrent const&) const override; }; diff --git a/qt/TorrentFilter.cc b/qt/TorrentFilter.cc index 593e751b5..b3a2f0389 100644 --- a/qt/TorrentFilter.cc +++ b/qt/TorrentFilter.cc @@ -322,18 +322,9 @@ void TorrentFilter::countTorrentsPerMode(int* setmeCounts) const { std::fill_n(setmeCounts, static_cast(FilterMode::NUM_MODES), 0); - for (int row(0);; ++row) + for (auto const& tor : dynamic_cast(sourceModel())->torrents()) { - QModelIndex index(sourceModel()->index(row, 0)); - - if (!index.isValid()) - { - break; - } - - Torrent const* tor(index.data(TorrentModel::TorrentRole).value()); - - for (int mode(0); mode < FilterMode::NUM_MODES; ++mode) + for (int mode = 0; mode < FilterMode::NUM_MODES; ++mode) { if (activityFilterAcceptsTorrent(tor, mode)) { diff --git a/qt/TorrentFilter.h b/qt/TorrentFilter.h index 0a8fb521b..b1ad4961b 100644 --- a/qt/TorrentFilter.h +++ b/qt/TorrentFilter.h @@ -39,8 +39,8 @@ public: protected: // QSortFilterProxyModel - virtual bool filterAcceptsRow(int, QModelIndex const&) const; - virtual bool lessThan(QModelIndex const&, QModelIndex const&) const; + bool filterAcceptsRow(int, QModelIndex const&) const override; + bool lessThan(QModelIndex const&, QModelIndex const&) const override; private: bool activityFilterAcceptsTorrent(Torrent const* tor, FilterMode const& mode) const; diff --git a/qt/TorrentModel.cc b/qt/TorrentModel.cc index 7e873b6ab..c01fc5472 100644 --- a/qt/TorrentModel.cc +++ b/qt/TorrentModel.cc @@ -44,9 +44,9 @@ struct TorrentIdLessThan }; template -QSet getIds(Iter it, Iter end) +auto getIds(Iter it, Iter end) { - QSet ids; + torrent_ids_t ids; for ( ; it != end; ++it) { @@ -82,7 +82,7 @@ void TorrentModel::clear() int TorrentModel::rowCount(QModelIndex const& parent) const { - Q_UNUSED(parent); + Q_UNUSED(parent) return myTorrents.size(); } @@ -124,10 +124,11 @@ QVariant TorrentModel::data(QModelIndex const& index, int role) const void TorrentModel::removeTorrents(tr_variant* list) { + torrents_t torrents; + torrents.reserve(tr_variantListSize(list)); + int i = 0; tr_variant* child; - QSet torrents; - while ((child = tr_variantListChild(list, i++)) != nullptr) { int64_t id; @@ -140,11 +141,11 @@ void TorrentModel::removeTorrents(tr_variant* list) if (torrent != nullptr) { - torrents.insert(torrent); + torrents.push_back(torrent); } } - if (!torrents.isEmpty()) + if (!torrents.empty()) { rowsRemove(torrents); } @@ -152,13 +153,13 @@ void TorrentModel::removeTorrents(tr_variant* list) void TorrentModel::updateTorrents(tr_variant* torrents, bool isCompleteList) { - auto const old = QSet::fromList(myTorrents.toList()); - auto added = QSet{}; - auto changed = QSet{}; - auto completed = QSet{}; + auto const old = isCompleteList ? myTorrents : torrents_t{}; + auto added = torrent_ids_t{}; + auto changed = torrent_ids_t{}; + auto completed = torrent_ids_t{}; auto instantiated = torrents_t{}; - auto needinfo = QSet{}; - auto processed = QSet{}; + auto needinfo = torrent_ids_t{}; + auto processed = torrents_t{}; auto const now = time(nullptr); auto const recently_added = [now](auto const& tor) @@ -179,6 +180,7 @@ void TorrentModel::updateTorrents(tr_variant* torrents, bool isCompleteList) char const* str; size_t len; size_t i = 0; + keys.reserve(tr_variantListSize(firstChild)); while (tr_variantGetStr(tr_variantListChild(firstChild, i++), &str, &len)) { keys.push_back(tr_quark_new(str, len)); @@ -210,6 +212,7 @@ void TorrentModel::updateTorrents(tr_variant* torrents, bool isCompleteList) values.reserve(keys.size()); size_t tor_index = table ? 1 : 0; tr_variant* v; + processed.reserve(tr_variantListSize(torrents)); while ((v = tr_variantListChild(torrents, tor_index++))) { // Build an array of values @@ -245,11 +248,13 @@ void TorrentModel::updateTorrents(tr_variant* torrents, bool isCompleteList) Torrent* tor = getTorrentFromId(id); std::optional leftUntilDone; + bool is_new = false; if (tor == nullptr) { tor = new Torrent(myPrefs, id); instantiated.push_back(tor); + is_new = true; } else { @@ -261,12 +266,12 @@ void TorrentModel::updateTorrents(tr_variant* torrents, bool isCompleteList) changed.insert(id); } - if (!tor->hasName() && !old.contains(tor)) + if (is_new && !tor->hasName()) { needinfo.insert(id); } - if (recently_added(tor) && tor->hasName() && !myAlreadyAdded.contains(id)) + if (recently_added(tor) && tor->hasName() && !myAlreadyAdded.count(id)) { added.insert(id); myAlreadyAdded.insert(id); @@ -277,39 +282,39 @@ void TorrentModel::updateTorrents(tr_variant* torrents, bool isCompleteList) completed.insert(id); } - processed.insert(tor); + processed.push_back(tor); } // model upkeep - if (!instantiated.isEmpty()) + if (!instantiated.empty()) { rowsAdd(instantiated); } - if (!changed.isEmpty()) + if (!changed.empty()) { rowsEmitChanged(changed); } // emit signals - if (!added.isEmpty()) + if (!added.empty()) { emit torrentsAdded(added); } - if (!needinfo.isEmpty()) + if (!needinfo.empty()) { emit torrentsNeedInfo(needinfo); } - if (!changed.isEmpty()) + if (!changed.empty()) { emit torrentsChanged(changed); } - if (!completed.isEmpty()) + if (!completed.empty()) { emit torrentsCompleted(completed); } @@ -318,11 +323,11 @@ void TorrentModel::updateTorrents(tr_variant* torrents, bool isCompleteList) if (isCompleteList) { - auto const removed = old - processed; - if (!removed.isEmpty()) - { - rowsRemove(removed); - } + std::sort(processed.begin(), processed.end(), TorrentIdLessThan()); + torrents_t removed; + removed.reserve(old.size()); + std::set_difference(old.begin(), old.end(), processed.begin(), processed.end(), std::back_inserter(removed)); + rowsRemove(removed); } } @@ -365,7 +370,7 @@ Torrent const* TorrentModel::getTorrentFromId(int id) const **** ***/ -std::vector TorrentModel::getSpans(QSet const& ids) const +std::vector TorrentModel::getSpans(torrent_ids_t const& ids) const { // ids -> rows std::vector rows; @@ -420,7 +425,7 @@ std::vector TorrentModel::getSpans(QSet const& ids) c **** ***/ -void TorrentModel::rowsEmitChanged(QSet const& ids) +void TorrentModel::rowsEmitChanged(torrent_ids_t const& ids) { for (auto const& span : getSpans(ids)) { @@ -432,7 +437,7 @@ void TorrentModel::rowsAdd(torrents_t const& torrents) { auto const compare = TorrentIdLessThan(); - if (myTorrents.isEmpty()) + if (myTorrents.empty()) { beginInsertRows(QModelIndex(), 0, torrents.size() - 1); myTorrents = torrents; @@ -453,7 +458,7 @@ void TorrentModel::rowsAdd(torrents_t const& torrents) } } -void TorrentModel::rowsRemove(QSet const& torrents) +void TorrentModel::rowsRemove(torrents_t const& torrents) { // must walk in reverse to avoid invalidating row numbers auto const& spans = getSpans(getIds(torrents.begin(), torrents.end())); @@ -474,29 +479,6 @@ void TorrentModel::rowsRemove(QSet const& torrents) **** ***/ -void TorrentModel::getTransferSpeed(Speed& uploadSpeed, size_t& uploadPeerCount, Speed& downloadSpeed, - size_t& downloadPeerCount) const -{ - Speed upSpeed; - Speed downSpeed; - size_t upCount = 0; - size_t downCount = 0; - - for (Torrent const* const tor : myTorrents) - { - upSpeed += tor->uploadSpeed(); - upCount += tor->peersWeAreUploadingTo(); - downSpeed += tor->downloadSpeed(); - downCount += tor->webseedsWeAreDownloadingFrom(); - downCount += tor->peersWeAreDownloadingFrom(); - } - - uploadSpeed = upSpeed; - uploadPeerCount = upCount; - downloadSpeed = downSpeed; - downloadPeerCount = downCount; -} - bool TorrentModel::hasTorrent(QString const& hashString) const { auto test = [hashString](auto const& tor) { return tor->hashString() == hashString; }; diff --git a/qt/TorrentModel.h b/qt/TorrentModel.h index 0b3d7c08d..3df7066fa 100644 --- a/qt/TorrentModel.h +++ b/qt/TorrentModel.h @@ -9,10 +9,12 @@ #pragma once #include +#include #include -#include -#include +// #include + +#include class Prefs; class Speed; @@ -34,7 +36,7 @@ public: }; explicit TorrentModel(Prefs const& prefs); - virtual ~TorrentModel(); + virtual ~TorrentModel() override; void clear(); bool hasTorrent(QString const& hashString) const; @@ -42,7 +44,8 @@ public: Torrent* getTorrentFromId(int id); Torrent const* getTorrentFromId(int id) const; - void getTransferSpeed(Speed& uploadSpeed, size_t& uploadPeerCount, Speed& downloadSpeed, size_t& downloadPeerCount) const; + using torrents_t = QVector; + torrents_t const& torrents() const { return myTorrents; } // QAbstractItemModel int rowCount(QModelIndex const& parent = QModelIndex()) const override; @@ -53,23 +56,22 @@ public slots: void removeTorrents(tr_variant* torrentList); signals: - void torrentsAdded(QSet); - void torrentsChanged(QSet); - void torrentsCompleted(QSet); - void torrentsNeedInfo(QSet); + void torrentsAdded(torrent_ids_t const&); + void torrentsChanged(torrent_ids_t const&); + void torrentsCompleted(torrent_ids_t const&); + void torrentsNeedInfo(torrent_ids_t const&); private: - using torrents_t = QVector; void rowsAdd(torrents_t const& torrents); - void rowsRemove(QSet const& torrents); - void rowsEmitChanged(QSet const& ids); + void rowsRemove(torrents_t const& torrents); + void rowsEmitChanged(torrent_ids_t const& ids); std::optional getRow(int id) const; std::optional getRow(Torrent const* tor) const; using span_t = std::pair; - std::vector getSpans(QSet const& ids) const; + std::vector getSpans(torrent_ids_t const& ids) const; Prefs const& myPrefs; + torrent_ids_t myAlreadyAdded; torrents_t myTorrents; - QSet myAlreadyAdded; }; diff --git a/qt/TorrentView.h b/qt/TorrentView.h index 94222e247..8c7925e74 100644 --- a/qt/TorrentView.h +++ b/qt/TorrentView.h @@ -24,7 +24,7 @@ signals: void headerDoubleClicked(); protected: - virtual void resizeEvent(QResizeEvent* event); + void resizeEvent(QResizeEvent* event) override; private: class HeaderWidget; diff --git a/qt/TrackerDelegate.cc b/qt/TrackerDelegate.cc index 458a9f816..873cd8a1c 100644 --- a/qt/TrackerDelegate.cc +++ b/qt/TrackerDelegate.cc @@ -88,7 +88,7 @@ ItemLayout::ItemLayout(QString const& text, bool suppressColors, Qt::LayoutDirec QSize TrackerDelegate::margin(QStyle const& style) const { - Q_UNUSED(style); + Q_UNUSED(style) return myMargin; } diff --git a/qt/TrackerDelegate.h b/qt/TrackerDelegate.h index b5d92aad4..ebdb1789e 100644 --- a/qt/TrackerDelegate.h +++ b/qt/TrackerDelegate.h @@ -26,15 +26,11 @@ public: { } - virtual ~TrackerDelegate() - { - } - void setShowMore(bool b); // QAbstractItemDelegate - virtual QSize sizeHint(QStyleOptionViewItem const& option, QModelIndex const& index) const; - virtual void paint(QPainter* painter, QStyleOptionViewItem const& option, QModelIndex const& index) const; + QSize sizeHint(QStyleOptionViewItem const& option, QModelIndex const& index) const override; + void paint(QPainter* painter, QStyleOptionViewItem const& option, QModelIndex const& index) const override; protected: QString getText(TrackerInfo const&) const; diff --git a/qt/TrackerModel.cc b/qt/TrackerModel.cc index 4d3e7cff4..94ff7bb3e 100644 --- a/qt/TrackerModel.cc +++ b/qt/TrackerModel.cc @@ -16,7 +16,7 @@ int TrackerModel::rowCount(QModelIndex const& parent) const { - Q_UNUSED(parent); + Q_UNUSED(parent) return parent.isValid() ? 0 : myRows.size(); } @@ -80,7 +80,7 @@ struct CompareTrackers } }; -void TrackerModel::refresh(TorrentModel const& torrentModel, QSet const& ids) +void TrackerModel::refresh(TorrentModel const& torrentModel, torrent_ids_t const& ids) { // build a list of the TrackerInfos QVector trackers; diff --git a/qt/TrackerModel.h b/qt/TrackerModel.h index a26425e82..61b66d8dc 100644 --- a/qt/TrackerModel.h +++ b/qt/TrackerModel.h @@ -9,10 +9,10 @@ #pragma once #include -#include #include #include "Torrent.h" +#include "Typedefs.h" class TorrentModel; @@ -35,20 +35,14 @@ public: }; public: - TrackerModel() - { - } + TrackerModel() = default; - virtual ~TrackerModel() - { - } - - void refresh(TorrentModel const&, QSet const& ids); + void refresh(TorrentModel const&, torrent_ids_t const& ids); int find(int torrentId, QString const& url) const; // QAbstractItemModel - virtual int rowCount(QModelIndex const& parent = QModelIndex()) const; - virtual QVariant data(QModelIndex const& index, int role = Qt::DisplayRole) const; + int rowCount(QModelIndex const& parent = QModelIndex()) const override; + QVariant data(QModelIndex const& index, int role = Qt::DisplayRole) const override; private: typedef QVector rows_t; diff --git a/qt/Typedefs.h b/qt/Typedefs.h new file mode 100644 index 000000000..58a0b2f79 --- /dev/null +++ b/qt/Typedefs.h @@ -0,0 +1,5 @@ +#pragma once + +#include + +using torrent_ids_t = std::unordered_set; diff --git a/qt/Utils.cc b/qt/Utils.cc index 3f2d1b9fe..ecd04b8a2 100644 --- a/qt/Utils.cc +++ b/qt/Utils.cc @@ -6,8 +6,9 @@ * */ -#include #include +#include +#include #ifdef _WIN32 #include @@ -109,12 +110,13 @@ QIcon fileIcon() return icon; } -std::map iconCache; +std::unordered_map iconCache; + QIcon const getMimeIcon(QString const& filename) { // If the suffix doesn't match a mime type, treat it as a folder. // This heuristic is fast and yields good results for torrent names. - static std::set suffixes; + static std::unordered_set suffixes; if (suffixes.empty()) { for (auto const& type : QMimeDatabase().allMimeTypes()) diff --git a/qt/Utils.h b/qt/Utils.h index 51c7ce041..9f7742bb1 100644 --- a/qt/Utils.h +++ b/qt/Utils.h @@ -80,7 +80,7 @@ public: return false; } - for (QChar const ch : s) + for (auto const& ch : s) { if (!isxdigit(ch.unicode())) { @@ -93,9 +93,22 @@ public: static bool isUriWithSupportedScheme(QString const& s) { - static QString const ftp = QString::fromUtf8("ftp://"); - static QString const http = QString::fromUtf8("http://"); - static QString const https = QString::fromUtf8("https://"); - return s.startsWith(http) || s.startsWith(https) || s.startsWith(ftp); + return s.startsWith(QStringLiteral("ftp://")) || + s.startsWith(QStringLiteral("http://")) || + s.startsWith(QStringLiteral("https://")); } }; + +namespace std +{ + +template<> +struct hash +{ + std::size_t operator ()(QString const& s) const + { + return qHash(s); + } +}; + +} // namespace std diff --git a/qt/WatchDir.cc b/qt/WatchDir.cc index 3fedf63f9..673a7f88b 100644 --- a/qt/WatchDir.cc +++ b/qt/WatchDir.cc @@ -28,10 +28,6 @@ WatchDir::WatchDir(TorrentModel const& model) : { } -WatchDir::~WatchDir() -{ -} - /*** **** ***/ diff --git a/qt/WatchDir.h b/qt/WatchDir.h index 515edee24..ff244750d 100644 --- a/qt/WatchDir.h +++ b/qt/WatchDir.h @@ -22,7 +22,6 @@ class WatchDir : public QObject public: WatchDir(TorrentModel const&); - virtual ~WatchDir(); void setPath(QString const& path, bool isEnabled); diff --git a/qt/qtr.pro b/qt/qtr.pro index 68a53a771..2042350c5 100644 --- a/qt/qtr.pro +++ b/qt/qtr.pro @@ -121,6 +121,6 @@ SOURCES += AboutDialog.cc \ Utils.cc \ WatchDir.cc HEADERS += $$replace(SOURCES, .cc, .h) -HEADERS += BaseDialog.h CustomVariantType.h Speed.h +HEADERS += BaseDialog.h CustomVariantType.h Speed.h Typedefs.h win32:RC_FILE = qtr.rc