Merge pull request #15793 from glassez/save-path

Redesign "Incomplete folder" feature
This commit is contained in:
Vladimir Golovnev
2022-01-02 22:25:00 +03:00
committed by GitHub
52 changed files with 1498 additions and 599 deletions

View File

@@ -68,6 +68,7 @@ namespace
const QString KEY_ENABLED = QStringLiteral(SETTINGS_KEY("Enabled"));
const QString KEY_TOPLEVEL = QStringLiteral(SETTINGS_KEY("TopLevel"));
const QString KEY_SAVEPATHHISTORY = QStringLiteral(SETTINGS_KEY("SavePathHistory"));
const QString KEY_DOWNLOADPATHHISTORY = QStringLiteral(SETTINGS_KEY("DownloadPathHistory"));
const QString KEY_SAVEPATHHISTORYLENGTH = QStringLiteral(SETTINGS_KEY("SavePathHistoryLength"));
// just a shortcut
@@ -120,6 +121,45 @@ namespace
const BitTorrent::TorrentInfo &m_torrentInfo;
QStringList &m_filePaths;
};
// savePath is a folder, not an absolute file path
int indexOfPath(const FileSystemPathComboEdit *fsPathEdit, const QString &savePath)
{
const QDir saveDir {savePath};
for (int i = 0; i < fsPathEdit->count(); ++i)
{
if (QDir(fsPathEdit->item(i)) == saveDir)
return i;
}
return -1;
}
void setPath(FileSystemPathComboEdit *fsPathEdit, const QString &newPath)
{
int existingIndex = indexOfPath(fsPathEdit, newPath);
if (existingIndex < 0)
{
// New path, prepend to combo box
fsPathEdit->insertItem(0, newPath);
existingIndex = 0;
}
fsPathEdit->setCurrentIndex(existingIndex);
}
void updatePathHistory(const QString &settingsKey, const QString &path, const int maxLength)
{
// Add last used save path to the front of history
auto pathList = settings()->loadValue<QStringList>(settingsKey);
const int selectedSavePathIndex = pathList.indexOf(path);
if (selectedSavePathIndex > 0)
pathList.removeAt(selectedSavePathIndex);
if (selectedSavePathIndex != 0)
pathList.prepend(path);
settings()->storeValue(settingsKey, QStringList(pathList.mid(0, maxLength)));
}
}
const int AddNewTorrentDialog::minPathHistoryLength;
@@ -155,13 +195,19 @@ AddNewTorrentDialog::AddNewTorrentDialog(const BitTorrent::AddTorrentParams &inP
const auto *session = BitTorrent::Session::instance();
m_ui->downloadPath->setMode(FileSystemPathEdit::Mode::DirectorySave);
m_ui->downloadPath->setDialogCaption(tr("Choose save path"));
m_ui->downloadPath->setMaxVisibleItems(20);
m_ui->startTorrentCheckBox->setChecked(!m_torrentParams.addPaused.value_or(session->isAddTorrentPaused()));
m_ui->comboTTM->blockSignals(true); // the TreeView size isn't correct if the slot does it job at this point
m_ui->comboTTM->setCurrentIndex(!session->isAutoTMMDisabledByDefault());
m_ui->comboTTM->setCurrentIndex(session->isAutoTMMDisabledByDefault() ? 0 : 1);
m_ui->comboTTM->blockSignals(false);
populateSavePathComboBox();
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);
@@ -176,7 +222,7 @@ AddNewTorrentDialog::AddNewTorrentDialog(const BitTorrent::AddTorrentParams &inP
m_ui->doNotDeleteTorrentCheckBox->setVisible(TorrentFileGuard::autoDeleteMode() != TorrentFileGuard::Never);
// Load categories
QStringList categories = session->categories().keys();
QStringList categories = session->categories();
std::sort(categories.begin(), categories.end(), Utils::Compare::NaturalLessThan<Qt::CaseInsensitive>());
const QString defaultCategory = m_storeDefaultCategory;
@@ -344,6 +390,7 @@ bool AddNewTorrentDialog::loadTorrentImpl()
m_ui->labelInfohash2Data->setText(m_torrentInfo.infoHash().v2().isValid() ? m_torrentInfo.infoHash().v2().toString() : tr("N/A"));
setupTreeview();
TMMChanged(m_ui->comboTTM->currentIndex());
return true;
}
@@ -409,36 +456,6 @@ void AddNewTorrentDialog::showEvent(QShowEvent *event)
raise();
}
void AddNewTorrentDialog::saveSavePathHistory() const
{
// Get current history
auto history = settings()->loadValue<QStringList>(KEY_SAVEPATHHISTORY);
QVector<QDir> historyDirs;
for (const QString &path : asConst(history))
historyDirs << QDir {path};
const QDir selectedSavePath {m_ui->savePath->selectedPath()};
const int selectedSavePathIndex = historyDirs.indexOf(selectedSavePath);
if (selectedSavePathIndex > 0)
history.removeAt(selectedSavePathIndex);
if (selectedSavePathIndex != 0)
// Add last used save path to the front of history
history.push_front(selectedSavePath.absolutePath());
// Save history
settings()->storeValue(KEY_SAVEPATHHISTORY, QStringList {history.mid(0, savePathHistoryLength())});
}
// savePath is a folder, not an absolute file path
int AddNewTorrentDialog::indexOfSavePath(const QString &savePath)
{
QDir saveDir(savePath);
for (int i = 0; i < m_ui->savePath->count(); ++i)
if (QDir(m_ui->savePath->item(i)) == saveDir)
return i;
return -1;
}
void AddNewTorrentDialog::updateDiskSpaceLabel()
{
// Determine torrent size
@@ -472,18 +489,42 @@ void AddNewTorrentDialog::onSavePathChanged(const QString &newPath)
{
Q_UNUSED(newPath);
// Remember index
m_oldIndex = m_ui->savePath->currentIndex();
m_savePathIndex = m_ui->savePath->currentIndex();
updateDiskSpaceLabel();
}
void AddNewTorrentDialog::onDownloadPathChanged(const QString &newPath)
{
Q_UNUSED(newPath);
// Remember index
const int currentPathIndex = m_ui->downloadPath->currentIndex();
if (currentPathIndex >= 0)
m_downloadPathIndex = m_ui->downloadPath->currentIndex();
}
void AddNewTorrentDialog::onUseDownloadPathChanged(const bool checked)
{
m_useDownloadPath = checked;
m_ui->downloadPath->setCurrentIndex(checked ? m_downloadPathIndex : -1);
}
void AddNewTorrentDialog::categoryChanged(int index)
{
Q_UNUSED(index);
if (m_ui->comboTTM->currentIndex() == 1)
{
QString savePath = BitTorrent::Session::instance()->categorySavePath(m_ui->categoryComboBox->currentText());
const auto *btSession = BitTorrent::Session::instance();
const QString categoryName = m_ui->categoryComboBox->currentText();
const QString savePath = btSession->categorySavePath(categoryName);
m_ui->savePath->setSelectedPath(Utils::Fs::toNativePath(savePath));
const QString downloadPath = btSession->categoryDownloadPath(categoryName);
m_ui->downloadPath->setSelectedPath(Utils::Fs::toNativePath(downloadPath));
m_ui->groupBoxDownloadPath->setChecked(!m_ui->downloadPath->selectedPath().isEmpty());
updateDiskSpaceLabel();
}
}
@@ -513,19 +554,6 @@ void AddNewTorrentDialog::contentLayoutChanged(const int index)
}
}
void AddNewTorrentDialog::setSavePath(const QString &newPath)
{
int existingIndex = indexOfSavePath(newPath);
if (existingIndex < 0)
{
// New path, prepend to combo box
m_ui->savePath->insertItem(0, newPath);
existingIndex = 0;
}
m_ui->savePath->setCurrentIndex(existingIndex);
onSavePathChanged(newPath);
}
void AddNewTorrentDialog::saveTorrentFile()
{
Q_ASSERT(hasMetadata());
@@ -555,22 +583,78 @@ bool AddNewTorrentDialog::hasMetadata() const
return m_torrentInfo.isValid();
}
void AddNewTorrentDialog::populateSavePathComboBox()
void AddNewTorrentDialog::populateSavePaths()
{
const auto *btSession = BitTorrent::Session::instance();
m_ui->savePath->blockSignals(true);
m_ui->savePath->clear();
const auto savePathHistory = settings()->loadValue<QStringList>(KEY_SAVEPATHHISTORY);
if (savePathHistory.size() > 0)
{
for (const QString &path : savePathHistory)
m_ui->savePath->addItem(path);
}
else
{
m_ui->savePath->addItem(btSession->savePath());
}
// Load save path history
const auto savePathHistory {settings()->loadValue<QStringList>(KEY_SAVEPATHHISTORY)};
for (const QString &savePath : savePathHistory)
m_ui->savePath->addItem(savePath);
if (m_savePathIndex >= 0)
{
m_ui->savePath->setCurrentIndex(std::min(m_savePathIndex, (m_ui->savePath->count() - 1)));
}
else
{
if (!m_torrentParams.savePath.isEmpty())
setPath(m_ui->savePath, m_torrentParams.savePath);
else if (!m_storeRememberLastSavePath)
setPath(m_ui->savePath, btSession->savePath());
else
m_ui->savePath->setCurrentIndex(0);
const QString defSavePath {BitTorrent::Session::instance()->defaultSavePath()};
m_savePathIndex = m_ui->savePath->currentIndex();
}
if (!m_torrentParams.savePath.isEmpty())
setSavePath(m_torrentParams.savePath);
else if (!m_storeRememberLastSavePath)
setSavePath(defSavePath);
// else last used save path will be selected since it is the first in the list
m_ui->savePath->blockSignals(false);
m_ui->downloadPath->blockSignals(true);
m_ui->downloadPath->clear();
const auto downloadPathHistory = settings()->loadValue<QStringList>(KEY_DOWNLOADPATHHISTORY);
if (downloadPathHistory.size() > 0)
{
for (const QString &path : downloadPathHistory)
m_ui->downloadPath->addItem(path);
}
else
{
m_ui->downloadPath->addItem(btSession->downloadPath());
}
if (m_downloadPathIndex >= 0)
{
m_ui->downloadPath->setCurrentIndex(m_useDownloadPath ? std::min(m_downloadPathIndex, (m_ui->downloadPath->count() - 1)) : -1);
m_ui->groupBoxDownloadPath->setChecked(m_useDownloadPath);
}
else
{
const bool useDownloadPath = m_torrentParams.useDownloadPath.value_or(btSession->isDownloadPathEnabled());
m_ui->groupBoxDownloadPath->setChecked(useDownloadPath);
if (!m_torrentParams.downloadPath.isEmpty())
setPath(m_ui->downloadPath, m_torrentParams.downloadPath);
else if (!m_storeRememberLastSavePath)
setPath(m_ui->downloadPath, btSession->downloadPath());
else
m_ui->downloadPath->setCurrentIndex(0);
m_downloadPathIndex = m_ui->downloadPath->currentIndex();
if (!useDownloadPath)
m_ui->downloadPath->setCurrentIndex(-1);
}
m_ui->downloadPath->blockSignals(false);
m_ui->groupBoxDownloadPath->blockSignals(false);
}
void AddNewTorrentDialog::displayContentTreeMenu(const QPoint &)
@@ -694,16 +778,21 @@ void AddNewTorrentDialog::accept()
m_torrentParams.sequential = m_ui->sequentialCheckBox->isChecked();
m_torrentParams.firstLastPiecePriority = m_ui->firstLastCheckBox->isChecked();
QString savePath = m_ui->savePath->selectedPath();
if (m_ui->comboTTM->currentIndex() != 1)
{ // 0 is Manual mode and 1 is Automatic mode. Handle all non 1 values as manual mode.
m_torrentParams.useAutoTMM = false;
m_torrentParams.savePath = savePath;
saveSavePathHistory();
}
else
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)
{
m_torrentParams.useAutoTMM = true;
const QString 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 QString downloadPath = m_ui->downloadPath->selectedPath();
m_torrentParams.downloadPath = downloadPath;
updatePathHistory(KEY_DOWNLOADPATHHISTORY, downloadPath, savePathHistoryLength());
}
}
setEnabled(!m_ui->checkBoxNeverShow->isChecked());
@@ -855,18 +944,28 @@ void AddNewTorrentDialog::TMMChanged(int index)
{
if (index != 1)
{ // 0 is Manual mode and 1 is Automatic mode. Handle all non 1 values as manual mode.
populateSavePathComboBox();
populateSavePaths();
m_ui->groupBoxSavePath->setEnabled(true);
m_ui->savePath->blockSignals(false);
m_ui->savePath->setCurrentIndex(m_oldIndex < m_ui->savePath->count() ? m_oldIndex : m_ui->savePath->count() - 1);
}
else
{
const auto *session = BitTorrent::Session::instance();
m_ui->groupBoxSavePath->setEnabled(false);
m_ui->savePath->blockSignals(true);
m_ui->savePath->clear();
QString savePath = BitTorrent::Session::instance()->categorySavePath(m_ui->categoryComboBox->currentText());
const QString savePath = session->categorySavePath(m_ui->categoryComboBox->currentText());
m_ui->savePath->addItem(savePath);
m_ui->downloadPath->blockSignals(true);
m_ui->downloadPath->clear();
const QString downloadPath = session->categoryDownloadPath(m_ui->categoryComboBox->currentText());
m_ui->downloadPath->addItem(downloadPath);
m_ui->groupBoxDownloadPath->blockSignals(true);
m_ui->groupBoxDownloadPath->setChecked(!downloadPath.isEmpty());
updateDiskSpaceLabel();
}
}

