diff --git a/src/gui/transferlistwidget.cpp b/src/gui/transferlistwidget.cpp index 9d656959d..627725c47 100644 --- a/src/gui/transferlistwidget.cpp +++ b/src/gui/transferlistwidget.cpp @@ -328,6 +328,24 @@ QVector TransferListWidget::getVisibleTorrents() const return torrents; } +void TransferListWidget::setSelectedTorrentsLocation() +{ + const QVector torrents = getSelectedTorrents(); + if (torrents.isEmpty()) return; + + const QString oldLocation = torrents[0]->savePath(); + const QString newLocation = QFileDialog::getExistingDirectory(this, tr("Choose save path"), oldLocation, + (QFileDialog::DontConfirmOverwrite | QFileDialog::ShowDirsOnly | QFileDialog::HideNameFilterDetails)); + if (newLocation.isEmpty() || !QDir(newLocation).exists()) return; + + // Actually move storage + for (BitTorrent::Torrent *const torrent : torrents) + { + torrent->setAutoTMMEnabled(false); + torrent->setSavePath(Utils::Fs::expandPathAbs(newLocation)); + } +} + void TransferListWidget::pauseAllTorrents() { for (BitTorrent::Torrent *const torrent : asConst(BitTorrent::Session::instance()->torrents())) @@ -644,6 +662,18 @@ void TransferListWidget::setSelectedTorrentsSuperSeeding(const bool enabled) con } } +void TransferListWidget::setSelectedTorrentsSequentialDownload(const bool enabled) const +{ + for (BitTorrent::Torrent *const torrent : asConst(getSelectedTorrents())) + torrent->setSequentialDownload(enabled); +} + +void TransferListWidget::setSelectedFirstLastPiecePrio(const bool enabled) const +{ + for (BitTorrent::Torrent *const torrent : asConst(getSelectedTorrents())) + torrent->setFirstLastPiecePriority(enabled); +} + void TransferListWidget::setSelectedAutoTMMEnabled(const bool enabled) const { for (BitTorrent::Torrent *const torrent : asConst(getSelectedTorrents())) @@ -818,6 +848,8 @@ void TransferListWidget::displayListMenu(const QPoint &) connect(actionTopQueuePos, &QAction::triggered, this, &TransferListWidget::topQueuePosSelectedTorrents); auto *actionBottomQueuePos = new QAction(UIThemeManager::instance()->getIcon("go-bottom"), tr("Move to bottom", "i.e. Move to bottom of the queue"), listMenu); connect(actionBottomQueuePos, &QAction::triggered, this, &TransferListWidget::bottomQueuePosSelectedTorrents); + auto *actionSetTorrentPath = new QAction(UIThemeManager::instance()->getIcon("inode-directory"), tr("Set location..."), listMenu); + connect(actionSetTorrentPath, &QAction::triggered, this, &TransferListWidget::setSelectedTorrentsLocation); auto *actionForceRecheck = new QAction(UIThemeManager::instance()->getIcon("document-edit-verify"), tr("Force recheck"), listMenu); connect(actionForceRecheck, &QAction::triggered, this, &TransferListWidget::recheckSelectedTorrents); auto *actionForceReannounce = new QAction(UIThemeManager::instance()->getIcon("document-edit-verify"), tr("Force reannounce"), listMenu); @@ -836,6 +868,12 @@ void TransferListWidget::displayListMenu(const QPoint &) connect(actionSuperSeedingMode, &QAction::triggered, this, &TransferListWidget::setSelectedTorrentsSuperSeeding); auto *actionRename = new QAction(UIThemeManager::instance()->getIcon("edit-rename"), tr("Rename..."), listMenu); connect(actionRename, &QAction::triggered, this, &TransferListWidget::renameSelectedTorrent); + auto *actionSequentialDownload = new TriStateAction(tr("Download in sequential order"), listMenu); + connect(actionSequentialDownload, &QAction::triggered, this, &TransferListWidget::setSelectedTorrentsSequentialDownload); + auto *actionFirstLastPiecePrio = new TriStateAction(tr("Download first and last pieces first"), listMenu); + connect(actionFirstLastPiecePrio, &QAction::triggered, this, &TransferListWidget::setSelectedFirstLastPiecePrio); + auto *actionAutoTMM = new TriStateAction(tr("Automatic Torrent Management"), listMenu); + connect(actionAutoTMM, &QAction::triggered, this, &TransferListWidget::setSelectedAutoTMMEnabled); auto *actionEditTracker = new QAction(UIThemeManager::instance()->getIcon("edit-rename"), tr("Edit trackers..."), listMenu); connect(actionEditTracker, &QAction::triggered, this, &TransferListWidget::editTorrentTrackers); // End of actions @@ -848,6 +886,8 @@ void TransferListWidget::displayListMenu(const QPoint &) bool sequentialDownloadMode = false, prioritizeFirstLast = false; bool oneHasMetadata = false, oneNotSeed = false; bool allSameCategory = true; + bool allSameAutoTMM = true; + bool firstAutoTMM = false; QString firstCategory; bool first = true; TagSet tagsInAny; @@ -871,6 +911,7 @@ void TransferListWidget::displayListMenu(const QPoint &) if (first) { + firstAutoTMM = torrent->isAutoTMMEnabled(); tagsInAll = torrentTags; } else @@ -878,6 +919,9 @@ void TransferListWidget::displayListMenu(const QPoint &) tagsInAll.intersect(torrentTags); } + if (firstAutoTMM != torrent->isAutoTMMEnabled()) + allSameAutoTMM = false; + if (torrent->hasMetadata()) oneHasMetadata = true; if (!torrent->isSeed()) @@ -937,7 +981,7 @@ void TransferListWidget::displayListMenu(const QPoint &) if (oneHasMetadata && oneNotSeed && !allSameSequentialDownloadMode && !allSamePrioFirstlast && !allSameSuperSeeding && !allSameCategory - && needsStart && needsForce && needsPause && needsPreview + && needsStart && needsForce && needsPause && needsPreview && !allSameAutoTMM && hasInfohashV1 && hasInfohashV2) { break; @@ -953,10 +997,36 @@ void TransferListWidget::displayListMenu(const QPoint &) listMenu->addSeparator(); listMenu->addAction(actionDelete); listMenu->addSeparator(); + listMenu->addAction(actionSetTorrentPath); if (selectedIndexes.size() == 1) listMenu->addAction(actionRename); listMenu->addAction(actionEditTracker); + // Category Menu + QStringList categories = BitTorrent::Session::instance()->categories(); + std::sort(categories.begin(), categories.end(), Utils::Compare::NaturalLessThan()); + + QMenu *categoryMenu = listMenu->addMenu(UIThemeManager::instance()->getIcon("view-categories"), tr("Category")); + + categoryMenu->addAction(UIThemeManager::instance()->getIcon("list-add"), tr("New...", "New category...") + , this, &TransferListWidget::askNewCategoryForSelection); + categoryMenu->addAction(UIThemeManager::instance()->getIcon("edit-clear"), tr("Reset", "Reset category") + , this, [this]() { setSelectionCategory(""); }); + categoryMenu->addSeparator(); + + for (const QString &category : asConst(categories)) + { + const QString escapedCategory = QString(category).replace('&', "&&"); // avoid '&' becomes accelerator key + QAction *categoryAction = categoryMenu->addAction(UIThemeManager::instance()->getIcon("inode-directory"), escapedCategory + , this, [this, category]() { setSelectionCategory(category); }); + + if (allSameCategory && (category == firstCategory)) + { + categoryAction->setCheckable(true); + categoryAction->setChecked(true); + } + } + // Tag Menu QStringList tags(BitTorrent::Session::instance()->tags().values()); std::sort(tags.begin(), tags.end(), Utils::Compare::NaturalLessThan()); @@ -995,6 +1065,11 @@ void TransferListWidget::displayListMenu(const QPoint &) tagsMenu->addAction(action); } + actionAutoTMM->setCheckState(allSameAutoTMM + ? (firstAutoTMM ? Qt::Checked : Qt::Unchecked) + : Qt::PartiallyChecked); + listMenu->addAction(actionAutoTMM); + listMenu->addSeparator(); listMenu->addAction(actionTorrentOptions); if (!oneNotSeed && oneHasMetadata) @@ -1012,6 +1087,17 @@ void TransferListWidget::displayListMenu(const QPoint &) addedPreviewAction = true; } if (oneNotSeed) + { + actionSequentialDownload->setCheckState(allSameSequentialDownloadMode + ? (sequentialDownloadMode ? Qt::Checked : Qt::Unchecked) + : Qt::PartiallyChecked); + listMenu->addAction(actionSequentialDownload); + + actionFirstLastPiecePrio->setCheckState(allSamePrioFirstlast + ? (prioritizeFirstLast ? Qt::Checked : Qt::Unchecked) + : Qt::PartiallyChecked); + listMenu->addAction(actionFirstLastPiecePrio); + } addedPreviewAction = true; if (addedPreviewAction) diff --git a/src/gui/transferlistwidget.h b/src/gui/transferlistwidget.h index 22c3314bb..7b83fbabf 100644 --- a/src/gui/transferlistwidget.h +++ b/src/gui/transferlistwidget.h @@ -65,6 +65,7 @@ public slots: void addSelectionTag(const QString &tag); void removeSelectionTag(const QString &tag); void clearSelectionTags(); + void setSelectedTorrentsLocation(); void pauseAllTorrents(); void resumeAllTorrents(); void startSelectedTorrents(); @@ -90,7 +91,7 @@ public slots: void setTorrentOptions(); void previewSelectedTorrents(); void hideQueuePosColumn(bool hide); - void displayDLHoSMenu(const QPoint&); + void displayDLHoSMenu(const QPoint &); void applyNameFilter(const QString &name); void applyStatusFilter(int f); void applyCategoryFilter(const QString &category); @@ -108,6 +109,8 @@ private slots: void displayListMenu(const QPoint &); void currentChanged(const QModelIndex ¤t, const QModelIndex&) override; void setSelectedTorrentsSuperSeeding(bool enabled) const; + void setSelectedTorrentsSequentialDownload(bool enabled) const; + void setSelectedFirstLastPiecePrio(bool enabled) const; void setSelectedAutoTMMEnabled(bool enabled) const; void askNewCategoryForSelection(); void saveSettings();