Make TorrentInfo immutable

This commit is contained in:
Vladimir Golovnev (Glassez)
2021-12-09 13:05:49 +03:00
parent 9d2bb67834
commit 62b50d1475
22 changed files with 382 additions and 255 deletions

View File

@@ -72,6 +72,51 @@ namespace
{
return SettingsStorage::instance();
}
class FileStorageAdaptor final : public BitTorrent::AbstractFileStorage
{
public:
FileStorageAdaptor(const BitTorrent::TorrentInfo &torrentInfo, QStringList &filePaths)
: m_torrentInfo {torrentInfo}
, m_filePaths {filePaths}
{
Q_ASSERT(filePaths.isEmpty() || (filePaths.size() == torrentInfo.filesCount()));
}
int filesCount() const override
{
return m_torrentInfo.filesCount();
}
qlonglong fileSize(const int index) const override
{
Q_ASSERT((index >= 0) && (index < filesCount()));
return m_torrentInfo.fileSize(index);
}
QString filePath(const int index) const override
{
Q_ASSERT((index >= 0) && (index < filesCount()));
return (m_filePaths.isEmpty() ? m_torrentInfo.filePath(index) : m_filePaths.at(index));
}
void renameFile(const int index, const QString &newFilePath) override
{
Q_ASSERT((index >= 0) && (index < filesCount()));
const QString currentFilePath = filePath(index);
if (currentFilePath == newFilePath)
return;
if (m_filePaths.isEmpty())
m_filePaths = m_torrentInfo.filePaths();
m_filePaths[index] = newFilePath;
}
private:
const BitTorrent::TorrentInfo &m_torrentInfo;
QStringList &m_filePaths;
};
}
const int AddNewTorrentDialog::minPathHistoryLength;
@@ -146,10 +191,8 @@ AddNewTorrentDialog::AddNewTorrentDialog(const BitTorrent::AddTorrentParams &inP
// Signal / slots
connect(m_ui->doNotDeleteTorrentCheckBox, &QCheckBox::clicked, this, &AddNewTorrentDialog::doNotDeleteTorrentClicked);
QShortcut *editHotkey = new QShortcut(Qt::Key_F2, m_ui->contentTreeView, nullptr, nullptr, Qt::WidgetShortcut);
connect(editHotkey, &QShortcut::activated
, this, [this]() { m_ui->contentTreeView->renameSelectedFile(m_torrentInfo); });
connect(m_ui->contentTreeView, &QAbstractItemView::doubleClicked
, this, [this]() { m_ui->contentTreeView->renameSelectedFile(m_torrentInfo); });
connect(editHotkey, &QShortcut::activated, this, &AddNewTorrentDialog::renameSelectedFile);
connect(m_ui->contentTreeView, &QAbstractItemView::doubleClicked, this, &AddNewTorrentDialog::renameSelectedFile);
m_ui->buttonBox->button(QDialogButtonBox::Ok)->setFocus();
}
@@ -251,7 +294,6 @@ bool AddNewTorrentDialog::loadTorrentFile(const QString &torrentPath)
: torrentPath;
const nonstd::expected<BitTorrent::TorrentInfo, QString> result = BitTorrent::TorrentInfo::loadFromFile(decodedPath);
m_torrentInfo = result.value_or(BitTorrent::TorrentInfo());
if (!result)
{
RaisedMessageBox::critical(this, tr("Invalid torrent")
@@ -260,6 +302,7 @@ bool AddNewTorrentDialog::loadTorrentFile(const QString &torrentPath)
return false;
}
m_torrentInfo = result.value();
m_torrentGuard = std::make_unique<TorrentFileGuard>(decodedPath);
return loadTorrentImpl();
@@ -267,7 +310,6 @@ bool AddNewTorrentDialog::loadTorrentFile(const QString &torrentPath)
bool AddNewTorrentDialog::loadTorrentImpl()
{
m_hasMetadata = true;
const auto torrentID = BitTorrent::TorrentID::fromInfoHash(m_torrentInfo.infoHash());
// Prevent showing the dialog if download is already present
@@ -398,7 +440,7 @@ void AddNewTorrentDialog::updateDiskSpaceLabel()
// Determine torrent size
qlonglong torrentSize = 0;
if (m_hasMetadata)
if (hasMetadata())
{
if (m_contentModel)
{
@@ -457,7 +499,7 @@ void AddNewTorrentDialog::setSavePath(const QString &newPath)
void AddNewTorrentDialog::saveTorrentFile()
{
Q_ASSERT(m_hasMetadata);
Q_ASSERT(hasMetadata());
const QString torrentFileExtension {C_TORRENT_FILE_EXTENSION};
const QString filter {tr("Torrent file (*%1)").arg(torrentFileExtension)};
@@ -479,6 +521,11 @@ void AddNewTorrentDialog::saveTorrentFile()
}
}
bool AddNewTorrentDialog::hasMetadata() const
{
return m_torrentInfo.isValid();
}
void AddNewTorrentDialog::populateSavePathComboBox()
{
m_ui->savePath->clear();
@@ -548,8 +595,7 @@ void AddNewTorrentDialog::displayContentTreeMenu(const QPoint &)
menu->setAttribute(Qt::WA_DeleteOnClose);
if (selectedRows.size() == 1)
{
menu->addAction(UIThemeManager::instance()->getIcon("edit-rename"), tr("Rename...")
, this, [this]() { m_ui->contentTreeView->renameSelectedFile(m_torrentInfo); });
menu->addAction(UIThemeManager::instance()->getIcon("edit-rename"), tr("Rename..."), this, &AddNewTorrentDialog::renameSelectedFile);
menu->addSeparator();
QMenu *priorityMenu = menu->addMenu(tr("Priority"));
@@ -634,7 +680,7 @@ void AddNewTorrentDialog::accept()
setEnabled(!m_ui->checkBoxNeverShow->isChecked());
// Add torrent
if (!m_hasMetadata)
if (!hasMetadata())
BitTorrent::Session::instance()->addTorrent(m_magnetURI, m_torrentParams);
else
BitTorrent::Session::instance()->addTorrent(m_torrentInfo, m_torrentParams);
@@ -645,7 +691,7 @@ void AddNewTorrentDialog::accept()
void AddNewTorrentDialog::reject()
{
if (!m_hasMetadata)
if (!hasMetadata())
{
setMetadataProgressIndicator(false);
BitTorrent::Session::instance()->cancelDownloadMetadata(m_magnetURI.infoHash().toTorrentID());
@@ -660,16 +706,8 @@ void AddNewTorrentDialog::updateMetadata(const BitTorrent::TorrentInfo &metadata
disconnect(BitTorrent::Session::instance(), &BitTorrent::Session::metadataDownloaded, this, &AddNewTorrentDialog::updateMetadata);
if (!metadata.isValid())
{
RaisedMessageBox::critical(this, tr("I/O Error"), ("Invalid metadata."));
setMetadataProgressIndicator(false, tr("Invalid metadata"));
return;
}
// Good to go
m_torrentInfo = metadata;
m_hasMetadata = true;
setMetadataProgressIndicator(true, tr("Parsing metadata..."));
// Update UI
@@ -694,7 +732,7 @@ void AddNewTorrentDialog::setMetadataProgressIndicator(bool visibleIndicator, co
void AddNewTorrentDialog::setupTreeview()
{
if (!m_hasMetadata)
if (!hasMetadata())
{
m_ui->labelCommentData->setText(tr("Not Available", "This comment is unavailable"));
m_ui->labelDateData->setText(tr("Not Available", "This date is unavailable"));
@@ -719,7 +757,7 @@ void AddNewTorrentDialog::setupTreeview()
connect(m_ui->contentTreeView, &QWidget::customContextMenuRequested, this, &AddNewTorrentDialog::displayContentTreeMenu);
// List files in torrent
m_contentModel->model()->setupModelData(m_torrentInfo);
m_contentModel->model()->setupModelData(FileStorageAdaptor(m_torrentInfo, m_torrentParams.filePaths));
if (const QByteArray state = m_storeTreeHeaderState; !state.isEmpty())
m_ui->contentTreeView->header()->restoreState(state);
@@ -747,7 +785,6 @@ void AddNewTorrentDialog::handleDownloadFinished(const Net::DownloadResult &down
case Net::DownloadStatus::Success:
{
const nonstd::expected<BitTorrent::TorrentInfo, QString> result = BitTorrent::TorrentInfo::load(downloadResult.data);
m_torrentInfo = result.value_or(BitTorrent::TorrentInfo());
if (!result)
{
RaisedMessageBox::critical(this, tr("Invalid torrent"), tr("Failed to load from URL: %1.\nError: %2")
@@ -755,6 +792,7 @@ void AddNewTorrentDialog::handleDownloadFinished(const Net::DownloadResult &down
return;
}
m_torrentInfo = result.value();
m_torrentGuard = std::make_unique<TorrentFileGuard>();
if (loadTorrentImpl())
@@ -800,3 +838,12 @@ void AddNewTorrentDialog::doNotDeleteTorrentClicked(bool checked)
{
m_torrentGuard->setAutoRemove(!checked);
}
void AddNewTorrentDialog::renameSelectedFile()
{
if (hasMetadata())
{
FileStorageAdaptor fileStorageAdaptor {m_torrentInfo, m_torrentParams.filePaths};
m_ui->contentTreeView->renameSelectedFile(fileStorageAdaptor);
}
}

View File

@@ -86,6 +86,7 @@ private slots:
void TMMChanged(int index);
void categoryChanged(int index);
void doNotDeleteTorrentClicked(bool checked);
void renameSelectedFile();
void accept() override;
void reject() override;
@@ -104,13 +105,13 @@ private:
void setupTreeview();
void setSavePath(const QString &newPath);
void saveTorrentFile();
bool hasMetadata() const;
void showEvent(QShowEvent *event) override;
Ui::AddNewTorrentDialog *m_ui;
TorrentContentFilterModel *m_contentModel = nullptr;
PropListDelegate *m_contentDelegate = nullptr;
bool m_hasMetadata = false;
BitTorrent::MagnetUri m_magnetURI;
BitTorrent::TorrentInfo m_torrentInfo;
int m_oldIndex = 0;

View File

@@ -471,7 +471,9 @@ void PeerListWidget::updatePeer(const BitTorrent::Torrent *torrent, const BitTor
setModelData(row, PeerListColumns::TOT_UP, totalUp, peer.totalUpload(), intDataTextAlignment);
setModelData(row, PeerListColumns::RELEVANCE, (Utils::String::fromDouble(peer.relevance() * 100, 1) + '%'), peer.relevance(), intDataTextAlignment);
const QStringList downloadingFiles {torrent->info().filesForPiece(peer.downloadingPieceIndex())};
const QStringList downloadingFiles {torrent->hasMetadata()
? torrent->info().filesForPiece(peer.downloadingPieceIndex())
: QStringList()};
const QString downloadingFilesDisplayValue = downloadingFiles.join(';');
setModelData(row, PeerListColumns::DOWNLOADING_PIECE, downloadingFilesDisplayValue, downloadingFilesDisplayValue, {}, downloadingFiles.join(QLatin1Char('\n')));

View File

@@ -256,13 +256,14 @@ void PiecesBar::showToolTip(const QHelpEvent *e)
const bool showDetailedInformation = QApplication::keyboardModifiers().testFlag(Qt::ShiftModifier);
if (showDetailedInformation && m_torrent->hasMetadata())
{
const BitTorrent::TorrentInfo torrentInfo = m_torrent->info();
const int imagePos = e->pos().x() - borderWidth;
if ((imagePos >=0) && (imagePos < m_image.width()))
{
stream << "<html><body>";
PieceIndexToImagePos transform {m_torrent->info(), m_image};
PieceIndexToImagePos transform {torrentInfo, m_image};
int pieceIndex = transform.pieceIndex(imagePos);
const QVector<int> files {m_torrent->info().fileIndicesForPiece(pieceIndex)};
const QVector<int> files {torrentInfo.fileIndicesForPiece(pieceIndex)};
QString tooltipTitle;
if (files.count() > 1)
@@ -271,7 +272,7 @@ void PiecesBar::showToolTip(const QHelpEvent *e)
}
else
{
if (m_torrent->info().fileSize(files.front()) == m_torrent->info().pieceLength(pieceIndex))
if (torrentInfo.fileSize(files.front()) == torrentInfo.pieceLength(pieceIndex))
tooltipTitle = tr("File in this piece");
else
tooltipTitle = tr("File in these pieces");
@@ -281,8 +282,8 @@ void PiecesBar::showToolTip(const QHelpEvent *e)
for (int f : files)
{
const QString filePath {m_torrent->info().filePath(f)};
renderer(Utils::Misc::friendlyUnit(m_torrent->info().fileSize(f)), filePath);
const QString filePath {torrentInfo.filePath(f)};
renderer(Utils::Misc::friendlyUnit(torrentInfo.fileSize(f)), filePath);
}
stream << "</body></html>";
}
@@ -306,13 +307,14 @@ void PiecesBar::highlightFile(int imagePos)
if (!m_torrent || !m_torrent->hasMetadata() || (imagePos < 0) || (imagePos >= m_image.width()))
return;
PieceIndexToImagePos transform {m_torrent->info(), m_image};
const BitTorrent::TorrentInfo torrentInfo = m_torrent->info();
PieceIndexToImagePos transform {torrentInfo, m_image};
int pieceIndex = transform.pieceIndex(imagePos);
QVector<int> fileIndices {m_torrent->info().fileIndicesForPiece(pieceIndex)};
QVector<int> fileIndices {torrentInfo.fileIndicesForPiece(pieceIndex)};
if (fileIndices.count() == 1)
{
BitTorrent::TorrentInfo::PieceRange filePieces = m_torrent->info().filePieces(fileIndices.first());
BitTorrent::TorrentInfo::PieceRange filePieces = torrentInfo.filePieces(fileIndices.first());
ImageRange imageRange = transform.imagePos(filePieces);
QRect newHighlightedRegion {imageRange.first(), 0, imageRange.size(), m_image.height()};

View File

@@ -528,7 +528,7 @@ void PropertiesWidget::loadDynamicData()
if (!isContentInitialized)
{
// List files in torrent
m_propListModel->model()->setupModelData(m_torrent->info());
m_propListModel->model()->setupModelData(*m_torrent);
// Load file priorities
m_propListModel->model()->updateFilesPriorities(m_torrent->filePriorities());
// Update file progress/availability

View File

@@ -50,8 +50,8 @@
#include <QPixmapCache>
#endif
#include "base/bittorrent/abstractfilestorage.h"
#include "base/bittorrent/downloadpriority.h"
#include "base/bittorrent/torrentinfo.h"
#include "base/global.h"
#include "base/utils/fs.h"
#include "torrentcontentmodelfile.h"
@@ -485,7 +485,7 @@ void TorrentContentModel::clear()
endResetModel();
}
void TorrentContentModel::setupModelData(const BitTorrent::TorrentInfo &info)
void TorrentContentModel::setupModelData(const BitTorrent::AbstractFileStorage &info)
{
qDebug("setup model data called");
const int filesCount = info.filesCount();

View File

@@ -41,7 +41,7 @@ class TorrentContentModelFile;
namespace BitTorrent
{
class TorrentInfo;
class AbstractFileStorage;
}
class TorrentContentModel final : public QAbstractItemModel
@@ -74,7 +74,7 @@ public:
QModelIndex parent(const QModelIndex &index) const override;
int rowCount(const QModelIndex &parent = {}) const override;
void clear();
void setupModelData(const BitTorrent::TorrentInfo &info);
void setupModelData(const BitTorrent::AbstractFileStorage &info);
signals:
void filteredFilesChanged();