View File

@@ -81,6 +81,8 @@ private slots:
void displayContentTreeMenu(const QPoint &);
void updateDiskSpaceLabel();
void onSavePathChanged(const QString &newPath);
void onDownloadPathChanged(const QString &newPath);
void onUseDownloadPathChanged(bool checked);
void updateMetadata(const BitTorrent::TorrentInfo &metadata);
void handleDownloadFinished(const Net::DownloadResult &downloadResult);
void TMMChanged(int index);
@@ -97,14 +99,11 @@ private:
bool loadTorrentFile(const QString &torrentPath);
bool loadTorrentImpl();
bool loadMagnet(const BitTorrent::MagnetUri &magnetUri);
void populateSavePathComboBox();
void saveSavePathHistory() const;
int indexOfSavePath(const QString &savePath);
void populateSavePaths();
void loadState();
void saveState();
void setMetadataProgressIndicator(bool visibleIndicator, const QString &labelText = {});
void setupTreeview();
void setSavePath(const QString &newPath);
void saveTorrentFile();
bool hasMetadata() const;
@@ -115,7 +114,9 @@ private:
PropListDelegate *m_contentDelegate = nullptr;
BitTorrent::MagnetUri m_magnetURI;
BitTorrent::TorrentInfo m_torrentInfo;
int m_oldIndex = 0;
int m_savePathIndex = -1;
int m_downloadPathIndex = -1;
bool m_useDownloadPath = false;
std::unique_ptr<TorrentFileGuard> m_torrentGuard;
BitTorrent::AddTorrentParams m_torrentParams;

View File

@@ -83,13 +83,48 @@
<item>
<widget class="FileSystemPathComboEdit" name="savePath" native="true"/>
</item>
<item alignment="Qt::AlignRight">
<widget class="QCheckBox" name="checkBoxRememberLastSavePath">
<property name="text">
<string>Remember last used save path</string>
<item>
<widget class="QGroupBox" name="groupBoxDownloadPath">
<property name="title">
<string>Use another path for incomplete torrent</string>
</property>
<property name="checkable">
<bool>true</bool>
</property>
<property name="checked">
<bool>false</bool>
</property>
<layout class="QVBoxLayout" name="verticalLayout_4">
<item>
<widget class="FileSystemPathComboEdit" name="downloadPath" native="true"/>
</item>
</layout>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="layoutRememberLastSavePath">
<item>
<spacer name="horizontalSpacer_5">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QCheckBox" name="checkBoxRememberLastSavePath">
<property name="text">
<string>Remember last used save path</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>

