Browse Source

Merge pull request #7490 from glassez/category-path

Allow to set explicit save path for Category from GUI
adaptive-webui-19844
Vladimir Golovnev 7 years ago committed by GitHub
parent
commit
66a6674cbc
  1. 5
      src/base/bittorrent/session.cpp
  2. 2
      src/base/bittorrent/session.h
  3. 3
      src/gui/CMakeLists.txt
  4. 2
      src/gui/addnewtorrentdialog.cpp
  5. 2
      src/gui/categoryfiltermodel.cpp
  6. 84
      src/gui/categoryfilterwidget.cpp
  7. 3
      src/gui/categoryfilterwidget.h
  8. 5
      src/gui/gui.pri
  9. 2
      src/gui/rss/automatedrssdownloader.cpp
  10. 125
      src/gui/torrentcategorydialog.cpp
  11. 58
      src/gui/torrentcategorydialog.h
  12. 129
      src/gui/torrentcategorydialog.ui
  13. 25
      src/gui/transferlistwidget.cpp
  14. 2
      src/webui/btjson.cpp

5
src/base/bittorrent/session.cpp

@ -699,9 +699,9 @@ QStringList Session::expandCategory(const QString &category)
return result; 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 QString Session::categorySavePath(const QString &categoryName) const
@ -747,6 +747,7 @@ bool Session::editCategory(const QString &name, const QString &savePath)
if (categorySavePath(name) == savePath) return false; if (categorySavePath(name) == savePath) return false;
m_categories[name] = savePath; m_categories[name] = savePath;
m_storedCategories = map_cast(m_categories);
if (isDisableAutoTMMWhenCategorySavePathChanged()) { if (isDisableAutoTMMWhenCategorySavePathChanged()) {
foreach (TorrentHandle *const torrent, torrents()) foreach (TorrentHandle *const torrent, torrents())
if (torrent->category() == name) if (torrent->category() == name)

2
src/base/bittorrent/session.h

@ -260,7 +260,7 @@ namespace BitTorrent
// returns category itself and all top level categories // returns category itself and all top level categories
static QStringList expandCategory(const QString &category); static QStringList expandCategory(const QString &category);
QStringList categories() const; const QStringMap &categories() const;
QString categorySavePath(const QString &categoryName) const; QString categorySavePath(const QString &categoryName) const;
bool addCategory(const QString &name, const QString &savePath = ""); bool addCategory(const QString &name, const QString &savePath = "");
bool editCategory(const QString &name, const QString &savePath); bool editCategory(const QString &name, const QString &savePath);

3
src/gui/CMakeLists.txt

@ -59,6 +59,7 @@ statusbar.h
tagfiltermodel.h tagfiltermodel.h
tagfilterproxymodel.h tagfilterproxymodel.h
tagfilterwidget.h tagfilterwidget.h
torrentcategorydialog.h
torrentcontentfiltermodel.h torrentcontentfiltermodel.h
torrentcontentmodel.h torrentcontentmodel.h
torrentcontentmodelfile.h torrentcontentmodelfile.h
@ -102,6 +103,7 @@ statusbar.cpp
tagfiltermodel.cpp tagfiltermodel.cpp
tagfilterproxymodel.cpp tagfilterproxymodel.cpp
tagfilterwidget.cpp tagfilterwidget.cpp
torrentcategorydialog.cpp
torrentcontentfiltermodel.cpp torrentcontentfiltermodel.cpp
torrentcontentmodel.cpp torrentcontentmodel.cpp
torrentcontentmodelfile.cpp torrentcontentmodelfile.cpp
@ -144,6 +146,7 @@ addnewtorrentdialog.ui
autoexpandabledialog.ui autoexpandabledialog.ui
statsdialog.ui statsdialog.ui
optionsdlg.ui optionsdlg.ui
torrentcategorydialog.ui
torrentcreatordlg.ui torrentcreatordlg.ui
shutdownconfirmdlg.ui shutdownconfirmdlg.ui
) )

2
src/gui/addnewtorrentdialog.cpp

