diff --git a/src/base/bittorrent/session.cpp b/src/base/bittorrent/session.cpp index 8803194a7..b091fbf0b 100644 --- a/src/base/bittorrent/session.cpp +++ b/src/base/bittorrent/session.cpp @@ -698,9 +698,9 @@ QStringList Session::expandCategory(const QString &category) return result; } -QStringList Session::categories() const +const QStringMap &Session::categories() const { - return m_categories.keys(); + return m_categories; } QString Session::categorySavePath(const QString &categoryName) const @@ -746,6 +746,7 @@ bool Session::editCategory(const QString &name, const QString &savePath) if (categorySavePath(name) == savePath) return false; m_categories[name] = savePath; + m_storedCategories = map_cast(m_categories); if (isDisableAutoTMMWhenCategorySavePathChanged()) { foreach (TorrentHandle *const torrent, torrents()) if (torrent->category() == name) diff --git a/src/base/bittorrent/session.h b/src/base/bittorrent/session.h index 8ba11b640..11f4f0fdd 100644 --- a/src/base/bittorrent/session.h +++ b/src/base/bittorrent/session.h @@ -251,7 +251,7 @@ namespace BitTorrent // returns category itself and all top level categories static QStringList expandCategory(const QString &category); - QStringList categories() const; + const QStringMap &categories() const; QString categorySavePath(const QString &categoryName) const; bool addCategory(const QString &name, const QString &savePath = ""); bool editCategory(const QString &name, const QString &savePath); diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt index c17fee4b8..3e6fd21b8 100644 --- a/src/gui/CMakeLists.txt +++ b/src/gui/CMakeLists.txt @@ -59,6 +59,7 @@ statusbar.h tagfiltermodel.h tagfilterproxymodel.h tagfilterwidget.h +torrentcategorydialog.h torrentcontentfiltermodel.h torrentcontentmodel.h torrentcontentmodelfile.h @@ -102,6 +103,7 @@ statusbar.cpp tagfiltermodel.cpp tagfilterproxymodel.cpp tagfilterwidget.cpp +torrentcategorydialog.cpp torrentcontentfiltermodel.cpp torrentcontentmodel.cpp torrentcontentmodelfile.cpp @@ -144,6 +146,7 @@ addnewtorrentdialog.ui autoexpandabledialog.ui statsdialog.ui optionsdlg.ui +torrentcategorydialog.ui torrentcreatordlg.ui shutdownconfirmdlg.ui ) diff --git a/src/gui/addnewtorrentdialog.cpp b/src/gui/addnewtorrentdialog.cpp index 509c2b6e9..af6cfcba8 100644 --- a/src/gui/addnewtorrentdialog.cpp +++ b/src/gui/addnewtorrentdialog.cpp @@ -121,7 +121,7 @@ AddNewTorrentDialog::AddNewTorrentDialog(const BitTorrent::AddTorrentParams &inP ui->doNotDeleteTorrentCheckBox->setVisible(TorrentFileGuard::autoDeleteMode() != TorrentFileGuard::Never); // Load categories - QStringList categories = session->categories(); + QStringList categories = session->categories().keys(); std::sort(categories.begin(), categories.end(), Utils::String::naturalCompareCaseInsensitive); QString defaultCategory = settings()->loadValue(KEY_DEFAULTCATEGORY).toString(); diff --git a/src/gui/categoryfiltermodel.cpp b/src/gui/categoryfiltermodel.cpp index ca6048d83..d2626535e 100644 --- a/src/gui/categoryfiltermodel.cpp +++ b/src/gui/categoryfiltermodel.cpp @@ -405,7 +405,7 @@ void CategoryFilterModel::populate() , [](Torrent *torrent) { return torrent->category().isEmpty(); }))); using Torrent = BitTorrent::TorrentHandle; - foreach (const QString &category, session->categories()) { + foreach (const QString &category, session->categories().keys()) { if (m_isSubcategoriesEnabled) { CategoryModelItem *parent = m_rootItem; foreach (const QString &subcat, session->expandCategory(category)) { diff --git a/src/gui/categoryfilterwidget.cpp b/src/gui/categoryfilterwidget.cpp index abec735cc..52669d221 100644 --- a/src/gui/categoryfilterwidget.cpp +++ b/src/gui/categoryfilterwidget.cpp @@ -36,10 +36,10 @@ #include "base/bittorrent/session.h" #include "base/utils/misc.h" -#include "autoexpandabledialog.h" #include "categoryfiltermodel.h" #include "categoryfilterproxymodel.h" #include "guiiconprovider.h" +#include "torrentcategorydialog.h" namespace { @@ -124,6 +124,11 @@ void CategoryFilterWidget::showMenu(QPoint) connect(addSubAct, &QAction::triggered, this, &CategoryFilterWidget::addSubcategory); } + QAction *editAct = menu.addAction( + GuiIconProvider::instance()->getIcon("document-edit") + , tr("Edit category...")); + connect(editAct, &QAction::triggered, this, &CategoryFilterWidget::editCategory); + QAction *removeAct = menu.addAction( GuiIconProvider::instance()->getIcon("list-remove") , tr("Remove category")); @@ -199,53 +204,19 @@ void CategoryFilterWidget::rowsInserted(const QModelIndex &parent, int start, in updateGeometry(); } -QString CategoryFilterWidget::askCategoryName() -{ - bool ok; - QString category = ""; - bool invalid; - do { - invalid = false; - category = AutoExpandableDialog::getText( - this, tr("New Category"), tr("Category:"), QLineEdit::Normal, category, &ok); - if (ok && !category.isEmpty()) { - if (!BitTorrent::Session::isValidCategoryName(category)) { - QMessageBox::warning( - this, tr("Invalid category name") - , tr("Category name must not contain '\\'.\n" - "Category name must not start/end with '/'.\n" - "Category name must not contain '//' sequence.")); - invalid = true; - } - } - } while (invalid); - - return ok ? category : QString(); -} - void CategoryFilterWidget::addCategory() { - const QString category = askCategoryName(); - if (category.isEmpty()) return; - - if (BitTorrent::Session::instance()->categories().contains(category)) - QMessageBox::warning(this, tr("Category exists"), tr("Category name already exists.")); - else - BitTorrent::Session::instance()->addCategory(category); + TorrentCategoryDialog::createCategory(this); } void CategoryFilterWidget::addSubcategory() { - const QString subcat = askCategoryName(); - if (subcat.isEmpty()) return; - - const QString category = QString(QStringLiteral("%1/%2")).arg(currentCategory()).arg(subcat); + TorrentCategoryDialog::createCategory(this, currentCategory()); +} - if (BitTorrent::Session::instance()->categories().contains(category)) - QMessageBox::warning(this, tr("Category exists") - , tr("Subcategory name already exists in selected category.")); - else - BitTorrent::Session::instance()->addCategory(category); +void CategoryFilterWidget::editCategory() +{ + TorrentCategoryDialog::editCategory(this, currentCategory()); } void CategoryFilterWidget::removeCategory() @@ -261,7 +232,7 @@ void CategoryFilterWidget::removeCategory() void CategoryFilterWidget::removeUnusedCategories() { auto session = BitTorrent::Session::instance(); - foreach (const QString &category, session->categories()) + foreach (const QString &category, session->categories().keys()) if (model()->data(static_cast(model())->index(category), Qt::UserRole) == 0) session->removeCategory(category); updateGeometry(); diff --git a/src/gui/categoryfilterwidget.h b/src/gui/categoryfilterwidget.h index c03477895..f9387beb2 100644 --- a/src/gui/categoryfilterwidget.h +++ b/src/gui/categoryfilterwidget.h @@ -50,6 +50,7 @@ private slots: void callUpdateGeometry(); void addCategory(); void addSubcategory(); + void editCategory(); void removeCategory(); void removeUnusedCategories(); @@ -57,7 +58,6 @@ private: QSize sizeHint() const override; QSize minimumSizeHint() const override; void rowsInserted(const QModelIndex &parent, int start, int end) override; - QString askCategoryName(); int m_defaultIndentation; }; diff --git a/src/gui/gui.pri b/src/gui/gui.pri index 81233251e..09c825673 100644 --- a/src/gui/gui.pri +++ b/src/gui/gui.pri @@ -11,6 +11,7 @@ HEADERS += \ $$PWD/transferlistdelegate.h \ $$PWD/transferlistfilterswidget.h \ $$PWD/transferlistsortmodel.h \ + $$PWD/torrentcategorydialog.h \ $$PWD/torrentcontentmodel.h \ $$PWD/torrentcontentmodelitem.h \ $$PWD/torrentcontentmodelfolder.h \ @@ -69,6 +70,7 @@ SOURCES += \ $$PWD/transferlistsortmodel.cpp \ $$PWD/transferlistdelegate.cpp \ $$PWD/transferlistfilterswidget.cpp \ + $$PWD/torrentcategorydialog.cpp \ $$PWD/torrentcontentmodel.cpp \ $$PWD/torrentcontentmodelitem.cpp \ $$PWD/torrentcontentmodelfolder.cpp \ @@ -149,6 +151,7 @@ FORMS += \ $$PWD/cookiesdialog.ui \ $$PWD/banlistoptions.ui \ $$PWD/rss/rsswidget.ui \ - $$PWD/rss/automatedrssdownloader.ui + $$PWD/rss/automatedrssdownloader.ui \ + $$PWD/torrentcategorydialog.ui RESOURCES += $$PWD/about.qrc diff --git a/src/gui/rss/automatedrssdownloader.cpp b/src/gui/rss/automatedrssdownloader.cpp index de6f928fd..a97ebcbb9 100644 --- a/src/gui/rss/automatedrssdownloader.cpp +++ b/src/gui/rss/automatedrssdownloader.cpp @@ -305,7 +305,7 @@ void AutomatedRssDownloader::clearRuleDefinitionBox() void AutomatedRssDownloader::initCategoryCombobox() { // Load torrent categories - QStringList categories = BitTorrent::Session::instance()->categories(); + QStringList categories = BitTorrent::Session::instance()->categories().keys(); std::sort(categories.begin(), categories.end(), Utils::String::naturalCompareCaseInsensitive); m_ui->comboCategory->addItem(""); m_ui->comboCategory->addItems(categories); diff --git a/src/gui/torrentcategorydialog.cpp b/src/gui/torrentcategorydialog.cpp new file mode 100644 index 000000000..099696371 --- /dev/null +++ b/src/gui/torrentcategorydialog.cpp @@ -0,0 +1,125 @@ +/* + * Bittorrent Client using Qt and libtorrent. + * Copyright (C) 2017 Vladimir Golovnev + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * In addition, as a special exception, the copyright holders give permission to + * link this program with the OpenSSL project's "OpenSSL" library (or with + * modified versions of it that use the same license as the "OpenSSL" library), + * and distribute the linked executables. You must obey the GNU General Public + * License in all respects for all of the code used other than "OpenSSL". If you + * modify file(s), you may extend this exception to your version of the file(s), + * but you are not obligated to do so. If you do not wish to do so, delete this + * exception statement from your version. + */ + +#include "torrentcategorydialog.h" + +#include +#include + +#include "base/bittorrent/session.h" +#include "ui_torrentcategorydialog.h" + +TorrentCategoryDialog::TorrentCategoryDialog(QWidget *parent) + : QDialog {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")); +} + +TorrentCategoryDialog::~TorrentCategoryDialog() +{ + delete m_ui; +} + +QString TorrentCategoryDialog::createCategory(QWidget *parent, const QString &parentCategoryName) +{ + using BitTorrent::Session; + + QString newCategoryName {parentCategoryName}; + if (!newCategoryName.isEmpty()) + newCategoryName += QLatin1Char('/'); + newCategoryName += tr("New Category"); + + TorrentCategoryDialog dialog(parent); + dialog.setCategoryName(newCategoryName); + while (dialog.exec() == TorrentCategoryDialog::Accepted) { + newCategoryName = dialog.categoryName(); + + if (!BitTorrent::Session::isValidCategoryName(newCategoryName)) { + QMessageBox::critical( + parent, tr("Invalid category name") + , tr("Category name cannot contain '\\'.\n" + "Category name cannot start/end with '/'.\n" + "Category name cannot contain '//' sequence.")); + } + else if (BitTorrent::Session::instance()->categories().contains(newCategoryName)) { + QMessageBox::critical( + parent, tr("Category creation error") + , tr("Category with the given name already exists.\n" + "Please choose a different name and try again.")); + } + else { + Session::instance()->addCategory(newCategoryName, dialog.savePath()); + return newCategoryName; + } + } + + return {}; +} + +void TorrentCategoryDialog::editCategory(QWidget *parent, const QString &categoryName) +{ + using BitTorrent::Session; + + Q_ASSERT(Session::instance()->categories().contains(categoryName)); + + TorrentCategoryDialog dialog(parent); + dialog.setCategoryNameEditable(false); + dialog.setCategoryName(categoryName); + dialog.setSavePath(Session::instance()->categories()[categoryName]); + if (dialog.exec() == TorrentCategoryDialog::Accepted) { + Session::instance()->editCategory(categoryName, dialog.savePath()); + } +} + +void TorrentCategoryDialog::setCategoryNameEditable(bool editable) +{ + m_ui->textCategoryName->setEnabled(editable); +} + +QString TorrentCategoryDialog::categoryName() const +{ + return m_ui->textCategoryName->text(); +} + +void TorrentCategoryDialog::setCategoryName(const QString &categoryName) +{ + m_ui->textCategoryName->setText(categoryName); +} + +QString TorrentCategoryDialog::savePath() const +{ + return m_ui->comboSavePath->selectedPath(); +} + +void TorrentCategoryDialog::setSavePath(const QString &savePath) +{ + m_ui->comboSavePath->setSelectedPath(savePath); +} diff --git a/src/gui/torrentcategorydialog.h b/src/gui/torrentcategorydialog.h new file mode 100644 index 000000000..9addebb8f --- /dev/null +++ b/src/gui/torrentcategorydialog.h @@ -0,0 +1,58 @@ +/* + * Bittorrent Client using Qt and libtorrent. + * Copyright (C) 2017 Vladimir Golovnev + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * In addition, as a special exception, the copyright holders give permission to + * link this program with the OpenSSL project's "OpenSSL" library (or with + * modified versions of it that use the same license as the "OpenSSL" library), + * and distribute the linked executables. You must obey the GNU General Public + * License in all respects for all of the code used other than "OpenSSL". If you + * modify file(s), you may extend this exception to your version of the file(s), + * but you are not obligated to do so. If you do not wish to do so, delete this + * exception statement from your version. + */ + +#pragma once + +#include + +namespace Ui +{ + class TorrentCategoryDialog; +} + +class TorrentCategoryDialog : public QDialog +{ + Q_OBJECT + Q_DISABLE_COPY(TorrentCategoryDialog) + +public: + static QString createCategory(QWidget *parent, const QString &parentCategoryName = QString()); + static void editCategory(QWidget *parent, const QString &categoryName); + + explicit TorrentCategoryDialog(QWidget *parent = nullptr); + ~TorrentCategoryDialog() override; + + void setCategoryNameEditable(bool editable); + QString categoryName() const; + void setCategoryName(const QString &categoryName); + QString savePath() const; + void setSavePath(const QString &savePath); + +private: + Ui::TorrentCategoryDialog *m_ui; +}; diff --git a/src/gui/torrentcategorydialog.ui b/src/gui/torrentcategorydialog.ui new file mode 100644 index 000000000..c7ed910b3 --- /dev/null +++ b/src/gui/torrentcategorydialog.ui @@ -0,0 +1,129 @@ + + + TorrentCategoryDialog + + + + 0 + 0 + 400 + 100 + + + + Torrent Category Properties + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + Name: + + + + + + + + + + Save path: + + + + + + + + 0 + 0 + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + FileSystemPathComboEdit + QWidget +
fspathedit.h
+ 1 +
+
+ + + + buttonBox + accepted() + TorrentCategoryDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + TorrentCategoryDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + +
diff --git a/src/gui/transferlistwidget.cpp b/src/gui/transferlistwidget.cpp index 84e919574..7fd1c3201 100644 --- a/src/gui/transferlistwidget.cpp +++ b/src/gui/transferlistwidget.cpp @@ -56,6 +56,7 @@ #include "optionsdlg.h" #include "previewselect.h" #include "speedlimitdlg.h" +#include "torrentcategorydialog.h" #include "torrentmodel.h" #include "transferlistdelegate.h" #include "transferlistsortmodel.h" @@ -729,25 +730,9 @@ void TransferListWidget::setSelectedAutoTMMEnabled(bool enabled) const void TransferListWidget::askNewCategoryForSelection() { - // Ask for category - bool ok; - bool invalid; - do { - invalid = false; - const QString category = AutoExpandableDialog::getText(this, tr("New Category"), tr("Category:"), QLineEdit::Normal, "", &ok).trimmed(); - if (ok && !category.isEmpty()) { - if (!BitTorrent::Session::isValidCategoryName(category)) { - QMessageBox::warning(this, tr("Invalid category name"), - tr("Category name must not contain '\\'.\n" - "Category name must not start/end with '/'.\n" - "Category name must not contain '//' sequence.")); - invalid = true; - } - else { - setSelectionCategory(category); - } - } - } while(invalid); + const QString newCategoryName = TorrentCategoryDialog::createCategory(this); + if (!newCategoryName.isEmpty()) + setSelectionCategory(newCategoryName); } void TransferListWidget::askAddTagsForSelection() @@ -998,7 +983,7 @@ void TransferListWidget::displayListMenu(const QPoint&) if (selectedIndexes.size() == 1) listMenu.addAction(&actionRename); // Category Menu - QStringList categories = BitTorrent::Session::instance()->categories(); + QStringList categories = BitTorrent::Session::instance()->categories().keys(); std::sort(categories.begin(), categories.end(), Utils::String::naturalCompareCaseInsensitive); QList categoryActions; QMenu *categoryMenu = listMenu.addMenu(GuiIconProvider::instance()->getIcon("view-categories"), tr("Category")); diff --git a/src/webui/btjson.cpp b/src/webui/btjson.cpp index 5dd6a14b1..dc9028b76 100644 --- a/src/webui/btjson.cpp +++ b/src/webui/btjson.cpp @@ -651,7 +651,7 @@ QByteArray btjson::getSyncMainData(int acceptedResponseId, QVariantMap &lastData data["torrents"] = torrents; QVariantList categories; - foreach (const QString &category, session->categories()) + foreach (const QString &category, session->categories().keys()) categories << category; data["categories"] = categories;