From c37d90bf6d08a41009ddb30b09fd1f550fe2500f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Bri=C3=A8re?= Date: Fri, 28 Apr 2017 18:49:06 -0400 Subject: [PATCH] Properly sort categories case-insensitively in filter widget. Closes #6708. --- src/gui/CMakeLists.txt | 2 + src/gui/categoryfiltermodel.cpp | 12 +++--- src/gui/categoryfilterproxymodel.cpp | 57 ++++++++++++++++++++++++++++ src/gui/categoryfilterproxymodel.h | 48 +++++++++++++++++++++++ src/gui/categoryfilterwidget.cpp | 17 ++++++--- src/gui/gui.pri | 2 + 6 files changed, 125 insertions(+), 13 deletions(-) create mode 100644 src/gui/categoryfilterproxymodel.cpp create mode 100644 src/gui/categoryfilterproxymodel.h diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt index 7a444a8e5..1f2794b54 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 @@ -76,6 +77,7 @@ advancedsettings.cpp autoexpandabledialog.cpp banlistoptions.cpp categoryfiltermodel.cpp +categoryfilterproxymodel.cpp categoryfilterwidget.cpp cookiesdialog.cpp cookiesmodel.cpp diff --git a/src/gui/categoryfiltermodel.cpp b/src/gui/categoryfiltermodel.cpp index 0968761ae..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(); } @@ -314,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/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 bf7952322..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()) { @@ -59,7 +60,10 @@ namespace 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); @@ -71,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())); @@ -88,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) @@ -233,7 +238,7 @@ void CategoryFilterWidget::removeCategory() auto selectedRows = selectionModel()->selectedRows(); if (!selectedRows.empty() && !CategoryFilterModel::isSpecialItem(selectedRows.first())) { BitTorrent::Session::instance()->removeCategory( - static_cast(model())->categoryName(selectedRows.first())); + static_cast(model())->categoryName(selectedRows.first())); updateGeometry(); } } @@ -242,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 5cb464ed7..8b28a54de 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 \ @@ -98,6 +99,7 @@ SOURCES += \ $$PWD/cookiesmodel.cpp \ $$PWD/cookiesdialog.cpp \ $$PWD/categoryfiltermodel.cpp \ + $$PWD/categoryfilterproxymodel.cpp \ $$PWD/categoryfilterwidget.cpp \ $$PWD/banlistoptions.cpp \ $$PWD/rss/rsswidget.cpp \