diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt index 818f3f975..10f3dd6fb 100644 --- a/src/gui/CMakeLists.txt +++ b/src/gui/CMakeLists.txt @@ -34,6 +34,7 @@ advancedsettings.h autoexpandabledialog.h banlistoptions.h categoryfiltermodel.h +categoryfilterproxymodel.h categoryfilterwidget.h cookiesdialog.h cookiesmodel.h @@ -78,6 +79,7 @@ advancedsettings.cpp autoexpandabledialog.cpp banlistoptions.cpp categoryfiltermodel.cpp +categoryfilterproxymodel.cpp categoryfilterwidget.cpp cookiesdialog.cpp cookiesmodel.cpp diff --git a/src/gui/addnewtorrentdialog.cpp b/src/gui/addnewtorrentdialog.cpp index 56ba3f8ff..609ea2b8e 100644 --- a/src/gui/addnewtorrentdialog.cpp +++ b/src/gui/addnewtorrentdialog.cpp @@ -135,7 +135,6 @@ AddNewTorrentDialog::AddNewTorrentDialog(const BitTorrent::AddTorrentParams &inP if (category != defaultCategory && category != m_torrentParams.category) ui->categoryComboBox->addItem(category); - ui->categoryComboBox->model()->sort(0); ui->contentTreeView->header()->setSortIndicator(0, Qt::AscendingOrder); loadState(); // Signal / slots diff --git a/src/gui/categoryfiltermodel.cpp b/src/gui/categoryfiltermodel.cpp index 715bdd7af..ca6048d83 100644 --- a/src/gui/categoryfiltermodel.cpp +++ b/src/gui/categoryfiltermodel.cpp @@ -139,8 +139,7 @@ public: item->m_parent = this; m_children[uid] = item; - auto pos = std::lower_bound(m_childUids.begin(), m_childUids.end(), uid); - m_childUids.insert(pos, uid); + m_childUids.append(uid); m_torrentsCount += item->torrentsCount(); } @@ -195,6 +194,13 @@ CategoryFilterModel::~CategoryFilterModel() delete m_rootItem; } +bool CategoryFilterModel::isSpecialItem(const QModelIndex &index) +{ + // the first two items at first level are special items: + // 'All' and 'Uncategorized' + return (!index.parent().isValid() && (index.row() <= 1)); +} + int CategoryFilterModel::columnCount(const QModelIndex &) const { return 1; @@ -307,11 +313,10 @@ void CategoryFilterModel::categoryAdded(const QString &categoryName) parent = findItem(expanded[expanded.count() - 2]); } - auto item = new CategoryModelItem( - parent, m_isSubcategoriesEnabled ? shortName(categoryName) : categoryName); - - QModelIndex i = index(item); - beginInsertRows(i.parent(), i.row(), i.row()); + int row = parent->childCount(); + beginInsertRows(index(parent), row, row); + new CategoryModelItem( + parent, m_isSubcategoriesEnabled ? shortName(categoryName) : categoryName); endInsertRows(); } diff --git a/src/gui/categoryfiltermodel.h b/src/gui/categoryfiltermodel.h index df92eab57..bf186faae 100644 --- a/src/gui/categoryfiltermodel.h +++ b/src/gui/categoryfiltermodel.h @@ -48,6 +48,8 @@ public: explicit CategoryFilterModel(QObject *parent = nullptr); ~CategoryFilterModel(); + static bool isSpecialItem(const QModelIndex &index); + int columnCount(const QModelIndex &parent = QModelIndex()) const override; QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; Qt::ItemFlags flags(const QModelIndex &index) const override; diff --git a/src/gui/categoryfilterproxymodel.cpp b/src/gui/categoryfilterproxymodel.cpp new file mode 100644 index 000000000..4862aa238 --- /dev/null +++ b/src/gui/categoryfilterproxymodel.cpp @@ -0,0 +1,57 @@ +/* + * Bittorrent Client using Qt and libtorrent. + * Copyright (C) 2017 Frédéric Brière + * + * 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 "categoryfilterproxymodel.h" + +#include "base/utils/string.h" +#include "categoryfiltermodel.h" + +CategoryFilterProxyModel::CategoryFilterProxyModel(QObject *parent) + : QSortFilterProxyModel(parent) +{ +} + +QModelIndex CategoryFilterProxyModel::index(const QString &categoryName) const +{ + return mapFromSource(static_cast(sourceModel())->index(categoryName)); +} + +QString CategoryFilterProxyModel::categoryName(const QModelIndex &index) const +{ + return static_cast(sourceModel())->categoryName(mapToSource(index)); +} + +bool CategoryFilterProxyModel::lessThan(const QModelIndex &left, const QModelIndex &right) const +{ + // "All" and "Uncategorized" must be left in place + if (CategoryFilterModel::isSpecialItem(left) || CategoryFilterModel::isSpecialItem(right)) + return left.row() < right.row(); + else + return Utils::String::naturalCompareCaseInsensitive( + left.data().toString(), right.data().toString()); +} diff --git a/src/gui/categoryfilterproxymodel.h b/src/gui/categoryfilterproxymodel.h new file mode 100644 index 000000000..1b1b2484a --- /dev/null +++ b/src/gui/categoryfilterproxymodel.h @@ -0,0 +1,48 @@ +/* + * Bittorrent Client using Qt and libtorrent. + * Copyright (C) 2017 Frédéric Brière + * + * 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. + */ + +#ifndef CATEGORYFILTERPROXYMODEL_H +#define CATEGORYFILTERPROXYMODEL_H + +#include +#include + +class CategoryFilterProxyModel: public QSortFilterProxyModel +{ +public: + explicit CategoryFilterProxyModel(QObject *parent = nullptr); + + // CategoryFilterModel methods which we need to relay + QModelIndex index(const QString &categoryName) const; + QString categoryName(const QModelIndex &index) const; + +protected: + bool lessThan(const QModelIndex &left, const QModelIndex &right) const override; +}; + +#endif // CATEGORYFILTERPROXYMODEL_H diff --git a/src/gui/categoryfilterwidget.cpp b/src/gui/categoryfilterwidget.cpp index 0da279b25..3fe43ab4b 100644 --- a/src/gui/categoryfilterwidget.cpp +++ b/src/gui/categoryfilterwidget.cpp @@ -38,11 +38,12 @@ #include "base/utils/misc.h" #include "autoexpandabledialog.h" #include "categoryfiltermodel.h" +#include "categoryfilterproxymodel.h" #include "guiiconprovider.h" namespace { - QString getCategoryFilter(const CategoryFilterModel *const model, const QModelIndex &index) + QString getCategoryFilter(const CategoryFilterProxyModel *const model, const QModelIndex &index) { QString categoryFilter; // Defaults to All if (index.isValid()) { @@ -54,19 +55,15 @@ namespace return categoryFilter; } - - bool isSpecialItem(const QModelIndex &index) - { - // the first two items at first level are special items: - // 'All' and 'Uncategorized' - return (!index.parent().isValid() && (index.row() <= 1)); - } } CategoryFilterWidget::CategoryFilterWidget(QWidget *parent) : QTreeView(parent) { - setModel(new CategoryFilterModel(this)); + CategoryFilterProxyModel *proxyModel = new CategoryFilterProxyModel(this); + proxyModel->setSortCaseSensitivity(Qt::CaseInsensitive); + proxyModel->setSourceModel(new CategoryFilterModel(this)); + setModel(proxyModel); setFrameShape(QFrame::NoFrame); setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); @@ -78,6 +75,7 @@ CategoryFilterWidget::CategoryFilterWidget(QWidget *parent) setAttribute(Qt::WA_MacShowFocusRect, false); #endif setContextMenuPolicy(Qt::CustomContextMenu); + sortByColumn(0, Qt::AscendingOrder); setCurrentIndex(model()->index(0, 0)); connect(this, SIGNAL(collapsed(QModelIndex)), SLOT(callUpdateGeometry())); @@ -95,14 +93,14 @@ QString CategoryFilterWidget::currentCategory() const if (!selectedRows.isEmpty()) current = selectedRows.first(); - return getCategoryFilter(static_cast(model()), current); + return getCategoryFilter(static_cast(model()), current); } void CategoryFilterWidget::onCurrentRowChanged(const QModelIndex ¤t, const QModelIndex &previous) { Q_UNUSED(previous); - emit categoryChanged(getCategoryFilter(static_cast(model()), current)); + emit categoryChanged(getCategoryFilter(static_cast(model()), current)); } void CategoryFilterWidget::showMenu(QPoint) @@ -115,7 +113,7 @@ void CategoryFilterWidget::showMenu(QPoint) connect(addAct, SIGNAL(triggered()), SLOT(addCategory())); auto selectedRows = selectionModel()->selectedRows(); - if (!selectedRows.empty() && !isSpecialItem(selectedRows.first())) { + if (!selectedRows.empty() && !CategoryFilterModel::isSpecialItem(selectedRows.first())) { if (BitTorrent::Session::instance()->isSubcategoriesEnabled()) { QAction *addSubAct = menu.addAction( GuiIconProvider::instance()->getIcon("list-add") @@ -238,9 +236,9 @@ void CategoryFilterWidget::addSubcategory() void CategoryFilterWidget::removeCategory() { auto selectedRows = selectionModel()->selectedRows(); - if (!selectedRows.empty() && !isSpecialItem(selectedRows.first())) { + if (!selectedRows.empty() && !CategoryFilterModel::isSpecialItem(selectedRows.first())) { BitTorrent::Session::instance()->removeCategory( - static_cast(model())->categoryName(selectedRows.first())); + static_cast(model())->categoryName(selectedRows.first())); updateGeometry(); } } @@ -249,7 +247,7 @@ void CategoryFilterWidget::removeUnusedCategories() { auto session = BitTorrent::Session::instance(); foreach (const QString &category, session->categories()) - if (model()->data(static_cast(model())->index(category), Qt::UserRole) == 0) + if (model()->data(static_cast(model())->index(category), Qt::UserRole) == 0) session->removeCategory(category); updateGeometry(); } diff --git a/src/gui/gui.pri b/src/gui/gui.pri index 85673e271..f9f3fdf65 100644 --- a/src/gui/gui.pri +++ b/src/gui/gui.pri @@ -50,6 +50,7 @@ HEADERS += \ $$PWD/cookiesmodel.h \ $$PWD/cookiesdialog.h \ $$PWD/categoryfiltermodel.h \ + $$PWD/categoryfilterproxymodel.h \ $$PWD/categoryfilterwidget.h \ $$PWD/banlistoptions.h \ $$PWD/rss/rsswidget.h \ @@ -100,6 +101,7 @@ SOURCES += \ $$PWD/cookiesmodel.cpp \ $$PWD/cookiesdialog.cpp \ $$PWD/categoryfiltermodel.cpp \ + $$PWD/categoryfilterproxymodel.cpp \ $$PWD/categoryfilterwidget.cpp \ $$PWD/banlistoptions.cpp \ $$PWD/rss/rsswidget.cpp \ diff --git a/src/gui/transferlistsortmodel.cpp b/src/gui/transferlistsortmodel.cpp index 423742979..4fedf7787 100644 --- a/src/gui/transferlistsortmodel.cpp +++ b/src/gui/transferlistsortmodel.cpp @@ -74,6 +74,7 @@ void TransferListSortModel::disableTrackerFilter() bool TransferListSortModel::lessThan(const QModelIndex &left, const QModelIndex &right) const { switch (sortColumn()) { + case TorrentModel::TR_CATEGORY: case TorrentModel::TR_NAME: { QVariant vL = left.data(); QVariant vR = right.data();