View File

@@ -404,40 +404,33 @@ void CategoryFilterModel::populate()
// Uncategorized torrents
using Torrent = BitTorrent::Torrent;
m_rootItem->addChild(
UID_UNCATEGORIZED
, new CategoryModelItem(
nullptr, tr("Uncategorized")
, std::count_if(torrents.begin(), torrents.end()
, [](Torrent *torrent) { return torrent->category().isEmpty(); })));
const int torrentsCount = std::count_if(torrents.begin(), torrents.end()
, [](Torrent *torrent) { return torrent->category().isEmpty(); });
m_rootItem->addChild(UID_UNCATEGORIZED, new CategoryModelItem(nullptr, tr("Uncategorized"), torrentsCount));
using Torrent = BitTorrent::Torrent;
const QStringMap categories = session->categories();
for (auto i = categories.cbegin(); i != categories.cend(); ++i)
using BitTorrent::Torrent;
for (const QString &categoryName : asConst(session->categories()))
{
const QString &category = i.key();
if (m_isSubcategoriesEnabled)
{
CategoryModelItem *parent = m_rootItem;
for (const QString &subcat : asConst(session->expandCategory(category)))
for (const QString &subcat : asConst(session->expandCategory(categoryName)))
{
const QString subcatName = shortName(subcat);
if (!parent->hasChild(subcatName))
{
new CategoryModelItem(
parent, subcatName
, std::count_if(torrents.cbegin(), torrents.cend()
, [subcat](Torrent *torrent) { return torrent->category() == subcat; }));
const int torrentsCount = std::count_if(torrents.cbegin(), torrents.cend()
, [subcat](Torrent *torrent) { return torrent->category() == subcat; });
new CategoryModelItem(parent, subcatName, torrentsCount);
}
parent = parent->child(subcatName);
}
}
else
{
new CategoryModelItem(
m_rootItem, category
, std::count_if(torrents.begin(), torrents.end()
, [category](Torrent *torrent) { return torrent->belongsToCategory(category); }));
const int torrentsCount = std::count_if(torrents.begin(), torrents.end()
, [categoryName](Torrent *torrent) { return torrent->belongsToCategory(categoryName); });
new CategoryModelItem(m_rootItem, categoryName, torrentsCount);
}
}
}

View File