@ -121,7 +121,7 @@ AddNewTorrentDialog::AddNewTorrentDialog(const BitTorrent::AddTorrentParams &inP
ui->doNotDeleteTorrentCheckBox->setVisible(TorrentFileGuard::autoDeleteMode() != TorrentFileGuard::Never); ui->doNotDeleteTorrentCheckBox->setVisible(TorrentFileGuard::autoDeleteMode() != TorrentFileGuard::Never);
// Load categories // Load categories
QStringList categories = session->categories(); QStringList categories = session->categories().keys();
std::sort(categories.begin(), categories.end(), Utils::String::naturalCompareCaseInsensitive); std::sort(categories.begin(), categories.end(), Utils::String::naturalCompareCaseInsensitive);
QString defaultCategory = settings()->loadValue(KEY_DEFAULTCATEGORY).toString(); QString defaultCategory = settings()->loadValue(KEY_DEFAULTCATEGORY).toString();

2
src/gui/categoryfiltermodel.cpp

@ -405,7 +405,7 @@ void CategoryFilterModel::populate()
, [](Torrent *torrent) { return torrent->category().isEmpty(); }))); , [](Torrent *torrent) { return torrent->category().isEmpty(); })));
using Torrent = BitTorrent::TorrentHandle; using Torrent = BitTorrent::TorrentHandle;
foreach (const QString &category, session->categories()) { foreach (const QString &category, session->categories().keys()) {
if (m_isSubcategoriesEnabled) { if (m_isSubcategoriesEnabled) {
CategoryModelItem *parent = m_rootItem; CategoryModelItem *parent = m_rootItem;
foreach (const QString &subcat, session->expandCategory(category)) { foreach (const QString &subcat, session->expandCategory(category)) {

84
src/gui/categoryfilterwidget.cpp

@ -36,10 +36,10 @@
#include "base/bittorrent/session.h" #include "base/bittorrent/session.h"
#include "base/utils/misc.h" #include "base/utils/misc.h"
#include "autoexpandabledialog.h"
#include "categoryfiltermodel.h" #include "categoryfiltermodel.h"
#include "categoryfilterproxymodel.h" #include "categoryfilterproxymodel.h"
#include "guiiconprovider.h" #include "guiiconprovider.h"
#include "torrentcategorydialog.h"
namespace namespace
{ {
@ -81,12 +81,12 @@ CategoryFilterWidget::CategoryFilterWidget(QWidget *parent)
sortByColumn(0, Qt::AscendingOrder); sortByColumn(0, Qt::AscendingOrder);
setCurrentIndex(model()->index(0, 0)); setCurrentIndex(model()->index(0, 0));
connect(this, SIGNAL(collapsed(QModelIndex)), SLOT(callUpdateGeometry())); connect(this, &QTreeView::collapsed, this, &CategoryFilterWidget::callUpdateGeometry);
connect(this, SIGNAL(expanded(QModelIndex)), SLOT(callUpdateGeometry())); connect(this, &QTreeView::expanded, this, &CategoryFilterWidget::callUpdateGeometry);
connect(this, SIGNAL(customContextMenuRequested(QPoint)), SLOT(showMenu(QPoint))); connect(this, &QWidget::customContextMenuRequested, this, &CategoryFilterWidget::showMenu);
connect(selectionModel(), SIGNAL(currentRowChanged(QModelIndex,QModelIndex)) connect(selectionModel(), &QItemSelectionModel::currentRowChanged
, SLOT(onCurrentRowChanged(QModelIndex,QModelIndex))); , this, &CategoryFilterWidget::onCurrentRowChanged);
connect(model(), SIGNAL(modelReset()), SLOT(callUpdateGeometry())); connect(model(), &QAbstractItemModel::modelReset, this, &CategoryFilterWidget::callUpdateGeometry);
} }
QString CategoryFilterWidget::currentCategory() const QString CategoryFilterWidget::currentCategory() const
@ -113,7 +113,7 @@ void CategoryFilterWidget::showMenu(QPoint)
QAction *addAct = menu.addAction( QAction *addAct = menu.addAction(
GuiIconProvider::instance()->getIcon("list-add") GuiIconProvider::instance()->getIcon("list-add")
, tr("Add category...")); , tr("Add category..."));
connect(addAct, SIGNAL(triggered()), SLOT(addCategory())); connect(addAct, &QAction::triggered, this, &CategoryFilterWidget::addCategory);
auto selectedRows = selectionModel()->selectedRows(); auto selectedRows = selectionModel()->selectedRows();
if (!selectedRows.empty() && !CategoryFilterModel::isSpecialItem(selectedRows.first())) { if (!selectedRows.empty() && !CategoryFilterModel::isSpecialItem(selectedRows.first())) {
@ -121,36 +121,41 @@ void CategoryFilterWidget::showMenu(QPoint)
QAction *addSubAct = menu.addAction( QAction *addSubAct = menu.addAction(
GuiIconProvider::instance()->getIcon("list-add") GuiIconProvider::instance()->getIcon("list-add")
, tr("Add subcategory...")); , tr("Add subcategory..."));
connect(addSubAct, SIGNAL(triggered()), SLOT(addSubcategory())); 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( QAction *removeAct = menu.addAction(
GuiIconProvider::instance()->getIcon("list-remove") GuiIconProvider::instance()->getIcon("list-remove")
, tr("Remove category")); , tr("Remove category"));
connect(removeAct, SIGNAL(triggered()), SLOT(removeCategory())); connect(removeAct, &QAction::triggered, this, &CategoryFilterWidget::removeCategory);
} }
QAction *removeUnusedAct = menu.addAction( QAction *removeUnusedAct = menu.addAction(
GuiIconProvider::instance()->getIcon("list-remove") GuiIconProvider::instance()->getIcon("list-remove")
, tr("Remove unused categories")); , tr("Remove unused categories"));
connect(removeUnusedAct, SIGNAL(triggered()), SLOT(removeUnusedCategories())); connect(removeUnusedAct, &QAction::triggered, this, &CategoryFilterWidget::removeUnusedCategories);
menu.addSeparator(); menu.addSeparator();
QAction *startAct = menu.addAction( QAction *startAct = menu.addAction(
GuiIconProvider::instance()->getIcon("media-playback-start") GuiIconProvider::instance()->getIcon("media-playback-start")
, tr("Resume torrents")); , tr("Resume torrents"));
connect(startAct, SIGNAL(triggered()), SIGNAL(actionResumeTorrentsTriggered())); connect(startAct, &QAction::triggered, this, &CategoryFilterWidget::actionResumeTorrentsTriggered);
QAction *pauseAct = menu.addAction( QAction *pauseAct = menu.addAction(
GuiIconProvider::instance()->getIcon("media-playback-pause") GuiIconProvider::instance()->getIcon("media-playback-pause")
, tr("Pause torrents")); , tr("Pause torrents"));
connect(pauseAct, SIGNAL(triggered()), SIGNAL(actionPauseTorrentsTriggered())); connect(pauseAct, &QAction::triggered, this, &CategoryFilterWidget::actionPauseTorrentsTriggered);
QAction *deleteTorrentsAct = menu.addAction( QAction *deleteTorrentsAct = menu.addAction(
GuiIconProvider::instance()->getIcon("edit-delete") GuiIconProvider::instance()->getIcon("edit-delete")
, tr("Delete torrents")); , tr("Delete torrents"));
connect(deleteTorrentsAct, SIGNAL(triggered()), SIGNAL(actionDeleteTorrentsTriggered())); connect(deleteTorrentsAct, &QAction::triggered, this, &CategoryFilterWidget::actionDeleteTorrentsTriggered);
menu.exec(QCursor::pos()); menu.exec(QCursor::pos());
} }
@ -167,6 +172,9 @@ void CategoryFilterWidget::callUpdateGeometry()
QSize CategoryFilterWidget::sizeHint() const QSize CategoryFilterWidget::sizeHint() const
{ {
// The sizeHint must depend on viewportSizeHint,
// otherwise widget will not correctly adjust the
// size when subcategories are used.
const QSize viewportSize {viewportSizeHint()}; const QSize viewportSize {viewportSizeHint()};
return { return {
viewportSize.width(), viewportSize.width(),
@ -196,53 +204,19 @@ void CategoryFilterWidget::rowsInserted(const QModelIndex &parent, int start, in
updateGeometry(); 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() void CategoryFilterWidget::addCategory()
{ {
const QString category = askCategoryName(); TorrentCategoryDialog::createCategory(this);
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);
} }
void CategoryFilterWidget::addSubcategory() void CategoryFilterWidget::addSubcategory()
{ {
const QString subcat = askCategoryName(); TorrentCategoryDialog::createCategory(this, currentCategory());
if (subcat.isEmpty()) return; }
const QString category = QString(QStringLiteral("%1/%2")).arg(currentCategory()).arg(subcat);
if (BitTorrent::Session::instance()->categories().contains(category)) void CategoryFilterWidget::editCategory()
QMessageBox::warning(this, tr("Category exists") {
, tr("Subcategory name already exists in selected category.")); TorrentCategoryDialog::editCategory(this, currentCategory());
else
BitTorrent::Session::instance()->addCategory(category);
} }
void CategoryFilterWidget::removeCategory() void CategoryFilterWidget::removeCategory()
@ -258,7 +232,7 @@ void CategoryFilterWidget::removeCategory()
void CategoryFilterWidget::removeUnusedCategories() void CategoryFilterWidget::removeUnusedCategories()
{ {
auto session = BitTorrent::Session::instance(); auto session = BitTorrent::Session::instance();
foreach (const QString &category, session->categories()) foreach (const QString &category, session->categories().keys())
if (model()->data(static_cast<CategoryFilterProxyModel *>(model())->index(category), Qt::UserRole) == 0) if (model()->data(static_cast<CategoryFilterProxyModel *>(model())->index(category), Qt::UserRole) == 0)
session->removeCategory(category); session->removeCategory(category);
updateGeometry(); updateGeometry();

3
src/gui/categoryfilterwidget.h

@ -31,6 +31,7 @@
class CategoryFilterWidget: public QTreeView class CategoryFilterWidget: public QTreeView
{ {
Q_OBJECT Q_OBJECT
Q_DISABLE_COPY(CategoryFilterWidget)
public: public:
explicit CategoryFilterWidget(QWidget *parent = nullptr); explicit CategoryFilterWidget(QWidget *parent = nullptr);
@ -49,6 +50,7 @@ private slots:
void callUpdateGeometry(); void callUpdateGeometry();
void addCategory(); void addCategory();
void addSubcategory(); void addSubcategory();
void editCategory();
void removeCategory(); void removeCategory();
void removeUnusedCategories(); void removeUnusedCategories();
@ -56,7 +58,6 @@ private:
QSize sizeHint() const override; QSize sizeHint() const override;
QSize minimumSizeHint() const override; QSize minimumSizeHint() const override;
void rowsInserted(const QModelIndex &parent, int start, int end) override; void rowsInserted(const QModelIndex &parent, int start, int end) override;
QString askCategoryName();
int m_defaultIndentation; int m_defaultIndentation;
}; };

5
src/gui/gui.pri

@ -11,6 +11,7 @@ HEADERS += \
$$PWD/transferlistdelegate.h \ $$PWD/transferlistdelegate.h \
$$PWD/transferlistfilterswidget.h \ $$PWD/transferlistfilterswidget.h \
$$PWD/transferlistsortmodel.h \ $$PWD/transferlistsortmodel.h \
$$PWD/torrentcategorydialog.h \
$$PWD/torrentcontentmodel.h \ $$PWD/torrentcontentmodel.h \
$$PWD/torrentcontentmodelitem.h \ $$PWD/torrentcontentmodelitem.h \
$$PWD/torrentcontentmodelfolder.h \ $$PWD/torrentcontentmodelfolder.h \
@ -69,6 +70,7 @@ SOURCES += \
$$PWD/transferlistsortmodel.cpp \ $$PWD/transferlistsortmodel.cpp \
$$PWD/transferlistdelegate.cpp \ $$PWD/transferlistdelegate.cpp \
$$PWD/transferlistfilterswidget.cpp \ $$PWD/transferlistfilterswidget.cpp \
$$PWD/torrentcategorydialog.cpp \
$$PWD/torrentcontentmodel.cpp \ $$PWD/torrentcontentmodel.cpp \
$$PWD/torrentcontentmodelitem.cpp \ $$PWD/torrentcontentmodelitem.cpp \
$$PWD/torrentcontentmodelfolder.cpp \ $$PWD/torrentcontentmodelfolder.cpp \
@ -149,6 +151,7 @@ FORMS += \
$$PWD/cookiesdialog.ui \ $$PWD/cookiesdialog.ui \
$$PWD/banlistoptions.ui \ $$PWD/banlistoptions.ui \
$$PWD/rss/rsswidget.ui \ $$PWD/rss/rsswidget.ui \
$$PWD/rss/automatedrssdownloader.ui $$PWD/rss/automatedrssdownloader.ui \
$$PWD/torrentcategorydialog.ui
RESOURCES += $$PWD/about.qrc RESOURCES += $$PWD/about.qrc

2
src/gui/rss/automatedrssdownloader.cpp

@ -305,7 +305,7 @@ void AutomatedRssDownloader::clearRuleDefinitionBox()
void AutomatedRssDownloader::initCategoryCombobox() void AutomatedRssDownloader::initCategoryCombobox()
{ {
// Load torrent categories // 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); std::sort(categories.begin(), categories.end(), Utils::String::naturalCompareCaseInsensitive);
m_ui->comboCategory->addItem(""); m_ui->comboCategory->addItem("");
m_ui->comboCategory->addItems(categories); m_ui->comboCategory->addItems(categories);

125
src/gui/torrentcategorydialog.cpp

@ -0,0 +1,125 @@
/*
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2017 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
* 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 <QMap>
#include <QMessageBox>
#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);
}

58
src/gui/torrentcategorydialog.h

@ -0,0 +1,58 @@
/*
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2017 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
* 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 <QDialog>
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;
};

129
src/gui/torrentcategorydialog.ui

@ -0,0 +1,129 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>TorrentCategoryDialog</class>
<widget class="QDialog" name="TorrentCategoryDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>100</height>
</rect>
</property>
<property name="windowTitle">
<string>Torrent Category Properties</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<spacer name="verticalSpacer_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QLabel" name="labelCategoryName">
<property name="text">
<string>Name:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="textCategoryName"/>
</item>
<item row="1" column="0">
<widget class="QLabel" name="labelSavePath">
<property name="text">
<string>Save path:</string>
</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>
</property>
</widget>
</item>
</layout>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>FileSystemPathComboEdit</class>
<extends>QWidget</extends>
<header>fspathedit.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>TorrentCategoryDialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>TorrentCategoryDialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
</ui>

25
src/gui/transferlistwidget.cpp

@ -56,6 +56,7 @@
#include "optionsdlg.h" #include "optionsdlg.h"
#include "previewselectdialog.h" #include "previewselectdialog.h"
#include "speedlimitdlg.h" #include "speedlimitdlg.h"
#include "torrentcategorydialog.h"
#include "torrentmodel.h" #include "torrentmodel.h"
#include "transferlistdelegate.h" #include "transferlistdelegate.h"
#include "transferlistsortmodel.h" #include "transferlistsortmodel.h"
@ -729,25 +730,9 @@ void TransferListWidget::setSelectedAutoTMMEnabled(bool enabled) const
void TransferListWidget::askNewCategoryForSelection() void TransferListWidget::askNewCategoryForSelection()
{ {
// Ask for category const QString newCategoryName = TorrentCategoryDialog::createCategory(this);
bool ok; if (!newCategoryName.isEmpty())
bool invalid; setSelectionCategory(newCategoryName);
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);
} }
void TransferListWidget::askAddTagsForSelection() void TransferListWidget::askAddTagsForSelection()
@ -998,7 +983,7 @@ void TransferListWidget::displayListMenu(const QPoint&)
if (selectedIndexes.size() == 1) if (selectedIndexes.size() == 1)
listMenu.addAction(&actionRename); listMenu.addAction(&actionRename);
// Category Menu // Category Menu
QStringList categories = BitTorrent::Session::instance()->categories(); QStringList categories = BitTorrent::Session::instance()->categories().keys();
std::sort(categories.begin(), categories.end(), Utils::String::naturalCompareCaseInsensitive); std::sort(categories.begin(), categories.end(), Utils::String::naturalCompareCaseInsensitive);
QList<QAction*> categoryActions; QList<QAction*> categoryActions;
QMenu *categoryMenu = listMenu.addMenu(GuiIconProvider::instance()->getIcon("view-categories"), tr("Category")); QMenu *categoryMenu = listMenu.addMenu(GuiIconProvider::instance()->getIcon("view-categories"), tr("Category"));

2
src/webui/btjson.cpp

@ -651,7 +651,7 @@ QByteArray btjson::getSyncMainData(int acceptedResponseId, QVariantMap &lastData
data["torrents"] = torrents; data["torrents"] = torrents;
QVariantList categories; QVariantList categories;
foreach (const QString &category, session->categories()) foreach (const QString &category, session->categories().keys())
categories << category; categories << category;
data["categories"] = categories; data["categories"] = categories;

Loading…
Cancel
Save