Improve structure of AddNewTorrentDialog code

Restructures the code to separate the basic logic from the logic that depends on the parameters and properties of the torrent being added.
Also fixes "Never show again" checkbox functionality.

PR #20848.
This commit is contained in:
Vladimir Golovnev 2024-05-18 10:38:49 +03:00 committed by GitHub
commit b8a774f1fb
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 309 additions and 230 deletions

View file

@ -42,17 +42,20 @@
#include <QMessageBox> #include <QMessageBox>
#include <QPushButton> #include <QPushButton>
#include <QShortcut> #include <QShortcut>
#include <QSignalBlocker>
#include <QSize> #include <QSize>
#include <QString> #include <QString>
#include <QUrl> #include <QUrl>
#include <QVector> #include <QVector>
#include "base/bittorrent/addtorrentparams.h"
#include "base/bittorrent/downloadpriority.h" #include "base/bittorrent/downloadpriority.h"
#include "base/bittorrent/infohash.h" #include "base/bittorrent/infohash.h"
#include "base/bittorrent/session.h" #include "base/bittorrent/session.h"
#include "base/bittorrent/torrent.h" #include "base/bittorrent/torrent.h"
#include "base/bittorrent/torrentcontenthandler.h" #include "base/bittorrent/torrentcontenthandler.h"
#include "base/bittorrent/torrentcontentlayout.h" #include "base/bittorrent/torrentcontentlayout.h"
#include "base/bittorrent/torrentdescriptor.h"
#include "base/global.h" #include "base/global.h"
#include "base/preferences.h" #include "base/preferences.h"
#include "base/settingsstorage.h" #include "base/settingsstorage.h"
@ -271,12 +274,16 @@ private:
BitTorrent::TorrentContentLayout m_currentContentLayout; BitTorrent::TorrentContentLayout m_currentContentLayout;
}; };
struct AddNewTorrentDialog::Context
{
BitTorrent::TorrentDescriptor torrentDescr;
BitTorrent::AddTorrentParams torrentParams;
};
AddNewTorrentDialog::AddNewTorrentDialog(const BitTorrent::TorrentDescriptor &torrentDescr AddNewTorrentDialog::AddNewTorrentDialog(const BitTorrent::TorrentDescriptor &torrentDescr
, const BitTorrent::AddTorrentParams &inParams, QWidget *parent) , const BitTorrent::AddTorrentParams &inParams, QWidget *parent)
: QDialog(parent) : QDialog(parent)
, m_ui {new Ui::AddNewTorrentDialog} , m_ui {new Ui::AddNewTorrentDialog}
, m_torrentDescr {torrentDescr}
, m_torrentParams {inParams}
, m_filterLine {new LineEdit(this)} , m_filterLine {new LineEdit(this)}
, m_storeDialogSize {SETTINGS_KEY(u"DialogSize"_s)} , m_storeDialogSize {SETTINGS_KEY(u"DialogSize"_s)}
, m_storeDefaultCategory {SETTINGS_KEY(u"DefaultCategory"_s)} , m_storeDefaultCategory {SETTINGS_KEY(u"DefaultCategory"_s)}
@ -284,115 +291,31 @@ AddNewTorrentDialog::AddNewTorrentDialog(const BitTorrent::TorrentDescriptor &to
, m_storeTreeHeaderState {u"GUI/Qt6/" SETTINGS_KEY(u"TreeHeaderState"_s)} , m_storeTreeHeaderState {u"GUI/Qt6/" SETTINGS_KEY(u"TreeHeaderState"_s)}
, m_storeSplitterState {u"GUI/Qt6/" SETTINGS_KEY(u"SplitterState"_s)} , m_storeSplitterState {u"GUI/Qt6/" SETTINGS_KEY(u"SplitterState"_s)}
{ {
// TODO: set dialog file properties using m_torrentParams.filePriorities
m_ui->setupUi(this); m_ui->setupUi(this);
connect(m_ui->buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept);
connect(m_ui->buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject);
m_ui->lblMetaLoading->setVisible(false);
m_ui->progMetaLoading->setVisible(false);
m_ui->buttonSave->setVisible(false);
connect(m_ui->buttonSave, &QPushButton::clicked, this, &AddNewTorrentDialog::saveTorrentFile);
m_ui->savePath->setMode(FileSystemPathEdit::Mode::DirectorySave); m_ui->savePath->setMode(FileSystemPathEdit::Mode::DirectorySave);
m_ui->savePath->setDialogCaption(tr("Choose save path")); m_ui->savePath->setDialogCaption(tr("Choose save path"));
m_ui->savePath->setMaxVisibleItems(20); m_ui->savePath->setMaxVisibleItems(20);
const auto *session = BitTorrent::Session::instance();
m_ui->downloadPath->setMode(FileSystemPathEdit::Mode::DirectorySave); m_ui->downloadPath->setMode(FileSystemPathEdit::Mode::DirectorySave);
m_ui->downloadPath->setDialogCaption(tr("Choose save path")); m_ui->downloadPath->setDialogCaption(tr("Choose save path"));
m_ui->downloadPath->setMaxVisibleItems(20); m_ui->downloadPath->setMaxVisibleItems(20);
m_ui->addToQueueTopCheckBox->setChecked(m_torrentParams.addToQueueTop.value_or(session->isAddTorrentToQueueTop()));
m_ui->stopConditionComboBox->setToolTip( m_ui->stopConditionComboBox->setToolTip(
u"<html><body><p><b>" + tr("None") + u"</b> - " + tr("No stop condition is set.") + u"</p><p><b>" + u"<html><body><p><b>" + tr("None") + u"</b> - " + tr("No stop condition is set.") + u"</p><p><b>"
tr("Metadata received") + u"</b> - " + tr("Torrent will stop after metadata is received.") + + tr("Metadata received") + u"</b> - " + tr("Torrent will stop after metadata is received.")
u" <em>" + tr("Torrents that have metadata initially will be added as stopped.") + u"</em></p><p><b>" + + u" <em>" + tr("Torrents that have metadata initially will be added as stopped.") + u"</em></p><p><b>"
tr("Files checked") + u"</b> - " + tr("Torrent will stop after files are initially checked.") + + tr("Files checked") + u"</b> - " + tr("Torrent will stop after files are initially checked.")
u" <em>" + tr("This will also download metadata if it wasn't there initially.") + u"</em></p></body></html>"); + u" <em>" + tr("This will also download metadata if it wasn't there initially.") + u"</em></p></body></html>");
m_ui->stopConditionComboBox->addItem(tr("None"), QVariant::fromValue(BitTorrent::Torrent::StopCondition::None)); m_ui->stopConditionComboBox->addItem(tr("None"), QVariant::fromValue(BitTorrent::Torrent::StopCondition::None));
if (!hasMetadata())
m_ui->stopConditionComboBox->addItem(tr("Metadata received"), QVariant::fromValue(BitTorrent::Torrent::StopCondition::MetadataReceived));
m_ui->stopConditionComboBox->addItem(tr("Files checked"), QVariant::fromValue(BitTorrent::Torrent::StopCondition::FilesChecked)); m_ui->stopConditionComboBox->addItem(tr("Files checked"), QVariant::fromValue(BitTorrent::Torrent::StopCondition::FilesChecked));
const auto stopCondition = m_torrentParams.stopCondition.value_or(session->torrentStopCondition());
if (hasMetadata() && (stopCondition == BitTorrent::Torrent::StopCondition::MetadataReceived))
{
m_ui->startTorrentCheckBox->setChecked(false);
m_ui->stopConditionComboBox->setCurrentIndex(m_ui->stopConditionComboBox->findData(QVariant::fromValue(BitTorrent::Torrent::StopCondition::None)));
}
else
{
m_ui->startTorrentCheckBox->setChecked(!m_torrentParams.addStopped.value_or(session->isAddTorrentStopped()));
m_ui->stopConditionComboBox->setCurrentIndex(m_ui->stopConditionComboBox->findData(QVariant::fromValue(stopCondition)));
}
m_ui->stopConditionLabel->setEnabled(m_ui->startTorrentCheckBox->isChecked());
m_ui->stopConditionComboBox->setEnabled(m_ui->startTorrentCheckBox->isChecked());
connect(m_ui->startTorrentCheckBox, &QCheckBox::toggled, this, [this](const bool checked)
{
m_ui->stopConditionLabel->setEnabled(checked);
m_ui->stopConditionComboBox->setEnabled(checked);
});
m_ui->comboTTM->blockSignals(true); // the TreeView size isn't correct if the slot does its job at this point
m_ui->comboTTM->setCurrentIndex(session->isAutoTMMDisabledByDefault() ? 0 : 1);
m_ui->comboTTM->blockSignals(false);
connect(m_ui->comboTTM, &QComboBox::currentIndexChanged, this, &AddNewTorrentDialog::TMMChanged);
connect(m_ui->savePath, &FileSystemPathEdit::selectedPathChanged, this, &AddNewTorrentDialog::onSavePathChanged);
connect(m_ui->downloadPath, &FileSystemPathEdit::selectedPathChanged, this, &AddNewTorrentDialog::onDownloadPathChanged);
connect(m_ui->groupBoxDownloadPath, &QGroupBox::toggled, this, &AddNewTorrentDialog::onUseDownloadPathChanged);
m_ui->checkBoxRememberLastSavePath->setChecked(m_storeRememberLastSavePath); m_ui->checkBoxRememberLastSavePath->setChecked(m_storeRememberLastSavePath);
m_ui->contentLayoutComboBox->setCurrentIndex(
static_cast<int>(m_torrentParams.contentLayout.value_or(session->torrentContentLayout())));
connect(m_ui->contentLayoutComboBox, &QComboBox::currentIndexChanged, this, &AddNewTorrentDialog::contentLayoutChanged);
m_ui->sequentialCheckBox->setChecked(m_torrentParams.sequential);
m_ui->firstLastCheckBox->setChecked(m_torrentParams.firstLastPiecePriority);
m_ui->skipCheckingCheckBox->setChecked(m_torrentParams.skipChecking);
m_ui->doNotDeleteTorrentCheckBox->setVisible(TorrentFileGuard::autoDeleteMode() != TorrentFileGuard::Never); m_ui->doNotDeleteTorrentCheckBox->setVisible(TorrentFileGuard::autoDeleteMode() != TorrentFileGuard::Never);
// Load categories
QStringList categories = session->categories();
std::sort(categories.begin(), categories.end(), Utils::Compare::NaturalLessThan<Qt::CaseInsensitive>());
const QString defaultCategory = m_storeDefaultCategory;
if (!m_torrentParams.category.isEmpty())
m_ui->categoryComboBox->addItem(m_torrentParams.category);
if (!defaultCategory.isEmpty())
m_ui->categoryComboBox->addItem(defaultCategory);
m_ui->categoryComboBox->addItem(u""_s);
for (const QString &category : asConst(categories))
{
if ((category != defaultCategory) && (category != m_torrentParams.category))
m_ui->categoryComboBox->addItem(category);
}
connect(m_ui->categoryComboBox, &QComboBox::currentIndexChanged, this, &AddNewTorrentDialog::categoryChanged);
m_ui->tagsLineEdit->setText(Utils::String::joinIntoString(m_torrentParams.tags, u", "_s));
connect(m_ui->tagsEditButton, &QAbstractButton::clicked, this, [this]
{
auto *dlg = new TorrentTagsDialog(m_torrentParams.tags, this);
dlg->setAttribute(Qt::WA_DeleteOnClose);
connect(dlg, &TorrentTagsDialog::accepted, this, [this, dlg]
{
m_torrentParams.tags = dlg->tags();
m_ui->tagsLineEdit->setText(Utils::String::joinIntoString(m_torrentParams.tags, u", "_s));
});
dlg->open();
});
// Torrent content filtering // Torrent content filtering
m_filterLine->setPlaceholderText(tr("Filter files...")); m_filterLine->setPlaceholderText(tr("Filter files..."));
m_filterLine->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); m_filterLine->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
connect(m_filterLine, &LineEdit::textChanged, m_ui->contentTreeView, &TorrentContentWidget::setFilterPattern);
m_ui->contentFilterLayout->insertWidget(3, m_filterLine); m_ui->contentFilterLayout->insertWidget(3, m_filterLine);
const auto *focusSearchHotkey = new QShortcut(QKeySequence::Find, this); const auto *focusSearchHotkey = new QShortcut(QKeySequence::Find, this);
connect(focusSearchHotkey, &QShortcut::activated, this, [this]() connect(focusSearchHotkey, &QShortcut::activated, this, [this]()
@ -403,9 +326,6 @@ AddNewTorrentDialog::AddNewTorrentDialog(const BitTorrent::TorrentDescriptor &to
loadState(); loadState();
connect(m_ui->buttonSelectAll, &QPushButton::clicked, m_ui->contentTreeView, &TorrentContentWidget::checkAll);
connect(m_ui->buttonSelectNone, &QPushButton::clicked, m_ui->contentTreeView, &TorrentContentWidget::checkNone);
if (const QByteArray state = m_storeTreeHeaderState; !state.isEmpty()) if (const QByteArray state = m_storeTreeHeaderState; !state.isEmpty())
m_ui->contentTreeView->header()->restoreState(state); m_ui->contentTreeView->header()->restoreState(state);
// Hide useless columns after loading the header state // Hide useless columns after loading the header state
@ -415,17 +335,34 @@ AddNewTorrentDialog::AddNewTorrentDialog(const BitTorrent::TorrentDescriptor &to
m_ui->contentTreeView->setColumnsVisibilityMode(TorrentContentWidget::ColumnsVisibilityMode::Locked); m_ui->contentTreeView->setColumnsVisibilityMode(TorrentContentWidget::ColumnsVisibilityMode::Locked);
m_ui->contentTreeView->setDoubleClickAction(TorrentContentWidget::DoubleClickAction::Rename); m_ui->contentTreeView->setDoubleClickAction(TorrentContentWidget::DoubleClickAction::Rename);
m_ui->labelCommentData->setText(tr("Not Available", "This comment is unavailable")); connect(m_ui->buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept);
m_ui->labelDateData->setText(tr("Not Available", "This date is unavailable")); connect(m_ui->buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject);
connect(m_ui->buttonSave, &QPushButton::clicked, this, &AddNewTorrentDialog::saveTorrentFile);
m_filterLine->blockSignals(true); connect(m_ui->savePath, &FileSystemPathEdit::selectedPathChanged, this, &AddNewTorrentDialog::onSavePathChanged);
connect(m_ui->downloadPath, &FileSystemPathEdit::selectedPathChanged, this, &AddNewTorrentDialog::onDownloadPathChanged);
// Default focus connect(m_ui->groupBoxDownloadPath, &QGroupBox::toggled, this, &AddNewTorrentDialog::onUseDownloadPathChanged);
if (m_ui->comboTTM->currentIndex() == 0) // 0 is Manual mode connect(m_ui->comboTMM, &QComboBox::currentIndexChanged, this, &AddNewTorrentDialog::TMMChanged);
m_ui->savePath->setFocus(); connect(m_ui->startTorrentCheckBox, &QCheckBox::toggled, this, [this](const bool checked)
else {
m_ui->categoryComboBox->setFocus(); m_ui->stopConditionLabel->setEnabled(checked);
m_ui->stopConditionComboBox->setEnabled(checked);
});
connect(m_ui->contentLayoutComboBox, &QComboBox::currentIndexChanged, this, &AddNewTorrentDialog::contentLayoutChanged);
connect(m_ui->categoryComboBox, &QComboBox::currentIndexChanged, this, &AddNewTorrentDialog::categoryChanged);
connect(m_ui->tagsEditButton, &QAbstractButton::clicked, this, [this]
{
auto *dlg = new TorrentTagsDialog(m_currentContext->torrentParams.tags, this);
dlg->setAttribute(Qt::WA_DeleteOnClose);
connect(dlg, &TorrentTagsDialog::accepted, this, [this, dlg]
{
m_currentContext->torrentParams.tags = dlg->tags();
m_ui->tagsLineEdit->setText(Utils::String::joinIntoString(m_currentContext->torrentParams.tags, u", "_s));
});
dlg->open();
});
connect(m_filterLine, &LineEdit::textChanged, m_ui->contentTreeView, &TorrentContentWidget::setFilterPattern);
connect(m_ui->buttonSelectAll, &QPushButton::clicked, m_ui->contentTreeView, &TorrentContentWidget::checkAll);
connect(m_ui->buttonSelectNone, &QPushButton::clicked, m_ui->contentTreeView, &TorrentContentWidget::checkNone);
connect(Preferences::instance(), &Preferences::changed, [] connect(Preferences::instance(), &Preferences::changed, []
{ {
const int length = Preferences::instance()->addNewTorrentDialogSavePathHistoryLength(); const int length = Preferences::instance()->addNewTorrentDialogSavePathHistoryLength();
@ -433,25 +370,7 @@ AddNewTorrentDialog::AddNewTorrentDialog(const BitTorrent::TorrentDescriptor &to
, QStringList(settings()->loadValue<QStringList>(KEY_SAVEPATHHISTORY).mid(0, length))); , QStringList(settings()->loadValue<QStringList>(KEY_SAVEPATHHISTORY).mid(0, length)));
}); });
const BitTorrent::InfoHash infoHash = m_torrentDescr.infoHash(); setCurrentContext(std::make_shared<Context>(Context {torrentDescr, inParams}));
m_ui->labelInfohash1Data->setText(infoHash.v1().isValid() ? infoHash.v1().toString() : tr("N/A"));
m_ui->labelInfohash2Data->setText(infoHash.v2().isValid() ? infoHash.v2().toString() : tr("N/A"));
if (hasMetadata())
{
setupTreeview();
}
else
{
// Set dialog title
const QString torrentName = m_torrentDescr.name();
setWindowTitle(torrentName.isEmpty() ? tr("Magnet link") : torrentName);
updateDiskSpaceLabel();
setMetadataProgressIndicator(true, tr("Retrieving metadata..."));
}
TMMChanged(m_ui->comboTTM->currentIndex());
} }
AddNewTorrentDialog::~AddNewTorrentDialog() AddNewTorrentDialog::~AddNewTorrentDialog()
@ -460,16 +379,6 @@ AddNewTorrentDialog::~AddNewTorrentDialog()
delete m_ui; delete m_ui;
} }
BitTorrent::TorrentDescriptor AddNewTorrentDialog::torrentDescriptor() const
{
return m_torrentDescr;
}
BitTorrent::AddTorrentParams AddNewTorrentDialog::addTorrentParams() const
{
return m_torrentParams;
}
bool AddNewTorrentDialog::isDoNotDeleteTorrentChecked() const bool AddNewTorrentDialog::isDoNotDeleteTorrentChecked() const
{ {
return m_ui->doNotDeleteTorrentCheckBox->isChecked(); return m_ui->doNotDeleteTorrentCheckBox->isChecked();
@ -485,9 +394,16 @@ void AddNewTorrentDialog::loadState()
void AddNewTorrentDialog::saveState() void AddNewTorrentDialog::saveState()
{ {
Q_ASSERT(m_currentContext);
if (!m_currentContext) [[unlikely]]
return;
const BitTorrent::TorrentDescriptor &torrentDescr = m_currentContext->torrentDescr;
const bool hasMetadata = torrentDescr.info().has_value();
m_storeDialogSize = size(); m_storeDialogSize = size();
m_storeSplitterState = m_ui->splitter->saveState(); m_storeSplitterState = m_ui->splitter->saveState();
if (hasMetadata()) if (hasMetadata)
m_storeTreeHeaderState = m_ui->contentTreeView->header()->saveState(); m_storeTreeHeaderState = m_ui->contentTreeView->header()->saveState();
} }
@ -501,14 +417,174 @@ void AddNewTorrentDialog::showEvent(QShowEvent *event)
raise(); raise();
} }
void AddNewTorrentDialog::setCurrentContext(const std::shared_ptr<Context> context)
{
Q_ASSERT(context);
if (!context) [[unlikely]]
return;
m_currentContext = context;
const QSignalBlocker comboTMMSignalBlocker {m_ui->comboTMM};
const QSignalBlocker startTorrentCheckBoxSignalBlocker {m_ui->startTorrentCheckBox};
const QSignalBlocker contentLayoutComboBoxSignalBlocker {m_ui->contentLayoutComboBox};
const QSignalBlocker categoryComboBoxSignalBlocker {m_ui->categoryComboBox};
const BitTorrent::AddTorrentParams &addTorrentParams = m_currentContext->torrentParams;
const auto *session = BitTorrent::Session::instance();
// TODO: set dialog file properties using m_torrentParams.filePriorities
m_ui->comboTMM->setCurrentIndex(addTorrentParams.useAutoTMM.value_or(!session->isAutoTMMDisabledByDefault()) ? 1 : 0);
m_ui->addToQueueTopCheckBox->setChecked(addTorrentParams.addToQueueTop.value_or(session->isAddTorrentToQueueTop()));
m_ui->startTorrentCheckBox->setChecked(!addTorrentParams.addStopped.value_or(session->isAddTorrentStopped()));
m_ui->stopConditionLabel->setEnabled(m_ui->startTorrentCheckBox->isChecked());
m_ui->stopConditionComboBox->setEnabled(m_ui->startTorrentCheckBox->isChecked());
m_ui->contentLayoutComboBox->setCurrentIndex(
static_cast<int>(addTorrentParams.contentLayout.value_or(session->torrentContentLayout())));
m_ui->sequentialCheckBox->setChecked(addTorrentParams.sequential);
m_ui->firstLastCheckBox->setChecked(addTorrentParams.firstLastPiecePriority);
m_ui->skipCheckingCheckBox->setChecked(addTorrentParams.skipChecking);
m_ui->tagsLineEdit->setText(Utils::String::joinIntoString(addTorrentParams.tags, u", "_s));
// Load categories
QStringList categories = session->categories();
std::sort(categories.begin(), categories.end(), Utils::Compare::NaturalLessThan<Qt::CaseInsensitive>());
const QString defaultCategory = m_storeDefaultCategory;
if (!addTorrentParams.category.isEmpty())
m_ui->categoryComboBox->addItem(addTorrentParams.category);
if (!defaultCategory.isEmpty())
m_ui->categoryComboBox->addItem(defaultCategory);
m_ui->categoryComboBox->addItem(u""_s);
for (const QString &category : asConst(categories))
{
if ((category != defaultCategory) && (category != addTorrentParams.category))
m_ui->categoryComboBox->addItem(category);
}
m_filterLine->blockSignals(true);
m_filterLine->clear();
// Default focus
if (m_ui->comboTMM->currentIndex() == 0) // 0 is Manual mode
m_ui->savePath->setFocus();
else
m_ui->categoryComboBox->setFocus();
const BitTorrent::TorrentDescriptor &torrentDescr = m_currentContext->torrentDescr;
const BitTorrent::InfoHash infoHash = torrentDescr.infoHash();
const bool hasMetadata = torrentDescr.info().has_value();
if (hasMetadata)
m_ui->stopConditionComboBox->removeItem(m_ui->stopConditionComboBox->findData(QVariant::fromValue(BitTorrent::Torrent::StopCondition::MetadataReceived)));
else
m_ui->stopConditionComboBox->insertItem(1, tr("Metadata received"), QVariant::fromValue(BitTorrent::Torrent::StopCondition::MetadataReceived));
const auto stopCondition = addTorrentParams.stopCondition.value_or(session->torrentStopCondition());
if (hasMetadata && (stopCondition == BitTorrent::Torrent::StopCondition::MetadataReceived))
{
m_ui->startTorrentCheckBox->setChecked(false);
m_ui->stopConditionComboBox->setCurrentIndex(m_ui->stopConditionComboBox->findData(QVariant::fromValue(BitTorrent::Torrent::StopCondition::None)));
}
else
{
m_ui->startTorrentCheckBox->setChecked(!addTorrentParams.addStopped.value_or(session->isAddTorrentStopped()));
m_ui->stopConditionComboBox->setCurrentIndex(m_ui->stopConditionComboBox->findData(QVariant::fromValue(stopCondition)));
}
m_ui->labelInfohash1Data->setText(infoHash.v1().isValid() ? infoHash.v1().toString() : tr("N/A"));
m_ui->labelInfohash2Data->setText(infoHash.v2().isValid() ? infoHash.v2().toString() : tr("N/A"));
if (hasMetadata)
{
m_ui->lblMetaLoading->setVisible(false);
m_ui->progMetaLoading->setVisible(false);
m_ui->buttonSave->setVisible(false);
setupTreeview();
}
else
{
// Set dialog title
const QString torrentName = torrentDescr.name();
setWindowTitle(torrentName.isEmpty() ? tr("Magnet link") : torrentName);
m_ui->labelCommentData->setText(tr("Not Available", "This comment is unavailable"));
m_ui->labelDateData->setText(tr("Not Available", "This date is unavailable"));
updateDiskSpaceLabel();
setMetadataProgressIndicator(true, tr("Retrieving metadata..."));
}
TMMChanged(m_ui->comboTMM->currentIndex());
}
void AddNewTorrentDialog::updateCurrentContext()
{
Q_ASSERT(m_currentContext);
if (!m_currentContext) [[unlikely]]
return;
BitTorrent::AddTorrentParams &addTorrentParams = m_currentContext->torrentParams;
addTorrentParams.skipChecking = m_ui->skipCheckingCheckBox->isChecked();
// Category
addTorrentParams.category = m_ui->categoryComboBox->currentText();
if (m_ui->defaultCategoryCheckbox->isChecked())
m_storeDefaultCategory = addTorrentParams.category;
m_storeRememberLastSavePath = m_ui->checkBoxRememberLastSavePath->isChecked();
addTorrentParams.addToQueueTop = m_ui->addToQueueTopCheckBox->isChecked();
addTorrentParams.addStopped = !m_ui->startTorrentCheckBox->isChecked();
addTorrentParams.stopCondition = m_ui->stopConditionComboBox->currentData().value<BitTorrent::Torrent::StopCondition>();
addTorrentParams.contentLayout = static_cast<BitTorrent::TorrentContentLayout>(m_ui->contentLayoutComboBox->currentIndex());
addTorrentParams.sequential = m_ui->sequentialCheckBox->isChecked();
addTorrentParams.firstLastPiecePriority = m_ui->firstLastCheckBox->isChecked();
const bool useAutoTMM = (m_ui->comboTMM->currentIndex() == 1); // 1 is Automatic mode. Handle all non 1 values as manual mode.
addTorrentParams.useAutoTMM = useAutoTMM;
if (!useAutoTMM)
{
const int savePathHistoryLength = Preferences::instance()->addNewTorrentDialogSavePathHistoryLength();
const Path savePath = m_ui->savePath->selectedPath();
addTorrentParams.savePath = savePath;
updatePathHistory(KEY_SAVEPATHHISTORY, savePath, savePathHistoryLength);
addTorrentParams.useDownloadPath = m_ui->groupBoxDownloadPath->isChecked();
if (addTorrentParams.useDownloadPath)
{
const Path downloadPath = m_ui->downloadPath->selectedPath();
addTorrentParams.downloadPath = downloadPath;
updatePathHistory(KEY_DOWNLOADPATHHISTORY, downloadPath, savePathHistoryLength);
}
else
{
addTorrentParams.downloadPath = Path();
}
}
else
{
addTorrentParams.savePath = Path();
addTorrentParams.downloadPath = Path();
addTorrentParams.useDownloadPath = std::nullopt;
}
}
void AddNewTorrentDialog::updateDiskSpaceLabel() void AddNewTorrentDialog::updateDiskSpaceLabel()
{ {
Q_ASSERT(m_currentContext);
if (!m_currentContext) [[unlikely]]
return;
const BitTorrent::TorrentDescriptor &torrentDescr = m_currentContext->torrentDescr;
const bool hasMetadata = torrentDescr.info().has_value();
// Determine torrent size // Determine torrent size
qlonglong torrentSize = 0; qlonglong torrentSize = 0;
if (hasMetadata)
if (hasMetadata())
{ {
const auto torrentInfo = *m_torrentDescr.info(); const auto torrentInfo = *torrentDescr.info();
const QVector<BitTorrent::DownloadPriority> &priorities = m_contentAdaptor->filePriorities(); const QVector<BitTorrent::DownloadPriority> &priorities = m_contentAdaptor->filePriorities();
Q_ASSERT(priorities.size() == torrentInfo.filesCount()); Q_ASSERT(priorities.size() == torrentInfo.filesCount());
for (int i = 0; i < priorities.size(); ++i) for (int i = 0; i < priorities.size(); ++i)
@ -548,7 +624,7 @@ void AddNewTorrentDialog::onUseDownloadPathChanged(const bool checked)
void AddNewTorrentDialog::categoryChanged([[maybe_unused]] const int index) void AddNewTorrentDialog::categoryChanged([[maybe_unused]] const int index)
{ {
if (m_ui->comboTTM->currentIndex() == 1) if (m_ui->comboTMM->currentIndex() == 1)
{ {
const auto *btSession = BitTorrent::Session::instance(); const auto *btSession = BitTorrent::Session::instance();
const QString categoryName = m_ui->categoryComboBox->currentText(); const QString categoryName = m_ui->categoryComboBox->currentText();
@ -567,7 +643,14 @@ void AddNewTorrentDialog::categoryChanged([[maybe_unused]] const int index)
void AddNewTorrentDialog::contentLayoutChanged() void AddNewTorrentDialog::contentLayoutChanged()
{ {
if (!hasMetadata()) Q_ASSERT(m_currentContext);
if (!m_currentContext) [[unlikely]]
return;
const BitTorrent::TorrentDescriptor &torrentDescr = m_currentContext->torrentDescr;
const bool hasMetadata = torrentDescr.info().has_value();
if (!hasMetadata)
return; return;
const auto contentLayout = static_cast<BitTorrent::TorrentContentLayout>(m_ui->contentLayoutComboBox->currentIndex()); const auto contentLayout = static_cast<BitTorrent::TorrentContentLayout>(m_ui->contentLayoutComboBox->currentIndex());
@ -577,37 +660,44 @@ void AddNewTorrentDialog::contentLayoutChanged()
void AddNewTorrentDialog::saveTorrentFile() void AddNewTorrentDialog::saveTorrentFile()
{ {
Q_ASSERT(hasMetadata()); Q_ASSERT(m_currentContext);
if (!hasMetadata()) [[unlikely]] if (!m_currentContext) [[unlikely]]
return; return;
const auto torrentInfo = *m_torrentDescr.info(); const BitTorrent::TorrentDescriptor &torrentDescr = m_currentContext->torrentDescr;
const bool hasMetadata = torrentDescr.info().has_value();
Q_ASSERT(hasMetadata);
if (!hasMetadata) [[unlikely]]
return;
const auto torrentInfo = *torrentDescr.info();
const QString filter {tr("Torrent file (*%1)").arg(TORRENT_FILE_EXTENSION)}; const QString filter {tr("Torrent file (*%1)").arg(TORRENT_FILE_EXTENSION)};
Path path {QFileDialog::getSaveFileName(this, tr("Save as torrent file") Path path {QFileDialog::getSaveFileName(this, tr("Save as torrent file")
, QDir::home().absoluteFilePath(torrentInfo.name() + TORRENT_FILE_EXTENSION) , QDir::home().absoluteFilePath(torrentInfo.name() + TORRENT_FILE_EXTENSION)
, filter)}; , filter)};
if (path.isEmpty()) return; if (path.isEmpty())
return;
if (!path.hasExtension(TORRENT_FILE_EXTENSION)) if (!path.hasExtension(TORRENT_FILE_EXTENSION))
path += TORRENT_FILE_EXTENSION; path += TORRENT_FILE_EXTENSION;
const auto result = m_torrentDescr.saveToFile(path); if (const auto result = torrentDescr.saveToFile(path); !result)
if (!result)
{ {
QMessageBox::critical(this, tr("I/O Error") QMessageBox::critical(this, tr("I/O Error")
, tr("Couldn't export torrent metadata file '%1'. Reason: %2.").arg(path.toString(), result.error())); , tr("Couldn't export torrent metadata file '%1'. Reason: %2.").arg(path.toString(), result.error()));
} }
} }
bool AddNewTorrentDialog::hasMetadata() const
{
return m_torrentDescr.info().has_value();
}
void AddNewTorrentDialog::populateSavePaths() void AddNewTorrentDialog::populateSavePaths()
{ {
Q_ASSERT(m_currentContext);
if (!m_currentContext) [[unlikely]]
return;
const BitTorrent::AddTorrentParams &addTorrentParams = m_currentContext->torrentParams;
const auto *btSession = BitTorrent::Session::instance(); const auto *btSession = BitTorrent::Session::instance();
m_ui->savePath->blockSignals(true); m_ui->savePath->blockSignals(true);
@ -629,8 +719,8 @@ void AddNewTorrentDialog::populateSavePaths()
} }
else else
{ {
if (!m_torrentParams.savePath.isEmpty()) if (!addTorrentParams.savePath.isEmpty())
setPath(m_ui->savePath, m_torrentParams.savePath); setPath(m_ui->savePath, addTorrentParams.savePath);
else if (!m_storeRememberLastSavePath) else if (!m_storeRememberLastSavePath)
setPath(m_ui->savePath, btSession->savePath()); setPath(m_ui->savePath, btSession->savePath());
else else
@ -661,11 +751,11 @@ void AddNewTorrentDialog::populateSavePaths()
} }
else else
{ {
const bool useDownloadPath = m_torrentParams.useDownloadPath.value_or(btSession->isDownloadPathEnabled()); const bool useDownloadPath = addTorrentParams.useDownloadPath.value_or(btSession->isDownloadPathEnabled());
m_ui->groupBoxDownloadPath->setChecked(useDownloadPath); m_ui->groupBoxDownloadPath->setChecked(useDownloadPath);
if (!m_torrentParams.downloadPath.isEmpty()) if (!addTorrentParams.downloadPath.isEmpty())
setPath(m_ui->downloadPath, m_torrentParams.downloadPath); setPath(m_ui->downloadPath, addTorrentParams.downloadPath);
else if (!m_storeRememberLastSavePath) else if (!m_storeRememberLastSavePath)
setPath(m_ui->downloadPath, btSession->downloadPath()); setPath(m_ui->downloadPath, btSession->downloadPath());
else else
@ -682,63 +772,30 @@ void AddNewTorrentDialog::populateSavePaths()
void AddNewTorrentDialog::accept() void AddNewTorrentDialog::accept()
{ {
// TODO: Check if destination actually exists Q_ASSERT(m_currentContext);
m_torrentParams.skipChecking = m_ui->skipCheckingCheckBox->isChecked(); if (!m_currentContext) [[unlikely]]
return;
// Category updateCurrentContext();
m_torrentParams.category = m_ui->categoryComboBox->currentText(); emit torrentAccepted(m_currentContext->torrentDescr, m_currentContext->torrentParams);
if (m_ui->defaultCategoryCheckbox->isChecked())
m_storeDefaultCategory = m_torrentParams.category;
m_storeRememberLastSavePath = m_ui->checkBoxRememberLastSavePath->isChecked(); Preferences::instance()->setAddNewTorrentDialogEnabled(!m_ui->checkBoxNeverShow->isChecked());
m_torrentParams.addToQueueTop = m_ui->addToQueueTopCheckBox->isChecked();
m_torrentParams.addStopped = !m_ui->startTorrentCheckBox->isChecked();
m_torrentParams.stopCondition = m_ui->stopConditionComboBox->currentData().value<BitTorrent::Torrent::StopCondition>();
m_torrentParams.contentLayout = static_cast<BitTorrent::TorrentContentLayout>(m_ui->contentLayoutComboBox->currentIndex());
m_torrentParams.sequential = m_ui->sequentialCheckBox->isChecked();
m_torrentParams.firstLastPiecePriority = m_ui->firstLastCheckBox->isChecked();
const bool useAutoTMM = (m_ui->comboTTM->currentIndex() == 1); // 1 is Automatic mode. Handle all non 1 values as manual mode.
m_torrentParams.useAutoTMM = useAutoTMM;
if (!useAutoTMM)
{
const int savePathHistoryLength = Preferences::instance()->addNewTorrentDialogSavePathHistoryLength();
const Path savePath = m_ui->savePath->selectedPath();
m_torrentParams.savePath = savePath;
updatePathHistory(KEY_SAVEPATHHISTORY, savePath, savePathHistoryLength);
m_torrentParams.useDownloadPath = m_ui->groupBoxDownloadPath->isChecked();
if (m_torrentParams.useDownloadPath)
{
const Path downloadPath = m_ui->downloadPath->selectedPath();
m_torrentParams.downloadPath = downloadPath;
updatePathHistory(KEY_DOWNLOADPATHHISTORY, downloadPath, savePathHistoryLength);
}
else
{
m_torrentParams.downloadPath = Path();
}
}
else
{
m_torrentParams.savePath = Path();
m_torrentParams.downloadPath = Path();
m_torrentParams.useDownloadPath = std::nullopt;
}
setEnabled(!m_ui->checkBoxNeverShow->isChecked());
QDialog::accept(); QDialog::accept();
} }
void AddNewTorrentDialog::reject() void AddNewTorrentDialog::reject()
{ {
if (!hasMetadata()) Q_ASSERT(m_currentContext);
if (!m_currentContext) [[unlikely]]
return;
const BitTorrent::TorrentDescriptor &torrentDescr = m_currentContext->torrentDescr;
const bool hasMetadata = torrentDescr.info().has_value();
if (!hasMetadata)
{ {
setMetadataProgressIndicator(false); setMetadataProgressIndicator(false);
BitTorrent::Session::instance()->cancelDownloadMetadata(m_torrentDescr.infoHash().toTorrentID()); BitTorrent::Session::instance()->cancelDownloadMetadata(torrentDescr.infoHash().toTorrentID());
} }
QDialog::reject(); QDialog::reject();
@ -746,15 +803,20 @@ void AddNewTorrentDialog::reject()
void AddNewTorrentDialog::updateMetadata(const BitTorrent::TorrentInfo &metadata) void AddNewTorrentDialog::updateMetadata(const BitTorrent::TorrentInfo &metadata)
{ {
Q_ASSERT(m_currentContext);
if (!m_currentContext) [[unlikely]]
return;
Q_ASSERT(metadata.isValid()); Q_ASSERT(metadata.isValid());
if (!metadata.isValid()) [[unlikely]] if (!metadata.isValid()) [[unlikely]]
return; return;
Q_ASSERT(metadata.matchesInfoHash(m_torrentDescr.infoHash())); BitTorrent::TorrentDescriptor &torrentDescr = m_currentContext->torrentDescr;
if (!metadata.matchesInfoHash(m_torrentDescr.infoHash())) [[unlikely]] Q_ASSERT(metadata.matchesInfoHash(torrentDescr.infoHash()));
if (!metadata.matchesInfoHash(torrentDescr.infoHash())) [[unlikely]]
return; return;
m_torrentDescr.setTorrentInfo(metadata); torrentDescr.setTorrentInfo(metadata);
setMetadataProgressIndicator(true, tr("Parsing metadata...")); setMetadataProgressIndicator(true, tr("Parsing metadata..."));
// Update UI // Update UI
@ -773,7 +835,7 @@ void AddNewTorrentDialog::updateMetadata(const BitTorrent::TorrentInfo &metadata
} }
m_ui->buttonSave->setVisible(true); m_ui->buttonSave->setVisible(true);
if (m_torrentDescr.infoHash().v2().isValid()) if (torrentDescr.infoHash().v2().isValid())
{ {
m_ui->buttonSave->setEnabled(false); m_ui->buttonSave->setEnabled(false);
m_ui->buttonSave->setToolTip(tr("Cannot create v2 torrent until its data is fully downloaded.")); m_ui->buttonSave->setToolTip(tr("Cannot create v2 torrent until its data is fully downloaded."));
@ -790,24 +852,32 @@ void AddNewTorrentDialog::setMetadataProgressIndicator(bool visibleIndicator, co
void AddNewTorrentDialog::setupTreeview() void AddNewTorrentDialog::setupTreeview()
{ {
Q_ASSERT(hasMetadata()); Q_ASSERT(m_currentContext);
if (!hasMetadata()) [[unlikely]] if (!m_currentContext) [[unlikely]]
return;
const BitTorrent::TorrentDescriptor &torrentDescr = m_currentContext->torrentDescr;
const bool hasMetadata = torrentDescr.info().has_value();
Q_ASSERT(hasMetadata);
if (!hasMetadata) [[unlikely]]
return; return;
// Set dialog title // Set dialog title
setWindowTitle(m_torrentDescr.name()); setWindowTitle(torrentDescr.name());
const auto &torrentInfo = *m_torrentDescr.info(); const auto &torrentInfo = *torrentDescr.info();
// Set torrent information // Set torrent information
m_ui->labelCommentData->setText(Utils::Misc::parseHtmlLinks(torrentInfo.comment().toHtmlEscaped())); m_ui->labelCommentData->setText(Utils::Misc::parseHtmlLinks(torrentInfo.comment().toHtmlEscaped()));
m_ui->labelDateData->setText(!torrentInfo.creationDate().isNull() ? QLocale().toString(torrentInfo.creationDate(), QLocale::ShortFormat) : tr("Not available")); m_ui->labelDateData->setText(!torrentInfo.creationDate().isNull() ? QLocale().toString(torrentInfo.creationDate(), QLocale::ShortFormat) : tr("Not available"));
if (m_torrentParams.filePaths.isEmpty()) BitTorrent::AddTorrentParams &addTorrentParams = m_currentContext->torrentParams;
m_torrentParams.filePaths = torrentInfo.filePaths(); if (addTorrentParams.filePaths.isEmpty())
addTorrentParams.filePaths = torrentInfo.filePaths();
m_contentAdaptor = std::make_unique<TorrentContentAdaptor>(torrentInfo, m_torrentParams.filePaths m_contentAdaptor = std::make_unique<TorrentContentAdaptor>(torrentInfo, addTorrentParams.filePaths
, m_torrentParams.filePriorities, [this] { updateDiskSpaceLabel(); }); , addTorrentParams.filePriorities, [this] { updateDiskSpaceLabel(); });
const auto contentLayout = static_cast<BitTorrent::TorrentContentLayout>(m_ui->contentLayoutComboBox->currentIndex()); const auto contentLayout = static_cast<BitTorrent::TorrentContentLayout>(m_ui->contentLayoutComboBox->currentIndex());
m_contentAdaptor->applyContentLayout(contentLayout); m_contentAdaptor->applyContentLayout(contentLayout);

View file

@ -33,13 +33,18 @@
#include <QDialog> #include <QDialog>
#include "base/bittorrent/addtorrentparams.h"
#include "base/bittorrent/torrentdescriptor.h"
#include "base/path.h" #include "base/path.h"
#include "base/settingvalue.h" #include "base/settingvalue.h"
class LineEdit; class LineEdit;
namespace BitTorrent
{
class TorrentDescriptor;
class TorrentInfo;
struct AddTorrentParams;
}
namespace Ui namespace Ui
{ {
class AddNewTorrentDialog; class AddNewTorrentDialog;
@ -55,12 +60,12 @@ public:
, const BitTorrent::AddTorrentParams &inParams, QWidget *parent); , const BitTorrent::AddTorrentParams &inParams, QWidget *parent);
~AddNewTorrentDialog() override; ~AddNewTorrentDialog() override;
BitTorrent::TorrentDescriptor torrentDescriptor() const;
BitTorrent::AddTorrentParams addTorrentParams() const;
bool isDoNotDeleteTorrentChecked() const; bool isDoNotDeleteTorrentChecked() const;
void updateMetadata(const BitTorrent::TorrentInfo &metadata); void updateMetadata(const BitTorrent::TorrentInfo &metadata);
signals:
void torrentAccepted(const BitTorrent::TorrentDescriptor &torrentDescriptor, const BitTorrent::AddTorrentParams &addTorrentParams);
private slots: private slots:
void updateDiskSpaceLabel(); void updateDiskSpaceLabel();
void onSavePathChanged(const Path &newPath); void onSavePathChanged(const Path &newPath);
@ -75,26 +80,28 @@ private slots:
private: private:
class TorrentContentAdaptor; class TorrentContentAdaptor;
struct Context;
void showEvent(QShowEvent *event) override;
void setCurrentContext(std::shared_ptr<Context> context);
void updateCurrentContext();
void populateSavePaths(); void populateSavePaths();
void loadState(); void loadState();
void saveState(); void saveState();
void setMetadataProgressIndicator(bool visibleIndicator, const QString &labelText = {}); void setMetadataProgressIndicator(bool visibleIndicator, const QString &labelText = {});
void setupTreeview(); void setupTreeview();
void saveTorrentFile(); void saveTorrentFile();
bool hasMetadata() const;
void showEvent(QShowEvent *event) override;
Ui::AddNewTorrentDialog *m_ui = nullptr; Ui::AddNewTorrentDialog *m_ui = nullptr;
std::unique_ptr<TorrentContentAdaptor> m_contentAdaptor; std::unique_ptr<TorrentContentAdaptor> m_contentAdaptor;
BitTorrent::TorrentDescriptor m_torrentDescr;
BitTorrent::AddTorrentParams m_torrentParams;
int m_savePathIndex = -1; int m_savePathIndex = -1;
int m_downloadPathIndex = -1; int m_downloadPathIndex = -1;
bool m_useDownloadPath = false; bool m_useDownloadPath = false;
LineEdit *m_filterLine = nullptr; LineEdit *m_filterLine = nullptr;
std::shared_ptr<Context> m_currentContext;
SettingValue<QSize> m_storeDialogSize; SettingValue<QSize> m_storeDialogSize;
SettingValue<QString> m_storeDefaultCategory; SettingValue<QString> m_storeDefaultCategory;
SettingValue<bool> m_storeRememberLastSavePath; SettingValue<bool> m_storeRememberLastSavePath;

View file

@ -76,7 +76,7 @@
</widget> </widget>
</item> </item>
<item> <item>
<widget class="QComboBox" name="comboTTM"> <widget class="QComboBox" name="comboTMM">
<property name="toolTip"> <property name="toolTip">
<string>Automatic mode means that various torrent properties(eg save path) will be decided by the associated category</string> <string>Automatic mode means that various torrent properties(eg save path) will be decided by the associated category</string>
</property> </property>

View file

@ -226,14 +226,16 @@ bool GUIAddTorrentManager::processTorrent(const QString &source, const BitTorren
dlg->setAttribute(Qt::WA_DeleteOnClose); dlg->setAttribute(Qt::WA_DeleteOnClose);
m_dialogs[infoHash] = dlg; m_dialogs[infoHash] = dlg;
connect(dlg, &QDialog::finished, this, [this, source, infoHash, dlg](int result) connect(dlg, &AddNewTorrentDialog::torrentAccepted, this
, [this, source](const BitTorrent::TorrentDescriptor &torrentDescr, const BitTorrent::AddTorrentParams &addTorrentParams)
{
addTorrentToSession(source, torrentDescr, addTorrentParams);
});
connect(dlg, &QDialog::finished, this, [this, source, infoHash, dlg]
{ {
if (dlg->isDoNotDeleteTorrentChecked()) if (dlg->isDoNotDeleteTorrentChecked())
releaseTorrentFileGuard(source); releaseTorrentFileGuard(source);
if (result == QDialog::Accepted)
addTorrentToSession(source, dlg->torrentDescriptor(), dlg->addTorrentParams());
m_dialogs.remove(infoHash); m_dialogs.remove(infoHash);
}); });