@@ -215,7 +215,7 @@ void CategoryFilterWidget::removeCategory()
void CategoryFilterWidget::removeUnusedCategories()
{
auto session = BitTorrent::Session::instance();
for (const QString &category : asConst(session->categories().keys()))
for (const QString &category : asConst(session->categories()))
{
if (model()->data(static_cast<CategoryFilterProxyModel *>(model())->index(category), Qt::UserRole) == 0)
session->removeCategory(category);

View File

@@ -247,6 +247,18 @@ void FileSystemPathEdit::setFileNameFilter(const QString &val)
#endif
}
QString FileSystemPathEdit::placeholder() const
{
Q_D(const FileSystemPathEdit);
return d->m_editor->placeholder();
}
void FileSystemPathEdit::setPlaceholder(const QString &val)
{
Q_D(FileSystemPathEdit);
d->m_editor->setPlaceholder(val);
}
bool FileSystemPathEdit::briefBrowseButtonCaption() const
{
Q_D(const FileSystemPathEdit);

View File

@@ -71,6 +71,9 @@ public:
QString fileNameFilter() const;
void setFileNameFilter(const QString &val);
QString placeholder() const;
void setPlaceholder(const QString &val);
/// The browse button caption is "..." if true, and "Browse" otherwise
bool briefBrowseButtonCaption() const;
void setBriefBrowseButtonCaption(bool brief);

View File

@@ -240,6 +240,16 @@ void Private::FileLineEdit::setValidator(QValidator *validator)
QLineEdit::setValidator(validator);
}
QString Private::FileLineEdit::placeholder() const
{
return placeholderText();
}
void Private::FileLineEdit::setPlaceholder(const QString &val)
{
setPlaceholderText(val);
}
QWidget *Private::FileLineEdit::widget()
{
return this;
@@ -346,6 +356,16 @@ void Private::FileComboEdit::setValidator(QValidator *validator)
lineEdit()->setValidator(validator);
}
QString Private::FileComboEdit::placeholder() const
{
return lineEdit()->placeholderText();
}
void Private::FileComboEdit::setPlaceholder(const QString &val)
{
lineEdit()->setPlaceholderText(val);
}
void Private::FileComboEdit::setFilenameFilters(const QStringList &filters)
{
static_cast<FileLineEdit *>(lineEdit())->setFilenameFilters(filters);

View File

@@ -105,6 +105,8 @@ namespace Private
virtual void setFilenameFilters(const QStringList &filters) = 0;
virtual void setBrowseAction(QAction *action) = 0;
virtual void setValidator(QValidator *validator) = 0;
virtual QString placeholder() const = 0;
virtual void setPlaceholder(const QString &val) = 0;
virtual QWidget *widget() = 0;
};
@@ -121,6 +123,8 @@ namespace Private
void setFilenameFilters(const QStringList &filters) override;
void setBrowseAction(QAction *action) override;
void setValidator(QValidator *validator) override;
QString placeholder() const override;
void setPlaceholder(const QString &val) override;
QWidget *widget() override;
protected:
@@ -149,6 +153,8 @@ namespace Private
void setFilenameFilters(const QStringList &filters) override;
void setBrowseAction(QAction *action) override;
void setValidator(QValidator *validator) override;
QString placeholder() const override;
void setPlaceholder(const QString &val) override;
QWidget *widget() override;
protected:

View File

@@ -354,7 +354,7 @@ OptionsDialog::OptionsDialog(QWidget *parent)
connect(m_ui->comboTorrentCategoryChanged, qComboBoxCurrentIndexChanged, this, &ThisType::enableApplyButton);
connect(m_ui->comboCategoryDefaultPathChanged, qComboBoxCurrentIndexChanged, this, &ThisType::enableApplyButton);
connect(m_ui->comboCategoryChanged, qComboBoxCurrentIndexChanged, this, &ThisType::enableApplyButton);
connect(m_ui->textTempPath, &FileSystemPathEdit::selectedPathChanged, this, &ThisType::enableApplyButton);
connect(m_ui->textDownloadPath, &FileSystemPathEdit::selectedPathChanged, this, &ThisType::enableApplyButton);
connect(m_ui->checkAppendqB, &QAbstractButton::toggled, this, &ThisType::enableApplyButton);
connect(m_ui->checkPreallocateAll, &QAbstractButton::toggled, this, &ThisType::enableApplyButton);
connect(m_ui->checkRecursiveDownload, &QAbstractButton::toggled, this, &ThisType::enableApplyButton);
@@ -372,8 +372,8 @@ OptionsDialog::OptionsDialog(QWidget *parent)
connect(m_ui->textExportDirFin, &FileSystemPathEdit::selectedPathChanged, this, &ThisType::enableApplyButton);
connect(m_ui->actionTorrentDlOnDblClBox, qComboBoxCurrentIndexChanged, this, &ThisType::enableApplyButton);
connect(m_ui->actionTorrentFnOnDblClBox, qComboBoxCurrentIndexChanged, this, &ThisType::enableApplyButton);
connect(m_ui->checkTempFolder, &QAbstractButton::toggled, this, &ThisType::enableApplyButton);
connect(m_ui->checkTempFolder, &QAbstractButton::toggled, m_ui->textTempPath, &QWidget::setEnabled);
connect(m_ui->checkUseDownloadPath, &QAbstractButton::toggled, this, &ThisType::enableApplyButton);
connect(m_ui->checkUseDownloadPath, &QAbstractButton::toggled, m_ui->textDownloadPath, &QWidget::setEnabled);
connect(m_ui->addWatchedFolderButton, &QAbstractButton::clicked, this, &ThisType::enableApplyButton);
connect(m_ui->removeWatchedFolderButton, &QAbstractButton::clicked, this, &ThisType::enableApplyButton);
connect(m_ui->groupMailNotification, &QGroupBox::toggled, this, &ThisType::enableApplyButton);
@@ -559,8 +559,8 @@ OptionsDialog::OptionsDialog(QWidget *parent)
m_ui->textSavePath->setDialogCaption(tr("Choose a save directory"));
m_ui->textSavePath->setMode(FileSystemPathEdit::Mode::DirectorySave);
m_ui->textTempPath->setDialogCaption(tr("Choose a save directory"));
m_ui->textTempPath->setMode(FileSystemPathEdit::Mode::DirectorySave);
m_ui->textDownloadPath->setDialogCaption(tr("Choose a save directory"));
m_ui->textDownloadPath->setMode(FileSystemPathEdit::Mode::DirectorySave);
// disable mouse wheel event on widgets to avoid mis-selection
auto *wheelEventEater = new WheelEventEater(this);
@@ -731,14 +731,14 @@ void OptionsDialog::saveOptions()
auto session = BitTorrent::Session::instance();
// Downloads preferences
session->setDefaultSavePath(Utils::Fs::expandPathAbs(m_ui->textSavePath->selectedPath()));
session->setSavePath(Utils::Fs::expandPathAbs(m_ui->textSavePath->selectedPath()));
session->setSubcategoriesEnabled(m_ui->checkUseSubcategories->isChecked());
session->setAutoTMMDisabledByDefault(m_ui->comboSavingMode->currentIndex() == 0);
session->setDisableAutoTMMWhenCategoryChanged(m_ui->comboTorrentCategoryChanged->currentIndex() == 1);
session->setDisableAutoTMMWhenCategorySavePathChanged(m_ui->comboCategoryChanged->currentIndex() == 1);
session->setDisableAutoTMMWhenDefaultSavePathChanged(m_ui->comboCategoryDefaultPathChanged->currentIndex() == 1);
session->setTempPathEnabled(m_ui->checkTempFolder->isChecked());
session->setTempPath(Utils::Fs::expandPathAbs(m_ui->textTempPath->selectedPath()));
session->setDownloadPathEnabled(m_ui->checkUseDownloadPath->isChecked());
session->setDownloadPath(Utils::Fs::expandPathAbs(m_ui->textDownloadPath->selectedPath()));
session->setAppendExtensionEnabled(m_ui->checkAppendqB->isChecked());
session->setPreallocationEnabled(preAllocateAllFiles());
pref->disableRecursiveDownload(!m_ui->checkRecursiveDownload->isChecked());
@@ -997,16 +997,16 @@ void OptionsDialog::loadOptions()
m_ui->deleteTorrentBox->setChecked(autoDeleteMode != TorrentFileGuard::Never);
m_ui->deleteCancelledTorrentBox->setChecked(autoDeleteMode == TorrentFileGuard::Always);
m_ui->textSavePath->setSelectedPath(session->defaultSavePath());
m_ui->textSavePath->setSelectedPath(session->savePath());
m_ui->checkUseSubcategories->setChecked(session->isSubcategoriesEnabled());
m_ui->comboSavingMode->setCurrentIndex(!session->isAutoTMMDisabledByDefault());
m_ui->comboTorrentCategoryChanged->setCurrentIndex(session->isDisableAutoTMMWhenCategoryChanged());
m_ui->comboCategoryChanged->setCurrentIndex(session->isDisableAutoTMMWhenCategorySavePathChanged());
m_ui->comboCategoryDefaultPathChanged->setCurrentIndex(session->isDisableAutoTMMWhenDefaultSavePathChanged());
m_ui->checkTempFolder->setChecked(session->isTempPathEnabled());
m_ui->textTempPath->setEnabled(m_ui->checkTempFolder->isChecked());
m_ui->textTempPath->setEnabled(m_ui->checkTempFolder->isChecked());
m_ui->textTempPath->setSelectedPath(Utils::Fs::toNativePath(session->tempPath()));
m_ui->checkUseDownloadPath->setChecked(session->isDownloadPathEnabled());
m_ui->textDownloadPath->setEnabled(m_ui->checkUseDownloadPath->isChecked());
m_ui->textDownloadPath->setEnabled(m_ui->checkUseDownloadPath->isChecked());
m_ui->textDownloadPath->setSelectedPath(Utils::Fs::toNativePath(session->downloadPath()));
m_ui->checkAppendqB->setChecked(session->isAppendExtensionEnabled());
m_ui->checkPreallocateAll->setChecked(session->isPreallocationEnabled());
m_ui->checkRecursiveDownload->setChecked(!pref->recursiveDownloadDisabled());

View File

@@ -1105,16 +1105,6 @@ Manual: Various torrent properties (e.g. save path) must be assigned manually</s
</item>
<item>
<layout class="QGridLayout" name="gridLayout_4">
<item row="1" column="1">
<widget class="FileSystemPathLineEdit" name="textTempPath" native="true"/>
</item>
<item row="3" column="0">
<widget class="QCheckBox" name="checkExportDirFin">
<property name="text">
<string>Copy .torrent files for finished downloads to:</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="labelSavePath">
<property name="text">
@@ -1122,6 +1112,19 @@ Manual: Various torrent properties (e.g. save path) must be assigned manually</s
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="FileSystemPathLineEdit" name="textSavePath" native="true"/>
</item>
<item row="1" column="0">
<widget class="QCheckBox" name="checkUseDownloadPath">
<property name="text">
<string>Use another path for incomplete torrents:</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="FileSystemPathLineEdit" name="textDownloadPath" native="true"/>
</item>
<item row="2" column="0">
<widget class="QCheckBox" name="checkExportDir">
<property name="text">
@@ -1129,21 +1132,18 @@ Manual: Various torrent properties (e.g. save path) must be assigned manually</s
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QCheckBox" name="checkTempFolder">
<property name="text">
<string>Keep incomplete torrents in:</string>
</property>
</widget>
<item row="2" column="1">
<widget class="FileSystemPathLineEdit" name="textExportDir" native="true"/>
</item>
<item row="3" column="1">
<widget class="FileSystemPathLineEdit" name="textExportDirFin" native="true"/>
</item>
<item row="0" column="1">
<widget class="FileSystemPathLineEdit" name="textSavePath" native="true"/>
</item>
<item row="2" column="1">
<widget class="FileSystemPathLineEdit" name="textExportDir" native="true"/>
<item row="3" column="0">
<widget class="QCheckBox" name="checkExportDirFin">
<property name="text">
<string>Copy .torrent files for finished downloads to:</string>
</property>
</widget>
</item>
</layout>
</item>
@@ -3490,8 +3490,8 @@ Use ';' to split multiple entries. Can use wildcard '*'.</string>
<tabstop>checkAdditionDialog</tabstop>
<tabstop>checkAdditionDialogFront</tabstop>
<tabstop>checkPreallocateAll</tabstop>
<tabstop>checkTempFolder</tabstop>
<tabstop>textTempPath</tabstop>
<tabstop>checkUseDownloadPath</tabstop>
<tabstop>textDownloadPath</tabstop>
<tabstop>checkAppendqB</tabstop>
<tabstop>scanFoldersView</tabstop>
<tabstop>addWatchedFolderButton</tabstop>

View File

@@ -581,11 +581,12 @@ void PropertiesWidget::loadUrlSeeds()
QString PropertiesWidget::getFullPath(const QModelIndex &index) const
{
const QDir saveDir {m_torrent->actualStorageLocation()};
if (m_propListModel->itemType(index) == TorrentContentModelItem::FileType)
{
const int fileIdx = m_propListModel->getFileIndex(index);
const QString filename {m_torrent->filePath(fileIdx)};
const QDir saveDir {m_torrent->savePath(true)};
const QString fullPath {Utils::Fs::expandPath(saveDir.absoluteFilePath(filename))};
return fullPath;
}
@@ -596,7 +597,6 @@ QString PropertiesWidget::getFullPath(const QModelIndex &index) const
for (QModelIndex modelIdx = m_propListModel->parent(nameIndex); modelIdx.isValid(); modelIdx = modelIdx.parent())
folderPath.prepend(modelIdx.data().toString() + '/');
const QDir saveDir {m_torrent->savePath(true)};
const QString fullPath {Utils::Fs::expandPath(saveDir.absoluteFilePath(folderPath))};
return fullPath;
}

View File

@@ -330,7 +330,7 @@ void AutomatedRssDownloader::clearRuleDefinitionBox()
void AutomatedRssDownloader::initCategoryCombobox()
{
// Load torrent categories
QStringList categories = BitTorrent::Session::instance()->categories().keys();
QStringList categories = BitTorrent::Session::instance()->categories();
std::sort(categories.begin(), categories.end(), Utils::Compare::NaturalLessThan<Qt::CaseInsensitive>());
m_ui->comboCategory->addItem("");
m_ui->comboCategory->addItems(categories);

View File

@@ -1,6 +1,6 @@
/*
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2017 Vladimir Golovnev <glassez@yandex.ru>
* Copyright (C) 2017, 2021 Vladimir Golovnev <glassez@yandex.ru>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -32,6 +32,7 @@
#include <QPushButton>
#include "base/bittorrent/session.h"
#include "base/utils/fs.h"
#include "ui_torrentcategorydialog.h"
TorrentCategoryDialog::TorrentCategoryDialog(QWidget *parent)
@@ -39,15 +40,20 @@ TorrentCategoryDialog::TorrentCategoryDialog(QWidget *parent)
, m_ui {new Ui::TorrentCategoryDialog}
{
m_ui->setupUi(this);
m_ui->comboSavePath->setMode(FileSystemPathEdit::Mode::DirectorySave);
m_ui->comboSavePath->setDialogCaption(tr("Choose save path"));
m_ui->comboDownloadPath->setMode(FileSystemPathEdit::Mode::DirectorySave);
m_ui->comboDownloadPath->setDialogCaption(tr("Choose download path"));
m_ui->comboDownloadPath->setEnabled(false);
m_ui->labelDownloadPath->setEnabled(false);
// disable save button
m_ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false);
connect(m_ui->textCategoryName, &QLineEdit::textChanged, this, [this](const QString &text)
{
m_ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(!text.isEmpty());
});
connect(m_ui->textCategoryName, &QLineEdit::textChanged, this, &TorrentCategoryDialog::categoryNameChanged);
connect(m_ui->comboUseDownloadPath, &QComboBox::currentIndexChanged, this, &TorrentCategoryDialog::useDownloadPathChanged);
}
TorrentCategoryDialog::~TorrentCategoryDialog()
@@ -57,20 +63,21 @@ TorrentCategoryDialog::~TorrentCategoryDialog()
QString TorrentCategoryDialog::createCategory(QWidget *parent, const QString &parentCategoryName)
{
using BitTorrent::CategoryOptions;
using BitTorrent::Session;
QString newCategoryName {parentCategoryName};
QString newCategoryName = parentCategoryName;
if (!newCategoryName.isEmpty())
newCategoryName += QLatin1Char('/');
newCategoryName += tr("New Category");
TorrentCategoryDialog dialog(parent);
TorrentCategoryDialog dialog {parent};
dialog.setCategoryName(newCategoryName);
while (dialog.exec() == TorrentCategoryDialog::Accepted)
{
newCategoryName = dialog.categoryName();
if (!BitTorrent::Session::isValidCategoryName(newCategoryName))
if (!Session::isValidCategoryName(newCategoryName))
{
QMessageBox::critical(
parent, tr("Invalid category name")
@@ -78,7 +85,7 @@ QString TorrentCategoryDialog::createCategory(QWidget *parent, const QString &pa
"Category name cannot start/end with '/'.\n"
"Category name cannot contain '//' sequence."));
}
else if (BitTorrent::Session::instance()->categories().contains(newCategoryName))
else if (Session::instance()->categories().contains(newCategoryName))
{
QMessageBox::critical(
parent, tr("Category creation error")
@@ -87,7 +94,7 @@ QString TorrentCategoryDialog::createCategory(QWidget *parent, const QString &pa
}
else
{
Session::instance()->addCategory(newCategoryName, dialog.savePath());
Session::instance()->addCategory(newCategoryName, dialog.categoryOptions());
return newCategoryName;
}
}
@@ -105,10 +112,10 @@ void TorrentCategoryDialog::editCategory(QWidget *parent, const QString &categor
dialog->setAttribute(Qt::WA_DeleteOnClose);
dialog->setCategoryNameEditable(false);
dialog->setCategoryName(categoryName);
dialog->setSavePath(Session::instance()->categories()[categoryName]);
dialog->setCategoryOptions(Session::instance()->categoryOptions(categoryName));
connect(dialog, &TorrentCategoryDialog::accepted, parent, [dialog, categoryName]()
{
Session::instance()->editCategory(categoryName, dialog->savePath());
Session::instance()->editCategory(categoryName, dialog->categoryOptions());
});
dialog->open();
}
@@ -128,12 +135,59 @@ void TorrentCategoryDialog::setCategoryName(const QString &categoryName)
m_ui->textCategoryName->setText(categoryName);
}
QString TorrentCategoryDialog::savePath() const
BitTorrent::CategoryOptions TorrentCategoryDialog::categoryOptions() const
{
return m_ui->comboSavePath->selectedPath();
BitTorrent::CategoryOptions categoryOptions;
categoryOptions.savePath = m_ui->comboSavePath->selectedPath();
if (m_ui->comboUseDownloadPath->currentIndex() == 1)
categoryOptions.downloadPath = {true, m_ui->comboDownloadPath->selectedPath()};
else if (m_ui->comboUseDownloadPath->currentIndex() == 2)
categoryOptions.downloadPath = {false};
return categoryOptions;
}
void TorrentCategoryDialog::setSavePath(const QString &savePath)
void TorrentCategoryDialog::setCategoryOptions(const BitTorrent::CategoryOptions &categoryOptions)
{
m_ui->comboSavePath->setSelectedPath(savePath);
m_ui->comboSavePath->setSelectedPath(categoryOptions.savePath);
if (categoryOptions.downloadPath)
{
m_ui->comboUseDownloadPath->setCurrentIndex(categoryOptions.downloadPath->enabled ? 1 : 2);
m_ui->comboDownloadPath->setSelectedPath(categoryOptions.downloadPath->enabled ? categoryOptions.downloadPath->path : QString());
}
else
{
m_ui->comboUseDownloadPath->setCurrentIndex(0);
m_ui->comboDownloadPath->setSelectedPath({});
}
}
void TorrentCategoryDialog::categoryNameChanged(const QString &categoryName)
{
const QString categoryPath = Utils::Fs::toValidFileSystemName(categoryName, true);
const auto *btSession = BitTorrent::Session::instance();
m_ui->comboSavePath->setPlaceholder(Utils::Fs::resolvePath(categoryPath, btSession->savePath()));
const int index = m_ui->comboUseDownloadPath->currentIndex();
const bool useDownloadPath = (index == 1) || ((index == 0) && BitTorrent::Session::instance()->isDownloadPathEnabled());
if (useDownloadPath)
m_ui->comboDownloadPath->setPlaceholder(Utils::Fs::resolvePath(categoryPath, btSession->downloadPath()));
m_ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(!categoryName.isEmpty());
}
void TorrentCategoryDialog::useDownloadPathChanged(const int index)
{
if (const QString selectedPath = m_ui->comboDownloadPath->selectedPath(); !selectedPath.isEmpty())
m_lastEnteredDownloadPath = selectedPath;
m_ui->labelDownloadPath->setEnabled(index == 1);
m_ui->comboDownloadPath->setEnabled(index == 1);
m_ui->comboDownloadPath->setSelectedPath((index == 1) ? m_lastEnteredDownloadPath : QString());
const QString categoryName = m_ui->textCategoryName->text();
const QString categoryPath = Utils::Fs::resolvePath(Utils::Fs::toValidFileSystemName(categoryName, true)
, BitTorrent::Session::instance()->downloadPath());
const bool useDownloadPath = (index == 1) || ((index == 0) && BitTorrent::Session::instance()->isDownloadPathEnabled());
m_ui->comboDownloadPath->setPlaceholder(useDownloadPath ? categoryPath : QString());
}

View File

@@ -1,6 +1,6 @@
/*
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2017 Vladimir Golovnev <glassez@yandex.ru>
* Copyright (C) 2017, 2021 Vladimir Golovnev <glassez@yandex.ru>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -30,6 +30,11 @@
#include <QDialog>
namespace BitTorrent
{
struct CategoryOptions;
}
namespace Ui
{
class TorrentCategoryDialog;
@@ -50,9 +55,14 @@ public:
void setCategoryNameEditable(bool editable);
QString categoryName() const;
void setCategoryName(const QString &categoryName);
QString savePath() const;
void setSavePath(const QString &savePath);
void setCategoryOptions(const BitTorrent::CategoryOptions &categoryOptions);
BitTorrent::CategoryOptions categoryOptions() const;
private slots:
void categoryNameChanged(const QString &categoryName);
void useDownloadPathChanged(int index);
private:
Ui::TorrentCategoryDialog *m_ui;
QString m_lastEnteredDownloadPath;
};

View File

@@ -6,8 +6,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>100</height>
<width>493</width>
<height>208</height>
</rect>
</property>
<property name="windowTitle">
@@ -15,7 +15,7 @@
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<spacer name="verticalSpacer_2">
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
@@ -29,10 +29,13 @@
</item>
<item>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QLabel" name="labelCategoryName">
<property name="text">
<string>Name:</string>
<item row="1" column="1">
<widget class="FileSystemPathComboEdit" name="comboSavePath" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
@@ -46,20 +49,99 @@
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="FileSystemPathComboEdit" name="comboSavePath" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
<item row="0" column="0">
<widget class="QLabel" name="labelCategoryName">
<property name="text">
<string>Name:</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<spacer name="verticalSpacer">
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>Save path for incomplete torrents:</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLabel" name="labelUseDownloadPath">
<property name="text">
<string>Use another path for incomplete torrents:</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="comboUseDownloadPath">
<property name="maxVisibleItems">
<number>3</number>
</property>
<item>
<property name="text">
<string>Default</string>
</property>
</item>
<item>
<property name="text">
<string>Yes</string>
</property>
</item>
<item>
<property name="text">
<string>No</string>
</property>
</item>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QLabel" name="labelDownloadPath">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Path:</string>
</property>
</widget>
</item>
<item>
<widget class="FileSystemPathComboEdit" name="comboDownloadPath" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item>
<spacer name="verticalSpacer_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>

View File

@@ -65,19 +65,33 @@ TorrentOptionsDialog::TorrentOptionsDialog(QWidget *parent, const QVector<BitTor
, m_storeDialogSize {SETTINGS_KEY("Size")}
, m_currentCategoriesString {QString::fromLatin1("--%1--").arg(tr("Currently used categories"))}
{
Q_ASSERT(!torrents.empty());
m_ui->setupUi(this);
m_ui->savePath->setMode(FileSystemPathEdit::Mode::DirectorySave);
m_ui->savePath->setDialogCaption(tr("Choose save path"));
Q_ASSERT(!torrents.empty());
m_ui->downloadPath->setMode(FileSystemPathEdit::Mode::DirectorySave);
m_ui->downloadPath->setDialogCaption(tr("Choose save path"));
const auto *session = BitTorrent::Session::instance();
bool allSameUpLimit = true, allSameDownLimit = true, allSameRatio = true, allSameSeedingTime = true
, allTorrentsArePrivate = true, allSameDHT = true, allSamePEX = true, allSameLSD = true
, allSameSequential = true, allSameFirstLastPieces = true, allSameAutoTMM = true, allSameSavePath = true;
bool allSameUpLimit = true;
bool allSameDownLimit = true;
bool allSameRatio = true;
bool allSameSeedingTime = true;
bool allTorrentsArePrivate = true;
bool allSameDHT = true;
bool allSamePEX = true;
bool allSameLSD = true;
bool allSameSequential = true;
bool allSameFirstLastPieces = true;
bool allSameAutoTMM = true;
bool allSameSavePath = true;
bool allSameDownloadPath = true;
const bool isFirstTorrentAutoTMMEnabled = torrents[0]->isAutoTMMEnabled();
const QString firstTorrentSavePath = torrents[0]->savePath();
const QString firstTorrentDownloadPath = torrents[0]->downloadPath();
const QString firstTorrentCategory = torrents[0]->category();
const int firstTorrentUpLimit = std::max(0, torrents[0]->uploadLimit());
@@ -107,6 +121,11 @@ TorrentOptionsDialog::TorrentOptionsDialog(QWidget *parent, const QVector<BitTor
if (torrent->savePath() != firstTorrentSavePath)
allSameSavePath = false;
}
if (allSameDownloadPath)
{
if (torrent->downloadPath() != firstTorrentDownloadPath)
allSameDownloadPath = false;
}
if (m_allSameCategory)
{
if (torrent->category() != firstTorrentCategory)
@@ -172,6 +191,16 @@ TorrentOptionsDialog::TorrentOptionsDialog(QWidget *parent, const QVector<BitTor
if (allSameSavePath)
m_ui->savePath->setSelectedPath(firstTorrentSavePath);
if (allSameDownloadPath)
{
m_ui->downloadPath->setSelectedPath(firstTorrentDownloadPath);
m_ui->checkUseDownloadPath->setChecked(!firstTorrentDownloadPath.isEmpty());
}
else
{
m_ui->checkUseDownloadPath->setCheckState(Qt::PartiallyChecked);
}
if (!m_allSameCategory)
{
m_ui->comboCategory->addItem(m_currentCategoriesString);
@@ -185,7 +214,7 @@ TorrentOptionsDialog::TorrentOptionsDialog(QWidget *parent, const QVector<BitTor
}
m_ui->comboCategory->addItem(QString());
m_categories = session->categories().keys();
m_categories = session->categories();
std::sort(m_categories.begin(), m_categories.end(), Utils::Compare::NaturalLessThan<Qt::CaseInsensitive>());
for (const QString &category : asConst(m_categories))
{
@@ -324,12 +353,14 @@ TorrentOptionsDialog::TorrentOptionsDialog(QWidget *parent, const QVector<BitTor
m_initialValues =
{
m_ui->savePath->selectedPath(),
m_ui->downloadPath->selectedPath(),
m_ui->comboCategory->currentText(),
getRatio(),
getSeedingTime(),
m_ui->spinUploadLimit->value(),
m_ui->spinDownloadLimit->value(),
m_ui->checkAutoTMM->checkState(),
m_ui->checkUseDownloadPath->checkState(),
m_ui->checkDisableDHT->checkState(),
m_ui->checkDisablePEX->checkState(),
m_ui->checkDisableLSD->checkState(),
@@ -339,9 +370,11 @@ TorrentOptionsDialog::TorrentOptionsDialog(QWidget *parent, const QVector<BitTor
// Needs to be called after the initial values struct is initialized
handleTMMChanged();
handleUseDownloadPathChanged();
handleRatioTypeChanged();
connect(m_ui->checkAutoTMM, &QCheckBox::clicked, this, &TorrentOptionsDialog::handleTMMChanged);
connect(m_ui->checkUseDownloadPath, &QCheckBox::clicked, this, &TorrentOptionsDialog::handleUseDownloadPathChanged);
connect(m_ui->comboCategory, &QComboBox::activated, this, &TorrentOptionsDialog::handleCategoryChanged);
// Sync up/down speed limit sliders with their corresponding spinboxes
@@ -380,11 +413,28 @@ void TorrentOptionsDialog::accept()
BitTorrent::Torrent *torrent = session->findTorrent(id);
if (!torrent) continue;
const QString savePath = m_ui->savePath->selectedPath();
if (m_initialValues.autoTMM != m_ui->checkAutoTMM->checkState())
torrent->setAutoTMMEnabled(m_ui->checkAutoTMM->isChecked());
if (!m_ui->checkAutoTMM->isChecked() && (m_initialValues.savePath != savePath))
torrent->move(Utils::Fs::expandPathAbs(savePath));
if (m_ui->checkAutoTMM->checkState() == Qt::Unchecked)
{
const QString savePath = m_ui->savePath->selectedPath();
if (m_initialValues.savePath != savePath)
torrent->setSavePath(Utils::Fs::expandPathAbs(savePath));
const Qt::CheckState useDownloadPathState = m_ui->checkUseDownloadPath->checkState();
if (useDownloadPathState == Qt::Checked)
{
const QString downloadPath = m_ui->downloadPath->selectedPath();
if (m_initialValues.downloadPath != downloadPath)
torrent->setDownloadPath(Utils::Fs::expandPathAbs(downloadPath));
}
else if (useDownloadPathState == Qt::Unchecked)
{
torrent->setDownloadPath(QString());
}
}
const QString category = m_ui->comboCategory->currentText();
// index 0 is always the current category
if ((m_initialValues.category != category) || (m_ui->comboCategory->currentIndex() != 0))
@@ -487,33 +537,48 @@ void TorrentOptionsDialog::handleTMMChanged()
{
if (m_ui->checkAutoTMM->checkState() == Qt::Unchecked)
{
m_ui->labelSavePath->setEnabled(true);
m_ui->savePath->setEnabled(true);
m_ui->groupBoxSavePath->setEnabled(true);
m_ui->savePath->setSelectedPath(Utils::Fs::toNativePath(m_initialValues.savePath));
m_ui->downloadPath->setSelectedPath(Utils::Fs::toNativePath(m_initialValues.downloadPath));
m_ui->checkUseDownloadPath->setCheckState(m_initialValues.useDownloadPath);
}
else
{
m_ui->labelSavePath->setEnabled(false);
m_ui->savePath->setEnabled(false);
m_ui->groupBoxSavePath->setEnabled(false);
if (m_ui->checkAutoTMM->checkState() == Qt::Checked)
{
if (!m_allSameCategory && (m_ui->comboCategory->currentIndex() == 0))
{
m_ui->savePath->setSelectedPath(QString());
m_ui->downloadPath->setSelectedPath(QString());
m_ui->checkUseDownloadPath->setCheckState(Qt::PartiallyChecked);
}
else
{
const QString savePath = BitTorrent::Session::instance()->categorySavePath(m_ui->comboCategory->currentText());
m_ui->savePath->setSelectedPath(Utils::Fs::toNativePath(savePath));
const QString downloadPath = BitTorrent::Session::instance()->categoryDownloadPath(m_ui->comboCategory->currentText());
m_ui->downloadPath->setSelectedPath(Utils::Fs::toNativePath(downloadPath));
m_ui->checkUseDownloadPath->setChecked(!downloadPath.isEmpty());
}
}
else // partially checked
{
m_ui->savePath->setSelectedPath(QString());
m_ui->downloadPath->setSelectedPath(QString());
m_ui->checkUseDownloadPath->setCheckState(Qt::PartiallyChecked);
}
}
}
void TorrentOptionsDialog::handleUseDownloadPathChanged()
{
const bool isChecked = m_ui->checkUseDownloadPath->checkState() == Qt::Checked;
m_ui->downloadPath->setEnabled(isChecked);
if (isChecked && m_ui->downloadPath->selectedPath().isEmpty())
m_ui->downloadPath->setSelectedPath(BitTorrent::Session::instance()->downloadPath());
}
void TorrentOptionsDialog::handleRatioTypeChanged()
{
if ((m_initialValues.ratio == MIXED_SHARE_LIMITS) || (m_initialValues.seedingTime == MIXED_SHARE_LIMITS))

View File

@@ -62,6 +62,7 @@ public slots:
private slots:
void handleCategoryChanged(int index);
void handleTMMChanged();
void handleUseDownloadPathChanged();
void handleUpSpeedLimitChanged();
void handleDownSpeedLimitChanged();
@@ -82,12 +83,14 @@ private:
struct
{
QString savePath;
QString downloadPath;
QString category;
qreal ratio;
int seedingTime;
int upSpeedLimit;
int downSpeedLimit;
Qt::CheckState autoTMM;
Qt::CheckState useDownloadPath;
Qt::CheckState disableDHT;
Qt::CheckState disablePEX;
Qt::CheckState disableLSD;

View File

@@ -13,7 +13,7 @@
<property name="windowTitle">
<string>Torrent Options</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
<widget class="QCheckBox" name="checkAutoTMM">
<property name="toolTip">
@@ -24,33 +24,35 @@
</property>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBoxSavePath">
<property name="title">
<string>Save at</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="FileSystemPathComboEdit" name="savePath" native="true"/>
</item>
<item>
<widget class="QCheckBox" name="checkUseDownloadPath">
<property name="text">
<string>Use another path for incomplete torrent</string>
</property>
</widget>
</item>
<item>
<widget class="FileSystemPathComboEdit" name="downloadPath" native="true">
<property name="enabled">
<bool>false</bool>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<layout class="QGridLayout" name="gridLayout_4">
<item row="0" column="0">
<widget class="QLabel" name="labelSavePath">
<property name="text">
<string>Save path:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="FileSystemPathLineEdit" name="savePath" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="labelCategory">
<property name="text">
<string>Category:</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QComboBox" name="comboCategory">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
@@ -69,6 +71,13 @@
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="labelCategory">
<property name="text">
<string>Category:</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
@@ -310,7 +319,7 @@
</widget>
<customwidgets>
<customwidget>
<class>FileSystemPathLineEdit</class>
<class>FileSystemPathComboEdit</class>
<extends>QWidget</extends>
<header>gui/fspathedit.h</header>
<container>1</container>

View File

@@ -106,12 +106,12 @@ namespace
void openDestinationFolder(const BitTorrent::Torrent *const torrent)
{
#ifdef Q_OS_MACOS
MacUtils::openFiles({torrent->contentPath(true)});
MacUtils::openFiles({torrent->contentPath()});
#else
if (torrent->filesCount() == 1)
Utils::Gui::openFolderSelect(torrent->contentPath(true));
Utils::Gui::openFolderSelect(torrent->contentPath());
else
Utils::Gui::openPath(torrent->contentPath(true));
Utils::Gui::openPath(torrent->contentPath());
#endif
}
@@ -525,22 +525,22 @@ void TransferListWidget::openSelectedTorrentsFolder() const
// folders prehilighted for opening, so we use a custom method.
for (BitTorrent::Torrent *const torrent : asConst(getSelectedTorrents()))
{
QString path = torrent->contentPath(true);
pathsList.insert(path);
const QString contentPath = QDir(torrent->actualStorageLocation()).absoluteFilePath(torrent->contentPath());
pathsList.insert(contentPath);
}
MacUtils::openFiles(pathsList);
#else
for (BitTorrent::Torrent *const torrent : asConst(getSelectedTorrents()))
{
QString path = torrent->contentPath(true);
if (!pathsList.contains(path))
const QString contentPath = torrent->contentPath();
if (!pathsList.contains(contentPath))
{
if (torrent->filesCount() == 1)
Utils::Gui::openFolderSelect(path);
Utils::Gui::openFolderSelect(contentPath);
else
Utils::Gui::openPath(path);
Utils::Gui::openPath(contentPath);
}
pathsList.insert(path);
pathsList.insert(contentPath);
}
#endif // Q_OS_MACOS
}

View File

@@ -44,6 +44,7 @@ WatchedFolderOptionsDialog::WatchedFolderOptionsDialog(
: QDialog {parent}
, m_ui {new Ui::WatchedFolderOptionsDialog}
, m_savePath {watchedFolderOptions.addTorrentParams.savePath}
, m_downloadPath {watchedFolderOptions.addTorrentParams.downloadPath}
, m_storeDialogSize {SETTINGS_KEY("DialogSize")}
{
m_ui->setupUi(this);
@@ -51,13 +52,18 @@ WatchedFolderOptionsDialog::WatchedFolderOptionsDialog(
m_ui->savePath->setMode(FileSystemPathEdit::Mode::DirectorySave);
m_ui->savePath->setDialogCaption(tr("Choose save path"));
const auto *session = BitTorrent::Session::instance();
m_ui->downloadPath->setMode(FileSystemPathEdit::Mode::DirectorySave);
m_ui->downloadPath->setDialogCaption(tr("Choose save path"));
m_ui->groupBoxDownloadPath->setChecked(watchedFolderOptions.addTorrentParams.useDownloadPath.value_or(session->isDownloadPathEnabled()));
connect(m_ui->comboTTM, qOverload<int>(&QComboBox::currentIndexChanged), this, &WatchedFolderOptionsDialog::onTMMChanged);
connect(m_ui->categoryComboBox, qOverload<int>(&QComboBox::currentIndexChanged), this, &WatchedFolderOptionsDialog::onCategoryChanged);
m_ui->checkBoxRecursive->setChecked(watchedFolderOptions.recursive);
populateSavePathComboBox();
populateSavePaths();
const auto *session = BitTorrent::Session::instance();
const BitTorrent::AddTorrentParams &torrentParams = watchedFolderOptions.addTorrentParams;
m_ui->startTorrentCheckBox->setChecked(!torrentParams.addPaused.value_or(session->isAddTorrentPaused()));
m_ui->skipCheckingCheckBox->setChecked(torrentParams.skipChecking);
@@ -66,7 +72,7 @@ WatchedFolderOptionsDialog::WatchedFolderOptionsDialog(
static_cast<int>(torrentParams.contentLayout.value_or(session->torrentContentLayout())));
// Load categories
QStringList categories = session->categories().keys();
QStringList categories = session->categories();
std::sort(categories.begin(), categories.end(), Utils::Compare::NaturalLessThan<Qt::CaseInsensitive>());
if (!torrentParams.category.isEmpty())
@@ -96,10 +102,16 @@ TorrentFilesWatcher::WatchedFolderOptions WatchedFolderOptionsDialog::watchedFol
watchedFolderOptions.recursive = m_ui->checkBoxRecursive->isChecked();
BitTorrent::AddTorrentParams &params = watchedFolderOptions.addTorrentParams;
params.useAutoTMM = (m_ui->comboTTM->currentIndex() == 1);
if (!*params.useAutoTMM)
const bool useAutoTMM = (m_ui->comboTTM->currentIndex() == 1);
if (!useAutoTMM)
{
params.savePath = m_ui->savePath->selectedPath();
params.category = m_ui->categoryComboBox->currentText();;
params.useDownloadPath = m_ui->groupBoxDownloadPath->isChecked();
if (params.useDownloadPath)
params.downloadPath = m_ui->downloadPath->selectedPath();
}
params.useAutoTMM = useAutoTMM;
params.category = m_ui->categoryComboBox->currentText();
params.addPaused = !m_ui->startTorrentCheckBox->isChecked();
params.skipChecking = m_ui->skipCheckingCheckBox->isChecked();
params.contentLayout = static_cast<BitTorrent::TorrentContentLayout>(m_ui->contentLayoutComboBox->currentIndex());
@@ -121,34 +133,60 @@ void WatchedFolderOptionsDialog::onCategoryChanged(const int index)
{
Q_UNUSED(index);
const QString category = m_ui->categoryComboBox->currentText();
if (m_ui->comboTTM->currentIndex() == 1)
{
const QString savePath = BitTorrent::Session::instance()->categorySavePath(category);
const auto *btSession = BitTorrent::Session::instance();
const QString categoryName = m_ui->categoryComboBox->currentText();
const QString savePath = btSession->categorySavePath(categoryName);
m_ui->savePath->setSelectedPath(Utils::Fs::toNativePath(savePath));
const QString finishedSavePath = btSession->categoryDownloadPath(categoryName);
m_ui->downloadPath->setSelectedPath(Utils::Fs::toNativePath(finishedSavePath));
m_ui->groupBoxDownloadPath->setChecked(!finishedSavePath.isEmpty());
}
}
void WatchedFolderOptionsDialog::populateSavePathComboBox()
void WatchedFolderOptionsDialog::populateSavePaths()
{
const QString defSavePath {BitTorrent::Session::instance()->defaultSavePath()};
m_ui->savePath->setSelectedPath(!m_savePath.isEmpty() ? m_savePath : defSavePath);
const auto *btSession = BitTorrent::Session::instance();
const QString defaultSavePath {btSession->savePath()};
m_ui->savePath->setSelectedPath(!m_savePath.isEmpty() ? m_savePath : defaultSavePath);
const QString defaultFinishedSavePath {btSession->downloadPath()};
m_ui->downloadPath->setSelectedPath(!m_downloadPath.isEmpty() ? m_downloadPath : defaultFinishedSavePath);
m_ui->groupBoxDownloadPath->setChecked(m_useDownloadPath);
}
void WatchedFolderOptionsDialog::onTMMChanged(const int index)
{
if (index != 1)
{ // 0 is Manual mode and 1 is Automatic mode. Handle all non 1 values as manual mode.
populateSavePathComboBox();
populateSavePaths();
m_ui->groupBoxSavePath->setEnabled(true);
m_ui->savePath->blockSignals(false);
m_ui->downloadPath->blockSignals(false);
}
else
{
m_ui->groupBoxSavePath->setEnabled(false);
const auto *btSession = BitTorrent::Session::instance();
m_ui->savePath->blockSignals(true);
m_savePath = m_ui->savePath->selectedPath();
const QString savePath = BitTorrent::Session::instance()->categorySavePath(m_ui->categoryComboBox->currentText());
const QString savePath = btSession->categorySavePath(m_ui->categoryComboBox->currentText());
m_ui->savePath->setSelectedPath(savePath);
m_ui->downloadPath->blockSignals(true);
m_downloadPath = m_ui->downloadPath->selectedPath();
const QString finishedSavePath = btSession->categoryDownloadPath(m_ui->categoryComboBox->currentText());
m_ui->downloadPath->setSelectedPath(finishedSavePath);
m_useDownloadPath = m_ui->groupBoxDownloadPath->isChecked();
m_ui->groupBoxDownloadPath->setChecked(!finishedSavePath.isEmpty());
}
}

View File

@@ -50,7 +50,7 @@ public:
TorrentFilesWatcher::WatchedFolderOptions watchedFolderOptions() const;
private:
void populateSavePathComboBox();
void populateSavePaths();
void loadState();
void saveState();
void onTMMChanged(int index);
@@ -58,5 +58,7 @@ private:
Ui::WatchedFolderOptionsDialog *m_ui;
QString m_savePath;
QString m_downloadPath;
bool m_useDownloadPath = false;
SettingValue<QSize> m_storeDialogSize;
};

View File

@@ -7,7 +7,7 @@
<x>0</x>
<y>0</y>
<width>462</width>
<height>325</height>
<height>364</height>
</rect>
</property>
<property name="windowTitle">
@@ -119,6 +119,24 @@
<item>
<widget class="FileSystemPathLineEdit" name="savePath" native="true"/>
</item>
<item>
<widget class="QGroupBox" name="groupBoxDownloadPath">
<property name="title">
<string>Use another path for incomplete torrents</string>
</property>
<property name="checkable">
<bool>true</bool>
</property>
<property name="checked">
<bool>false</bool>
</property>
<layout class="QVBoxLayout" name="verticalLayout_4">
<item>
<widget class="FileSystemPathLineEdit" name="downloadPath" native="true"/>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
</item>
@@ -296,6 +314,12 @@
<header>gui/fspathedit.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>FileSystemPathLineEdit</class>
<extends>QWidget</extends>
<header>gui/fspathedit.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources/>
<connections>