diff --git a/src/base/bittorrent/peerinfo.cpp b/src/base/bittorrent/peerinfo.cpp index cf27ba302..3706b1e81 100644 --- a/src/base/bittorrent/peerinfo.cpp +++ b/src/base/bittorrent/peerinfo.cpp @@ -367,6 +367,10 @@ void PeerInfo::determineFlags() if (useUTPSocket()) updateFlags(u'P', C_UTP); + // h = Peer is using NAT hole punching + if (isHolepunched()) + updateFlags(u'h', tr("Peer is using NAT hole punching")); + m_flags.chop(1); m_flagsDescription.chop(1); } diff --git a/src/base/bittorrent/torrentimpl.cpp b/src/base/bittorrent/torrentimpl.cpp index b29f8f06e..da5e4690e 100644 --- a/src/base/bittorrent/torrentimpl.cpp +++ b/src/base/bittorrent/torrentimpl.cpp @@ -460,6 +460,8 @@ Path TorrentImpl::savePath() const void TorrentImpl::setSavePath(const Path &path) { Q_ASSERT(!isAutoTMMEnabled()); + if (isAutoTMMEnabled()) [[unlikely]] + return; const Path basePath = m_session->useCategoryPathsInManualMode() ? m_session->categorySavePath(category()) : m_session->savePath(); @@ -487,6 +489,8 @@ Path TorrentImpl::downloadPath() const void TorrentImpl::setDownloadPath(const Path &path) { Q_ASSERT(!isAutoTMMEnabled()); + if (isAutoTMMEnabled()) [[unlikely]] + return; const Path basePath = m_session->useCategoryPathsInManualMode() ? m_session->categoryDownloadPath(category()) : m_session->downloadPath(); @@ -1958,8 +1962,17 @@ void TorrentImpl::moveStorage(const Path &newPath, const MoveStorageContext cont { if (!hasMetadata()) { - m_savePath = newPath; - m_session->handleTorrentSavePathChanged(this); + if (context == MoveStorageContext::ChangeSavePath) + { + m_savePath = newPath; + m_session->handleTorrentSavePathChanged(this); + } + else if (context == MoveStorageContext::ChangeDownloadPath) + { + m_downloadPath = newPath; + m_session->handleTorrentSavePathChanged(this); + } + return; } diff --git a/src/gui/properties/downloadedpiecesbar.cpp b/src/gui/properties/downloadedpiecesbar.cpp index 486d761d4..d7b1a849d 100644 --- a/src/gui/properties/downloadedpiecesbar.cpp +++ b/src/gui/properties/downloadedpiecesbar.cpp @@ -1,5 +1,6 @@ /* * Bittorrent Client using Qt and libtorrent. + * Copyright (C) 2024 Vladimir Golovnev * Copyright (C) 2006 Christophe Dumez * * This program is free software; you can redistribute it and/or @@ -46,9 +47,9 @@ namespace } DownloadedPiecesBar::DownloadedPiecesBar(QWidget *parent) - : base {parent} - , m_dlPieceColor {dlPieceColor(pieceColor())} + : base(parent) { + updateColorsImpl(); } QVector DownloadedPiecesBar::bitfieldToFloatVector(const QBitArray &vecin, int reqSize) @@ -128,25 +129,24 @@ QVector DownloadedPiecesBar::bitfieldToFloatVector(const QBitArray &vecin return result; } -bool DownloadedPiecesBar::updateImage(QImage &image) +QImage DownloadedPiecesBar::renderImage() { // qDebug() << "updateImage"; - QImage image2(width() - 2 * borderWidth, 1, QImage::Format_RGB888); - if (image2.isNull()) + QImage image {width() - 2 * borderWidth, 1, QImage::Format_RGB888}; + if (image.isNull()) { - qDebug() << "QImage image2() allocation failed, width():" << width(); - return false; + qDebug() << "QImage allocation failed, width():" << width(); + return image; } if (m_pieces.isEmpty()) { - image2.fill(backgroundColor()); - image = image2; - return true; + image.fill(backgroundColor()); + return image; } - QVector scaledPieces = bitfieldToFloatVector(m_pieces, image2.width()); - QVector scaledPiecesDl = bitfieldToFloatVector(m_downloadedPieces, image2.width()); + QVector scaledPieces = bitfieldToFloatVector(m_pieces, image.width()); + QVector scaledPiecesDl = bitfieldToFloatVector(m_downloadedPieces, image.width()); // filling image for (int x = 0; x < scaledPieces.size(); ++x) @@ -161,15 +161,15 @@ bool DownloadedPiecesBar::updateImage(QImage &image) QRgb mixedColor = mixTwoColors(pieceColor().rgb(), m_dlPieceColor.rgb(), ratio); mixedColor = mixTwoColors(backgroundColor().rgb(), mixedColor, fillRatio); - image2.setPixel(x, 0, mixedColor); + image.setPixel(x, 0, mixedColor); } else { - image2.setPixel(x, 0, pieceColors()[piecesToValue * 255]); + image.setPixel(x, 0, pieceColors()[piecesToValue * 255]); } } - image = image2; - return true; + + return image; } void DownloadedPiecesBar::setProgress(const QBitArray &pieces, const QBitArray &downloadedPieces) @@ -177,7 +177,7 @@ void DownloadedPiecesBar::setProgress(const QBitArray &pieces, const QBitArray & m_pieces = pieces; m_downloadedPieces = downloadedPieces; - requestImageUpdate(); + redraw(); } void DownloadedPiecesBar::clear() @@ -198,3 +198,14 @@ QString DownloadedPiecesBar::simpleToolTipText() const + u""; } + +void DownloadedPiecesBar::updateColors() +{ + PiecesBar::updateColors(); + updateColorsImpl(); +} + +void DownloadedPiecesBar::updateColorsImpl() +{ + m_dlPieceColor = dlPieceColor(pieceColor()); +} diff --git a/src/gui/properties/downloadedpiecesbar.h b/src/gui/properties/downloadedpiecesbar.h index d98a114b2..2e8a1e00b 100644 --- a/src/gui/properties/downloadedpiecesbar.h +++ b/src/gui/properties/downloadedpiecesbar.h @@ -1,5 +1,6 @@ /* * Bittorrent Client using Qt and libtorrent. + * Copyright (C) 2024 Vladimir Golovnev * Copyright (C) 2006 Christophe Dumez * * This program is free software; you can redistribute it and/or @@ -52,11 +53,13 @@ public: private: // scale bitfield vector to float vector QVector bitfieldToFloatVector(const QBitArray &vecin, int reqSize); - bool updateImage(QImage &image) override; + QImage renderImage() override; QString simpleToolTipText() const override; + void updateColors() override; + void updateColorsImpl(); // incomplete piece color - const QColor m_dlPieceColor; + QColor m_dlPieceColor; // last used bitfields, uses to better resize redraw // TODO: make a diff pieces to new pieces and update only changed pixels, speedup when update > 20x faster QBitArray m_pieces; diff --git a/src/gui/properties/pieceavailabilitybar.cpp b/src/gui/properties/pieceavailabilitybar.cpp index 0f33d704b..66b375e92 100644 --- a/src/gui/properties/pieceavailabilitybar.cpp +++ b/src/gui/properties/pieceavailabilitybar.cpp @@ -1,5 +1,6 @@ /* * Bittorrent Client using Qt and libtorrent. + * Copyright (C) 2024 Vladimir Golovnev * Copyright (C) 2006 Christophe Dumez * * This program is free software; you can redistribute it and/or @@ -126,39 +127,38 @@ QVector PieceAvailabilityBar::intToFloatVector(const QVector &vecin, return result; } -bool PieceAvailabilityBar::updateImage(QImage &image) +QImage PieceAvailabilityBar::renderImage() { - QImage image2(width() - 2 * borderWidth, 1, QImage::Format_RGB888); - if (image2.isNull()) + QImage image {width() - 2 * borderWidth, 1, QImage::Format_RGB888}; + if (image.isNull()) { - qDebug() << "QImage image2() allocation failed, width():" << width(); - return false; + qDebug() << "QImage allocation failed, width():" << width(); + return image; } if (m_pieces.empty()) { - image2.fill(backgroundColor()); - image = image2; - return true; + image.fill(backgroundColor()); + return image; } - QVector scaledPieces = intToFloatVector(m_pieces, image2.width()); + QVector scaledPieces = intToFloatVector(m_pieces, image.width()); // filling image for (int x = 0; x < scaledPieces.size(); ++x) { float piecesToValue = scaledPieces.at(x); - image2.setPixel(x, 0, pieceColors()[piecesToValue * 255]); + image.setPixel(x, 0, pieceColors()[piecesToValue * 255]); } - image = image2; - return true; + + return image; } void PieceAvailabilityBar::setAvailability(const QVector &avail) { m_pieces = avail; - requestImageUpdate(); + redraw(); } void PieceAvailabilityBar::clear() diff --git a/src/gui/properties/pieceavailabilitybar.h b/src/gui/properties/pieceavailabilitybar.h index 05472d489..07ece9701 100644 --- a/src/gui/properties/pieceavailabilitybar.h +++ b/src/gui/properties/pieceavailabilitybar.h @@ -1,5 +1,6 @@ /* * Bittorrent Client using Qt and libtorrent. + * Copyright (C) 2024 Vladimir Golovnev * Copyright (C) 2006 Christophe Dumez * * This program is free software; you can redistribute it and/or @@ -46,7 +47,7 @@ public: void clear() override; private: - bool updateImage(QImage &image) override; + QImage renderImage() override; QString simpleToolTipText() const override; // last used int vector, uses to better resize redraw diff --git a/src/gui/properties/piecesbar.cpp b/src/gui/properties/piecesbar.cpp index 0d42ec2ab..065fd26e2 100644 --- a/src/gui/properties/piecesbar.cpp +++ b/src/gui/properties/piecesbar.cpp @@ -1,5 +1,6 @@ /* * Bittorrent Client using Qt and libtorrent. + * Copyright (C) 2024 Vladimir Golovnev * Copyright (C) 2016 Eugene Shalygin * Copyright (C) 2006 Christophe Dumez * @@ -41,6 +42,7 @@ #include "base/indexrange.h" #include "base/path.h" #include "base/utils/misc.h" +#include "gui/uithememanager.h" namespace { @@ -114,10 +116,16 @@ namespace } PiecesBar::PiecesBar(QWidget *parent) - : QWidget {parent} + : QWidget(parent) { - updatePieceColors(); setMouseTracking(true); + + updateColorsImpl(); + connect(UIThemeManager::instance(), &UIThemeManager::themeChanged, this, [this] + { + updateColors(); + redraw(); + }); } void PiecesBar::setTorrent(const BitTorrent::Torrent *torrent) @@ -154,7 +162,7 @@ void PiecesBar::leaveEvent(QEvent *e) { m_hovered = false; m_highlightedRegion = {}; - requestImageUpdate(); + redraw(); base::leaveEvent(e); } @@ -178,7 +186,10 @@ void PiecesBar::paintEvent(QPaintEvent *) else { if (m_image.width() != imageRect.width()) - updateImage(m_image); + { + if (const QImage image = renderImage(); !image.isNull()) + m_image = image; + } painter.drawImage(imageRect, m_image); } @@ -196,30 +207,33 @@ void PiecesBar::paintEvent(QPaintEvent *) painter.drawPath(border); } -void PiecesBar::requestImageUpdate() +void PiecesBar::redraw() { - if (updateImage(m_image)) + if (const QImage image = renderImage(); !image.isNull()) + { + m_image = image; update(); + } } QColor PiecesBar::backgroundColor() const { - return palette().color(QPalette::Base); + return palette().color(QPalette::Active, QPalette::Base); } QColor PiecesBar::borderColor() const { - return palette().color(QPalette::Dark); + return palette().color(QPalette::Active, QPalette::Dark); } QColor PiecesBar::pieceColor() const { - return palette().color(QPalette::Highlight); + return palette().color(QPalette::Active, QPalette::Highlight); } QColor PiecesBar::colorBoxBorderColor() const { - return palette().color(QPalette::ToolTipText); + return palette().color(QPalette::Active, QPalette::ToolTipText); } const QVector &PiecesBar::pieceColors() const @@ -325,12 +339,17 @@ void PiecesBar::highlightFile(int imagePos) } } -void PiecesBar::updatePieceColors() +void PiecesBar::updateColors() +{ + updateColorsImpl(); +} + +void PiecesBar::updateColorsImpl() { m_pieceColors = QVector(256); for (int i = 0; i < 256; ++i) { - float ratio = (i / 255.0); + const float ratio = (i / 255.0); m_pieceColors[i] = mixTwoColors(backgroundColor().rgb(), pieceColor().rgb(), ratio); } } diff --git a/src/gui/properties/piecesbar.h b/src/gui/properties/piecesbar.h index 2f6e20615..c7f74d2ba 100644 --- a/src/gui/properties/piecesbar.h +++ b/src/gui/properties/piecesbar.h @@ -1,5 +1,6 @@ /* * Bittorrent Client using Qt and libtorrent. + * Copyright (C) 2024 Vladimir Golovnev * Copyright (C) 2016 Eugene Shalygin * Copyright (C) 2006 Christophe Dumez * @@ -54,17 +55,15 @@ public: virtual void clear(); - // QObject interface - bool event(QEvent *e) override; - protected: - // QWidget interface + bool event(QEvent *e) override; void enterEvent(QEnterEvent *e) override; void leaveEvent(QEvent *e) override; void mouseMoveEvent(QMouseEvent *e) override; - void paintEvent(QPaintEvent *e) override; - void requestImageUpdate(); + + virtual void updateColors(); + void redraw(); QColor backgroundColor() const; QColor borderColor() const; @@ -82,11 +81,9 @@ private: void highlightFile(int imagePos); virtual QString simpleToolTipText() const = 0; + virtual QImage renderImage() = 0; - // draw new image to replace the actual image - // returns true if image was successfully updated - virtual bool updateImage(QImage &image) = 0; - void updatePieceColors(); + void updateColorsImpl(); const BitTorrent::Torrent *m_torrent = nullptr; QImage m_image; diff --git a/src/gui/transferlistfilters/statusfilterwidget.cpp b/src/gui/transferlistfilters/statusfilterwidget.cpp index 802b5535b..dbb75e0da 100644 --- a/src/gui/transferlistfilters/statusfilterwidget.cpp +++ b/src/gui/transferlistfilters/statusfilterwidget.cpp @@ -235,10 +235,7 @@ void StatusFilterWidget::applyFilter(int row) void StatusFilterWidget::handleTorrentsLoaded(const QVector &torrents) { - for (const BitTorrent::Torrent *torrent : torrents) - updateTorrentStatus(torrent); - - updateTexts(); + update(torrents); } void StatusFilterWidget::torrentAboutToBeDeleted(BitTorrent::Torrent *const torrent) @@ -273,6 +270,12 @@ void StatusFilterWidget::torrentAboutToBeDeleted(BitTorrent::Torrent *const torr m_nbStalled = m_nbStalledUploading + m_nbStalledDownloading; updateTexts(); + + if (Preferences::instance()->getHideZeroStatusFilters()) + { + hideZeroItems(); + updateGeometry(); + } } void StatusFilterWidget::configure() diff --git a/src/webui/www/private/scripts/client.js b/src/webui/www/private/scripts/client.js index d92880bdb..c9d8e7032 100644 --- a/src/webui/www/private/scripts/client.js +++ b/src/webui/www/private/scripts/client.js @@ -736,9 +736,13 @@ window.addEventListener("DOMContentLoaded", () => { const full_update = (response["full_update"] === true); if (full_update) { torrentsTableSelectedRows = torrentsTable.selectedRowsIds(); + update_categories = true; + updateTags = true; + updateTrackers = true; torrentsTable.clear(); category_list.clear(); tagList.clear(); + trackerList.clear(); } if (response["rid"]) syncMainDataLastResponseId = response["rid"]; diff --git a/src/webui/www/private/scripts/dynamicTable.js b/src/webui/www/private/scripts/dynamicTable.js index 43f9dda12..ad7d6da6d 100644 --- a/src/webui/www/private/scripts/dynamicTable.js +++ b/src/webui/www/private/scripts/dynamicTable.js @@ -947,6 +947,9 @@ window.qBittorrent.DynamicTable = (function() { this.newColumn("seen_complete", "", "QBT_TR(Last Seen Complete)QBT_TR[CONTEXT=TransferListModel]", 100, false); this.newColumn("last_activity", "", "QBT_TR(Last Activity)QBT_TR[CONTEXT=TransferListModel]", 100, false); this.newColumn("availability", "", "QBT_TR(Availability)QBT_TR[CONTEXT=TransferListModel]", 100, false); + this.newColumn("download_path", "", "QBT_TR(Incomplete Save Path)QBT_TR[CONTEXT=TransferListModel]", 100, false); + this.newColumn("infohash_v1", "", "QBT_TR(Info Hash v1)QBT_TR[CONTEXT=TransferListModel]", 100, false); + this.newColumn("infohash_v2", "", "QBT_TR(Info Hash v2)QBT_TR[CONTEXT=TransferListModel]", 100, false); this.newColumn("reannounce", "", "QBT_TR(Reannounce In)QBT_TR[CONTEXT=TransferListModel]", 100, false); this.newColumn("private", "", "QBT_TR(Private)QBT_TR[CONTEXT=TransferListModel]", 100, false); @@ -1326,6 +1329,22 @@ window.qBittorrent.DynamicTable = (function() { td.set("title", value); }; + // infohash_v1 + this.columns["infohash_v1"].updateTd = function(td, row) { + const sourceInfohashV1 = this.getRowValue(row); + const infohashV1 = (sourceInfohashV1 !== "") ? sourceInfohashV1 : "QBT_TR(N/A)QBT_TR[CONTEXT=TransferListDelegate]"; + td.textContent = infohashV1; + td.title = infohashV1; + }; + + // infohash_v2 + this.columns["infohash_v2"].updateTd = function(td, row) { + const sourceInfohashV2 = this.getRowValue(row); + const infohashV2 = (sourceInfohashV2 !== "") ? sourceInfohashV2 : "QBT_TR(N/A)QBT_TR[CONTEXT=TransferListDelegate]"; + td.textContent = infohashV2; + td.title = infohashV2; + }; + // reannounce this.columns["reannounce"].updateTd = function(td, row) { const time = window.qBittorrent.Misc.friendlyDuration(this.getRowValue(row));